1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2018, The Linux Foundation. All rights reserved. |
4 | */ |
5 | |
6 | #include <dt-bindings/sound/qcom,q6afe.h> |
7 | #include <linux/module.h> |
8 | #include <linux/platform_device.h> |
9 | #include <sound/core.h> |
10 | #include <sound/pcm.h> |
11 | #include <sound/pcm_params.h> |
12 | #include <sound/jack.h> |
13 | #include <sound/soc.h> |
14 | #include <linux/soundwire/sdw.h> |
15 | #include <uapi/linux/input-event-codes.h> |
16 | #include "common.h" |
17 | #include "qdsp6/q6afe.h" |
18 | #include "../codecs/rt5663.h" |
19 | |
20 | #define DRIVER_NAME "sdm845" |
21 | #define DEFAULT_SAMPLE_RATE_48K 48000 |
22 | #define DEFAULT_MCLK_RATE 24576000 |
23 | #define TDM_BCLK_RATE 6144000 |
24 | #define MI2S_BCLK_RATE 1536000 |
25 | #define LEFT_SPK_TDM_TX_MASK 0x30 |
26 | #define RIGHT_SPK_TDM_TX_MASK 0xC0 |
27 | #define SPK_TDM_RX_MASK 0x03 |
28 | #define NUM_TDM_SLOTS 8 |
29 | #define SLIM_MAX_TX_PORTS 16 |
30 | #define SLIM_MAX_RX_PORTS 13 |
31 | #define WCD934X_DEFAULT_MCLK_RATE 9600000 |
32 | |
33 | struct sdm845_snd_data { |
34 | struct snd_soc_jack jack; |
35 | bool jack_setup; |
36 | bool slim_port_setup; |
37 | bool stream_prepared[AFE_PORT_MAX]; |
38 | struct snd_soc_card *card; |
39 | uint32_t pri_mi2s_clk_count; |
40 | uint32_t sec_mi2s_clk_count; |
41 | uint32_t quat_tdm_clk_count; |
42 | struct sdw_stream_runtime *sruntime[AFE_PORT_MAX]; |
43 | }; |
44 | |
45 | static struct snd_soc_jack_pin sdm845_jack_pins[] = { |
46 | { |
47 | .pin = "Headphone Jack" , |
48 | .mask = SND_JACK_HEADPHONE, |
49 | }, |
50 | { |
51 | .pin = "Headset Mic" , |
52 | .mask = SND_JACK_MICROPHONE, |
53 | }, |
54 | }; |
55 | |
56 | static unsigned int tdm_slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; |
57 | |
58 | static int sdm845_slim_snd_hw_params(struct snd_pcm_substream *substream, |
59 | struct snd_pcm_hw_params *params) |
60 | { |
61 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
62 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
63 | struct snd_soc_dai *codec_dai; |
64 | struct sdm845_snd_data *pdata = snd_soc_card_get_drvdata(card: rtd->card); |
65 | u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; |
66 | struct sdw_stream_runtime *sruntime; |
67 | u32 rx_ch_cnt = 0, tx_ch_cnt = 0; |
68 | int ret = 0, i; |
69 | |
70 | for_each_rtd_codec_dais(rtd, i, codec_dai) { |
71 | sruntime = snd_soc_dai_get_stream(dai: codec_dai, |
72 | direction: substream->stream); |
73 | if (sruntime != ERR_PTR(error: -ENOTSUPP)) |
74 | pdata->sruntime[cpu_dai->id] = sruntime; |
75 | |
76 | ret = snd_soc_dai_get_channel_map(dai: codec_dai, |
77 | tx_num: &tx_ch_cnt, tx_slot: tx_ch, rx_num: &rx_ch_cnt, rx_slot: rx_ch); |
78 | |
79 | if (ret != 0 && ret != -ENOTSUPP) { |
80 | pr_err("failed to get codec chan map, err:%d\n" , ret); |
81 | return ret; |
82 | } else if (ret == -ENOTSUPP) { |
83 | /* Ignore unsupported */ |
84 | continue; |
85 | } |
86 | |
87 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
88 | ret = snd_soc_dai_set_channel_map(dai: cpu_dai, tx_num: 0, NULL, |
89 | rx_num: rx_ch_cnt, rx_slot: rx_ch); |
90 | else |
91 | ret = snd_soc_dai_set_channel_map(dai: cpu_dai, tx_num: tx_ch_cnt, |
92 | tx_slot: tx_ch, rx_num: 0, NULL); |
93 | } |
94 | |
95 | return 0; |
96 | } |
97 | |
98 | static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, |
99 | struct snd_pcm_hw_params *params) |
100 | { |
101 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
102 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
103 | struct snd_soc_dai *codec_dai; |
104 | int ret = 0, j; |
105 | int channels, slot_width; |
106 | |
107 | switch (params_format(p: params)) { |
108 | case SNDRV_PCM_FORMAT_S16_LE: |
109 | slot_width = 16; |
110 | break; |
111 | default: |
112 | dev_err(rtd->dev, "%s: invalid param format 0x%x\n" , |
113 | __func__, params_format(params)); |
114 | return -EINVAL; |
115 | } |
116 | |
117 | channels = params_channels(p: params); |
118 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
119 | ret = snd_soc_dai_set_tdm_slot(dai: cpu_dai, tx_mask: 0, rx_mask: 0x3, |
120 | slots: 8, slot_width); |
121 | if (ret < 0) { |
122 | dev_err(rtd->dev, "%s: failed to set tdm slot, err:%d\n" , |
123 | __func__, ret); |
124 | goto end; |
125 | } |
126 | |
127 | ret = snd_soc_dai_set_channel_map(dai: cpu_dai, tx_num: 0, NULL, |
128 | rx_num: channels, rx_slot: tdm_slot_offset); |
129 | if (ret < 0) { |
130 | dev_err(rtd->dev, "%s: failed to set channel map, err:%d\n" , |
131 | __func__, ret); |
132 | goto end; |
133 | } |
134 | } else { |
135 | ret = snd_soc_dai_set_tdm_slot(dai: cpu_dai, tx_mask: 0xf, rx_mask: 0, |
136 | slots: 8, slot_width); |
137 | if (ret < 0) { |
138 | dev_err(rtd->dev, "%s: failed to set tdm slot, err:%d\n" , |
139 | __func__, ret); |
140 | goto end; |
141 | } |
142 | |
143 | ret = snd_soc_dai_set_channel_map(dai: cpu_dai, tx_num: channels, |
144 | tx_slot: tdm_slot_offset, rx_num: 0, NULL); |
145 | if (ret < 0) { |
146 | dev_err(rtd->dev, "%s: failed to set channel map, err:%d\n" , |
147 | __func__, ret); |
148 | goto end; |
149 | } |
150 | } |
151 | |
152 | for_each_rtd_codec_dais(rtd, j, codec_dai) { |
153 | |
154 | if (!strcmp(codec_dai->component->name_prefix, "Left" )) { |
155 | ret = snd_soc_dai_set_tdm_slot( |
156 | dai: codec_dai, LEFT_SPK_TDM_TX_MASK, |
157 | SPK_TDM_RX_MASK, NUM_TDM_SLOTS, |
158 | slot_width); |
159 | if (ret < 0) { |
160 | dev_err(rtd->dev, |
161 | "DEV0 TDM slot err:%d\n" , ret); |
162 | return ret; |
163 | } |
164 | } |
165 | |
166 | if (!strcmp(codec_dai->component->name_prefix, "Right" )) { |
167 | ret = snd_soc_dai_set_tdm_slot( |
168 | dai: codec_dai, RIGHT_SPK_TDM_TX_MASK, |
169 | SPK_TDM_RX_MASK, NUM_TDM_SLOTS, |
170 | slot_width); |
171 | if (ret < 0) { |
172 | dev_err(rtd->dev, |
173 | "DEV1 TDM slot err:%d\n" , ret); |
174 | return ret; |
175 | } |
176 | } |
177 | } |
178 | |
179 | end: |
180 | return ret; |
181 | } |
182 | |
183 | static int sdm845_snd_hw_params(struct snd_pcm_substream *substream, |
184 | struct snd_pcm_hw_params *params) |
185 | { |
186 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
187 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
188 | struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); |
189 | int ret = 0; |
190 | |
191 | switch (cpu_dai->id) { |
192 | case PRIMARY_MI2S_RX: |
193 | case PRIMARY_MI2S_TX: |
194 | /* |
195 | * Use ASRC for internal clocks, as PLL rate isn't multiple |
196 | * of BCLK. |
197 | */ |
198 | rt5663_sel_asrc_clk_src( |
199 | component: codec_dai->component, |
200 | filter_mask: RT5663_DA_STEREO_FILTER | RT5663_AD_STEREO_FILTER, |
201 | clk_src: RT5663_CLK_SEL_I2S1_ASRC); |
202 | ret = snd_soc_dai_set_sysclk( |
203 | dai: codec_dai, clk_id: RT5663_SCLK_S_MCLK, DEFAULT_MCLK_RATE, |
204 | SND_SOC_CLOCK_IN); |
205 | if (ret < 0) |
206 | dev_err(rtd->dev, |
207 | "snd_soc_dai_set_sysclk err = %d\n" , ret); |
208 | break; |
209 | case QUATERNARY_TDM_RX_0: |
210 | case QUATERNARY_TDM_TX_0: |
211 | ret = sdm845_tdm_snd_hw_params(substream, params); |
212 | break; |
213 | case SLIMBUS_0_RX...SLIMBUS_6_TX: |
214 | ret = sdm845_slim_snd_hw_params(substream, params); |
215 | break; |
216 | case QUATERNARY_MI2S_RX: |
217 | break; |
218 | default: |
219 | pr_err("%s: invalid dai id 0x%x\n" , __func__, cpu_dai->id); |
220 | break; |
221 | } |
222 | return ret; |
223 | } |
224 | |
225 | static void sdm845_jack_free(struct snd_jack *jack) |
226 | { |
227 | struct snd_soc_component *component = jack->private_data; |
228 | |
229 | snd_soc_component_set_jack(component, NULL, NULL); |
230 | } |
231 | |
232 | static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) |
233 | { |
234 | struct snd_soc_component *component; |
235 | struct snd_soc_card *card = rtd->card; |
236 | struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); |
237 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
238 | struct sdm845_snd_data *pdata = snd_soc_card_get_drvdata(card); |
239 | struct snd_soc_dai_link *link = rtd->dai_link; |
240 | struct snd_jack *jack; |
241 | /* |
242 | * Codec SLIMBUS configuration |
243 | * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13 |
244 | * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 |
245 | * TX14, TX15, TX16 |
246 | */ |
247 | unsigned int rx_ch[SLIM_MAX_RX_PORTS] = {144, 145, 146, 147, 148, 149, |
248 | 150, 151, 152, 153, 154, 155, 156}; |
249 | unsigned int tx_ch[SLIM_MAX_TX_PORTS] = {128, 129, 130, 131, 132, 133, |
250 | 134, 135, 136, 137, 138, 139, |
251 | 140, 141, 142, 143}; |
252 | int rval, i; |
253 | |
254 | |
255 | if (!pdata->jack_setup) { |
256 | rval = snd_soc_card_jack_new_pins(card, id: "Headset Jack" , |
257 | type: SND_JACK_HEADSET | |
258 | SND_JACK_HEADPHONE | |
259 | SND_JACK_BTN_0 | SND_JACK_BTN_1 | |
260 | SND_JACK_BTN_2 | SND_JACK_BTN_3, |
261 | jack: &pdata->jack, |
262 | pins: sdm845_jack_pins, |
263 | ARRAY_SIZE(sdm845_jack_pins)); |
264 | |
265 | if (rval < 0) { |
266 | dev_err(card->dev, "Unable to add Headphone Jack\n" ); |
267 | return rval; |
268 | } |
269 | |
270 | jack = pdata->jack.jack; |
271 | |
272 | snd_jack_set_key(jack, type: SND_JACK_BTN_0, KEY_PLAYPAUSE); |
273 | snd_jack_set_key(jack, type: SND_JACK_BTN_1, KEY_VOICECOMMAND); |
274 | snd_jack_set_key(jack, type: SND_JACK_BTN_2, KEY_VOLUMEUP); |
275 | snd_jack_set_key(jack, type: SND_JACK_BTN_3, KEY_VOLUMEDOWN); |
276 | pdata->jack_setup = true; |
277 | } |
278 | |
279 | switch (cpu_dai->id) { |
280 | case PRIMARY_MI2S_RX: |
281 | jack = pdata->jack.jack; |
282 | component = codec_dai->component; |
283 | |
284 | jack->private_data = component; |
285 | jack->private_free = sdm845_jack_free; |
286 | rval = snd_soc_component_set_jack(component, |
287 | jack: &pdata->jack, NULL); |
288 | if (rval != 0 && rval != -ENOTSUPP) { |
289 | dev_warn(card->dev, "Failed to set jack: %d\n" , rval); |
290 | return rval; |
291 | } |
292 | break; |
293 | case SLIMBUS_0_RX...SLIMBUS_6_TX: |
294 | /* setting up wcd multiple times for slim port is redundant */ |
295 | if (pdata->slim_port_setup || !link->no_pcm) |
296 | return 0; |
297 | |
298 | for_each_rtd_codec_dais(rtd, i, codec_dai) { |
299 | rval = snd_soc_dai_set_channel_map(dai: codec_dai, |
300 | ARRAY_SIZE(tx_ch), |
301 | tx_slot: tx_ch, |
302 | ARRAY_SIZE(rx_ch), |
303 | rx_slot: rx_ch); |
304 | if (rval != 0 && rval != -ENOTSUPP) |
305 | return rval; |
306 | |
307 | snd_soc_dai_set_sysclk(dai: codec_dai, clk_id: 0, |
308 | WCD934X_DEFAULT_MCLK_RATE, |
309 | dir: SNDRV_PCM_STREAM_PLAYBACK); |
310 | |
311 | rval = snd_soc_component_set_jack(component: codec_dai->component, |
312 | jack: &pdata->jack, NULL); |
313 | if (rval != 0 && rval != -ENOTSUPP) { |
314 | dev_warn(card->dev, "Failed to set jack: %d\n" , rval); |
315 | return rval; |
316 | } |
317 | } |
318 | |
319 | pdata->slim_port_setup = true; |
320 | |
321 | break; |
322 | default: |
323 | break; |
324 | } |
325 | |
326 | return 0; |
327 | } |
328 | |
329 | |
330 | static int sdm845_snd_startup(struct snd_pcm_substream *substream) |
331 | { |
332 | unsigned int fmt = SND_SOC_DAIFMT_BP_FP; |
333 | unsigned int codec_dai_fmt = SND_SOC_DAIFMT_BC_FC; |
334 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
335 | struct snd_soc_card *card = rtd->card; |
336 | struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card); |
337 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
338 | struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); |
339 | int j; |
340 | int ret; |
341 | |
342 | switch (cpu_dai->id) { |
343 | case PRIMARY_MI2S_RX: |
344 | case PRIMARY_MI2S_TX: |
345 | codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF; |
346 | if (++(data->pri_mi2s_clk_count) == 1) { |
347 | snd_soc_dai_set_sysclk(dai: cpu_dai, |
348 | Q6AFE_LPASS_CLK_ID_MCLK_1, |
349 | DEFAULT_MCLK_RATE, dir: SNDRV_PCM_STREAM_PLAYBACK); |
350 | snd_soc_dai_set_sysclk(dai: cpu_dai, |
351 | Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, |
352 | MI2S_BCLK_RATE, dir: SNDRV_PCM_STREAM_PLAYBACK); |
353 | } |
354 | snd_soc_dai_set_fmt(dai: cpu_dai, fmt); |
355 | snd_soc_dai_set_fmt(dai: codec_dai, fmt: codec_dai_fmt); |
356 | break; |
357 | |
358 | case SECONDARY_MI2S_TX: |
359 | codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S; |
360 | if (++(data->sec_mi2s_clk_count) == 1) { |
361 | snd_soc_dai_set_sysclk(dai: cpu_dai, |
362 | Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, |
363 | MI2S_BCLK_RATE, dir: SNDRV_PCM_STREAM_CAPTURE); |
364 | } |
365 | snd_soc_dai_set_fmt(dai: cpu_dai, fmt); |
366 | snd_soc_dai_set_fmt(dai: codec_dai, fmt: codec_dai_fmt); |
367 | break; |
368 | case QUATERNARY_MI2S_RX: |
369 | snd_soc_dai_set_sysclk(dai: cpu_dai, |
370 | Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT, |
371 | MI2S_BCLK_RATE, dir: SNDRV_PCM_STREAM_PLAYBACK); |
372 | snd_soc_dai_set_fmt(dai: cpu_dai, fmt); |
373 | |
374 | |
375 | break; |
376 | |
377 | case QUATERNARY_TDM_RX_0: |
378 | case QUATERNARY_TDM_TX_0: |
379 | if (++(data->quat_tdm_clk_count) == 1) { |
380 | snd_soc_dai_set_sysclk(dai: cpu_dai, |
381 | Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT, |
382 | TDM_BCLK_RATE, dir: SNDRV_PCM_STREAM_PLAYBACK); |
383 | } |
384 | |
385 | codec_dai_fmt |= SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_DSP_B; |
386 | |
387 | for_each_rtd_codec_dais(rtd, j, codec_dai) { |
388 | |
389 | if (!strcmp(codec_dai->component->name_prefix, |
390 | "Left" )) { |
391 | ret = snd_soc_dai_set_fmt( |
392 | dai: codec_dai, fmt: codec_dai_fmt); |
393 | if (ret < 0) { |
394 | dev_err(rtd->dev, |
395 | "Left TDM fmt err:%d\n" , ret); |
396 | return ret; |
397 | } |
398 | } |
399 | |
400 | if (!strcmp(codec_dai->component->name_prefix, |
401 | "Right" )) { |
402 | ret = snd_soc_dai_set_fmt( |
403 | dai: codec_dai, fmt: codec_dai_fmt); |
404 | if (ret < 0) { |
405 | dev_err(rtd->dev, |
406 | "Right TDM slot err:%d\n" , ret); |
407 | return ret; |
408 | } |
409 | } |
410 | } |
411 | break; |
412 | case SLIMBUS_0_RX...SLIMBUS_6_TX: |
413 | break; |
414 | |
415 | default: |
416 | pr_err("%s: invalid dai id 0x%x\n" , __func__, cpu_dai->id); |
417 | break; |
418 | } |
419 | return 0; |
420 | } |
421 | |
422 | static void sdm845_snd_shutdown(struct snd_pcm_substream *substream) |
423 | { |
424 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
425 | struct snd_soc_card *card = rtd->card; |
426 | struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card); |
427 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
428 | |
429 | switch (cpu_dai->id) { |
430 | case PRIMARY_MI2S_RX: |
431 | case PRIMARY_MI2S_TX: |
432 | if (--(data->pri_mi2s_clk_count) == 0) { |
433 | snd_soc_dai_set_sysclk(dai: cpu_dai, |
434 | Q6AFE_LPASS_CLK_ID_MCLK_1, |
435 | freq: 0, dir: SNDRV_PCM_STREAM_PLAYBACK); |
436 | snd_soc_dai_set_sysclk(dai: cpu_dai, |
437 | Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, |
438 | freq: 0, dir: SNDRV_PCM_STREAM_PLAYBACK); |
439 | } |
440 | break; |
441 | |
442 | case SECONDARY_MI2S_TX: |
443 | if (--(data->sec_mi2s_clk_count) == 0) { |
444 | snd_soc_dai_set_sysclk(dai: cpu_dai, |
445 | Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, |
446 | freq: 0, dir: SNDRV_PCM_STREAM_CAPTURE); |
447 | } |
448 | break; |
449 | |
450 | case QUATERNARY_TDM_RX_0: |
451 | case QUATERNARY_TDM_TX_0: |
452 | if (--(data->quat_tdm_clk_count) == 0) { |
453 | snd_soc_dai_set_sysclk(dai: cpu_dai, |
454 | Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT, |
455 | freq: 0, dir: SNDRV_PCM_STREAM_PLAYBACK); |
456 | } |
457 | break; |
458 | case SLIMBUS_0_RX...SLIMBUS_6_TX: |
459 | case QUATERNARY_MI2S_RX: |
460 | break; |
461 | |
462 | default: |
463 | pr_err("%s: invalid dai id 0x%x\n" , __func__, cpu_dai->id); |
464 | break; |
465 | } |
466 | } |
467 | |
468 | static int sdm845_snd_prepare(struct snd_pcm_substream *substream) |
469 | { |
470 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
471 | struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card: rtd->card); |
472 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
473 | struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; |
474 | int ret; |
475 | |
476 | if (!sruntime) |
477 | return 0; |
478 | |
479 | if (data->stream_prepared[cpu_dai->id]) { |
480 | sdw_disable_stream(stream: sruntime); |
481 | sdw_deprepare_stream(stream: sruntime); |
482 | data->stream_prepared[cpu_dai->id] = false; |
483 | } |
484 | |
485 | ret = sdw_prepare_stream(stream: sruntime); |
486 | if (ret) |
487 | return ret; |
488 | |
489 | /** |
490 | * NOTE: there is a strict hw requirement about the ordering of port |
491 | * enables and actual WSA881x PA enable. PA enable should only happen |
492 | * after soundwire ports are enabled if not DC on the line is |
493 | * accumulated resulting in Click/Pop Noise |
494 | * PA enable/mute are handled as part of codec DAPM and digital mute. |
495 | */ |
496 | |
497 | ret = sdw_enable_stream(stream: sruntime); |
498 | if (ret) { |
499 | sdw_deprepare_stream(stream: sruntime); |
500 | return ret; |
501 | } |
502 | data->stream_prepared[cpu_dai->id] = true; |
503 | |
504 | return ret; |
505 | } |
506 | |
507 | static int sdm845_snd_hw_free(struct snd_pcm_substream *substream) |
508 | { |
509 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
510 | struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card: rtd->card); |
511 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
512 | struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; |
513 | |
514 | if (sruntime && data->stream_prepared[cpu_dai->id]) { |
515 | sdw_disable_stream(stream: sruntime); |
516 | sdw_deprepare_stream(stream: sruntime); |
517 | data->stream_prepared[cpu_dai->id] = false; |
518 | } |
519 | |
520 | return 0; |
521 | } |
522 | |
523 | static const struct snd_soc_ops sdm845_be_ops = { |
524 | .hw_params = sdm845_snd_hw_params, |
525 | .hw_free = sdm845_snd_hw_free, |
526 | .prepare = sdm845_snd_prepare, |
527 | .startup = sdm845_snd_startup, |
528 | .shutdown = sdm845_snd_shutdown, |
529 | }; |
530 | |
531 | static int sdm845_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, |
532 | struct snd_pcm_hw_params *params) |
533 | { |
534 | struct snd_interval *rate = hw_param_interval(params, |
535 | SNDRV_PCM_HW_PARAM_RATE); |
536 | struct snd_interval *channels = hw_param_interval(params, |
537 | SNDRV_PCM_HW_PARAM_CHANNELS); |
538 | struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); |
539 | |
540 | rate->min = rate->max = DEFAULT_SAMPLE_RATE_48K; |
541 | channels->min = channels->max = 2; |
542 | snd_mask_set_format(mask: fmt, SNDRV_PCM_FORMAT_S16_LE); |
543 | |
544 | return 0; |
545 | } |
546 | |
547 | static const struct snd_soc_dapm_widget sdm845_snd_widgets[] = { |
548 | SND_SOC_DAPM_HP("Headphone Jack" , NULL), |
549 | SND_SOC_DAPM_MIC("Headset Mic" , NULL), |
550 | SND_SOC_DAPM_SPK("Left Spk" , NULL), |
551 | SND_SOC_DAPM_SPK("Right Spk" , NULL), |
552 | SND_SOC_DAPM_MIC("Int Mic" , NULL), |
553 | }; |
554 | |
555 | static const struct snd_kcontrol_new sdm845_snd_controls[] = { |
556 | SOC_DAPM_PIN_SWITCH("Headphone Jack" ), |
557 | SOC_DAPM_PIN_SWITCH("Headset Mic" ), |
558 | }; |
559 | |
560 | static void sdm845_add_ops(struct snd_soc_card *card) |
561 | { |
562 | struct snd_soc_dai_link *link; |
563 | int i; |
564 | |
565 | for_each_card_prelinks(card, i, link) { |
566 | if (link->no_pcm == 1) { |
567 | link->ops = &sdm845_be_ops; |
568 | link->be_hw_params_fixup = sdm845_be_hw_params_fixup; |
569 | } |
570 | link->init = sdm845_dai_init; |
571 | } |
572 | } |
573 | |
574 | static int sdm845_snd_platform_probe(struct platform_device *pdev) |
575 | { |
576 | struct snd_soc_card *card; |
577 | struct sdm845_snd_data *data; |
578 | struct device *dev = &pdev->dev; |
579 | int ret; |
580 | |
581 | card = devm_kzalloc(dev, size: sizeof(*card), GFP_KERNEL); |
582 | if (!card) |
583 | return -ENOMEM; |
584 | |
585 | /* Allocate the private data */ |
586 | data = devm_kzalloc(dev, size: sizeof(*data), GFP_KERNEL); |
587 | if (!data) |
588 | return -ENOMEM; |
589 | |
590 | card->driver_name = DRIVER_NAME; |
591 | card->dapm_widgets = sdm845_snd_widgets; |
592 | card->num_dapm_widgets = ARRAY_SIZE(sdm845_snd_widgets); |
593 | card->controls = sdm845_snd_controls; |
594 | card->num_controls = ARRAY_SIZE(sdm845_snd_controls); |
595 | card->dev = dev; |
596 | card->owner = THIS_MODULE; |
597 | dev_set_drvdata(dev, data: card); |
598 | ret = qcom_snd_parse_of(card); |
599 | if (ret) |
600 | return ret; |
601 | |
602 | data->card = card; |
603 | snd_soc_card_set_drvdata(card, data); |
604 | |
605 | sdm845_add_ops(card); |
606 | return devm_snd_soc_register_card(dev, card); |
607 | } |
608 | |
609 | static const struct of_device_id sdm845_snd_device_id[] = { |
610 | { .compatible = "qcom,sdm845-sndcard" }, |
611 | /* Do not grow the list for compatible devices */ |
612 | { .compatible = "qcom,db845c-sndcard" }, |
613 | { .compatible = "lenovo,yoga-c630-sndcard" }, |
614 | {}, |
615 | }; |
616 | MODULE_DEVICE_TABLE(of, sdm845_snd_device_id); |
617 | |
618 | static struct platform_driver sdm845_snd_driver = { |
619 | .probe = sdm845_snd_platform_probe, |
620 | .driver = { |
621 | .name = "msm-snd-sdm845" , |
622 | .of_match_table = sdm845_snd_device_id, |
623 | }, |
624 | }; |
625 | module_platform_driver(sdm845_snd_driver); |
626 | |
627 | MODULE_DESCRIPTION("sdm845 ASoC Machine Driver" ); |
628 | MODULE_LICENSE("GPL" ); |
629 | |