1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2021 The Linux Foundation. All rights reserved.
5 * lpass-cdc-dma.c -- ALSA SoC CDC DMA CPU DAI driver for QTi LPASS
8 #include <dt-bindings/sound/qcom,lpass.h>
10 #include <linux/module.h>
11 #include <linux/export.h>
12 #include <sound/soc.h>
13 #include <sound/soc-dai.h>
15 #include "lpass-lpaif-reg.h"
18 #define CODEC_MEM_HZ_NORMAL 153600000
20 enum codec_dma_interfaces
{
21 LPASS_CDC_DMA_INTERFACE1
= 1,
22 LPASS_CDC_DMA_INTERFACE2
,
23 LPASS_CDC_DMA_INTERFACE3
,
24 LPASS_CDC_DMA_INTERFACE4
,
25 LPASS_CDC_DMA_INTERFACE5
,
26 LPASS_CDC_DMA_INTERFACE6
,
27 LPASS_CDC_DMA_INTERFACE7
,
28 LPASS_CDC_DMA_INTERFACE8
,
29 LPASS_CDC_DMA_INTERFACE9
,
30 LPASS_CDC_DMA_INTERFACE10
,
33 static void __lpass_get_dmactl_handle(struct snd_pcm_substream
*substream
, struct snd_soc_dai
*dai
,
34 struct lpaif_dmactl
**dmactl
, int *id
)
36 struct snd_soc_pcm_runtime
*soc_runtime
= snd_soc_substream_to_rtd(substream
);
37 struct snd_soc_dai
*cpu_dai
= snd_soc_rtd_to_cpu(soc_runtime
, 0);
38 struct lpass_data
*drvdata
= snd_soc_dai_get_drvdata(dai
);
39 struct snd_pcm_runtime
*rt
= substream
->runtime
;
40 struct lpass_pcm_data
*pcm_data
= rt
->private_data
;
41 const struct lpass_variant
*v
= drvdata
->variant
;
42 unsigned int dai_id
= cpu_dai
->driver
->id
;
45 case LPASS_CDC_DMA_RX0
... LPASS_CDC_DMA_RX9
:
46 *dmactl
= drvdata
->rxtx_rd_dmactl
;
47 *id
= pcm_data
->dma_ch
;
49 case LPASS_CDC_DMA_TX0
... LPASS_CDC_DMA_TX8
:
50 *dmactl
= drvdata
->rxtx_wr_dmactl
;
51 *id
= pcm_data
->dma_ch
- v
->rxtx_wrdma_channel_start
;
53 case LPASS_CDC_DMA_VA_TX0
... LPASS_CDC_DMA_VA_TX8
:
54 *dmactl
= drvdata
->va_wr_dmactl
;
55 *id
= pcm_data
->dma_ch
- v
->va_wrdma_channel_start
;
58 dev_err(soc_runtime
->dev
, "invalid dai id for dma ctl: %d\n", dai_id
);
63 static int __lpass_get_codec_dma_intf_type(int dai_id
)
68 case LPASS_CDC_DMA_RX0
:
69 case LPASS_CDC_DMA_TX0
:
70 case LPASS_CDC_DMA_VA_TX0
:
71 ret
= LPASS_CDC_DMA_INTERFACE1
;
73 case LPASS_CDC_DMA_RX1
:
74 case LPASS_CDC_DMA_TX1
:
75 case LPASS_CDC_DMA_VA_TX1
:
76 ret
= LPASS_CDC_DMA_INTERFACE2
;
78 case LPASS_CDC_DMA_RX2
:
79 case LPASS_CDC_DMA_TX2
:
80 case LPASS_CDC_DMA_VA_TX2
:
81 ret
= LPASS_CDC_DMA_INTERFACE3
;
83 case LPASS_CDC_DMA_RX3
:
84 case LPASS_CDC_DMA_TX3
:
85 case LPASS_CDC_DMA_VA_TX3
:
86 ret
= LPASS_CDC_DMA_INTERFACE4
;
88 case LPASS_CDC_DMA_RX4
:
89 case LPASS_CDC_DMA_TX4
:
90 case LPASS_CDC_DMA_VA_TX4
:
91 ret
= LPASS_CDC_DMA_INTERFACE5
;
93 case LPASS_CDC_DMA_RX5
:
94 case LPASS_CDC_DMA_TX5
:
95 case LPASS_CDC_DMA_VA_TX5
:
96 ret
= LPASS_CDC_DMA_INTERFACE6
;
98 case LPASS_CDC_DMA_RX6
:
99 case LPASS_CDC_DMA_TX6
:
100 case LPASS_CDC_DMA_VA_TX6
:
101 ret
= LPASS_CDC_DMA_INTERFACE7
;
103 case LPASS_CDC_DMA_RX7
:
104 case LPASS_CDC_DMA_TX7
:
105 case LPASS_CDC_DMA_VA_TX7
:
106 ret
= LPASS_CDC_DMA_INTERFACE8
;
108 case LPASS_CDC_DMA_RX8
:
109 case LPASS_CDC_DMA_TX8
:
110 case LPASS_CDC_DMA_VA_TX8
:
111 ret
= LPASS_CDC_DMA_INTERFACE9
;
113 case LPASS_CDC_DMA_RX9
:
114 ret
= LPASS_CDC_DMA_INTERFACE10
;
123 static int __lpass_platform_codec_intf_init(struct snd_soc_dai
*dai
,
124 struct snd_pcm_substream
*substream
)
126 struct snd_soc_pcm_runtime
*soc_runtime
= snd_soc_substream_to_rtd(substream
);
127 struct snd_soc_dai
*cpu_dai
= snd_soc_rtd_to_cpu(soc_runtime
, 0);
128 struct lpaif_dmactl
*dmactl
= NULL
;
129 struct device
*dev
= soc_runtime
->dev
;
130 int ret
, id
, codec_intf
;
131 unsigned int dai_id
= cpu_dai
->driver
->id
;
133 codec_intf
= __lpass_get_codec_dma_intf_type(dai_id
);
134 if (codec_intf
< 0) {
135 dev_err(dev
, "failed to get codec_intf: %d\n", codec_intf
);
139 __lpass_get_dmactl_handle(substream
, dai
, &dmactl
, &id
);
143 ret
= regmap_fields_write(dmactl
->codec_intf
, id
, codec_intf
);
145 dev_err(dev
, "error writing to dmactl codec_intf reg field: %d\n", ret
);
148 ret
= regmap_fields_write(dmactl
->codec_fs_sel
, id
, 0x0);
150 dev_err(dev
, "error writing to dmactl codec_fs_sel reg field: %d\n", ret
);
153 ret
= regmap_fields_write(dmactl
->codec_fs_delay
, id
, 0x0);
155 dev_err(dev
, "error writing to dmactl codec_fs_delay reg field: %d\n", ret
);
158 ret
= regmap_fields_write(dmactl
->codec_pack
, id
, 0x1);
160 dev_err(dev
, "error writing to dmactl codec_pack reg field: %d\n", ret
);
163 ret
= regmap_fields_write(dmactl
->codec_enable
, id
, LPAIF_DMACTL_ENABLE_ON
);
165 dev_err(dev
, "error writing to dmactl codec_enable reg field: %d\n", ret
);
171 static int lpass_cdc_dma_daiops_startup(struct snd_pcm_substream
*substream
,
172 struct snd_soc_dai
*dai
)
174 struct lpass_data
*drvdata
= snd_soc_dai_get_drvdata(dai
);
175 struct snd_soc_pcm_runtime
*soc_runtime
= snd_soc_substream_to_rtd(substream
);
178 case LPASS_CDC_DMA_RX0
... LPASS_CDC_DMA_RX9
:
179 case LPASS_CDC_DMA_TX0
... LPASS_CDC_DMA_TX8
:
180 clk_set_rate(drvdata
->codec_mem0
, CODEC_MEM_HZ_NORMAL
);
181 clk_prepare_enable(drvdata
->codec_mem0
);
183 case LPASS_CDC_DMA_VA_TX0
... LPASS_CDC_DMA_VA_TX0
:
184 clk_set_rate(drvdata
->va_mem0
, CODEC_MEM_HZ_NORMAL
);
185 clk_prepare_enable(drvdata
->va_mem0
);
188 dev_err(soc_runtime
->dev
, "%s: invalid interface: %d\n", __func__
, dai
->id
);
194 static void lpass_cdc_dma_daiops_shutdown(struct snd_pcm_substream
*substream
,
195 struct snd_soc_dai
*dai
)
197 struct lpass_data
*drvdata
= snd_soc_dai_get_drvdata(dai
);
198 struct snd_soc_pcm_runtime
*soc_runtime
= snd_soc_substream_to_rtd(substream
);
201 case LPASS_CDC_DMA_RX0
... LPASS_CDC_DMA_RX9
:
202 case LPASS_CDC_DMA_TX0
... LPASS_CDC_DMA_TX8
:
203 clk_disable_unprepare(drvdata
->codec_mem0
);
205 case LPASS_CDC_DMA_VA_TX0
... LPASS_CDC_DMA_VA_TX0
:
206 clk_disable_unprepare(drvdata
->va_mem0
);
209 dev_err(soc_runtime
->dev
, "%s: invalid interface: %d\n", __func__
, dai
->id
);
214 static int lpass_cdc_dma_daiops_hw_params(struct snd_pcm_substream
*substream
,
215 struct snd_pcm_hw_params
*params
,
216 struct snd_soc_dai
*dai
)
218 struct snd_soc_pcm_runtime
*soc_runtime
= snd_soc_substream_to_rtd(substream
);
219 struct lpaif_dmactl
*dmactl
= NULL
;
220 unsigned int ret
, regval
;
221 unsigned int channels
= params_channels(params
);
226 regval
= LPASS_CDC_DMA_INTF_ONE_CHANNEL
;
229 regval
= LPASS_CDC_DMA_INTF_TWO_CHANNEL
;
232 regval
= LPASS_CDC_DMA_INTF_FOUR_CHANNEL
;
235 regval
= LPASS_CDC_DMA_INTF_SIX_CHANNEL
;
238 regval
= LPASS_CDC_DMA_INTF_EIGHT_CHANNEL
;
241 dev_err(soc_runtime
->dev
, "invalid PCM config\n");
245 __lpass_get_dmactl_handle(substream
, dai
, &dmactl
, &id
);
249 ret
= regmap_fields_write(dmactl
->codec_channel
, id
, regval
);
251 dev_err(soc_runtime
->dev
,
252 "error writing to dmactl codec_channel reg field: %d\n", ret
);
258 static int lpass_cdc_dma_daiops_trigger(struct snd_pcm_substream
*substream
,
259 int cmd
, struct snd_soc_dai
*dai
)
261 struct snd_soc_pcm_runtime
*soc_runtime
= snd_soc_substream_to_rtd(substream
);
262 struct lpaif_dmactl
*dmactl
= NULL
;
266 case SNDRV_PCM_TRIGGER_START
:
267 case SNDRV_PCM_TRIGGER_RESUME
:
268 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
269 __lpass_platform_codec_intf_init(dai
, substream
);
271 case SNDRV_PCM_TRIGGER_STOP
:
272 case SNDRV_PCM_TRIGGER_SUSPEND
:
273 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
274 __lpass_get_dmactl_handle(substream
, dai
, &dmactl
, &id
);
278 ret
= regmap_fields_write(dmactl
->codec_enable
, id
, LPAIF_DMACTL_ENABLE_OFF
);
280 dev_err(soc_runtime
->dev
,
281 "error writing to dmactl codec_enable reg: %d\n", ret
);
287 dev_err(soc_runtime
->dev
, "%s: invalid %d interface\n", __func__
, cmd
);
293 const struct snd_soc_dai_ops asoc_qcom_lpass_cdc_dma_dai_ops
= {
294 .startup
= lpass_cdc_dma_daiops_startup
,
295 .shutdown
= lpass_cdc_dma_daiops_shutdown
,
296 .hw_params
= lpass_cdc_dma_daiops_hw_params
,
297 .trigger
= lpass_cdc_dma_daiops_trigger
,
299 EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cdc_dma_dai_ops
);
301 MODULE_DESCRIPTION("QTi LPASS CDC DMA Driver");
302 MODULE_LICENSE("GPL");