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
33struct 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
45static 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
56static unsigned int tdm_slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28};
57
58static 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
98static 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
179end:
180 return ret;
181}
182
183static 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
225static 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
232static 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
330static 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
422static 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
468static 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
507static 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
523static 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
531static 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
547static 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
555static const struct snd_kcontrol_new sdm845_snd_controls[] = {
556 SOC_DAPM_PIN_SWITCH("Headphone Jack"),
557 SOC_DAPM_PIN_SWITCH("Headset Mic"),
558};
559
560static 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
574static 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
609static 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};
616MODULE_DEVICE_TABLE(of, sdm845_snd_device_id);
617
618static 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};
625module_platform_driver(sdm845_snd_driver);
626
627MODULE_DESCRIPTION("sdm845 ASoC Machine Driver");
628MODULE_LICENSE("GPL");
629

source code of linux/sound/soc/qcom/sdm845.c