18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * lpass-ipq806x.c -- ALSA SoC CPU DAI driver for QTi LPASS 68c2ecf20Sopenharmony_ci * Splited out the IPQ8064 soc specific from lpass-cpu.c 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <sound/pcm.h> 178c2ecf20Sopenharmony_ci#include <sound/soc.h> 188c2ecf20Sopenharmony_ci#include <sound/soc-dai.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "lpass-lpaif-reg.h" 218c2ecf20Sopenharmony_ci#include "lpass.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cienum lpaif_i2s_ports { 248c2ecf20Sopenharmony_ci IPQ806X_LPAIF_I2S_PORT_CODEC_SPK, 258c2ecf20Sopenharmony_ci IPQ806X_LPAIF_I2S_PORT_CODEC_MIC, 268c2ecf20Sopenharmony_ci IPQ806X_LPAIF_I2S_PORT_SEC_SPK, 278c2ecf20Sopenharmony_ci IPQ806X_LPAIF_I2S_PORT_SEC_MIC, 288c2ecf20Sopenharmony_ci IPQ806X_LPAIF_I2S_PORT_MI2S, 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cienum lpaif_dma_channels { 328c2ecf20Sopenharmony_ci IPQ806X_LPAIF_RDMA_CHAN_MI2S, 338c2ecf20Sopenharmony_ci IPQ806X_LPAIF_RDMA_CHAN_PCM0, 348c2ecf20Sopenharmony_ci IPQ806X_LPAIF_RDMA_CHAN_PCM1, 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = { 388c2ecf20Sopenharmony_ci .id = IPQ806X_LPAIF_I2S_PORT_MI2S, 398c2ecf20Sopenharmony_ci .playback = { 408c2ecf20Sopenharmony_ci .stream_name = "lpass-cpu-playback", 418c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16 | 428c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24 | 438c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32, 448c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000 | 458c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_16000 | 468c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_32000 | 478c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_48000 | 488c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_96000, 498c2ecf20Sopenharmony_ci .rate_min = 8000, 508c2ecf20Sopenharmony_ci .rate_max = 96000, 518c2ecf20Sopenharmony_ci .channels_min = 1, 528c2ecf20Sopenharmony_ci .channels_max = 8, 538c2ecf20Sopenharmony_ci }, 548c2ecf20Sopenharmony_ci .probe = &asoc_qcom_lpass_cpu_dai_probe, 558c2ecf20Sopenharmony_ci .ops = &asoc_qcom_lpass_cpu_dai_ops, 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int ipq806x_lpass_init(struct platform_device *pdev) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct lpass_data *drvdata = platform_get_drvdata(pdev); 618c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 628c2ecf20Sopenharmony_ci int ret; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci drvdata->ahbix_clk = devm_clk_get(dev, "ahbix-clk"); 658c2ecf20Sopenharmony_ci if (IS_ERR(drvdata->ahbix_clk)) { 668c2ecf20Sopenharmony_ci dev_err(dev, "error getting ahbix-clk: %ld\n", 678c2ecf20Sopenharmony_ci PTR_ERR(drvdata->ahbix_clk)); 688c2ecf20Sopenharmony_ci ret = PTR_ERR(drvdata->ahbix_clk); 698c2ecf20Sopenharmony_ci goto err_ahbix_clk; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY); 738c2ecf20Sopenharmony_ci if (ret) { 748c2ecf20Sopenharmony_ci dev_err(dev, "error setting rate on ahbix_clk: %d\n", ret); 758c2ecf20Sopenharmony_ci goto err_ahbix_clk; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci dev_dbg(dev, "set ahbix_clk rate to %lu\n", 788c2ecf20Sopenharmony_ci clk_get_rate(drvdata->ahbix_clk)); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci ret = clk_prepare_enable(drvdata->ahbix_clk); 818c2ecf20Sopenharmony_ci if (ret) { 828c2ecf20Sopenharmony_ci dev_err(dev, "error enabling ahbix_clk: %d\n", ret); 838c2ecf20Sopenharmony_ci goto err_ahbix_clk; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cierr_ahbix_clk: 878c2ecf20Sopenharmony_ci return ret; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int ipq806x_lpass_exit(struct platform_device *pdev) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct lpass_data *drvdata = platform_get_drvdata(pdev); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci clk_disable_unprepare(drvdata->ahbix_clk); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir, unsigned int dai_id) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci if (dir == SNDRV_PCM_STREAM_PLAYBACK) 1028c2ecf20Sopenharmony_ci return IPQ806X_LPAIF_RDMA_CHAN_MI2S; 1038c2ecf20Sopenharmony_ci else /* Capture currently not implemented */ 1048c2ecf20Sopenharmony_ci return -EINVAL; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic struct lpass_variant ipq806x_data = { 1138c2ecf20Sopenharmony_ci .i2sctrl_reg_base = 0x0010, 1148c2ecf20Sopenharmony_ci .i2sctrl_reg_stride = 0x04, 1158c2ecf20Sopenharmony_ci .i2s_ports = 5, 1168c2ecf20Sopenharmony_ci .irq_reg_base = 0x3000, 1178c2ecf20Sopenharmony_ci .irq_reg_stride = 0x1000, 1188c2ecf20Sopenharmony_ci .irq_ports = 3, 1198c2ecf20Sopenharmony_ci .rdma_reg_base = 0x6000, 1208c2ecf20Sopenharmony_ci .rdma_reg_stride = 0x1000, 1218c2ecf20Sopenharmony_ci .rdma_channels = 4, 1228c2ecf20Sopenharmony_ci .wrdma_reg_base = 0xB000, 1238c2ecf20Sopenharmony_ci .wrdma_reg_stride = 0x1000, 1248c2ecf20Sopenharmony_ci .wrdma_channel_start = 5, 1258c2ecf20Sopenharmony_ci .wrdma_channels = 4, 1268c2ecf20Sopenharmony_ci .loopback = REG_FIELD_ID(0x0010, 15, 15, 5, 0x4), 1278c2ecf20Sopenharmony_ci .spken = REG_FIELD_ID(0x0010, 14, 14, 5, 0x4), 1288c2ecf20Sopenharmony_ci .spkmode = REG_FIELD_ID(0x0010, 10, 13, 5, 0x4), 1298c2ecf20Sopenharmony_ci .spkmono = REG_FIELD_ID(0x0010, 9, 9, 5, 0x4), 1308c2ecf20Sopenharmony_ci .micen = REG_FIELD_ID(0x0010, 8, 8, 5, 0x4), 1318c2ecf20Sopenharmony_ci .micmode = REG_FIELD_ID(0x0010, 4, 7, 5, 0x4), 1328c2ecf20Sopenharmony_ci .micmono = REG_FIELD_ID(0x0010, 3, 3, 5, 0x4), 1338c2ecf20Sopenharmony_ci .wssrc = REG_FIELD_ID(0x0010, 2, 2, 5, 0x4), 1348c2ecf20Sopenharmony_ci .bitwidth = REG_FIELD_ID(0x0010, 0, 1, 5, 0x4), 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci .rdma_dyncclk = REG_FIELD_ID(0x6000, 12, 12, 4, 0x1000), 1378c2ecf20Sopenharmony_ci .rdma_bursten = REG_FIELD_ID(0x6000, 11, 11, 4, 0x1000), 1388c2ecf20Sopenharmony_ci .rdma_wpscnt = REG_FIELD_ID(0x6000, 8, 10, 4, 0x1000), 1398c2ecf20Sopenharmony_ci .rdma_intf = REG_FIELD_ID(0x6000, 4, 7, 4, 0x1000), 1408c2ecf20Sopenharmony_ci .rdma_fifowm = REG_FIELD_ID(0x6000, 1, 3, 4, 0x1000), 1418c2ecf20Sopenharmony_ci .rdma_enable = REG_FIELD_ID(0x6000, 0, 0, 4, 0x1000), 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci .wrdma_dyncclk = REG_FIELD_ID(0xB000, 12, 12, 4, 0x1000), 1448c2ecf20Sopenharmony_ci .wrdma_bursten = REG_FIELD_ID(0xB000, 11, 11, 4, 0x1000), 1458c2ecf20Sopenharmony_ci .wrdma_wpscnt = REG_FIELD_ID(0xB000, 8, 10, 4, 0x1000), 1468c2ecf20Sopenharmony_ci .wrdma_intf = REG_FIELD_ID(0xB000, 4, 7, 4, 0x1000), 1478c2ecf20Sopenharmony_ci .wrdma_fifowm = REG_FIELD_ID(0xB000, 1, 3, 4, 0x1000), 1488c2ecf20Sopenharmony_ci .wrdma_enable = REG_FIELD_ID(0xB000, 0, 0, 4, 0x1000), 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci .dai_driver = &ipq806x_lpass_cpu_dai_driver, 1518c2ecf20Sopenharmony_ci .num_dai = 1, 1528c2ecf20Sopenharmony_ci .dai_osr_clk_names = (const char *[]) { 1538c2ecf20Sopenharmony_ci "mi2s-osr-clk", 1548c2ecf20Sopenharmony_ci }, 1558c2ecf20Sopenharmony_ci .dai_bit_clk_names = (const char *[]) { 1568c2ecf20Sopenharmony_ci "mi2s-bit-clk", 1578c2ecf20Sopenharmony_ci }, 1588c2ecf20Sopenharmony_ci .init = ipq806x_lpass_init, 1598c2ecf20Sopenharmony_ci .exit = ipq806x_lpass_exit, 1608c2ecf20Sopenharmony_ci .alloc_dma_channel = ipq806x_lpass_alloc_dma_channel, 1618c2ecf20Sopenharmony_ci .free_dma_channel = ipq806x_lpass_free_dma_channel, 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic const struct of_device_id ipq806x_lpass_cpu_device_id[] = { 1658c2ecf20Sopenharmony_ci { .compatible = "qcom,lpass-cpu", .data = &ipq806x_data }, 1668c2ecf20Sopenharmony_ci {} 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ipq806x_lpass_cpu_device_id); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic struct platform_driver ipq806x_lpass_cpu_platform_driver = { 1718c2ecf20Sopenharmony_ci .driver = { 1728c2ecf20Sopenharmony_ci .name = "lpass-cpu", 1738c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(ipq806x_lpass_cpu_device_id), 1748c2ecf20Sopenharmony_ci }, 1758c2ecf20Sopenharmony_ci .probe = asoc_qcom_lpass_cpu_platform_probe, 1768c2ecf20Sopenharmony_ci .remove = asoc_qcom_lpass_cpu_platform_remove, 1778c2ecf20Sopenharmony_ci}; 1788c2ecf20Sopenharmony_cimodule_platform_driver(ipq806x_lpass_cpu_platform_driver); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("QTi LPASS CPU Driver"); 1818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 182