1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
5 * lpass-apq8016.c -- ALSA SoC CPU DAI driver for APQ8016 LPASS
10 #include <linux/device.h>
11 #include <linux/err.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
15 #include <linux/platform_device.h>
16 #include <sound/pcm.h>
17 #include <sound/pcm_params.h>
18 #include <sound/soc.h>
19 #include <sound/soc-dai.h>
21 #include <dt-bindings/sound/apq8016-lpass.h>
22 #include "lpass-lpaif-reg.h"
25 static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver
[] = {
28 .name
= "Primary MI2S",
30 .stream_name
= "Primary Playback",
31 .formats
= SNDRV_PCM_FMTBIT_S16
|
32 SNDRV_PCM_FMTBIT_S24
|
34 .rates
= SNDRV_PCM_RATE_8000
|
35 SNDRV_PCM_RATE_16000
|
36 SNDRV_PCM_RATE_32000
|
37 SNDRV_PCM_RATE_48000
|
44 .probe
= &asoc_qcom_lpass_cpu_dai_probe
,
45 .ops
= &asoc_qcom_lpass_cpu_dai_ops
,
49 .name
= "Secondary MI2S",
51 .stream_name
= "Secondary Playback",
52 .formats
= SNDRV_PCM_FMTBIT_S16
|
53 SNDRV_PCM_FMTBIT_S24
|
55 .rates
= SNDRV_PCM_RATE_8000
|
56 SNDRV_PCM_RATE_16000
|
57 SNDRV_PCM_RATE_32000
|
58 SNDRV_PCM_RATE_48000
|
65 .probe
= &asoc_qcom_lpass_cpu_dai_probe
,
66 .ops
= &asoc_qcom_lpass_cpu_dai_ops
,
70 .name
= "Tertiary MI2S",
72 .stream_name
= "Tertiary Capture",
73 .formats
= SNDRV_PCM_FMTBIT_S16
|
74 SNDRV_PCM_FMTBIT_S24
|
76 .rates
= SNDRV_PCM_RATE_8000
|
77 SNDRV_PCM_RATE_16000
|
78 SNDRV_PCM_RATE_32000
|
79 SNDRV_PCM_RATE_48000
|
86 .probe
= &asoc_qcom_lpass_cpu_dai_probe
,
87 .ops
= &asoc_qcom_lpass_cpu_dai_ops
,
90 .id
= MI2S_QUATERNARY
,
91 .name
= "Quatenary MI2S",
93 .stream_name
= "Quatenary Playback",
94 .formats
= SNDRV_PCM_FMTBIT_S16
|
95 SNDRV_PCM_FMTBIT_S24
|
97 .rates
= SNDRV_PCM_RATE_8000
|
98 SNDRV_PCM_RATE_16000
|
99 SNDRV_PCM_RATE_32000
|
100 SNDRV_PCM_RATE_48000
|
101 SNDRV_PCM_RATE_96000
,
108 .stream_name
= "Quatenary Capture",
109 .formats
= SNDRV_PCM_FMTBIT_S16
|
110 SNDRV_PCM_FMTBIT_S24
|
111 SNDRV_PCM_FMTBIT_S32
,
112 .rates
= SNDRV_PCM_RATE_8000
|
113 SNDRV_PCM_RATE_16000
|
114 SNDRV_PCM_RATE_32000
|
115 SNDRV_PCM_RATE_48000
|
116 SNDRV_PCM_RATE_96000
,
122 .probe
= &asoc_qcom_lpass_cpu_dai_probe
,
123 .ops
= &asoc_qcom_lpass_cpu_dai_ops
,
127 static int apq8016_lpass_alloc_dma_channel(struct lpass_data
*drvdata
,
128 int direction
, unsigned int dai_id
)
130 struct lpass_variant
*v
= drvdata
->variant
;
133 if (direction
== SNDRV_PCM_STREAM_PLAYBACK
) {
134 chan
= find_first_zero_bit(&drvdata
->dma_ch_bit_map
,
137 if (chan
>= v
->rdma_channels
)
140 chan
= find_next_zero_bit(&drvdata
->dma_ch_bit_map
,
141 v
->wrdma_channel_start
+
143 v
->wrdma_channel_start
);
145 if (chan
>= v
->wrdma_channel_start
+ v
->wrdma_channels
)
149 set_bit(chan
, &drvdata
->dma_ch_bit_map
);
154 static int apq8016_lpass_free_dma_channel(struct lpass_data
*drvdata
, int chan
, unsigned int dai_id
)
156 clear_bit(chan
, &drvdata
->dma_ch_bit_map
);
161 static int apq8016_lpass_init(struct platform_device
*pdev
)
163 struct lpass_data
*drvdata
= platform_get_drvdata(pdev
);
164 struct lpass_variant
*variant
= drvdata
->variant
;
165 struct device
*dev
= &pdev
->dev
;
169 drvdata
->clks
= devm_kcalloc(dev
, variant
->num_clks
,
170 sizeof(*drvdata
->clks
), GFP_KERNEL
);
173 drvdata
->num_clks
= variant
->num_clks
;
175 for (i
= 0; i
< drvdata
->num_clks
; i
++)
176 drvdata
->clks
[i
].id
= variant
->clk_name
[i
];
178 ret
= devm_clk_bulk_get(dev
, drvdata
->num_clks
, drvdata
->clks
);
180 dev_err(dev
, "Failed to get clocks %d\n", ret
);
184 ret
= clk_bulk_prepare_enable(drvdata
->num_clks
, drvdata
->clks
);
186 dev_err(dev
, "apq8016 clk_enable failed\n");
190 drvdata
->ahbix_clk
= devm_clk_get(dev
, "ahbix-clk");
191 if (IS_ERR(drvdata
->ahbix_clk
)) {
192 dev_err(dev
, "error getting ahbix-clk: %ld\n",
193 PTR_ERR(drvdata
->ahbix_clk
));
194 ret
= PTR_ERR(drvdata
->ahbix_clk
);
198 ret
= clk_set_rate(drvdata
->ahbix_clk
, LPASS_AHBIX_CLOCK_FREQUENCY
);
200 dev_err(dev
, "error setting rate on ahbix_clk: %d\n", ret
);
203 dev_dbg(dev
, "set ahbix_clk rate to %lu\n",
204 clk_get_rate(drvdata
->ahbix_clk
));
206 ret
= clk_prepare_enable(drvdata
->ahbix_clk
);
208 dev_err(dev
, "error enabling ahbix_clk: %d\n", ret
);
215 clk_bulk_disable_unprepare(drvdata
->num_clks
, drvdata
->clks
);
219 static int apq8016_lpass_exit(struct platform_device
*pdev
)
221 struct lpass_data
*drvdata
= platform_get_drvdata(pdev
);
223 clk_bulk_disable_unprepare(drvdata
->num_clks
, drvdata
->clks
);
224 clk_disable_unprepare(drvdata
->ahbix_clk
);
230 static struct lpass_variant apq8016_data
= {
231 .i2sctrl_reg_base
= 0x1000,
232 .i2sctrl_reg_stride
= 0x1000,
234 .irq_reg_base
= 0x6000,
235 .irq_reg_stride
= 0x1000,
237 .rdma_reg_base
= 0x8400,
238 .rdma_reg_stride
= 0x1000,
240 .dmactl_audif_start
= 1,
241 .wrdma_reg_base
= 0xB000,
242 .wrdma_reg_stride
= 0x1000,
243 .wrdma_channel_start
= 5,
245 .loopback
= REG_FIELD_ID(0x1000, 15, 15, 4, 0x1000),
246 .spken
= REG_FIELD_ID(0x1000, 14, 14, 4, 0x1000),
247 .spkmode
= REG_FIELD_ID(0x1000, 10, 13, 4, 0x1000),
248 .spkmono
= REG_FIELD_ID(0x1000, 9, 9, 4, 0x1000),
249 .micen
= REG_FIELD_ID(0x1000, 8, 8, 4, 0x1000),
250 .micmode
= REG_FIELD_ID(0x1000, 4, 7, 4, 0x1000),
251 .micmono
= REG_FIELD_ID(0x1000, 3, 3, 4, 0x1000),
252 .wssrc
= REG_FIELD_ID(0x1000, 2, 2, 4, 0x1000),
253 .bitwidth
= REG_FIELD_ID(0x1000, 0, 0, 4, 0x1000),
255 .rdma_dyncclk
= REG_FIELD_ID(0x8400, 12, 12, 2, 0x1000),
256 .rdma_bursten
= REG_FIELD_ID(0x8400, 11, 11, 2, 0x1000),
257 .rdma_wpscnt
= REG_FIELD_ID(0x8400, 8, 10, 2, 0x1000),
258 .rdma_intf
= REG_FIELD_ID(0x8400, 4, 7, 2, 0x1000),
259 .rdma_fifowm
= REG_FIELD_ID(0x8400, 1, 3, 2, 0x1000),
260 .rdma_enable
= REG_FIELD_ID(0x8400, 0, 0, 2, 0x1000),
262 .wrdma_dyncclk
= REG_FIELD_ID(0xB000, 12, 12, 2, 0x1000),
263 .wrdma_bursten
= REG_FIELD_ID(0xB000, 11, 11, 2, 0x1000),
264 .wrdma_wpscnt
= REG_FIELD_ID(0xB000, 8, 10, 2, 0x1000),
265 .wrdma_intf
= REG_FIELD_ID(0xB000, 4, 7, 2, 0x1000),
266 .wrdma_fifowm
= REG_FIELD_ID(0xB000, 1, 3, 2, 0x1000),
267 .wrdma_enable
= REG_FIELD_ID(0xB000, 0, 0, 2, 0x1000),
269 .clk_name
= (const char*[]) {
274 .dai_driver
= apq8016_lpass_cpu_dai_driver
,
275 .num_dai
= ARRAY_SIZE(apq8016_lpass_cpu_dai_driver
),
276 .dai_osr_clk_names
= (const char *[]) {
282 .dai_bit_clk_names
= (const char *[]) {
288 .init
= apq8016_lpass_init
,
289 .exit
= apq8016_lpass_exit
,
290 .alloc_dma_channel
= apq8016_lpass_alloc_dma_channel
,
291 .free_dma_channel
= apq8016_lpass_free_dma_channel
,
294 static const struct of_device_id apq8016_lpass_cpu_device_id
[] __maybe_unused
= {
295 { .compatible
= "qcom,lpass-cpu-apq8016", .data
= &apq8016_data
},
298 MODULE_DEVICE_TABLE(of
, apq8016_lpass_cpu_device_id
);
300 static struct platform_driver apq8016_lpass_cpu_platform_driver
= {
302 .name
= "apq8016-lpass-cpu",
303 .of_match_table
= of_match_ptr(apq8016_lpass_cpu_device_id
),
305 .probe
= asoc_qcom_lpass_cpu_platform_probe
,
306 .remove
= asoc_qcom_lpass_cpu_platform_remove
,
308 module_platform_driver(apq8016_lpass_cpu_platform_driver
);
310 MODULE_DESCRIPTION("APQ8016 LPASS CPU Driver");
311 MODULE_LICENSE("GPL v2");