1 // SPDX-License-Identifier: GPL-2.0-only
3 * bytcht-da7213.c - ASoc Machine driver for Intel Baytrail and
4 * Cherrytrail-based platforms, with Dialog DA7213 codec
6 * Copyright (C) 2017 Intel Corporation
7 * Author: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14 #include <linux/module.h>
15 #include <linux/acpi.h>
16 #include <linux/platform_device.h>
17 #include <linux/slab.h>
18 #include <sound/pcm.h>
19 #include <sound/pcm_params.h>
20 #include <sound/soc.h>
21 #include <sound/soc-acpi.h>
22 #include "../../codecs/da7213.h"
23 #include "../atom/sst-atom-controls.h"
25 static const struct snd_kcontrol_new controls
[] = {
26 SOC_DAPM_PIN_SWITCH("Headphone Jack"),
27 SOC_DAPM_PIN_SWITCH("Headset Mic"),
28 SOC_DAPM_PIN_SWITCH("Mic"),
29 SOC_DAPM_PIN_SWITCH("Aux In"),
32 static const struct snd_soc_dapm_widget dapm_widgets
[] = {
33 SND_SOC_DAPM_HP("Headphone Jack", NULL
),
34 SND_SOC_DAPM_MIC("Headset Mic", NULL
),
35 SND_SOC_DAPM_MIC("Mic", NULL
),
36 SND_SOC_DAPM_LINE("Aux In", NULL
),
39 static const struct snd_soc_dapm_route audio_map
[] = {
40 {"Headphone Jack", NULL
, "HPL"},
41 {"Headphone Jack", NULL
, "HPR"},
43 {"AUXL", NULL
, "Aux In"},
44 {"AUXR", NULL
, "Aux In"},
46 /* Assume Mic1 is linked to Headset and Mic2 to on-board mic */
47 {"MIC1", NULL
, "Headset Mic"},
48 {"MIC2", NULL
, "Mic"},
51 {"ssp2 Tx", NULL
, "codec_out0"},
52 {"ssp2 Tx", NULL
, "codec_out1"},
53 {"codec_in0", NULL
, "ssp2 Rx"},
54 {"codec_in1", NULL
, "ssp2 Rx"},
56 {"Playback", NULL
, "ssp2 Tx"},
57 {"ssp2 Rx", NULL
, "Capture"},
60 static int codec_fixup(struct snd_soc_pcm_runtime
*rtd
,
61 struct snd_pcm_hw_params
*params
)
64 struct snd_interval
*rate
= hw_param_interval(params
,
65 SNDRV_PCM_HW_PARAM_RATE
);
66 struct snd_interval
*channels
= hw_param_interval(params
,
67 SNDRV_PCM_HW_PARAM_CHANNELS
);
69 /* The DSP will convert the FE rate to 48k, stereo, 24bits */
70 rate
->min
= rate
->max
= 48000;
71 channels
->min
= channels
->max
= 2;
73 /* set SSP2 to 24-bit */
74 params_set_format(params
, SNDRV_PCM_FORMAT_S24_LE
);
77 * Default mode for SSP configuration is TDM 4 slot, override config
78 * with explicit setting to I2S 2ch 24-bit. The word length is set with
79 * dai_set_tdm_slot() since there is no other API exposed
81 ret
= snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd
, 0),
83 SND_SOC_DAIFMT_NB_NF
|
84 SND_SOC_DAIFMT_CBS_CFS
);
86 dev_err(rtd
->dev
, "can't set format to I2S, err %d\n", ret
);
90 ret
= snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd
, 0), 0x3, 0x3, 2, 24);
92 dev_err(rtd
->dev
, "can't set I2S config, err %d\n", ret
);
99 static int aif1_startup(struct snd_pcm_substream
*substream
)
101 return snd_pcm_hw_constraint_single(substream
->runtime
,
102 SNDRV_PCM_HW_PARAM_RATE
, 48000);
105 static int aif1_hw_params(struct snd_pcm_substream
*substream
,
106 struct snd_pcm_hw_params
*params
)
108 struct snd_soc_pcm_runtime
*rtd
= asoc_substream_to_rtd(substream
);
109 struct snd_soc_dai
*codec_dai
= asoc_rtd_to_codec(rtd
, 0);
112 ret
= snd_soc_dai_set_sysclk(codec_dai
, DA7213_CLKSRC_MCLK
,
113 19200000, SND_SOC_CLOCK_IN
);
115 dev_err(codec_dai
->dev
, "can't set codec sysclk configuration\n");
117 ret
= snd_soc_dai_set_pll(codec_dai
, 0,
118 DA7213_SYSCLK_PLL_SRM
, 0, DA7213_PLL_FREQ_OUT_98304000
);
120 dev_err(codec_dai
->dev
, "failed to start PLL: %d\n", ret
);
127 static int aif1_hw_free(struct snd_pcm_substream
*substream
)
129 struct snd_soc_pcm_runtime
*rtd
= asoc_substream_to_rtd(substream
);
130 struct snd_soc_dai
*codec_dai
= asoc_rtd_to_codec(rtd
, 0);
133 ret
= snd_soc_dai_set_pll(codec_dai
, 0,
134 DA7213_SYSCLK_MCLK
, 0, 0);
136 dev_err(codec_dai
->dev
, "failed to stop PLL: %d\n", ret
);
143 static const struct snd_soc_ops aif1_ops
= {
144 .startup
= aif1_startup
,
147 static const struct snd_soc_ops ssp2_ops
= {
148 .hw_params
= aif1_hw_params
,
149 .hw_free
= aif1_hw_free
,
153 SND_SOC_DAILINK_DEF(dummy
,
154 DAILINK_COMP_ARRAY(COMP_DUMMY()));
156 SND_SOC_DAILINK_DEF(media
,
157 DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
159 SND_SOC_DAILINK_DEF(deepbuffer
,
160 DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));
162 SND_SOC_DAILINK_DEF(ssp2_port
,
163 DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));
164 SND_SOC_DAILINK_DEF(ssp2_codec
,
165 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-DLGS7213:00",
168 SND_SOC_DAILINK_DEF(platform
,
169 DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));
171 static struct snd_soc_dai_link dailink
[] = {
172 [MERR_DPCM_AUDIO
] = {
173 .name
= "Audio Port",
174 .stream_name
= "Audio",
180 SND_SOC_DAILINK_REG(media
, dummy
, platform
),
182 [MERR_DPCM_DEEP_BUFFER
] = {
183 .name
= "Deep-Buffer Audio Port",
184 .stream_name
= "Deep-Buffer Audio",
189 SND_SOC_DAILINK_REG(deepbuffer
, dummy
, platform
),
191 /* CODEC<->CODEC link */
194 .name
= "SSP2-Codec",
197 .dai_fmt
= SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
198 | SND_SOC_DAIFMT_CBS_CFS
,
199 .be_hw_params_fixup
= codec_fixup
,
204 SND_SOC_DAILINK_REG(ssp2_port
, ssp2_codec
, platform
),
208 /* use space before codec name to simplify card ID, and simplify driver name */
209 #define SOF_CARD_NAME "bytcht da7213" /* card name will be 'sof-bytcht da7213' */
210 #define SOF_DRIVER_NAME "SOF"
212 #define CARD_NAME "bytcht-da7213"
213 #define DRIVER_NAME NULL /* card name will be used for driver name */
216 static struct snd_soc_card bytcht_da7213_card
= {
218 .driver_name
= DRIVER_NAME
,
219 .owner
= THIS_MODULE
,
221 .num_links
= ARRAY_SIZE(dailink
),
222 .controls
= controls
,
223 .num_controls
= ARRAY_SIZE(controls
),
224 .dapm_widgets
= dapm_widgets
,
225 .num_dapm_widgets
= ARRAY_SIZE(dapm_widgets
),
226 .dapm_routes
= audio_map
,
227 .num_dapm_routes
= ARRAY_SIZE(audio_map
),
230 static char codec_name
[SND_ACPI_I2C_ID_LEN
];
232 static int bytcht_da7213_probe(struct platform_device
*pdev
)
234 struct snd_soc_card
*card
;
235 struct snd_soc_acpi_mach
*mach
;
236 const char *platform_name
;
237 struct acpi_device
*adev
;
243 mach
= pdev
->dev
.platform_data
;
244 card
= &bytcht_da7213_card
;
245 card
->dev
= &pdev
->dev
;
247 /* fix index of codec dai */
248 for (i
= 0; i
< ARRAY_SIZE(dailink
); i
++) {
249 if (!strcmp(dailink
[i
].codecs
->name
, "i2c-DLGS7213:00")) {
255 /* fixup codec name based on HID */
256 adev
= acpi_dev_get_first_match_dev(mach
->id
, NULL
, -1);
258 snprintf(codec_name
, sizeof(codec_name
),
259 "i2c-%s", acpi_dev_name(adev
));
260 put_device(&adev
->dev
);
261 dailink
[dai_index
].codecs
->name
= codec_name
;
264 /* override plaform name, if required */
265 platform_name
= mach
->mach_params
.platform
;
267 ret_val
= snd_soc_fixup_dai_links_platform_name(card
, platform_name
);
271 sof_parent
= snd_soc_acpi_sof_parent(&pdev
->dev
);
273 /* set card and driver name */
275 bytcht_da7213_card
.name
= SOF_CARD_NAME
;
276 bytcht_da7213_card
.driver_name
= SOF_DRIVER_NAME
;
278 bytcht_da7213_card
.name
= CARD_NAME
;
279 bytcht_da7213_card
.driver_name
= DRIVER_NAME
;
284 pdev
->dev
.driver
->pm
= &snd_soc_pm_ops
;
286 ret_val
= devm_snd_soc_register_card(&pdev
->dev
, card
);
289 "snd_soc_register_card failed %d\n", ret_val
);
292 platform_set_drvdata(pdev
, card
);
296 static struct platform_driver bytcht_da7213_driver
= {
298 .name
= "bytcht_da7213",
300 .probe
= bytcht_da7213_probe
,
302 module_platform_driver(bytcht_da7213_driver
);
304 MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail+DA7213 Machine driver");
305 MODULE_AUTHOR("Pierre-Louis Bossart");
306 MODULE_LICENSE("GPL v2");
307 MODULE_ALIAS("platform:bytcht_da7213");