^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * lpass-ipq806x.c -- ALSA SoC CPU DAI driver for QTi LPASS
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Splited out the IPQ8064 soc specific from lpass-cpu.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/clk.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/of.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <sound/pcm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <sound/soc.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <sound/soc-dai.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include "lpass-lpaif-reg.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include "lpass.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) enum lpaif_i2s_ports {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) IPQ806X_LPAIF_I2S_PORT_CODEC_SPK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) IPQ806X_LPAIF_I2S_PORT_CODEC_MIC,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) IPQ806X_LPAIF_I2S_PORT_SEC_SPK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) IPQ806X_LPAIF_I2S_PORT_SEC_MIC,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) IPQ806X_LPAIF_I2S_PORT_MI2S,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) enum lpaif_dma_channels {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) IPQ806X_LPAIF_RDMA_CHAN_MI2S,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) IPQ806X_LPAIF_RDMA_CHAN_PCM0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) IPQ806X_LPAIF_RDMA_CHAN_PCM1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) .id = IPQ806X_LPAIF_I2S_PORT_MI2S,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) .playback = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) .stream_name = "lpass-cpu-playback",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) .formats = SNDRV_PCM_FMTBIT_S16 |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) SNDRV_PCM_FMTBIT_S24 |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) SNDRV_PCM_FMTBIT_S32,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) .rates = SNDRV_PCM_RATE_8000 |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) SNDRV_PCM_RATE_16000 |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) SNDRV_PCM_RATE_32000 |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) SNDRV_PCM_RATE_48000 |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) SNDRV_PCM_RATE_96000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) .rate_min = 8000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) .rate_max = 96000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) .channels_min = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) .channels_max = 8,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) .probe = &asoc_qcom_lpass_cpu_dai_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) .ops = &asoc_qcom_lpass_cpu_dai_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) static int ipq806x_lpass_init(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) struct lpass_data *drvdata = platform_get_drvdata(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) struct device *dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) drvdata->ahbix_clk = devm_clk_get(dev, "ahbix-clk");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) if (IS_ERR(drvdata->ahbix_clk)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) dev_err(dev, "error getting ahbix-clk: %ld\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) PTR_ERR(drvdata->ahbix_clk));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) ret = PTR_ERR(drvdata->ahbix_clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) goto err_ahbix_clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) dev_err(dev, "error setting rate on ahbix_clk: %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) goto err_ahbix_clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) dev_dbg(dev, "set ahbix_clk rate to %lu\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) clk_get_rate(drvdata->ahbix_clk));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) ret = clk_prepare_enable(drvdata->ahbix_clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) dev_err(dev, "error enabling ahbix_clk: %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) goto err_ahbix_clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) err_ahbix_clk:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) static int ipq806x_lpass_exit(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) struct lpass_data *drvdata = platform_get_drvdata(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) clk_disable_unprepare(drvdata->ahbix_clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir, unsigned int dai_id)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) if (dir == SNDRV_PCM_STREAM_PLAYBACK)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) return IPQ806X_LPAIF_RDMA_CHAN_MI2S;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) else /* Capture currently not implemented */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) static struct lpass_variant ipq806x_data = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) .i2sctrl_reg_base = 0x0010,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) .i2sctrl_reg_stride = 0x04,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) .i2s_ports = 5,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) .irq_reg_base = 0x3000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) .irq_reg_stride = 0x1000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) .irq_ports = 3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) .rdma_reg_base = 0x6000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) .rdma_reg_stride = 0x1000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) .rdma_channels = 4,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) .wrdma_reg_base = 0xB000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) .wrdma_reg_stride = 0x1000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) .wrdma_channel_start = 5,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) .wrdma_channels = 4,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) .loopback = REG_FIELD_ID(0x0010, 15, 15, 5, 0x4),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) .spken = REG_FIELD_ID(0x0010, 14, 14, 5, 0x4),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) .spkmode = REG_FIELD_ID(0x0010, 10, 13, 5, 0x4),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) .spkmono = REG_FIELD_ID(0x0010, 9, 9, 5, 0x4),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) .micen = REG_FIELD_ID(0x0010, 8, 8, 5, 0x4),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) .micmode = REG_FIELD_ID(0x0010, 4, 7, 5, 0x4),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) .micmono = REG_FIELD_ID(0x0010, 3, 3, 5, 0x4),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) .wssrc = REG_FIELD_ID(0x0010, 2, 2, 5, 0x4),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) .bitwidth = REG_FIELD_ID(0x0010, 0, 1, 5, 0x4),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) .rdma_dyncclk = REG_FIELD_ID(0x6000, 12, 12, 4, 0x1000),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) .rdma_bursten = REG_FIELD_ID(0x6000, 11, 11, 4, 0x1000),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) .rdma_wpscnt = REG_FIELD_ID(0x6000, 8, 10, 4, 0x1000),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) .rdma_intf = REG_FIELD_ID(0x6000, 4, 7, 4, 0x1000),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) .rdma_fifowm = REG_FIELD_ID(0x6000, 1, 3, 4, 0x1000),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) .rdma_enable = REG_FIELD_ID(0x6000, 0, 0, 4, 0x1000),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) .wrdma_dyncclk = REG_FIELD_ID(0xB000, 12, 12, 4, 0x1000),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) .wrdma_bursten = REG_FIELD_ID(0xB000, 11, 11, 4, 0x1000),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) .wrdma_wpscnt = REG_FIELD_ID(0xB000, 8, 10, 4, 0x1000),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) .wrdma_intf = REG_FIELD_ID(0xB000, 4, 7, 4, 0x1000),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) .wrdma_fifowm = REG_FIELD_ID(0xB000, 1, 3, 4, 0x1000),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) .wrdma_enable = REG_FIELD_ID(0xB000, 0, 0, 4, 0x1000),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) .dai_driver = &ipq806x_lpass_cpu_dai_driver,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) .num_dai = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) .dai_osr_clk_names = (const char *[]) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) "mi2s-osr-clk",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) .dai_bit_clk_names = (const char *[]) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) "mi2s-bit-clk",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) .init = ipq806x_lpass_init,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) .exit = ipq806x_lpass_exit,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) .alloc_dma_channel = ipq806x_lpass_alloc_dma_channel,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) .free_dma_channel = ipq806x_lpass_free_dma_channel,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) static const struct of_device_id ipq806x_lpass_cpu_device_id[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) { .compatible = "qcom,lpass-cpu", .data = &ipq806x_data },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) {}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) MODULE_DEVICE_TABLE(of, ipq806x_lpass_cpu_device_id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) static struct platform_driver ipq806x_lpass_cpu_platform_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) .name = "lpass-cpu",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) .of_match_table = of_match_ptr(ipq806x_lpass_cpu_device_id),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) .probe = asoc_qcom_lpass_cpu_platform_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) .remove = asoc_qcom_lpass_cpu_platform_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) module_platform_driver(ipq806x_lpass_cpu_platform_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) MODULE_DESCRIPTION("QTi LPASS CPU Driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) MODULE_LICENSE("GPL v2");