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
)
50 struct snd_soc_pcm_runtime
*rtd
;
52 list_for_each_entry(rtd
, &card
->rtd_list
, list
) {
53 if (!strncmp(rtd
->codec_dai
->name
, CHT_CODEC_DAI
,
54 strlen(CHT_CODEC_DAI
)))
55 return rtd
->codec_dai
;
60 static int platform_clock_control(struct snd_soc_dapm_widget
*w
,
61 struct snd_kcontrol
*k
, int event
)
63 struct snd_soc_dapm_context
*dapm
= w
->dapm
;
64 struct snd_soc_card
*card
= dapm
->card
;
65 struct snd_soc_dai
*codec_dai
;
68 codec_dai
= cht_get_codec_dai(card
);
70 dev_err(card
->dev
, "Codec dai not found; Unable to set platform clock\n");
74 if (!SND_SOC_DAPM_EVENT_OFF(event
))
77 /* Set codec sysclk source to its internal clock because codec PLL will
78 * be off when idle and MCLK will also be off by ACPI when codec is
79 * runtime suspended. Codec needs clock for jack detection and button
82 ret
= snd_soc_dai_set_sysclk(codec_dai
, RT5645_SCLK_S_RCCLK
,
85 dev_err(card
->dev
, "can't set codec sysclk: %d\n", ret
);
92 static const struct snd_soc_dapm_widget cht_dapm_widgets
[] = {
93 SND_SOC_DAPM_HP("Headphone", NULL
),
94 SND_SOC_DAPM_MIC("Headset Mic", NULL
),
95 SND_SOC_DAPM_MIC("Int Mic", NULL
),
96 SND_SOC_DAPM_SPK("Ext Spk", NULL
),
97 SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM
, 0, 0,
98 platform_clock_control
, SND_SOC_DAPM_POST_PMD
),
101 static const struct snd_soc_dapm_route cht_rt5645_audio_map
[] = {
102 {"IN1P", NULL
, "Headset Mic"},
103 {"IN1N", NULL
, "Headset Mic"},
104 {"DMIC L1", NULL
, "Int Mic"},
105 {"DMIC R1", NULL
, "Int Mic"},
106 {"Headphone", NULL
, "HPOL"},
107 {"Headphone", NULL
, "HPOR"},
108 {"Ext Spk", NULL
, "SPOL"},
109 {"Ext Spk", NULL
, "SPOR"},
110 {"AIF1 Playback", NULL
, "ssp2 Tx"},
111 {"ssp2 Tx", NULL
, "codec_out0"},
112 {"ssp2 Tx", NULL
, "codec_out1"},
113 {"codec_in0", NULL
, "ssp2 Rx" },
114 {"codec_in1", NULL
, "ssp2 Rx" },
115 {"ssp2 Rx", NULL
, "AIF1 Capture"},
116 {"Headphone", NULL
, "Platform Clock"},
117 {"Headset Mic", NULL
, "Platform Clock"},
118 {"Int Mic", NULL
, "Platform Clock"},
119 {"Ext Spk", NULL
, "Platform Clock"},
122 static const struct snd_soc_dapm_route cht_rt5650_audio_map
[] = {
123 {"IN1P", NULL
, "Headset Mic"},
124 {"IN1N", NULL
, "Headset Mic"},
125 {"DMIC L2", NULL
, "Int Mic"},
126 {"DMIC R2", NULL
, "Int Mic"},
127 {"Headphone", NULL
, "HPOL"},
128 {"Headphone", NULL
, "HPOR"},
129 {"Ext Spk", NULL
, "SPOL"},
130 {"Ext Spk", NULL
, "SPOR"},
131 {"AIF1 Playback", NULL
, "ssp2 Tx"},
132 {"ssp2 Tx", NULL
, "codec_out0"},
133 {"ssp2 Tx", NULL
, "codec_out1"},
134 {"codec_in0", NULL
, "ssp2 Rx" },
135 {"codec_in1", NULL
, "ssp2 Rx" },
136 {"ssp2 Rx", NULL
, "AIF1 Capture"},
137 {"Headphone", NULL
, "Platform Clock"},
138 {"Headset Mic", NULL
, "Platform Clock"},
139 {"Int Mic", NULL
, "Platform Clock"},
140 {"Ext Spk", NULL
, "Platform Clock"},
143 static const struct snd_kcontrol_new cht_mc_controls
[] = {
144 SOC_DAPM_PIN_SWITCH("Headphone"),
145 SOC_DAPM_PIN_SWITCH("Headset Mic"),
146 SOC_DAPM_PIN_SWITCH("Int Mic"),
147 SOC_DAPM_PIN_SWITCH("Ext Spk"),
150 static struct snd_soc_jack_pin cht_bsw_jack_pins
[] = {
153 .mask
= SND_JACK_HEADPHONE
,
156 .pin
= "Headset Mic",
157 .mask
= SND_JACK_MICROPHONE
,
161 static int cht_aif1_hw_params(struct snd_pcm_substream
*substream
,
162 struct snd_pcm_hw_params
*params
)
164 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
165 struct snd_soc_dai
*codec_dai
= rtd
->codec_dai
;
168 /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
169 ret
= snd_soc_dai_set_pll(codec_dai
, 0, RT5645_PLL1_S_MCLK
,
170 CHT_PLAT_CLK_3_HZ
, params_rate(params
) * 512);
172 dev_err(rtd
->dev
, "can't set codec pll: %d\n", ret
);
176 ret
= snd_soc_dai_set_sysclk(codec_dai
, RT5645_SCLK_S_PLL1
,
177 params_rate(params
) * 512, SND_SOC_CLOCK_IN
);
179 dev_err(rtd
->dev
, "can't set codec sysclk: %d\n", ret
);
186 static int cht_codec_init(struct snd_soc_pcm_runtime
*runtime
)
190 struct snd_soc_codec
*codec
= runtime
->codec
;
191 struct snd_soc_dai
*codec_dai
= runtime
->codec_dai
;
192 struct cht_mc_private
*ctx
= snd_soc_card_get_drvdata(runtime
->card
);
194 /* Select clk_i2s1_asrc as ASRC clock source */
195 rt5645_sel_asrc_clk_src(codec
,
196 RT5645_DA_STEREO_FILTER
|
197 RT5645_DA_MONO_L_FILTER
|
198 RT5645_DA_MONO_R_FILTER
|
199 RT5645_AD_STEREO_FILTER
,
200 RT5645_CLK_SEL_I2S1_ASRC
);
202 /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
203 ret
= snd_soc_dai_set_tdm_slot(codec_dai
, 0xF, 0xF, 4, 24);
205 dev_err(runtime
->dev
, "can't set codec TDM slot %d\n", ret
);
209 if (ctx
->acpi_card
->codec_type
== CODEC_TYPE_RT5650
)
210 jack_type
= SND_JACK_HEADPHONE
| SND_JACK_MICROPHONE
|
211 SND_JACK_BTN_0
| SND_JACK_BTN_1
|
212 SND_JACK_BTN_2
| SND_JACK_BTN_3
;
214 jack_type
= SND_JACK_HEADPHONE
| SND_JACK_MICROPHONE
;
216 ret
= snd_soc_card_jack_new(runtime
->card
, "Headset",
217 jack_type
, &ctx
->jack
,
218 cht_bsw_jack_pins
, ARRAY_SIZE(cht_bsw_jack_pins
));
220 dev_err(runtime
->dev
, "Headset jack creation failed %d\n", ret
);
224 rt5645_set_jack_detect(codec
, &ctx
->jack
, &ctx
->jack
, &ctx
->jack
);
229 static int cht_codec_fixup(struct snd_soc_pcm_runtime
*rtd
,
230 struct snd_pcm_hw_params
*params
)
232 struct snd_interval
*rate
= hw_param_interval(params
,
233 SNDRV_PCM_HW_PARAM_RATE
);
234 struct snd_interval
*channels
= hw_param_interval(params
,
235 SNDRV_PCM_HW_PARAM_CHANNELS
);
237 /* The DSP will covert the FE rate to 48k, stereo, 24bits */
238 rate
->min
= rate
->max
= 48000;
239 channels
->min
= channels
->max
= 2;
241 /* set SSP2 to 24-bit */
242 params_set_format(params
, SNDRV_PCM_FORMAT_S24_LE
);
246 static int cht_aif1_startup(struct snd_pcm_substream
*substream
)
248 return snd_pcm_hw_constraint_single(substream
->runtime
,
249 SNDRV_PCM_HW_PARAM_RATE
, 48000);
252 static struct snd_soc_ops cht_aif1_ops
= {
253 .startup
= cht_aif1_startup
,
256 static struct snd_soc_ops cht_be_ssp2_ops
= {
257 .hw_params
= cht_aif1_hw_params
,
260 static struct snd_soc_dai_link cht_dailink
[] = {
261 [MERR_DPCM_AUDIO
] = {
262 .name
= "Audio Port",
263 .stream_name
= "Audio",
264 .cpu_dai_name
= "media-cpu-dai",
265 .codec_dai_name
= "snd-soc-dummy-dai",
266 .codec_name
= "snd-soc-dummy",
267 .platform_name
= "sst-mfld-platform",
272 .ops
= &cht_aif1_ops
,
274 [MERR_DPCM_DEEP_BUFFER
] = {
275 .name
= "Deep-Buffer Audio Port",
276 .stream_name
= "Deep-Buffer Audio",
277 .cpu_dai_name
= "deepbuffer-cpu-dai",
278 .codec_dai_name
= "snd-soc-dummy-dai",
279 .codec_name
= "snd-soc-dummy",
280 .platform_name
= "sst-mfld-platform",
284 .ops
= &cht_aif1_ops
,
286 [MERR_DPCM_COMPR
] = {
287 .name
= "Compressed Port",
288 .stream_name
= "Compress",
289 .cpu_dai_name
= "compress-cpu-dai",
290 .codec_dai_name
= "snd-soc-dummy-dai",
291 .codec_name
= "snd-soc-dummy",
292 .platform_name
= "sst-mfld-platform",
294 /* CODEC<->CODEC link */
297 .name
= "SSP2-Codec",
299 .cpu_dai_name
= "ssp2-port",
300 .platform_name
= "sst-mfld-platform",
302 .codec_dai_name
= "rt5645-aif1",
303 .codec_name
= "i2c-10EC5645:00",
304 .dai_fmt
= SND_SOC_DAIFMT_DSP_B
| SND_SOC_DAIFMT_IB_NF
305 | SND_SOC_DAIFMT_CBS_CFS
,
306 .init
= cht_codec_init
,
307 .be_hw_params_fixup
= cht_codec_fixup
,
311 .ops
= &cht_be_ssp2_ops
,
316 static struct snd_soc_card snd_soc_card_chtrt5645
= {
318 .owner
= THIS_MODULE
,
319 .dai_link
= cht_dailink
,
320 .num_links
= ARRAY_SIZE(cht_dailink
),
321 .dapm_widgets
= cht_dapm_widgets
,
322 .num_dapm_widgets
= ARRAY_SIZE(cht_dapm_widgets
),
323 .dapm_routes
= cht_rt5645_audio_map
,
324 .num_dapm_routes
= ARRAY_SIZE(cht_rt5645_audio_map
),
325 .controls
= cht_mc_controls
,
326 .num_controls
= ARRAY_SIZE(cht_mc_controls
),
329 static struct snd_soc_card snd_soc_card_chtrt5650
= {
331 .owner
= THIS_MODULE
,
332 .dai_link
= cht_dailink
,
333 .num_links
= ARRAY_SIZE(cht_dailink
),
334 .dapm_widgets
= cht_dapm_widgets
,
335 .num_dapm_widgets
= ARRAY_SIZE(cht_dapm_widgets
),
336 .dapm_routes
= cht_rt5650_audio_map
,
337 .num_dapm_routes
= ARRAY_SIZE(cht_rt5650_audio_map
),
338 .controls
= cht_mc_controls
,
339 .num_controls
= ARRAY_SIZE(cht_mc_controls
),
342 static struct cht_acpi_card snd_soc_cards
[] = {
343 {"10EC5645", CODEC_TYPE_RT5645
, &snd_soc_card_chtrt5645
},
344 {"10EC5650", CODEC_TYPE_RT5650
, &snd_soc_card_chtrt5650
},
347 static int snd_cht_mc_probe(struct platform_device
*pdev
)
351 struct cht_mc_private
*drv
;
352 struct snd_soc_card
*card
= snd_soc_cards
[0].soc_card
;
355 drv
= devm_kzalloc(&pdev
->dev
, sizeof(*drv
), GFP_ATOMIC
);
359 for (i
= 0; i
< ARRAY_SIZE(snd_soc_cards
); i
++) {
360 if (acpi_dev_present(snd_soc_cards
[i
].codec_id
)) {
362 "found codec %s\n", snd_soc_cards
[i
].codec_id
);
363 card
= snd_soc_cards
[i
].soc_card
;
364 drv
->acpi_card
= &snd_soc_cards
[i
];
368 card
->dev
= &pdev
->dev
;
369 sprintf(codec_name
, "i2c-%s:00", drv
->acpi_card
->codec_id
);
371 /* set correct codec name */
372 for (i
= 0; i
< ARRAY_SIZE(cht_dailink
); i
++)
373 if (!strcmp(card
->dai_link
[i
].codec_name
, "i2c-10EC5645:00"))
374 card
->dai_link
[i
].codec_name
= kstrdup(codec_name
, GFP_KERNEL
);
376 snd_soc_card_set_drvdata(card
, drv
);
377 ret_val
= devm_snd_soc_register_card(&pdev
->dev
, card
);
380 "snd_soc_register_card failed %d\n", ret_val
);
383 platform_set_drvdata(pdev
, card
);
387 static struct platform_driver snd_cht_mc_driver
= {
389 .name
= "cht-bsw-rt5645",
391 .probe
= snd_cht_mc_probe
,
394 module_platform_driver(snd_cht_mc_driver
)
396 MODULE_DESCRIPTION("ASoC Intel(R) Braswell Machine driver");
397 MODULE_AUTHOR("Fang, Yang A,N,Harshapriya");
398 MODULE_LICENSE("GPL v2");
399 MODULE_ALIAS("platform:cht-bsw-rt5645");