blob: 944283f569c6d584e066db2b63531353756eaf7a [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>
Subhransu S. Prusty996cc842014-11-19 15:13:27 +053027#include <sound/pcm.h>
28#include <sound/pcm_params.h>
29#include <sound/soc.h>
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -060030#include <sound/jack.h>
Jie Yange56c72d2015-04-02 15:37:02 +080031#include "../../codecs/rt5640.h"
Jie Yangb97169d2015-04-02 15:37:04 +080032#include "../atom/sst-atom-controls.h"
Subhransu S. Prusty996cc842014-11-19 15:13:27 +053033
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -060034static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
Subhransu S. Prusty996cc842014-11-19 15:13:27 +053035 SND_SOC_DAPM_HP("Headphone", NULL),
36 SND_SOC_DAPM_MIC("Headset Mic", NULL),
Pierre-Louis Bossarte2be1da2015-12-17 20:35:40 -060037 SND_SOC_DAPM_MIC("Internal Mic", NULL),
38 SND_SOC_DAPM_SPK("Speaker", NULL),
Subhransu S. Prusty996cc842014-11-19 15:13:27 +053039};
40
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -060041static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -060042 {"AIF1 Playback", NULL, "ssp2 Tx"},
43 {"ssp2 Tx", NULL, "codec_out0"},
44 {"ssp2 Tx", NULL, "codec_out1"},
45 {"codec_in0", NULL, "ssp2 Rx"},
46 {"codec_in1", NULL, "ssp2 Rx"},
47 {"ssp2 Rx", NULL, "AIF1 Capture"},
48
Subhransu S. Prusty996cc842014-11-19 15:13:27 +053049 {"Headset Mic", NULL, "MICBIAS1"},
Pierre-Louis Bossarte2be1da2015-12-17 20:35:40 -060050 {"IN2P", NULL, "Headset Mic"},
Subhransu S. Prusty996cc842014-11-19 15:13:27 +053051 {"Headphone", NULL, "HPOL"},
52 {"Headphone", NULL, "HPOR"},
Pierre-Louis Bossarte2be1da2015-12-17 20:35:40 -060053 {"Speaker", NULL, "SPOLP"},
54 {"Speaker", NULL, "SPOLN"},
55 {"Speaker", NULL, "SPORP"},
56 {"Speaker", NULL, "SPORN"},
Subhransu S. Prusty996cc842014-11-19 15:13:27 +053057};
58
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -060059static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic1_map[] = {
60 {"DMIC1", NULL, "Internal Mic"},
61};
62
63static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic2_map[] = {
64 {"DMIC2", NULL, "Internal Mic"},
65};
66
67static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = {
68 {"Internal Mic", NULL, "MICBIAS1"},
69 {"IN1P", NULL, "Internal Mic"},
70};
71
72enum {
73 BYT_RT5640_DMIC1_MAP,
74 BYT_RT5640_DMIC2_MAP,
75 BYT_RT5640_IN1_MAP,
76};
77
78#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff)
79#define BYT_RT5640_DMIC_EN BIT(16)
80
81static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
82 BYT_RT5640_DMIC_EN;
83
84static const struct snd_kcontrol_new byt_rt5640_controls[] = {
Subhransu S. Prusty996cc842014-11-19 15:13:27 +053085 SOC_DAPM_PIN_SWITCH("Headphone"),
86 SOC_DAPM_PIN_SWITCH("Headset Mic"),
Pierre-Louis Bossarte2be1da2015-12-17 20:35:40 -060087 SOC_DAPM_PIN_SWITCH("Internal Mic"),
88 SOC_DAPM_PIN_SWITCH("Speaker"),
Subhransu S. Prusty996cc842014-11-19 15:13:27 +053089};
90
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -060091static int byt_rt5640_aif1_hw_params(struct snd_pcm_substream *substream,
Subhransu S. Prusty996cc842014-11-19 15:13:27 +053092 struct snd_pcm_hw_params *params)
93{
94 struct snd_soc_pcm_runtime *rtd = substream->private_data;
95 struct snd_soc_dai *codec_dai = rtd->codec_dai;
96 int ret;
97
98 snd_soc_dai_set_bclk_ratio(codec_dai, 50);
99
100 ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
101 params_rate(params) * 512,
102 SND_SOC_CLOCK_IN);
103 if (ret < 0) {
104 dev_err(rtd->dev, "can't set codec clock %d\n", ret);
105 return ret;
106 }
107
108 ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1,
109 params_rate(params) * 50,
110 params_rate(params) * 512);
111 if (ret < 0) {
112 dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
113 return ret;
114 }
115
116 return 0;
117}
118
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600119static int byt_rt5640_quirk_cb(const struct dmi_system_id *id)
120{
121 byt_rt5640_quirk = (unsigned long)id->driver_data;
122 return 1;
123}
124
125static const struct dmi_system_id byt_rt5640_quirk_table[] = {
126 {
127 .callback = byt_rt5640_quirk_cb,
128 .matches = {
129 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
130 DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"),
131 },
132 .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP,
133 },
134 {
135 .callback = byt_rt5640_quirk_cb,
136 .matches = {
137 DMI_MATCH(DMI_SYS_VENDOR, "DellInc."),
138 DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"),
139 },
140 .driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP |
141 BYT_RT5640_DMIC_EN),
142 },
143 {}
144};
145
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600146static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
147{
148 int ret;
149 struct snd_soc_codec *codec = runtime->codec;
150 struct snd_soc_card *card = runtime->card;
151 const struct snd_soc_dapm_route *custom_map;
152 int num_routes;
153
154 card->dapm.idle_bias_off = true;
155
156 ret = snd_soc_add_card_controls(card, byt_rt5640_controls,
157 ARRAY_SIZE(byt_rt5640_controls));
158 if (ret) {
159 dev_err(card->dev, "unable to add card controls\n");
160 return ret;
161 }
162
163 dmi_check_system(byt_rt5640_quirk_table);
164 switch (BYT_RT5640_MAP(byt_rt5640_quirk)) {
165 case BYT_RT5640_IN1_MAP:
166 custom_map = byt_rt5640_intmic_in1_map;
167 num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map);
168 break;
169 case BYT_RT5640_DMIC2_MAP:
170 custom_map = byt_rt5640_intmic_dmic2_map;
171 num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map);
172 break;
173 default:
174 custom_map = byt_rt5640_intmic_dmic1_map;
175 num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic1_map);
176 }
177
178 ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
179 if (ret)
180 return ret;
181
182 if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) {
183 ret = rt5640_dmic_enable(codec, 0, 0);
184 if (ret)
185 return ret;
186 }
187
188 snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
189 snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
190
191 return ret;
192}
193
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600194static const struct snd_soc_pcm_stream byt_rt5640_dai_params = {
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530195 .formats = SNDRV_PCM_FMTBIT_S24_LE,
196 .rate_min = 48000,
197 .rate_max = 48000,
198 .channels_min = 2,
199 .channels_max = 2,
200};
201
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600202static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd,
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530203 struct snd_pcm_hw_params *params)
204{
205 struct snd_interval *rate = hw_param_interval(params,
206 SNDRV_PCM_HW_PARAM_RATE);
207 struct snd_interval *channels = hw_param_interval(params,
208 SNDRV_PCM_HW_PARAM_CHANNELS);
Sebastien Guiriec3f27ded2015-12-17 20:35:39 -0600209 int ret;
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530210
211 /* The DSP will covert the FE rate to 48k, stereo, 24bits */
212 rate->min = rate->max = 48000;
213 channels->min = channels->max = 2;
214
215 /* set SSP2 to 24-bit */
Fang, Yang A369a9f52015-02-09 00:18:12 -0800216 params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
Sebastien Guiriec3f27ded2015-12-17 20:35:39 -0600217
218 /*
219 * Default mode for SSP configuration is TDM 4 slot, override config
220 * with explicit setting to I2S 2ch 24-bit. The word length is set with
221 * dai_set_tdm_slot() since there is no other API exposed
222 */
223 ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
224 SND_SOC_DAIFMT_I2S |
225 SND_SOC_DAIFMT_NB_IF |
226 SND_SOC_DAIFMT_CBS_CFS
227 );
228 if (ret < 0) {
229 dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
230 return ret;
231 }
232
233 ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
234 if (ret < 0) {
235 dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
236 return ret;
237 }
238
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530239 return 0;
240}
241
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600242static int byt_rt5640_aif1_startup(struct snd_pcm_substream *substream)
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530243{
Lars-Peter Clausend0a1b662015-10-18 15:39:30 +0200244 return snd_pcm_hw_constraint_single(substream->runtime,
245 SNDRV_PCM_HW_PARAM_RATE, 48000);
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530246}
247
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600248static struct snd_soc_ops byt_rt5640_aif1_ops = {
249 .startup = byt_rt5640_aif1_startup,
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530250};
251
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600252static struct snd_soc_ops byt_rt5640_be_ssp2_ops = {
253 .hw_params = byt_rt5640_aif1_hw_params,
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530254};
255
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600256static struct snd_soc_dai_link byt_rt5640_dais[] = {
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530257 [MERR_DPCM_AUDIO] = {
258 .name = "Baytrail Audio Port",
259 .stream_name = "Baytrail Audio",
260 .cpu_dai_name = "media-cpu-dai",
261 .codec_dai_name = "snd-soc-dummy-dai",
262 .codec_name = "snd-soc-dummy",
263 .platform_name = "sst-mfld-platform",
264 .ignore_suspend = 1,
265 .dynamic = 1,
266 .dpcm_playback = 1,
267 .dpcm_capture = 1,
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600268 .ops = &byt_rt5640_aif1_ops,
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530269 },
270 [MERR_DPCM_COMPR] = {
271 .name = "Baytrail Compressed Port",
272 .stream_name = "Baytrail Compress",
273 .cpu_dai_name = "compress-cpu-dai",
274 .codec_dai_name = "snd-soc-dummy-dai",
275 .codec_name = "snd-soc-dummy",
276 .platform_name = "sst-mfld-platform",
277 },
278 /* back ends */
279 {
280 .name = "SSP2-Codec",
281 .be_id = 1,
282 .cpu_dai_name = "ssp2-port",
283 .platform_name = "sst-mfld-platform",
284 .no_pcm = 1,
285 .codec_dai_name = "rt5640-aif1",
286 .codec_name = "i2c-10EC5640:00",
287 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
288 | SND_SOC_DAIFMT_CBS_CFS,
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600289 .be_hw_params_fixup = byt_rt5640_codec_fixup,
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530290 .ignore_suspend = 1,
291 .dpcm_playback = 1,
292 .dpcm_capture = 1,
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600293 .init = byt_rt5640_init,
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600294 .ops = &byt_rt5640_be_ssp2_ops,
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530295 },
296};
297
298/* SoC card */
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600299static struct snd_soc_card byt_rt5640_card = {
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600300 .name = "bytcr-rt5640",
Axel Lin54d86972015-08-21 20:59:21 +0800301 .owner = THIS_MODULE,
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600302 .dai_link = byt_rt5640_dais,
303 .num_links = ARRAY_SIZE(byt_rt5640_dais),
304 .dapm_widgets = byt_rt5640_widgets,
305 .num_dapm_widgets = ARRAY_SIZE(byt_rt5640_widgets),
306 .dapm_routes = byt_rt5640_audio_map,
307 .num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map),
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600308 .fully_routed = true,
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530309};
310
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600311static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530312{
313 int ret_val = 0;
314
315 /* register the soc card */
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600316 byt_rt5640_card.dev = &pdev->dev;
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530317
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600318 ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card);
319
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530320 if (ret_val) {
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600321 dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n",
322 ret_val);
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530323 return ret_val;
324 }
Pierre-Louis Bossart9fd57472015-12-17 20:35:42 -0600325 platform_set_drvdata(pdev, &byt_rt5640_card);
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530326 return ret_val;
327}
328
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600329static struct platform_driver snd_byt_rt5640_mc_driver = {
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530330 .driver = {
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600331 .name = "bytcr_rt5640",
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530332 .pm = &snd_soc_pm_ops,
333 },
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600334 .probe = snd_byt_rt5640_mc_probe,
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530335};
336
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600337module_platform_driver(snd_byt_rt5640_mc_driver);
Subhransu S. Prusty996cc842014-11-19 15:13:27 +0530338
339MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
340MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
341MODULE_LICENSE("GPL v2");
Pierre-Louis Bossarta2d55632015-12-17 20:35:41 -0600342MODULE_ALIAS("platform:bytcr_rt5640");