2 * cht-bsw-rt5645.c - ASoc Machine driver for Intel Cherryview-based platforms
3 * Cherrytrail and Braswell, with RT5645 codec.
5 * Copyright (C) 2015 Intel Corp
6 * Author: Fang, Yang A <yang.a.fang@intel.com>
7 * N,Harshapriya <harshapriya.n@intel.com>
8 * This file is modified from cht_bsw_rt5672.c
9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; version 2 of the License.
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23 #include <linux/module.h>
24 #include <linux/acpi.h>
25 #include <linux/platform_device.h>
26 #include <linux/slab.h>
27 #include <sound/pcm.h>
28 #include <sound/pcm_params.h>
29 #include <sound/soc.h>
30 #include <sound/jack.h>
31 #include "../../codecs/rt5645.h"
32 #include "../atom/sst-atom-controls.h"
34 #define CHT_PLAT_CLK_3_HZ 19200000
35 #define CHT_CODEC_DAI "rt5645-aif1"
37 struct cht_acpi_card
{
40 struct snd_soc_card
*soc_card
;
43 struct cht_mc_private
{
44 struct snd_soc_jack jack
;
45 struct cht_acpi_card
*acpi_card
;
48 static inline struct snd_soc_dai
*cht_get_codec_dai(struct snd_soc_card
*card
)
52 for (i
= 0; i
< card
->num_rtd
; i
++) {
53 struct snd_soc_pcm_runtime
*rtd
;
56 if (!strncmp(rtd
->codec_dai
->name
, CHT_CODEC_DAI
,
57 strlen(CHT_CODEC_DAI
)))
58 return rtd
->codec_dai
;
63 static int platform_clock_control(struct snd_soc_dapm_widget
*w
,
64 struct snd_kcontrol
*k
, int event
)
66 struct snd_soc_dapm_context
*dapm
= w
->dapm
;
67 struct snd_soc_card
*card
= dapm
->card
;
68 struct snd_soc_dai
*codec_dai
;
71 codec_dai
= cht_get_codec_dai(card
);
73 dev_err(card
->dev
, "Codec dai not found; Unable to set platform clock\n");
77 if (!SND_SOC_DAPM_EVENT_OFF(event
))
80 /* Set codec sysclk source to its internal clock because codec PLL will
81 * be off when idle and MCLK will also be off by ACPI when codec is
82 * runtime suspended. Codec needs clock for jack detection and button
85 ret
= snd_soc_dai_set_sysclk(codec_dai
, RT5645_SCLK_S_RCCLK
,
88 dev_err(card
->dev
, "can't set codec sysclk: %d\n", ret
);
95 static const struct snd_soc_dapm_widget cht_dapm_widgets
[] = {
96 SND_SOC_DAPM_HP("Headphone", NULL
),
97 SND_SOC_DAPM_MIC("Headset Mic", NULL
),
98 SND_SOC_DAPM_MIC("Int Mic", NULL
),
99 SND_SOC_DAPM_SPK("Ext Spk", NULL
),
100 SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM
, 0, 0,
101 platform_clock_control
, SND_SOC_DAPM_POST_PMD
),
104 static const struct snd_soc_dapm_route cht_rt5645_audio_map
[] = {
105 {"IN1P", NULL
, "Headset Mic"},
106 {"IN1N", NULL
, "Headset Mic"},
107 {"DMIC L1", NULL
, "Int Mic"},
108 {"DMIC R1", NULL
, "Int Mic"},
109 {"Headphone", NULL
, "HPOL"},
110 {"Headphone", NULL
, "HPOR"},
111 {"Ext Spk", NULL
, "SPOL"},
112 {"Ext Spk", NULL
, "SPOR"},
113 {"AIF1 Playback", NULL
, "ssp2 Tx"},
114 {"ssp2 Tx", NULL
, "codec_out0"},
115 {"ssp2 Tx", NULL
, "codec_out1"},
116 {"codec_in0", NULL
, "ssp2 Rx" },
117 {"codec_in1", NULL
, "ssp2 Rx" },
118 {"ssp2 Rx", NULL
, "AIF1 Capture"},
119 {"Headphone", NULL
, "Platform Clock"},
120 {"Headset Mic", NULL
, "Platform Clock"},
121 {"Int Mic", NULL
, "Platform Clock"},
122 {"Ext Spk", NULL
, "Platform Clock"},
125 static const struct snd_soc_dapm_route cht_rt5650_audio_map
[] = {
126 {"IN1P", NULL
, "Headset Mic"},
127 {"IN1N", NULL
, "Headset Mic"},
128 {"DMIC L2", NULL
, "Int Mic"},
129 {"DMIC R2", NULL
, "Int Mic"},
130 {"Headphone", NULL
, "HPOL"},
131 {"Headphone", NULL
, "HPOR"},
132 {"Ext Spk", NULL
, "SPOL"},
133 {"Ext Spk", NULL
, "SPOR"},
134 {"AIF1 Playback", NULL
, "ssp2 Tx"},
135 {"ssp2 Tx", NULL
, "codec_out0"},
136 {"ssp2 Tx", NULL
, "codec_out1"},
137 {"codec_in0", NULL
, "ssp2 Rx" },
138 {"codec_in1", NULL
, "ssp2 Rx" },
139 {"ssp2 Rx", NULL
, "AIF1 Capture"},
140 {"Headphone", NULL
, "Platform Clock"},
141 {"Headset Mic", NULL
, "Platform Clock"},
142 {"Int Mic", NULL
, "Platform Clock"},
143 {"Ext Spk", NULL
, "Platform Clock"},
146 static const struct snd_kcontrol_new cht_mc_controls
[] = {
147 SOC_DAPM_PIN_SWITCH("Headphone"),
148 SOC_DAPM_PIN_SWITCH("Headset Mic"),
149 SOC_DAPM_PIN_SWITCH("Int Mic"),
150 SOC_DAPM_PIN_SWITCH("Ext Spk"),
153 static int cht_aif1_hw_params(struct snd_pcm_substream
*substream
,
154 struct snd_pcm_hw_params
*params
)
156 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
157 struct snd_soc_dai
*codec_dai
= rtd
->codec_dai
;
160 /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
161 ret
= snd_soc_dai_set_pll(codec_dai
, 0, RT5645_PLL1_S_MCLK
,
162 CHT_PLAT_CLK_3_HZ
, params_rate(params
) * 512);
164 dev_err(rtd
->dev
, "can't set codec pll: %d\n", ret
);
168 ret
= snd_soc_dai_set_sysclk(codec_dai
, RT5645_SCLK_S_PLL1
,
169 params_rate(params
) * 512, SND_SOC_CLOCK_IN
);
171 dev_err(rtd
->dev
, "can't set codec sysclk: %d\n", ret
);
178 static int cht_codec_init(struct snd_soc_pcm_runtime
*runtime
)
182 struct snd_soc_codec
*codec
= runtime
->codec
;
183 struct snd_soc_dai
*codec_dai
= runtime
->codec_dai
;
184 struct cht_mc_private
*ctx
= snd_soc_card_get_drvdata(runtime
->card
);
186 /* Select clk_i2s1_asrc as ASRC clock source */
187 rt5645_sel_asrc_clk_src(codec
,
188 RT5645_DA_STEREO_FILTER
|
189 RT5645_DA_MONO_L_FILTER
|
190 RT5645_DA_MONO_R_FILTER
|
191 RT5645_AD_STEREO_FILTER
,
192 RT5645_CLK_SEL_I2S1_ASRC
);
194 /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
195 ret
= snd_soc_dai_set_tdm_slot(codec_dai
, 0xF, 0xF, 4, 24);
197 dev_err(runtime
->dev
, "can't set codec TDM slot %d\n", ret
);
201 if (ctx
->acpi_card
->codec_type
== CODEC_TYPE_RT5650
)
202 jack_type
= SND_JACK_HEADPHONE
| SND_JACK_MICROPHONE
|
203 SND_JACK_BTN_0
| SND_JACK_BTN_1
|
204 SND_JACK_BTN_2
| SND_JACK_BTN_3
;
206 jack_type
= SND_JACK_HEADPHONE
| SND_JACK_MICROPHONE
;
208 ret
= snd_soc_card_jack_new(runtime
->card
, "Headset Jack",
209 jack_type
, &ctx
->jack
,
212 dev_err(runtime
->dev
, "Headset jack creation failed %d\n", ret
);
216 rt5645_set_jack_detect(codec
, &ctx
->jack
, &ctx
->jack
, &ctx
->jack
);
221 static int cht_codec_fixup(struct snd_soc_pcm_runtime
*rtd
,
222 struct snd_pcm_hw_params
*params
)
224 struct snd_interval
*rate
= hw_param_interval(params
,
225 SNDRV_PCM_HW_PARAM_RATE
);
226 struct snd_interval
*channels
= hw_param_interval(params
,
227 SNDRV_PCM_HW_PARAM_CHANNELS
);
229 /* The DSP will covert the FE rate to 48k, stereo, 24bits */
230 rate
->min
= rate
->max
= 48000;
231 channels
->min
= channels
->max
= 2;
233 /* set SSP2 to 24-bit */
234 params_set_format(params
, SNDRV_PCM_FORMAT_S24_LE
);
238 static int cht_aif1_startup(struct snd_pcm_substream
*substream
)
240 return snd_pcm_hw_constraint_single(substream
->runtime
,
241 SNDRV_PCM_HW_PARAM_RATE
, 48000);
244 static struct snd_soc_ops cht_aif1_ops
= {
245 .startup
= cht_aif1_startup
,
248 static struct snd_soc_ops cht_be_ssp2_ops
= {
249 .hw_params
= cht_aif1_hw_params
,
252 static struct snd_soc_dai_link cht_dailink
[] = {
253 [MERR_DPCM_AUDIO
] = {
254 .name
= "Audio Port",
255 .stream_name
= "Audio",
256 .cpu_dai_name
= "media-cpu-dai",
257 .codec_dai_name
= "snd-soc-dummy-dai",
258 .codec_name
= "snd-soc-dummy",
259 .platform_name
= "sst-mfld-platform",
264 .ops
= &cht_aif1_ops
,
266 [MERR_DPCM_COMPR
] = {
267 .name
= "Compressed Port",
268 .stream_name
= "Compress",
269 .cpu_dai_name
= "compress-cpu-dai",
270 .codec_dai_name
= "snd-soc-dummy-dai",
271 .codec_name
= "snd-soc-dummy",
272 .platform_name
= "sst-mfld-platform",
274 /* CODEC<->CODEC link */
277 .name
= "SSP2-Codec",
279 .cpu_dai_name
= "ssp2-port",
280 .platform_name
= "sst-mfld-platform",
282 .codec_dai_name
= "rt5645-aif1",
283 .codec_name
= "i2c-10EC5645:00",
284 .dai_fmt
= SND_SOC_DAIFMT_DSP_B
| SND_SOC_DAIFMT_IB_NF
285 | SND_SOC_DAIFMT_CBS_CFS
,
286 .init
= cht_codec_init
,
287 .be_hw_params_fixup
= cht_codec_fixup
,
291 .ops
= &cht_be_ssp2_ops
,
296 static struct snd_soc_card snd_soc_card_chtrt5645
= {
298 .owner
= THIS_MODULE
,
299 .dai_link
= cht_dailink
,
300 .num_links
= ARRAY_SIZE(cht_dailink
),
301 .dapm_widgets
= cht_dapm_widgets
,
302 .num_dapm_widgets
= ARRAY_SIZE(cht_dapm_widgets
),
303 .dapm_routes
= cht_rt5645_audio_map
,
304 .num_dapm_routes
= ARRAY_SIZE(cht_rt5645_audio_map
),
305 .controls
= cht_mc_controls
,
306 .num_controls
= ARRAY_SIZE(cht_mc_controls
),
309 static struct snd_soc_card snd_soc_card_chtrt5650
= {
311 .owner
= THIS_MODULE
,
312 .dai_link
= cht_dailink
,
313 .num_links
= ARRAY_SIZE(cht_dailink
),
314 .dapm_widgets
= cht_dapm_widgets
,
315 .num_dapm_widgets
= ARRAY_SIZE(cht_dapm_widgets
),
316 .dapm_routes
= cht_rt5650_audio_map
,
317 .num_dapm_routes
= ARRAY_SIZE(cht_rt5650_audio_map
),
318 .controls
= cht_mc_controls
,
319 .num_controls
= ARRAY_SIZE(cht_mc_controls
),
322 static struct cht_acpi_card snd_soc_cards
[] = {
323 {"10EC5645", CODEC_TYPE_RT5645
, &snd_soc_card_chtrt5645
},
324 {"10EC5650", CODEC_TYPE_RT5650
, &snd_soc_card_chtrt5650
},
327 static acpi_status
snd_acpi_codec_match(acpi_handle handle
, u32 level
,
328 void *context
, void **ret
)
330 *(bool *)context
= true;
334 static int snd_cht_mc_probe(struct platform_device
*pdev
)
338 struct cht_mc_private
*drv
;
339 struct snd_soc_card
*card
= snd_soc_cards
[0].soc_card
;
343 drv
= devm_kzalloc(&pdev
->dev
, sizeof(*drv
), GFP_ATOMIC
);
347 for (i
= 0; i
< ARRAY_SIZE(snd_soc_cards
); i
++) {
348 if (ACPI_SUCCESS(acpi_get_devices(
349 snd_soc_cards
[i
].codec_id
,
350 snd_acpi_codec_match
,
351 &found
, NULL
)) && found
) {
353 "found codec %s\n", snd_soc_cards
[i
].codec_id
);
354 card
= snd_soc_cards
[i
].soc_card
;
355 drv
->acpi_card
= &snd_soc_cards
[i
];
359 card
->dev
= &pdev
->dev
;
360 sprintf(codec_name
, "i2c-%s:00", drv
->acpi_card
->codec_id
);
361 /* set correct codec name */
362 strcpy((char *)card
->dai_link
[2].codec_name
, codec_name
);
363 snd_soc_card_set_drvdata(card
, drv
);
364 ret_val
= devm_snd_soc_register_card(&pdev
->dev
, card
);
367 "snd_soc_register_card failed %d\n", ret_val
);
370 platform_set_drvdata(pdev
, card
);
374 static struct platform_driver snd_cht_mc_driver
= {
376 .name
= "cht-bsw-rt5645",
378 .probe
= snd_cht_mc_probe
,
381 module_platform_driver(snd_cht_mc_driver
)
383 MODULE_DESCRIPTION("ASoC Intel(R) Braswell Machine driver");
384 MODULE_AUTHOR("Fang, Yang A,N,Harshapriya");
385 MODULE_LICENSE("GPL v2");
386 MODULE_ALIAS("platform:cht-bsw-rt5645");