1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
5 * lpass-sc7180.c -- ALSA SoC platform-machine driver for QTi LPASS
9 #include <linux/device.h>
10 #include <linux/err.h>
11 #include <linux/kernel.h>
12 #include <linux/module.h>
14 #include <linux/platform_device.h>
16 #include <dt-bindings/sound/sc7180-lpass.h>
17 #include <sound/pcm.h>
18 #include <sound/soc.h>
20 #include "lpass-lpaif-reg.h"
23 static struct snd_soc_dai_driver sc7180_lpass_cpu_dai_driver
[] = {
26 .name
= "Primary MI2S",
28 .stream_name
= "Primary Playback",
29 .formats
= SNDRV_PCM_FMTBIT_S16
,
30 .rates
= SNDRV_PCM_RATE_48000
,
37 .stream_name
= "Primary Capture",
38 .formats
= SNDRV_PCM_FMTBIT_S16
|
40 .rates
= SNDRV_PCM_RATE_48000
,
46 .ops
= &asoc_qcom_lpass_cpu_dai_ops
,
49 .name
= "Secondary MI2S",
51 .stream_name
= "Secondary Playback",
52 .formats
= SNDRV_PCM_FMTBIT_S16
,
53 .rates
= SNDRV_PCM_RATE_48000
,
59 .ops
= &asoc_qcom_lpass_cpu_dai_ops2
,
64 .stream_name
= "Hdmi Playback",
65 .formats
= SNDRV_PCM_FMTBIT_S24
,
66 .rates
= SNDRV_PCM_RATE_48000
,
72 .ops
= &asoc_qcom_lpass_hdmi_dai_ops
,
76 static int sc7180_lpass_alloc_dma_channel(struct lpass_data
*drvdata
,
77 int direction
, unsigned int dai_id
)
79 const struct lpass_variant
*v
= drvdata
->variant
;
82 if (dai_id
== LPASS_DP_RX
) {
83 if (direction
== SNDRV_PCM_STREAM_PLAYBACK
) {
84 chan
= find_first_zero_bit(&drvdata
->hdmi_dma_ch_bit_map
,
85 v
->hdmi_rdma_channels
);
87 if (chan
>= v
->hdmi_rdma_channels
)
90 set_bit(chan
, &drvdata
->hdmi_dma_ch_bit_map
);
92 if (direction
== SNDRV_PCM_STREAM_PLAYBACK
) {
93 chan
= find_first_zero_bit(&drvdata
->dma_ch_bit_map
,
96 if (chan
>= v
->rdma_channels
)
99 chan
= find_next_zero_bit(&drvdata
->dma_ch_bit_map
,
100 v
->wrdma_channel_start
+
102 v
->wrdma_channel_start
);
104 if (chan
>= v
->wrdma_channel_start
+ v
->wrdma_channels
)
108 set_bit(chan
, &drvdata
->dma_ch_bit_map
);
113 static int sc7180_lpass_free_dma_channel(struct lpass_data
*drvdata
, int chan
, unsigned int dai_id
)
115 if (dai_id
== LPASS_DP_RX
)
116 clear_bit(chan
, &drvdata
->hdmi_dma_ch_bit_map
);
118 clear_bit(chan
, &drvdata
->dma_ch_bit_map
);
123 static int sc7180_lpass_init(struct platform_device
*pdev
)
125 struct lpass_data
*drvdata
= platform_get_drvdata(pdev
);
126 const struct lpass_variant
*variant
= drvdata
->variant
;
127 struct device
*dev
= &pdev
->dev
;
130 drvdata
->clks
= devm_kcalloc(dev
, variant
->num_clks
,
131 sizeof(*drvdata
->clks
), GFP_KERNEL
);
135 drvdata
->num_clks
= variant
->num_clks
;
137 for (i
= 0; i
< drvdata
->num_clks
; i
++)
138 drvdata
->clks
[i
].id
= variant
->clk_name
[i
];
140 ret
= devm_clk_bulk_get(dev
, drvdata
->num_clks
, drvdata
->clks
);
142 dev_err(dev
, "Failed to get clocks %d\n", ret
);
146 ret
= clk_bulk_prepare_enable(drvdata
->num_clks
, drvdata
->clks
);
148 dev_err(dev
, "sc7180 clk_enable failed\n");
155 static int sc7180_lpass_exit(struct platform_device
*pdev
)
157 struct lpass_data
*drvdata
= platform_get_drvdata(pdev
);
159 clk_bulk_disable_unprepare(drvdata
->num_clks
, drvdata
->clks
);
163 static int __maybe_unused
sc7180_lpass_dev_resume(struct device
*dev
)
165 struct lpass_data
*drvdata
= dev_get_drvdata(dev
);
167 return clk_bulk_prepare_enable(drvdata
->num_clks
, drvdata
->clks
);
170 static int __maybe_unused
sc7180_lpass_dev_suspend(struct device
*dev
)
172 struct lpass_data
*drvdata
= dev_get_drvdata(dev
);
174 clk_bulk_disable_unprepare(drvdata
->num_clks
, drvdata
->clks
);
178 static const struct dev_pm_ops sc7180_lpass_pm_ops
= {
179 SET_SYSTEM_SLEEP_PM_OPS(sc7180_lpass_dev_suspend
, sc7180_lpass_dev_resume
)
182 static const struct lpass_variant sc7180_data
= {
183 .i2sctrl_reg_base
= 0x1000,
184 .i2sctrl_reg_stride
= 0x1000,
186 .irq_reg_base
= 0x9000,
187 .irq_reg_stride
= 0x1000,
189 .rdma_reg_base
= 0xC000,
190 .rdma_reg_stride
= 0x1000,
192 .hdmi_rdma_reg_base
= 0x64000,
193 .hdmi_rdma_reg_stride
= 0x1000,
194 .hdmi_rdma_channels
= 4,
195 .dmactl_audif_start
= 1,
196 .wrdma_reg_base
= 0x18000,
197 .wrdma_reg_stride
= 0x1000,
198 .wrdma_channel_start
= 5,
201 .loopback
= REG_FIELD_ID(0x1000, 17, 17, 3, 0x1000),
202 .spken
= REG_FIELD_ID(0x1000, 16, 16, 3, 0x1000),
203 .spkmode
= REG_FIELD_ID(0x1000, 11, 15, 3, 0x1000),
204 .spkmono
= REG_FIELD_ID(0x1000, 10, 10, 3, 0x1000),
205 .micen
= REG_FIELD_ID(0x1000, 9, 9, 3, 0x1000),
206 .micmode
= REG_FIELD_ID(0x1000, 4, 8, 3, 0x1000),
207 .micmono
= REG_FIELD_ID(0x1000, 3, 3, 3, 0x1000),
208 .wssrc
= REG_FIELD_ID(0x1000, 2, 2, 3, 0x1000),
209 .bitwidth
= REG_FIELD_ID(0x1000, 0, 1, 3, 0x1000),
211 .rdma_dyncclk
= REG_FIELD_ID(0xC000, 21, 21, 5, 0x1000),
212 .rdma_bursten
= REG_FIELD_ID(0xC000, 20, 20, 5, 0x1000),
213 .rdma_wpscnt
= REG_FIELD_ID(0xC000, 16, 19, 5, 0x1000),
214 .rdma_intf
= REG_FIELD_ID(0xC000, 12, 15, 5, 0x1000),
215 .rdma_fifowm
= REG_FIELD_ID(0xC000, 1, 5, 5, 0x1000),
216 .rdma_enable
= REG_FIELD_ID(0xC000, 0, 0, 5, 0x1000),
218 .wrdma_dyncclk
= REG_FIELD_ID(0x18000, 22, 22, 4, 0x1000),
219 .wrdma_bursten
= REG_FIELD_ID(0x18000, 21, 21, 4, 0x1000),
220 .wrdma_wpscnt
= REG_FIELD_ID(0x18000, 17, 20, 4, 0x1000),
221 .wrdma_intf
= REG_FIELD_ID(0x18000, 12, 16, 4, 0x1000),
222 .wrdma_fifowm
= REG_FIELD_ID(0x18000, 1, 5, 4, 0x1000),
223 .wrdma_enable
= REG_FIELD_ID(0x18000, 0, 0, 4, 0x1000),
225 .hdmi_tx_ctl_addr
= 0x1000,
226 .hdmi_legacy_addr
= 0x1008,
227 .hdmi_vbit_addr
= 0x610c0,
228 .hdmi_ch_lsb_addr
= 0x61048,
229 .hdmi_ch_msb_addr
= 0x6104c,
231 .hdmi_parity_addr
= 0x61034,
232 .hdmi_dmactl_addr
= 0x61038,
233 .hdmi_dma_stride
= 0x4,
234 .hdmi_DP_addr
= 0x610c8,
235 .hdmi_sstream_addr
= 0x6101c,
236 .hdmi_irq_reg_base
= 0x63000,
239 .hdmi_rdma_dyncclk
= REG_FIELD_ID(0x64000, 14, 14, 4, 0x1000),
240 .hdmi_rdma_bursten
= REG_FIELD_ID(0x64000, 13, 13, 4, 0x1000),
241 .hdmi_rdma_burst8
= REG_FIELD_ID(0x64000, 15, 15, 4, 0x1000),
242 .hdmi_rdma_burst16
= REG_FIELD_ID(0x64000, 16, 16, 4, 0x1000),
243 .hdmi_rdma_dynburst
= REG_FIELD_ID(0x64000, 18, 18, 4, 0x1000),
244 .hdmi_rdma_wpscnt
= REG_FIELD_ID(0x64000, 10, 12, 4, 0x1000),
245 .hdmi_rdma_fifowm
= REG_FIELD_ID(0x64000, 1, 5, 4, 0x1000),
246 .hdmi_rdma_enable
= REG_FIELD_ID(0x64000, 0, 0, 4, 0x1000),
248 .sstream_en
= REG_FIELD(0x6101c, 0, 0),
249 .dma_sel
= REG_FIELD(0x6101c, 1, 2),
250 .auto_bbit_en
= REG_FIELD(0x6101c, 3, 3),
251 .layout
= REG_FIELD(0x6101c, 4, 4),
252 .layout_sp
= REG_FIELD(0x6101c, 5, 8),
253 .set_sp_on_en
= REG_FIELD(0x6101c, 10, 10),
254 .dp_audio
= REG_FIELD(0x6101c, 11, 11),
255 .dp_staffing_en
= REG_FIELD(0x6101c, 12, 12),
256 .dp_sp_b_hw_en
= REG_FIELD(0x6101c, 13, 13),
258 .mute
= REG_FIELD(0x610c8, 0, 0),
259 .as_sdp_cc
= REG_FIELD(0x610c8, 1, 3),
260 .as_sdp_ct
= REG_FIELD(0x610c8, 4, 7),
261 .aif_db4
= REG_FIELD(0x610c8, 8, 15),
262 .frequency
= REG_FIELD(0x610c8, 16, 21),
263 .mst_index
= REG_FIELD(0x610c8, 28, 29),
264 .dptx_index
= REG_FIELD(0x610c8, 30, 31),
266 .soft_reset
= REG_FIELD(0x1000, 31, 31),
267 .force_reset
= REG_FIELD(0x1000, 30, 30),
269 .use_hw_chs
= REG_FIELD(0x61038, 0, 0),
270 .use_hw_usr
= REG_FIELD(0x61038, 1, 1),
271 .hw_chs_sel
= REG_FIELD(0x61038, 2, 4),
272 .hw_usr_sel
= REG_FIELD(0x61038, 5, 6),
274 .replace_vbit
= REG_FIELD(0x610c0, 0, 0),
275 .vbit_stream
= REG_FIELD(0x610c0, 1, 1),
277 .legacy_en
= REG_FIELD(0x1008, 0, 0),
278 .calc_en
= REG_FIELD(0x61034, 0, 0),
279 .lsb_bits
= REG_FIELD(0x61048, 0, 31),
280 .msb_bits
= REG_FIELD(0x6104c, 0, 31),
283 .clk_name
= (const char*[]) {
289 .dai_driver
= sc7180_lpass_cpu_dai_driver
,
290 .num_dai
= ARRAY_SIZE(sc7180_lpass_cpu_dai_driver
),
291 .dai_osr_clk_names
= (const char *[]) {
295 .dai_bit_clk_names
= (const char *[]) {
299 .init
= sc7180_lpass_init
,
300 .exit
= sc7180_lpass_exit
,
301 .alloc_dma_channel
= sc7180_lpass_alloc_dma_channel
,
302 .free_dma_channel
= sc7180_lpass_free_dma_channel
,
305 static const struct of_device_id sc7180_lpass_cpu_device_id
[] __maybe_unused
= {
306 {.compatible
= "qcom,sc7180-lpass-cpu", .data
= &sc7180_data
},
309 MODULE_DEVICE_TABLE(of
, sc7180_lpass_cpu_device_id
);
311 static struct platform_driver sc7180_lpass_cpu_platform_driver
= {
313 .name
= "sc7180-lpass-cpu",
314 .of_match_table
= of_match_ptr(sc7180_lpass_cpu_device_id
),
315 .pm
= &sc7180_lpass_pm_ops
,
317 .probe
= asoc_qcom_lpass_cpu_platform_probe
,
318 .remove
= asoc_qcom_lpass_cpu_platform_remove
,
319 .shutdown
= asoc_qcom_lpass_cpu_platform_shutdown
,
322 module_platform_driver(sc7180_lpass_cpu_platform_driver
);
324 MODULE_DESCRIPTION("SC7180 LPASS CPU DRIVER");
325 MODULE_LICENSE("GPL");