blob: 230fc233343446033103ef73b3292358b5b02338 [file] [log] [blame]
Subhransu S. Prusty996cc842014-11-19 15:13:27 +05301/*
2 * byt_cr_dpcm_rt5640.c - ASoc Machine driver for Intel Byt CR platform
3 *
4 * Copyright (C) 2014 Intel Corp
5 * Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
18 */
19
20#include <linux/init.h>
21#include <linux/module.h>
22#include <linux/platform_device.h>
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -060023#include <linux/acpi.h>
Subhransu S. Prusty996cc842014-11-19 15:13:27 +053024#include <linux/device.h>
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -060025#include <linux/dmi.h>
Subhransu S. Prusty996cc842014-11-19 15:13:27 +053026#include <linux/slab.h>
Pierre-Louis Bossarte214f5e2016-08-12 16:27:53 -050027#include <asm/cpu_device_id.h>
28#include <asm/platform_sst_audio.h>
Irina Tirdeadf1a2772016-08-12 16:27:57 -050029#include <linux/clk.h>
Subhransu S. Prusty996cc842014-11-19 15:13:27 +053030#include <sound/pcm.h>
31#include <sound/pcm_params.h>
32#include <sound/soc.h>
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -060033#include <sound/jack.h>
Jie Yange56c72d2015-04-02 15:37:02 +080034#include "../../codecs/rt5640.h"
Jie Yangb97169d2015-04-02 15:37:04 +080035#include "../atom/sst-atom-controls.h"
Pierre-Louis Bossartcaf94ed2016-01-04 17:20:28 -060036#include "../common/sst-acpi.h"
Pierre-Louis Bossarte214f5e2016-08-12 16:27:53 -050037#include "../common/sst-dsp.h"
Subhransu S. Prusty996cc842014-11-19 15:13:27 +053038
Pierre-Louis Bossartab738e42016-03-03 21:36:37 -060039enum {
40 BYT_RT5640_DMIC1_MAP,
41 BYT_RT5640_DMIC2_MAP,
42 BYT_RT5640_IN1_MAP,
Pierre-Louis Bossart59e8b652016-08-12 16:27:54 -050043 BYT_RT5640_IN3_MAP,
Pierre-Louis Bossartab738e42016-03-03 21:36:37 -060044};
45
46#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff)
47#define BYT_RT5640_DMIC_EN BIT(16)
Pierre-Louis Bossart68817cd2016-08-12 16:27:45 -050048#define BYT_RT5640_MONO_SPEAKER BIT(17)
Pierre-Louis Bossart5d98f582016-08-12 16:27:46 -050049#define BYT_RT5640_DIFF_MIC BIT(18) /* defaut is single-ended */
Pierre-Louis Bossart89b89072016-08-12 16:27:50 -050050#define BYT_RT5640_SSP2_AIF2 BIT(19) /* default is using AIF1 */
Pierre-Louis Bossartf47088d2016-08-12 16:27:51 -050051#define BYT_RT5640_SSP0_AIF1 BIT(20)
52#define BYT_RT5640_SSP0_AIF2 BIT(21)
Irina Tirdeadf1a2772016-08-12 16:27:57 -050053#define BYT_RT5640_MCLK_EN BIT(22)
54#define BYT_RT5640_MCLK_25MHZ BIT(23)
55
56struct byt_rt5640_private {
57 struct clk *mclk;
58};
Pierre-Louis Bossartab738e42016-03-03 21:36:37 -060059
60static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
Irina Tirdeadf1a2772016-08-12 16:27:57 -050061 BYT_RT5640_DMIC_EN |
62 BYT_RT5640_MCLK_EN;
63
64#define BYT_CODEC_DAI1 "rt5640-aif1"
65#define BYT_CODEC_DAI2 "rt5640-aif2"
66
67static inline struct snd_soc_dai *byt_get_codec_dai(struct snd_soc_card *card)
68{
69 struct snd_soc_pcm_runtime *rtd;
70
71 list_for_each_entry(rtd, &card->rtd_list, list) {
72 if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI1,
73 strlen(BYT_CODEC_DAI1)))
74 return rtd->codec_dai;
75 if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI2,
76 strlen(BYT_CODEC_DAI2)))
77 return rtd->codec_dai;
78
79 }
80 return NULL;
81}
82
83static int platform_clock_control(struct snd_soc_dapm_widget *w,
84 struct snd_kcontrol *k, int event)
85{
86 struct snd_soc_dapm_context *dapm = w->dapm;
87 struct snd_soc_card *card = dapm->card;
88 struct snd_soc_dai *codec_dai;
89 struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card);
90 int ret;
91
92 codec_dai = byt_get_codec_dai(card);
93 if (!codec_dai) {
94 dev_err(card->dev,
95 "Codec dai not found; Unable to set platform clock\n");
96 return -EIO;
97 }
98
99 if (SND_SOC_DAPM_EVENT_ON(event)) {
100 if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) {
101 ret = clk_prepare_enable(priv->mclk);
102 if (ret < 0) {
103 dev_err(card->dev,
104 "could not configure MCLK state");
105 return ret;
106 }
107 }
108 ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
109 48000 * 512,
110 SND_SOC_CLOCK_IN);
111 } else {
112 /*
113 * Set codec clock source to internal clock before
114 * turning off the platform clock. Codec needs clock
115 * for Jack detection and button press
116 */
117 ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_RCCLK,
118 0,
119 SND_SOC_CLOCK_IN);
120 if (!ret) {
121 if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk)
122 clk_disable_unprepare(priv->mclk);
123 }
124 }
125
126 if (ret < 0) {
127 dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
128 return ret;
129 }
130
131 return 0;
132}
Pierre-Louis Bossartab738e42016-03-03 21:36:37 -0600133
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600134static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530135 SND_SOC_DAPM_HP("Headphone", NULL),
136 SND_SOC_DAPM_MIC("Headset Mic", NULL),
Pierre-Louis Bossarte2be1da2015-12-17 20:35:40 -0600137 SND_SOC_DAPM_MIC("Internal Mic", NULL),
138 SND_SOC_DAPM_SPK("Speaker", NULL),
Irina Tirdeadf1a2772016-08-12 16:27:57 -0500139 SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
140 platform_clock_control, SND_SOC_DAPM_PRE_PMU |
141 SND_SOC_DAPM_POST_PMD),
142
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530143};
144
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600145static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
Irina Tirdeadf1a2772016-08-12 16:27:57 -0500146 {"Headphone", NULL, "Platform Clock"},
147 {"Headset Mic", NULL, "Platform Clock"},
148 {"Internal Mic", NULL, "Platform Clock"},
149 {"Speaker", NULL, "Platform Clock"},
150
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530151 {"Headset Mic", NULL, "MICBIAS1"},
Pierre-Louis Bossarte2be1da2015-12-17 20:35:40 -0600152 {"IN2P", NULL, "Headset Mic"},
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530153 {"Headphone", NULL, "HPOL"},
154 {"Headphone", NULL, "HPOR"},
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530155};
156
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600157static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic1_map[] = {
158 {"DMIC1", NULL, "Internal Mic"},
159};
160
161static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic2_map[] = {
162 {"DMIC2", NULL, "Internal Mic"},
163};
164
165static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = {
166 {"Internal Mic", NULL, "MICBIAS1"},
167 {"IN1P", NULL, "Internal Mic"},
168};
169
Pierre-Louis Bossart59e8b652016-08-12 16:27:54 -0500170static const struct snd_soc_dapm_route byt_rt5640_intmic_in3_map[] = {
171 {"Internal Mic", NULL, "MICBIAS1"},
172 {"IN3P", NULL, "Internal Mic"},
173};
174
Pierre-Louis Bossart89b89072016-08-12 16:27:50 -0500175static const struct snd_soc_dapm_route byt_rt5640_ssp2_aif1_map[] = {
176 {"ssp2 Tx", NULL, "codec_out0"},
177 {"ssp2 Tx", NULL, "codec_out1"},
178 {"codec_in0", NULL, "ssp2 Rx"},
179 {"codec_in1", NULL, "ssp2 Rx"},
180
181 {"AIF1 Playback", NULL, "ssp2 Tx"},
182 {"ssp2 Rx", NULL, "AIF1 Capture"},
183};
184
185static const struct snd_soc_dapm_route byt_rt5640_ssp2_aif2_map[] = {
186 {"ssp2 Tx", NULL, "codec_out0"},
187 {"ssp2 Tx", NULL, "codec_out1"},
188 {"codec_in0", NULL, "ssp2 Rx"},
189 {"codec_in1", NULL, "ssp2 Rx"},
190
191 {"AIF2 Playback", NULL, "ssp2 Tx"},
192 {"ssp2 Rx", NULL, "AIF2 Capture"},
193};
194
Pierre-Louis Bossartf47088d2016-08-12 16:27:51 -0500195static const struct snd_soc_dapm_route byt_rt5640_ssp0_aif1_map[] = {
196 {"ssp0 Tx", NULL, "modem_out"},
197 {"modem_in", NULL, "ssp0 Rx"},
198
199 {"AIF1 Playback", NULL, "ssp0 Tx"},
200 {"ssp0 Rx", NULL, "AIF1 Capture"},
201};
202
203static const struct snd_soc_dapm_route byt_rt5640_ssp0_aif2_map[] = {
204 {"ssp0 Tx", NULL, "modem_out"},
205 {"modem_in", NULL, "ssp0 Rx"},
206
207 {"AIF2 Playback", NULL, "ssp0 Tx"},
208 {"ssp0 Rx", NULL, "AIF2 Capture"},
209};
210
Pierre-Louis Bossart68817cd2016-08-12 16:27:45 -0500211static const struct snd_soc_dapm_route byt_rt5640_stereo_spk_map[] = {
212 {"Speaker", NULL, "SPOLP"},
213 {"Speaker", NULL, "SPOLN"},
214 {"Speaker", NULL, "SPORP"},
215 {"Speaker", NULL, "SPORN"},
216};
217
218static const struct snd_soc_dapm_route byt_rt5640_mono_spk_map[] = {
219 {"Speaker", NULL, "SPOLP"},
220 {"Speaker", NULL, "SPOLN"},
221};
222
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600223static const struct snd_kcontrol_new byt_rt5640_controls[] = {
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530224 SOC_DAPM_PIN_SWITCH("Headphone"),
225 SOC_DAPM_PIN_SWITCH("Headset Mic"),
Pierre-Louis Bossarte2be1da2015-12-17 20:35:40 -0600226 SOC_DAPM_PIN_SWITCH("Internal Mic"),
227 SOC_DAPM_PIN_SWITCH("Speaker"),
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530228};
229
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600230static int byt_rt5640_aif1_hw_params(struct snd_pcm_substream *substream,
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530231 struct snd_pcm_hw_params *params)
232{
233 struct snd_soc_pcm_runtime *rtd = substream->private_data;
234 struct snd_soc_dai *codec_dai = rtd->codec_dai;
235 int ret;
236
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530237 ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
238 params_rate(params) * 512,
239 SND_SOC_CLOCK_IN);
Irina Tirdeadf1a2772016-08-12 16:27:57 -0500240
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530241 if (ret < 0) {
242 dev_err(rtd->dev, "can't set codec clock %d\n", ret);
243 return ret;
244 }
245
Irina Tirdeadf1a2772016-08-12 16:27:57 -0500246 if (!(byt_rt5640_quirk & BYT_RT5640_MCLK_EN)) {
247 /* use bitclock as PLL input */
248 if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) ||
249 (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) {
Pierre-Louis Bossart038a50e2016-08-12 16:27:52 -0500250
Irina Tirdeadf1a2772016-08-12 16:27:57 -0500251 /* 2x16 bit slots on SSP0 */
252 ret = snd_soc_dai_set_pll(codec_dai, 0,
253 RT5640_PLL1_S_BCLK1,
254 params_rate(params) * 32,
255 params_rate(params) * 512);
256 } else {
257 /* 2x15 bit slots on SSP2 */
258 ret = snd_soc_dai_set_pll(codec_dai, 0,
259 RT5640_PLL1_S_BCLK1,
260 params_rate(params) * 50,
261 params_rate(params) * 512);
262 }
Pierre-Louis Bossart038a50e2016-08-12 16:27:52 -0500263 } else {
Irina Tirdeadf1a2772016-08-12 16:27:57 -0500264 if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) {
265 ret = snd_soc_dai_set_pll(codec_dai, 0,
266 RT5640_PLL1_S_MCLK,
267 25000000,
268 params_rate(params) * 512);
269 } else {
270 ret = snd_soc_dai_set_pll(codec_dai, 0,
271 RT5640_PLL1_S_MCLK,
272 19200000,
273 params_rate(params) * 512);
274 }
Pierre-Louis Bossart038a50e2016-08-12 16:27:52 -0500275 }
276
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530277 if (ret < 0) {
278 dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
279 return ret;
280 }
281
282 return 0;
283}
284
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600285static int byt_rt5640_quirk_cb(const struct dmi_system_id *id)
286{
287 byt_rt5640_quirk = (unsigned long)id->driver_data;
288 return 1;
289}
290
291static const struct dmi_system_id byt_rt5640_quirk_table[] = {
292 {
293 .callback = byt_rt5640_quirk_cb,
294 .matches = {
Pierre-Louis Bossart73442e32016-08-12 16:27:44 -0500295 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
296 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"),
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600297 },
Irina Tirdeadf1a2772016-08-12 16:27:57 -0500298 .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP |
299 BYT_RT5640_MCLK_EN),
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600300 },
301 {
302 .callback = byt_rt5640_quirk_cb,
303 .matches = {
Pierre-Louis Bossart73442e32016-08-12 16:27:44 -0500304 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
305 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TAF"),
306 },
Pierre-Louis Bossart68817cd2016-08-12 16:27:45 -0500307 .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP |
Pierre-Louis Bossart5d98f582016-08-12 16:27:46 -0500308 BYT_RT5640_MONO_SPEAKER |
Pierre-Louis Bossarte214f5e2016-08-12 16:27:53 -0500309 BYT_RT5640_DIFF_MIC |
Irina Tirdeadf1a2772016-08-12 16:27:57 -0500310 BYT_RT5640_SSP0_AIF2 |
311 BYT_RT5640_MCLK_EN
Pierre-Louis Bossart68817cd2016-08-12 16:27:45 -0500312 ),
Pierre-Louis Bossart73442e32016-08-12 16:27:44 -0500313 },
314 {
315 .callback = byt_rt5640_quirk_cb,
316 .matches = {
317 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "DellInc."),
318 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"),
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600319 },
320 .driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP |
Irina Tirdeadf1a2772016-08-12 16:27:57 -0500321 BYT_RT5640_DMIC_EN |
322 BYT_RT5640_MCLK_EN),
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600323 },
Jorge Fernandez Monteagudo55fc2052016-01-04 17:20:30 -0600324 {
325 .callback = byt_rt5640_quirk_cb,
326 .matches = {
Pierre-Louis Bossart73442e32016-08-12 16:27:44 -0500327 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
328 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"),
Jorge Fernandez Monteagudo55fc2052016-01-04 17:20:30 -0600329 },
Irina Tirdeadf1a2772016-08-12 16:27:57 -0500330 .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP |
331 BYT_RT5640_MCLK_EN),
Jorge Fernandez Monteagudo55fc2052016-01-04 17:20:30 -0600332 },
Irina Tirdea0565e772016-08-12 16:27:58 -0500333 {
334 .callback = byt_rt5640_quirk_cb,
335 .matches = {
336 DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"),
337 DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"),
338 },
339 .driver_data = (unsigned long *)(BYT_RT5640_DMIC1_MAP |
340 BYT_RT5640_DMIC_EN),
341 },
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600342 {}
343};
344
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600345static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
346{
347 int ret;
348 struct snd_soc_codec *codec = runtime->codec;
349 struct snd_soc_card *card = runtime->card;
350 const struct snd_soc_dapm_route *custom_map;
Irina Tirdeadf1a2772016-08-12 16:27:57 -0500351 struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card);
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600352 int num_routes;
353
354 card->dapm.idle_bias_off = true;
355
Pierre-Louis Bossart0ec66e2d2016-01-04 17:20:27 -0600356 rt5640_sel_asrc_clk_src(codec,
357 RT5640_DA_STEREO_FILTER |
Irina Tirdeadf1a2772016-08-12 16:27:57 -0500358 RT5640_DA_MONO_L_FILTER |
359 RT5640_DA_MONO_R_FILTER |
360 RT5640_AD_STEREO_FILTER |
361 RT5640_AD_MONO_L_FILTER |
362 RT5640_AD_MONO_R_FILTER,
Pierre-Louis Bossart0ec66e2d2016-01-04 17:20:27 -0600363 RT5640_CLK_SEL_ASRC);
364
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600365 ret = snd_soc_add_card_controls(card, byt_rt5640_controls,
366 ARRAY_SIZE(byt_rt5640_controls));
367 if (ret) {
368 dev_err(card->dev, "unable to add card controls\n");
369 return ret;
370 }
371
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600372 switch (BYT_RT5640_MAP(byt_rt5640_quirk)) {
373 case BYT_RT5640_IN1_MAP:
374 custom_map = byt_rt5640_intmic_in1_map;
375 num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map);
376 break;
Pierre-Louis Bossart59e8b652016-08-12 16:27:54 -0500377 case BYT_RT5640_IN3_MAP:
378 custom_map = byt_rt5640_intmic_in3_map;
379 num_routes = ARRAY_SIZE(byt_rt5640_intmic_in3_map);
380 break;
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600381 case BYT_RT5640_DMIC2_MAP:
382 custom_map = byt_rt5640_intmic_dmic2_map;
383 num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map);
384 break;
385 default:
386 custom_map = byt_rt5640_intmic_dmic1_map;
387 num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic1_map);
388 }
389
390 ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
391 if (ret)
392 return ret;
393
Pierre-Louis Bossart89b89072016-08-12 16:27:50 -0500394 if (byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) {
395 ret = snd_soc_dapm_add_routes(&card->dapm,
396 byt_rt5640_ssp2_aif2_map,
397 ARRAY_SIZE(byt_rt5640_ssp2_aif2_map));
Pierre-Louis Bossartf47088d2016-08-12 16:27:51 -0500398 } else if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) {
399 ret = snd_soc_dapm_add_routes(&card->dapm,
400 byt_rt5640_ssp0_aif1_map,
401 ARRAY_SIZE(byt_rt5640_ssp0_aif1_map));
402 } else if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2) {
403 ret = snd_soc_dapm_add_routes(&card->dapm,
404 byt_rt5640_ssp0_aif2_map,
405 ARRAY_SIZE(byt_rt5640_ssp0_aif2_map));
Pierre-Louis Bossart89b89072016-08-12 16:27:50 -0500406 } else {
407 ret = snd_soc_dapm_add_routes(&card->dapm,
408 byt_rt5640_ssp2_aif1_map,
409 ARRAY_SIZE(byt_rt5640_ssp2_aif1_map));
410 }
411 if (ret)
412 return ret;
413
Pierre-Louis Bossart68817cd2016-08-12 16:27:45 -0500414 if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) {
415 ret = snd_soc_dapm_add_routes(&card->dapm,
416 byt_rt5640_mono_spk_map,
417 ARRAY_SIZE(byt_rt5640_mono_spk_map));
418 } else {
419 ret = snd_soc_dapm_add_routes(&card->dapm,
420 byt_rt5640_stereo_spk_map,
421 ARRAY_SIZE(byt_rt5640_stereo_spk_map));
422 }
423 if (ret)
424 return ret;
425
Pierre-Louis Bossart5d98f582016-08-12 16:27:46 -0500426 if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC) {
427 snd_soc_update_bits(codec, RT5640_IN1_IN2, RT5640_IN_DF1,
428 RT5640_IN_DF1);
429 }
430
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600431 if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) {
432 ret = rt5640_dmic_enable(codec, 0, 0);
433 if (ret)
434 return ret;
435 }
436
437 snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
438 snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
439
Irina Tirdeadf1a2772016-08-12 16:27:57 -0500440 if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) {
441 /*
442 * The firmware might enable the clock at
443 * boot (this information may or may not
444 * be reflected in the enable clock register).
445 * To change the rate we must disable the clock
446 * first to cover these cases. Due to common
447 * clock framework restrictions that do not allow
448 * to disable a clock that has not been enabled,
449 * we need to enable the clock first.
450 */
451 ret = clk_prepare_enable(priv->mclk);
452 if (!ret)
453 clk_disable_unprepare(priv->mclk);
454
455 if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ)
456 ret = clk_set_rate(priv->mclk, 25000000);
457 else
458 ret = clk_set_rate(priv->mclk, 19200000);
459
460 if (ret)
461 dev_err(card->dev, "unable to set MCLK rate\n");
462 }
463
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600464 return ret;
465}
466
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600467static const struct snd_soc_pcm_stream byt_rt5640_dai_params = {
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530468 .formats = SNDRV_PCM_FMTBIT_S24_LE,
469 .rate_min = 48000,
470 .rate_max = 48000,
471 .channels_min = 2,
472 .channels_max = 2,
473};
474
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600475static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd,
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530476 struct snd_pcm_hw_params *params)
477{
478 struct snd_interval *rate = hw_param_interval(params,
479 SNDRV_PCM_HW_PARAM_RATE);
480 struct snd_interval *channels = hw_param_interval(params,
481 SNDRV_PCM_HW_PARAM_CHANNELS);
Sebastien Guiriec3f27ded2015-12-17 20:35:39 -0600482 int ret;
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530483
Pierre-Louis Bossart038a50e2016-08-12 16:27:52 -0500484 /* The DSP will covert the FE rate to 48k, stereo */
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530485 rate->min = rate->max = 48000;
486 channels->min = channels->max = 2;
487
Pierre-Louis Bossart038a50e2016-08-12 16:27:52 -0500488 if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) ||
489 (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) {
Sebastien Guiriec3f27ded2015-12-17 20:35:39 -0600490
Pierre-Louis Bossart038a50e2016-08-12 16:27:52 -0500491 /* set SSP2 to 16-bit */
492 params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
493
494 /*
495 * Default mode for SSP configuration is TDM 4 slot, override config
496 * with explicit setting to I2S 2ch 16-bit. The word length is set with
497 * dai_set_tdm_slot() since there is no other API exposed
498 */
499 ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
500 SND_SOC_DAIFMT_I2S |
501 SND_SOC_DAIFMT_NB_IF |
502 SND_SOC_DAIFMT_CBS_CFS
503 );
504 if (ret < 0) {
505 dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
506 return ret;
507 }
508
509 ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 16);
510 if (ret < 0) {
511 dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
512 return ret;
513 }
514
515 } else {
516
517 /* set SSP2 to 24-bit */
518 params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
519
520 /*
521 * Default mode for SSP configuration is TDM 4 slot, override config
522 * with explicit setting to I2S 2ch 24-bit. The word length is set with
523 * dai_set_tdm_slot() since there is no other API exposed
524 */
525 ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
526 SND_SOC_DAIFMT_I2S |
527 SND_SOC_DAIFMT_NB_IF |
528 SND_SOC_DAIFMT_CBS_CFS
529 );
530 if (ret < 0) {
531 dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
532 return ret;
533 }
534
535 ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
536 if (ret < 0) {
537 dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
538 return ret;
539 }
Sebastien Guiriec3f27ded2015-12-17 20:35:39 -0600540 }
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530541 return 0;
542}
543
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600544static int byt_rt5640_aif1_startup(struct snd_pcm_substream *substream)
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530545{
Lars-Peter Clausend0a1b662015-10-18 15:39:30 +0200546 return snd_pcm_hw_constraint_single(substream->runtime,
547 SNDRV_PCM_HW_PARAM_RATE, 48000);
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530548}
549
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600550static struct snd_soc_ops byt_rt5640_aif1_ops = {
551 .startup = byt_rt5640_aif1_startup,
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530552};
553
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600554static struct snd_soc_ops byt_rt5640_be_ssp2_ops = {
555 .hw_params = byt_rt5640_aif1_hw_params,
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530556};
557
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600558static struct snd_soc_dai_link byt_rt5640_dais[] = {
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530559 [MERR_DPCM_AUDIO] = {
560 .name = "Baytrail Audio Port",
561 .stream_name = "Baytrail Audio",
562 .cpu_dai_name = "media-cpu-dai",
563 .codec_dai_name = "snd-soc-dummy-dai",
564 .codec_name = "snd-soc-dummy",
565 .platform_name = "sst-mfld-platform",
566 .ignore_suspend = 1,
567 .dynamic = 1,
568 .dpcm_playback = 1,
569 .dpcm_capture = 1,
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600570 .ops = &byt_rt5640_aif1_ops,
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530571 },
Pierre-Louis Bossartd35eb962015-12-17 20:35:45 -0600572 [MERR_DPCM_DEEP_BUFFER] = {
573 .name = "Deep-Buffer Audio Port",
574 .stream_name = "Deep-Buffer Audio",
575 .cpu_dai_name = "deepbuffer-cpu-dai",
576 .codec_dai_name = "snd-soc-dummy-dai",
577 .codec_name = "snd-soc-dummy",
578 .platform_name = "sst-mfld-platform",
579 .ignore_suspend = 1,
580 .nonatomic = true,
581 .dynamic = 1,
582 .dpcm_playback = 1,
583 .ops = &byt_rt5640_aif1_ops,
584 },
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530585 [MERR_DPCM_COMPR] = {
586 .name = "Baytrail Compressed Port",
587 .stream_name = "Baytrail Compress",
588 .cpu_dai_name = "compress-cpu-dai",
589 .codec_dai_name = "snd-soc-dummy-dai",
590 .codec_name = "snd-soc-dummy",
591 .platform_name = "sst-mfld-platform",
592 },
593 /* back ends */
594 {
595 .name = "SSP2-Codec",
Mengdong Lin2f0ad492016-04-19 13:12:35 +0800596 .id = 1,
Pierre-Louis Bossartf47088d2016-08-12 16:27:51 -0500597 .cpu_dai_name = "ssp2-port", /* overwritten for ssp0 routing */
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530598 .platform_name = "sst-mfld-platform",
599 .no_pcm = 1,
Pierre-Louis Bossart89b89072016-08-12 16:27:50 -0500600 .codec_dai_name = "rt5640-aif1", /* changed w/ quirk */
Pierre-Louis Bossart7762ef42016-01-04 17:20:29 -0600601 .codec_name = "i2c-10EC5640:00", /* overwritten with HID */
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530602 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
603 | SND_SOC_DAIFMT_CBS_CFS,
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600604 .be_hw_params_fixup = byt_rt5640_codec_fixup,
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530605 .ignore_suspend = 1,
606 .dpcm_playback = 1,
607 .dpcm_capture = 1,
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600608 .init = byt_rt5640_init,
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600609 .ops = &byt_rt5640_be_ssp2_ops,
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530610 },
611};
612
613/* SoC card */
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600614static struct snd_soc_card byt_rt5640_card = {
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600615 .name = "bytcr-rt5640",
Axel Lin54d86972015-08-21 20:59:21 +0800616 .owner = THIS_MODULE,
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600617 .dai_link = byt_rt5640_dais,
618 .num_links = ARRAY_SIZE(byt_rt5640_dais),
619 .dapm_widgets = byt_rt5640_widgets,
620 .num_dapm_widgets = ARRAY_SIZE(byt_rt5640_widgets),
621 .dapm_routes = byt_rt5640_audio_map,
622 .num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map),
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600623 .fully_routed = true,
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530624};
625
Pierre-Louis Bossartcaf94ed2016-01-04 17:20:28 -0600626static char byt_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
Pierre-Louis Bossart89b89072016-08-12 16:27:50 -0500627static char byt_rt5640_codec_aif_name[12]; /* = "rt5640-aif[1|2]" */
Pierre-Louis Bossartf47088d2016-08-12 16:27:51 -0500628static char byt_rt5640_cpu_dai_name[10]; /* = "ssp[0|2]-port" */
Pierre-Louis Bossartcaf94ed2016-01-04 17:20:28 -0600629
Pierre-Louis Bossarte214f5e2016-08-12 16:27:53 -0500630static bool is_valleyview(void)
631{
632 static const struct x86_cpu_id cpu_ids[] __initconst = {
633 { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */
634 {}
635 };
636
637 if (!x86_match_cpu(cpu_ids))
638 return false;
639 return true;
640}
641
Irina Tirdeadf1a2772016-08-12 16:27:57 -0500642
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600643static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530644{
645 int ret_val = 0;
Pierre-Louis Bossartcaf94ed2016-01-04 17:20:28 -0600646 struct sst_acpi_mach *mach;
Pierre-Louis Bossarta232b962016-03-03 21:36:38 -0600647 const char *i2c_name = NULL;
648 int i;
649 int dai_index;
Irina Tirdeadf1a2772016-08-12 16:27:57 -0500650 struct byt_rt5640_private *priv;
651
652 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
653 if (!priv)
654 return -ENOMEM;
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530655
656 /* register the soc card */
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600657 byt_rt5640_card.dev = &pdev->dev;
Pierre-Louis Bossartcaf94ed2016-01-04 17:20:28 -0600658 mach = byt_rt5640_card.dev->platform_data;
Irina Tirdeadf1a2772016-08-12 16:27:57 -0500659 snd_soc_card_set_drvdata(&byt_rt5640_card, priv);
Pierre-Louis Bossartcaf94ed2016-01-04 17:20:28 -0600660
Pierre-Louis Bossarta232b962016-03-03 21:36:38 -0600661 /* fix index of codec dai */
662 dai_index = MERR_DPCM_COMPR + 1;
663 for (i = 0; i < ARRAY_SIZE(byt_rt5640_dais); i++) {
664 if (!strcmp(byt_rt5640_dais[i].codec_name, "i2c-10EC5640:00")) {
665 dai_index = i;
666 break;
667 }
668 }
669
Pierre-Louis Bossartcaf94ed2016-01-04 17:20:28 -0600670 /* fixup codec name based on HID */
Pierre-Louis Bossarta232b962016-03-03 21:36:38 -0600671 i2c_name = sst_acpi_find_name_from_hid(mach->id);
672 if (i2c_name != NULL) {
673 snprintf(byt_rt5640_codec_name, sizeof(byt_rt5640_codec_name),
674 "%s%s", "i2c-", i2c_name);
675
676 byt_rt5640_dais[dai_index].codec_name = byt_rt5640_codec_name;
677 }
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530678
Pierre-Louis Bossarte214f5e2016-08-12 16:27:53 -0500679 /*
680 * swap SSP0 if bytcr is detected
681 * (will be overridden if DMI quirk is detected)
682 */
683 if (is_valleyview()) {
684 struct sst_platform_info *p_info = mach->pdata;
685 const struct sst_res_info *res_info = p_info->res_info;
686
687 /* TODO: use CHAN package info from BIOS to detect AIF1/AIF2 */
688 if (res_info->acpi_ipc_irq_index == 0) {
689 byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF2;
690 }
691 }
692
Pierre-Louis Bossartab738e42016-03-03 21:36:37 -0600693 /* check quirks before creating card */
694 dmi_check_system(byt_rt5640_quirk_table);
695
Pierre-Louis Bossartf47088d2016-08-12 16:27:51 -0500696 if ((byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) ||
697 (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) {
Pierre-Louis Bossart89b89072016-08-12 16:27:50 -0500698
699 /* fixup codec aif name */
700 snprintf(byt_rt5640_codec_aif_name,
701 sizeof(byt_rt5640_codec_aif_name),
702 "%s", "rt5640-aif2");
703
704 byt_rt5640_dais[dai_index].codec_dai_name =
705 byt_rt5640_codec_aif_name;
706 }
707
Pierre-Louis Bossartf47088d2016-08-12 16:27:51 -0500708 if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) ||
709 (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) {
710
711 /* fixup cpu dai name name */
712 snprintf(byt_rt5640_cpu_dai_name,
713 sizeof(byt_rt5640_cpu_dai_name),
714 "%s", "ssp0-port");
715
716 byt_rt5640_dais[dai_index].cpu_dai_name =
717 byt_rt5640_cpu_dai_name;
718 }
719
Irina Tirdeadf1a2772016-08-12 16:27:57 -0500720 if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && (is_valleyview())) {
721 priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
722 if (IS_ERR(priv->mclk)) {
723 dev_err(&pdev->dev,
724 "Failed to get MCLK from pmc_plt_clk_3: %ld\n",
725 PTR_ERR(priv->mclk));
726 return PTR_ERR(priv->mclk);
727 }
728 }
729
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600730 ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card);
731
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530732 if (ret_val) {
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600733 dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n",
734 ret_val);
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530735 return ret_val;
736 }
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600737 platform_set_drvdata(pdev, &byt_rt5640_card);
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530738 return ret_val;
739}
740
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600741static struct platform_driver snd_byt_rt5640_mc_driver = {
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530742 .driver = {
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600743 .name = "bytcr_rt5640",
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530744 .pm = &snd_soc_pm_ops,
745 },
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600746 .probe = snd_byt_rt5640_mc_probe,
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530747};
748
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600749module_platform_driver(snd_byt_rt5640_mc_driver);
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530750
751MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
752MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
753MODULE_LICENSE("GPL v2");
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600754MODULE_ALIAS("platform:bytcr_rt5640");