1 // SPDX-License-Identifier: GPL-2.0-only
3 * cht_bsw_rt5672.c - ASoc Machine driver for Intel Cherryview-based platforms
4 * Cherrytrail and Braswell, with RT5672 codec.
6 * Copyright (C) 2014 Intel Corp
7 * Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
8 * Mengdong Lin <mengdong.lin@intel.com>
11 #include <linux/gpio/consumer.h>
12 #include <linux/input.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/slab.h>
16 #include <linux/clk.h>
17 #include <sound/pcm.h>
18 #include <sound/pcm_params.h>
19 #include <sound/soc.h>
20 #include <sound/jack.h>
21 #include <sound/soc-acpi.h>
22 #include "../../codecs/rt5670.h"
23 #include "../atom/sst-atom-controls.h"
26 /* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */
27 #define CHT_PLAT_CLK_3_HZ 19200000
28 #define CHT_CODEC_DAI "rt5670-aif1"
30 struct cht_mc_private
{
31 struct snd_soc_jack headset
;
32 char codec_name
[SND_ACPI_I2C_ID_LEN
];
36 /* Headset jack detection DAPM pins */
37 static struct snd_soc_jack_pin cht_bsw_headset_pins
[] = {
40 .mask
= SND_JACK_MICROPHONE
,
44 .mask
= SND_JACK_HEADPHONE
,
48 static int platform_clock_control(struct snd_soc_dapm_widget
*w
,
49 struct snd_kcontrol
*k
, int event
)
51 struct snd_soc_dapm_context
*dapm
= w
->dapm
;
52 struct snd_soc_card
*card
= dapm
->card
;
53 struct snd_soc_dai
*codec_dai
;
54 struct cht_mc_private
*ctx
= snd_soc_card_get_drvdata(card
);
57 codec_dai
= snd_soc_card_get_codec_dai(card
, CHT_CODEC_DAI
);
59 dev_err(card
->dev
, "Codec dai not found; Unable to set platform clock\n");
63 if (SND_SOC_DAPM_EVENT_ON(event
)) {
65 ret
= clk_prepare_enable(ctx
->mclk
);
68 "could not configure MCLK state");
73 /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
74 ret
= snd_soc_dai_set_pll(codec_dai
, 0, RT5670_PLL1_S_MCLK
,
75 CHT_PLAT_CLK_3_HZ
, 48000 * 512);
77 dev_err(card
->dev
, "can't set codec pll: %d\n", ret
);
81 /* set codec sysclk source to PLL */
82 ret
= snd_soc_dai_set_sysclk(codec_dai
, RT5670_SCLK_S_PLL1
,
83 48000 * 512, SND_SOC_CLOCK_IN
);
85 dev_err(card
->dev
, "can't set codec sysclk: %d\n", ret
);
89 /* Set codec sysclk source to its internal clock because codec
90 * PLL will be off when idle and MCLK will also be off by ACPI
91 * when codec is runtime suspended. Codec needs clock for jack
92 * detection and button press.
94 snd_soc_dai_set_sysclk(codec_dai
, RT5670_SCLK_S_RCCLK
,
95 48000 * 512, SND_SOC_CLOCK_IN
);
98 clk_disable_unprepare(ctx
->mclk
);
103 static const struct snd_soc_dapm_widget cht_dapm_widgets
[] = {
104 SND_SOC_DAPM_HP("Headphone", NULL
),
105 SND_SOC_DAPM_MIC("Headset Mic", NULL
),
106 SND_SOC_DAPM_MIC("Int Mic", NULL
),
107 SND_SOC_DAPM_SPK("Ext Spk", NULL
),
108 SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM
, 0, 0,
109 platform_clock_control
, SND_SOC_DAPM_PRE_PMU
|
110 SND_SOC_DAPM_POST_PMD
),
113 static const struct snd_soc_dapm_route cht_audio_map
[] = {
114 {"IN1P", NULL
, "Headset Mic"},
115 {"IN1N", NULL
, "Headset Mic"},
116 {"DMIC L1", NULL
, "Int Mic"},
117 {"DMIC R1", NULL
, "Int Mic"},
118 {"Headphone", NULL
, "HPOL"},
119 {"Headphone", NULL
, "HPOR"},
120 {"Ext Spk", NULL
, "SPOLP"},
121 {"Ext Spk", NULL
, "SPOLN"},
122 {"Ext Spk", NULL
, "SPORP"},
123 {"Ext Spk", NULL
, "SPORN"},
124 {"AIF1 Playback", NULL
, "ssp2 Tx"},
125 {"ssp2 Tx", NULL
, "codec_out0"},
126 {"ssp2 Tx", NULL
, "codec_out1"},
127 {"codec_in0", NULL
, "ssp2 Rx"},
128 {"codec_in1", NULL
, "ssp2 Rx"},
129 {"ssp2 Rx", NULL
, "AIF1 Capture"},
130 {"Headphone", NULL
, "Platform Clock"},
131 {"Headset Mic", NULL
, "Platform Clock"},
132 {"Int Mic", NULL
, "Platform Clock"},
133 {"Ext Spk", NULL
, "Platform Clock"},
136 static const struct snd_kcontrol_new cht_mc_controls
[] = {
137 SOC_DAPM_PIN_SWITCH("Headphone"),
138 SOC_DAPM_PIN_SWITCH("Headset Mic"),
139 SOC_DAPM_PIN_SWITCH("Int Mic"),
140 SOC_DAPM_PIN_SWITCH("Ext Spk"),
143 static int cht_aif1_hw_params(struct snd_pcm_substream
*substream
,
144 struct snd_pcm_hw_params
*params
)
146 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
147 struct snd_soc_dai
*codec_dai
= rtd
->codec_dai
;
150 /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
151 ret
= snd_soc_dai_set_pll(codec_dai
, 0, RT5670_PLL1_S_MCLK
,
152 CHT_PLAT_CLK_3_HZ
, params_rate(params
) * 512);
154 dev_err(rtd
->dev
, "can't set codec pll: %d\n", ret
);
158 /* set codec sysclk source to PLL */
159 ret
= snd_soc_dai_set_sysclk(codec_dai
, RT5670_SCLK_S_PLL1
,
160 params_rate(params
) * 512,
163 dev_err(rtd
->dev
, "can't set codec sysclk: %d\n", ret
);
169 static const struct acpi_gpio_params headset_gpios
= { 0, 0, false };
171 static const struct acpi_gpio_mapping cht_rt5672_gpios
[] = {
172 { "headset-gpios", &headset_gpios
, 1 },
176 static int cht_codec_init(struct snd_soc_pcm_runtime
*runtime
)
179 struct snd_soc_dai
*codec_dai
= runtime
->codec_dai
;
180 struct snd_soc_component
*component
= codec_dai
->component
;
181 struct cht_mc_private
*ctx
= snd_soc_card_get_drvdata(runtime
->card
);
183 if (devm_acpi_dev_add_driver_gpios(component
->dev
, cht_rt5672_gpios
))
184 dev_warn(runtime
->dev
, "Unable to add GPIO mapping table\n");
186 /* Select codec ASRC clock source to track I2S1 clock, because codec
187 * is in slave mode and 100fs I2S format (BCLK = 100 * LRCLK) cannot
188 * be supported by RT5672. Otherwise, ASRC will be disabled and cause
191 rt5670_sel_asrc_clk_src(component
,
192 RT5670_DA_STEREO_FILTER
193 | RT5670_DA_MONO_L_FILTER
194 | RT5670_DA_MONO_R_FILTER
195 | RT5670_AD_STEREO_FILTER
196 | RT5670_AD_MONO_L_FILTER
197 | RT5670_AD_MONO_R_FILTER
,
198 RT5670_CLK_SEL_I2S1_ASRC
);
200 ret
= snd_soc_card_jack_new(runtime
->card
, "Headset",
201 SND_JACK_HEADSET
| SND_JACK_BTN_0
|
202 SND_JACK_BTN_1
| SND_JACK_BTN_2
,
204 cht_bsw_headset_pins
,
205 ARRAY_SIZE(cht_bsw_headset_pins
));
209 snd_jack_set_key(ctx
->headset
.jack
, SND_JACK_BTN_0
, KEY_PLAYPAUSE
);
210 snd_jack_set_key(ctx
->headset
.jack
, SND_JACK_BTN_1
, KEY_VOLUMEUP
);
211 snd_jack_set_key(ctx
->headset
.jack
, SND_JACK_BTN_2
, KEY_VOLUMEDOWN
);
213 rt5670_set_jack_detect(component
, &ctx
->headset
);
216 * The firmware might enable the clock at
217 * boot (this information may or may not
218 * be reflected in the enable clock register).
219 * To change the rate we must disable the clock
220 * first to cover these cases. Due to common
221 * clock framework restrictions that do not allow
222 * to disable a clock that has not been enabled,
223 * we need to enable the clock first.
225 ret
= clk_prepare_enable(ctx
->mclk
);
227 clk_disable_unprepare(ctx
->mclk
);
229 ret
= clk_set_rate(ctx
->mclk
, CHT_PLAT_CLK_3_HZ
);
232 dev_err(runtime
->dev
, "unable to set MCLK rate\n");
239 static int cht_codec_fixup(struct snd_soc_pcm_runtime
*rtd
,
240 struct snd_pcm_hw_params
*params
)
242 struct snd_interval
*rate
= hw_param_interval(params
,
243 SNDRV_PCM_HW_PARAM_RATE
);
244 struct snd_interval
*channels
= hw_param_interval(params
,
245 SNDRV_PCM_HW_PARAM_CHANNELS
);
248 /* The DSP will covert the FE rate to 48k, stereo, 24bits */
249 rate
->min
= rate
->max
= 48000;
250 channels
->min
= channels
->max
= 2;
252 /* set SSP2 to 24-bit */
253 params_set_format(params
, SNDRV_PCM_FORMAT_S24_LE
);
256 * Default mode for SSP configuration is TDM 4 slot
258 ret
= snd_soc_dai_set_fmt(rtd
->codec_dai
,
259 SND_SOC_DAIFMT_DSP_B
|
260 SND_SOC_DAIFMT_IB_NF
|
261 SND_SOC_DAIFMT_CBS_CFS
);
263 dev_err(rtd
->dev
, "can't set format to TDM %d\n", ret
);
267 /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
268 ret
= snd_soc_dai_set_tdm_slot(rtd
->codec_dai
, 0xF, 0xF, 4, 24);
270 dev_err(rtd
->dev
, "can't set codec TDM slot %d\n", ret
);
277 static int cht_aif1_startup(struct snd_pcm_substream
*substream
)
279 return snd_pcm_hw_constraint_single(substream
->runtime
,
280 SNDRV_PCM_HW_PARAM_RATE
, 48000);
283 static const struct snd_soc_ops cht_aif1_ops
= {
284 .startup
= cht_aif1_startup
,
287 static const struct snd_soc_ops cht_be_ssp2_ops
= {
288 .hw_params
= cht_aif1_hw_params
,
291 SND_SOC_DAILINK_DEF(dummy
,
292 DAILINK_COMP_ARRAY(COMP_DUMMY()));
294 SND_SOC_DAILINK_DEF(media
,
295 DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
297 SND_SOC_DAILINK_DEF(deepbuffer
,
298 DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));
300 SND_SOC_DAILINK_DEF(ssp2_port
,
301 DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));
302 SND_SOC_DAILINK_DEF(ssp2_codec
,
303 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5670:00",
306 SND_SOC_DAILINK_DEF(platform
,
307 DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));
309 static struct snd_soc_dai_link cht_dailink
[] = {
310 /* Front End DAI links */
311 [MERR_DPCM_AUDIO
] = {
312 .name
= "Audio Port",
313 .stream_name
= "Audio",
318 .ops
= &cht_aif1_ops
,
319 SND_SOC_DAILINK_REG(media
, dummy
, platform
),
321 [MERR_DPCM_DEEP_BUFFER
] = {
322 .name
= "Deep-Buffer Audio Port",
323 .stream_name
= "Deep-Buffer Audio",
327 .ops
= &cht_aif1_ops
,
328 SND_SOC_DAILINK_REG(deepbuffer
, dummy
, platform
),
331 /* Back End DAI links */
334 .name
= "SSP2-Codec",
338 .init
= cht_codec_init
,
339 .be_hw_params_fixup
= cht_codec_fixup
,
342 .ops
= &cht_be_ssp2_ops
,
343 SND_SOC_DAILINK_REG(ssp2_port
, ssp2_codec
, platform
),
347 static int cht_suspend_pre(struct snd_soc_card
*card
)
349 struct snd_soc_component
*component
;
350 struct cht_mc_private
*ctx
= snd_soc_card_get_drvdata(card
);
352 for_each_card_components(card
, component
) {
353 if (!strncmp(component
->name
,
354 ctx
->codec_name
, sizeof(ctx
->codec_name
))) {
356 dev_dbg(component
->dev
, "disabling jack detect before going to suspend.\n");
357 rt5670_jack_suspend(component
);
364 static int cht_resume_post(struct snd_soc_card
*card
)
366 struct snd_soc_component
*component
;
367 struct cht_mc_private
*ctx
= snd_soc_card_get_drvdata(card
);
369 for_each_card_components(card
, component
) {
370 if (!strncmp(component
->name
,
371 ctx
->codec_name
, sizeof(ctx
->codec_name
))) {
373 dev_dbg(component
->dev
, "enabling jack detect for resume.\n");
374 rt5670_jack_resume(component
);
383 static struct snd_soc_card snd_soc_card_cht
= {
384 .name
= "cht-bsw-rt5672",
385 .owner
= THIS_MODULE
,
386 .dai_link
= cht_dailink
,
387 .num_links
= ARRAY_SIZE(cht_dailink
),
388 .dapm_widgets
= cht_dapm_widgets
,
389 .num_dapm_widgets
= ARRAY_SIZE(cht_dapm_widgets
),
390 .dapm_routes
= cht_audio_map
,
391 .num_dapm_routes
= ARRAY_SIZE(cht_audio_map
),
392 .controls
= cht_mc_controls
,
393 .num_controls
= ARRAY_SIZE(cht_mc_controls
),
394 .suspend_pre
= cht_suspend_pre
,
395 .resume_post
= cht_resume_post
,
398 #define RT5672_I2C_DEFAULT "i2c-10EC5670:00"
400 static int snd_cht_mc_probe(struct platform_device
*pdev
)
403 struct cht_mc_private
*drv
;
404 struct snd_soc_acpi_mach
*mach
= pdev
->dev
.platform_data
;
405 const char *platform_name
;
406 struct acpi_device
*adev
;
409 drv
= devm_kzalloc(&pdev
->dev
, sizeof(*drv
), GFP_KERNEL
);
413 strcpy(drv
->codec_name
, RT5672_I2C_DEFAULT
);
415 /* fixup codec name based on HID */
416 adev
= acpi_dev_get_first_match_dev(mach
->id
, NULL
, -1);
418 snprintf(drv
->codec_name
, sizeof(drv
->codec_name
),
419 "i2c-%s", acpi_dev_name(adev
));
420 put_device(&adev
->dev
);
421 for (i
= 0; i
< ARRAY_SIZE(cht_dailink
); i
++) {
422 if (!strcmp(cht_dailink
[i
].codecs
->name
,
423 RT5672_I2C_DEFAULT
)) {
424 cht_dailink
[i
].codecs
->name
= drv
->codec_name
;
430 /* override plaform name, if required */
431 snd_soc_card_cht
.dev
= &pdev
->dev
;
432 platform_name
= mach
->mach_params
.platform
;
434 ret_val
= snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht
,
439 drv
->mclk
= devm_clk_get(&pdev
->dev
, "pmc_plt_clk_3");
440 if (IS_ERR(drv
->mclk
)) {
442 "Failed to get MCLK from pmc_plt_clk_3: %ld\n",
444 return PTR_ERR(drv
->mclk
);
446 snd_soc_card_set_drvdata(&snd_soc_card_cht
, drv
);
448 /* register the soc card */
449 ret_val
= devm_snd_soc_register_card(&pdev
->dev
, &snd_soc_card_cht
);
452 "snd_soc_register_card failed %d\n", ret_val
);
455 platform_set_drvdata(pdev
, &snd_soc_card_cht
);
459 static struct platform_driver snd_cht_mc_driver
= {
461 .name
= "cht-bsw-rt5672",
463 .probe
= snd_cht_mc_probe
,
466 module_platform_driver(snd_cht_mc_driver
);
468 MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
469 MODULE_AUTHOR("Subhransu S. Prusty, Mengdong Lin");
470 MODULE_LICENSE("GPL v2");
471 MODULE_ALIAS("platform:cht-bsw-rt5672");