Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  1/*
  2 * Intel Broxton-P I2S Machine Driver
  3 *
  4 * Copyright (C) 2014-2016, Intel Corporation. All rights reserved.
  5 *
  6 * Modified from:
  7 *   Intel Skylake I2S Machine driver
  8 *
  9 * This program is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU General Public License version
 11 * 2 as published by the Free Software Foundation.
 12 *
 13 * This program is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 * GNU General Public License for more details.
 17 */
 18
 19#include <linux/module.h>
 20#include <linux/platform_device.h>
 21#include <sound/core.h>
 22#include <sound/pcm.h>
 23#include <sound/soc.h>
 24#include <sound/jack.h>
 25#include <sound/pcm_params.h>
 26#include "../../codecs/hdac_hdmi.h"
 27#include "../../codecs/rt298.h"
 28
 29static struct snd_soc_jack broxton_headset;
 30/* Headset jack detection DAPM pins */
 31
 32enum {
 33	BXT_DPCM_AUDIO_PB = 0,
 34	BXT_DPCM_AUDIO_CP,
 35	BXT_DPCM_AUDIO_REF_CP,
 36	BXT_DPCM_AUDIO_DMIC_CP,
 37	BXT_DPCM_AUDIO_HDMI1_PB,
 38	BXT_DPCM_AUDIO_HDMI2_PB,
 39	BXT_DPCM_AUDIO_HDMI3_PB,
 40};
 41
 42static struct snd_soc_jack_pin broxton_headset_pins[] = {
 43	{
 44		.pin = "Mic Jack",
 45		.mask = SND_JACK_MICROPHONE,
 46	},
 47	{
 48		.pin = "Headphone Jack",
 49		.mask = SND_JACK_HEADPHONE,
 50	},
 51};
 52
 53static const struct snd_kcontrol_new broxton_controls[] = {
 54	SOC_DAPM_PIN_SWITCH("Speaker"),
 55	SOC_DAPM_PIN_SWITCH("Headphone Jack"),
 56	SOC_DAPM_PIN_SWITCH("Mic Jack"),
 57};
 58
 59static const struct snd_soc_dapm_widget broxton_widgets[] = {
 60	SND_SOC_DAPM_HP("Headphone Jack", NULL),
 61	SND_SOC_DAPM_SPK("Speaker", NULL),
 62	SND_SOC_DAPM_MIC("Mic Jack", NULL),
 63	SND_SOC_DAPM_MIC("DMIC2", NULL),
 64	SND_SOC_DAPM_MIC("SoC DMIC", NULL),
 65	SND_SOC_DAPM_SPK("HDMI1", NULL),
 66	SND_SOC_DAPM_SPK("HDMI2", NULL),
 67	SND_SOC_DAPM_SPK("HDMI3", NULL),
 68};
 69
 70static const struct snd_soc_dapm_route broxton_rt298_map[] = {
 71	/* speaker */
 72	{"Speaker", NULL, "SPOR"},
 73	{"Speaker", NULL, "SPOL"},
 74
 75	/* HP jack connectors - unknown if we have jack detect */
 76	{"Headphone Jack", NULL, "HPO Pin"},
 77
 78	/* other jacks */
 79	{"MIC1", NULL, "Mic Jack"},
 80
 81	/* digital mics */
 82	{"DMIC1 Pin", NULL, "DMIC2"},
 83	{"DMic", NULL, "SoC DMIC"},
 84
 85	{"HDMI1", NULL, "hif5 Output"},
 86	{"HDMI2", NULL, "hif6 Output"},
 87	{"HDMI3", NULL, "hif7 Output"},
 88
 89	/* CODEC BE connections */
 90	{ "AIF1 Playback", NULL, "ssp5 Tx"},
 91	{ "ssp5 Tx", NULL, "codec0_out"},
 92	{ "ssp5 Tx", NULL, "codec1_out"},
 93
 94	{ "codec0_in", NULL, "ssp5 Rx" },
 95	{ "ssp5 Rx", NULL, "AIF1 Capture" },
 96
 97	{ "dmic01_hifi", NULL, "DMIC01 Rx" },
 98	{ "DMIC01 Rx", NULL, "Capture" },
 99
100	{ "hifi3", NULL, "iDisp3 Tx"},
101	{ "iDisp3 Tx", NULL, "iDisp3_out"},
102	{ "hifi2", NULL, "iDisp2 Tx"},
103	{ "iDisp2 Tx", NULL, "iDisp2_out"},
104	{ "hifi1", NULL, "iDisp1 Tx"},
105	{ "iDisp1 Tx", NULL, "iDisp1_out"},
106
107};
108
109static int broxton_rt298_fe_init(struct snd_soc_pcm_runtime *rtd)
110{
111	struct snd_soc_dapm_context *dapm;
112	struct snd_soc_component *component = rtd->cpu_dai->component;
113
114	dapm = snd_soc_component_get_dapm(component);
115	snd_soc_dapm_ignore_suspend(dapm, "Reference Capture");
116
117	return 0;
118}
119
120static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd)
121{
122	struct snd_soc_codec *codec = rtd->codec;
123	int ret = 0;
124
125	ret = snd_soc_card_jack_new(rtd->card, "Headset",
126		SND_JACK_HEADSET | SND_JACK_BTN_0,
127		&broxton_headset,
128		broxton_headset_pins, ARRAY_SIZE(broxton_headset_pins));
129
130	if (ret)
131		return ret;
132
133	rt298_mic_detect(codec, &broxton_headset);
134
135	snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
136
137	return 0;
138}
139
140static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd)
141{
142	struct snd_soc_dai *dai = rtd->codec_dai;
143
144	return hdac_hdmi_jack_init(dai, BXT_DPCM_AUDIO_HDMI1_PB + dai->id);
145}
146
147static int broxton_ssp5_fixup(struct snd_soc_pcm_runtime *rtd,
148			struct snd_pcm_hw_params *params)
149{
150	struct snd_interval *rate = hw_param_interval(params,
151					SNDRV_PCM_HW_PARAM_RATE);
152	struct snd_interval *channels = hw_param_interval(params,
153					SNDRV_PCM_HW_PARAM_CHANNELS);
154	struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
155
156	/* The ADSP will covert the FE rate to 48k, stereo */
157	rate->min = rate->max = 48000;
158	channels->min = channels->max = 2;
159
160	/* set SSP5 to 24 bit */
161	snd_mask_none(fmt);
162	snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
163
164	return 0;
165}
166
167static int broxton_rt298_hw_params(struct snd_pcm_substream *substream,
168	struct snd_pcm_hw_params *params)
169{
170	struct snd_soc_pcm_runtime *rtd = substream->private_data;
171	struct snd_soc_dai *codec_dai = rtd->codec_dai;
172	int ret;
173
174	ret = snd_soc_dai_set_sysclk(codec_dai, RT298_SCLK_S_PLL,
175					19200000, SND_SOC_CLOCK_IN);
176	if (ret < 0) {
177		dev_err(rtd->dev, "can't set codec sysclk configuration\n");
178		return ret;
179	}
180
181	return ret;
182}
183
184static const struct snd_soc_ops broxton_rt298_ops = {
185	.hw_params = broxton_rt298_hw_params,
186};
187
188static unsigned int rates[] = {
189	48000,
190};
191
192static struct snd_pcm_hw_constraint_list constraints_rates = {
193	.count = ARRAY_SIZE(rates),
194	.list  = rates,
195	.mask = 0,
196};
197
198static int broxton_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
199				struct snd_pcm_hw_params *params)
200{
201	struct snd_interval *channels = hw_param_interval(params,
202						SNDRV_PCM_HW_PARAM_CHANNELS);
203	if (params_channels(params) == 2)
204		channels->min = channels->max = 2;
205	else
206		channels->min = channels->max = 4;
207
208	return 0;
209}
210
211static unsigned int channels_dmic[] = {
212	2, 4,
213};
214
215static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
216	.count = ARRAY_SIZE(channels_dmic),
217	.list = channels_dmic,
218	.mask = 0,
219};
220
221static int broxton_dmic_startup(struct snd_pcm_substream *substream)
222{
223	struct snd_pcm_runtime *runtime = substream->runtime;
224
225	runtime->hw.channels_max = 4;
226	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
227					&constraints_dmic_channels);
228
229	return snd_pcm_hw_constraint_list(substream->runtime, 0,
230				SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
231}
232
233static const struct snd_soc_ops broxton_dmic_ops = {
234	.startup = broxton_dmic_startup,
235};
236
237static unsigned int channels[] = {
238	2,
239};
240
241static struct snd_pcm_hw_constraint_list constraints_channels = {
242	.count = ARRAY_SIZE(channels),
243	.list = channels,
244	.mask = 0,
245};
246
247static int bxt_fe_startup(struct snd_pcm_substream *substream)
248{
249	struct snd_pcm_runtime *runtime = substream->runtime;
250
251	/*
252	 * on this platform for PCM device we support:
253	 *      48Khz
254	 *      stereo
255	 */
256
257	runtime->hw.channels_max = 2;
258	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
259				&constraints_channels);
260
261	snd_pcm_hw_constraint_list(runtime, 0,
262				SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
263
264	return 0;
265}
266
267static const struct snd_soc_ops broxton_rt286_fe_ops = {
268	.startup = bxt_fe_startup,
269};
270
271/* broxton digital audio interface glue - connects codec <--> CPU */
272static struct snd_soc_dai_link broxton_rt298_dais[] = {
273	/* Front End DAI links */
274	[BXT_DPCM_AUDIO_PB] =
275	{
276		.name = "Bxt Audio Port",
277		.stream_name = "Audio",
278		.cpu_dai_name = "System Pin",
279		.platform_name = "0000:00:0e.0",
280		.nonatomic = 1,
281		.dynamic = 1,
282		.codec_name = "snd-soc-dummy",
283		.codec_dai_name = "snd-soc-dummy-dai",
284		.init = broxton_rt298_fe_init,
285		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
286		.dpcm_playback = 1,
287		.ops = &broxton_rt286_fe_ops,
288	},
289	[BXT_DPCM_AUDIO_CP] =
290	{
291		.name = "Bxt Audio Capture Port",
292		.stream_name = "Audio Record",
293		.cpu_dai_name = "System Pin",
294		.platform_name = "0000:00:0e.0",
295		.nonatomic = 1,
296		.dynamic = 1,
297		.codec_name = "snd-soc-dummy",
298		.codec_dai_name = "snd-soc-dummy-dai",
299		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
300		.dpcm_capture = 1,
301		.ops = &broxton_rt286_fe_ops,
302	},
303	[BXT_DPCM_AUDIO_REF_CP] =
304	{
305		.name = "Bxt Audio Reference cap",
306		.stream_name = "refcap",
307		.cpu_dai_name = "Reference Pin",
308		.codec_name = "snd-soc-dummy",
309		.codec_dai_name = "snd-soc-dummy-dai",
310		.platform_name = "0000:00:0e.0",
311		.init = NULL,
312		.dpcm_capture = 1,
313		.nonatomic = 1,
314		.dynamic = 1,
315	},
316	[BXT_DPCM_AUDIO_DMIC_CP] =
317	{
318		.name = "Bxt Audio DMIC cap",
319		.stream_name = "dmiccap",
320		.cpu_dai_name = "DMIC Pin",
321		.codec_name = "snd-soc-dummy",
322		.codec_dai_name = "snd-soc-dummy-dai",
323		.platform_name = "0000:00:0e.0",
324		.init = NULL,
325		.dpcm_capture = 1,
326		.nonatomic = 1,
327		.dynamic = 1,
328		.ops = &broxton_dmic_ops,
329	},
330	[BXT_DPCM_AUDIO_HDMI1_PB] =
331	{
332		.name = "Bxt HDMI Port1",
333		.stream_name = "Hdmi1",
334		.cpu_dai_name = "HDMI1 Pin",
335		.codec_name = "snd-soc-dummy",
336		.codec_dai_name = "snd-soc-dummy-dai",
337		.platform_name = "0000:00:0e.0",
338		.dpcm_playback = 1,
339		.init = NULL,
340		.nonatomic = 1,
341		.dynamic = 1,
342	},
343	[BXT_DPCM_AUDIO_HDMI2_PB] =
344	{
345		.name = "Bxt HDMI Port2",
346		.stream_name = "Hdmi2",
347		.cpu_dai_name = "HDMI2 Pin",
348		.codec_name = "snd-soc-dummy",
349		.codec_dai_name = "snd-soc-dummy-dai",
350		.platform_name = "0000:00:0e.0",
351		.dpcm_playback = 1,
352		.init = NULL,
353		.nonatomic = 1,
354		.dynamic = 1,
355	},
356	[BXT_DPCM_AUDIO_HDMI3_PB] =
357	{
358		.name = "Bxt HDMI Port3",
359		.stream_name = "Hdmi3",
360		.cpu_dai_name = "HDMI3 Pin",
361		.codec_name = "snd-soc-dummy",
362		.codec_dai_name = "snd-soc-dummy-dai",
363		.platform_name = "0000:00:0e.0",
364		.dpcm_playback = 1,
365		.init = NULL,
366		.nonatomic = 1,
367		.dynamic = 1,
368	},
369	/* Back End DAI links */
370	{
371		/* SSP5 - Codec */
372		.name = "SSP5-Codec",
373		.id = 0,
374		.cpu_dai_name = "SSP5 Pin",
375		.platform_name = "0000:00:0e.0",
376		.no_pcm = 1,
377		.codec_name = "i2c-INT343A:00",
378		.codec_dai_name = "rt298-aif1",
379		.init = broxton_rt298_codec_init,
380		.dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF |
381						SND_SOC_DAIFMT_CBS_CFS,
382		.ignore_pmdown_time = 1,
383		.be_hw_params_fixup = broxton_ssp5_fixup,
384		.ops = &broxton_rt298_ops,
385		.dpcm_playback = 1,
386		.dpcm_capture = 1,
387	},
388	{
389		.name = "dmic01",
390		.id = 1,
391		.cpu_dai_name = "DMIC01 Pin",
392		.codec_name = "dmic-codec",
393		.codec_dai_name = "dmic-hifi",
394		.platform_name = "0000:00:0e.0",
395		.be_hw_params_fixup = broxton_dmic_fixup,
396		.ignore_suspend = 1,
397		.dpcm_capture = 1,
398		.no_pcm = 1,
399	},
400	{
401		.name = "iDisp1",
402		.id = 3,
403		.cpu_dai_name = "iDisp1 Pin",
404		.codec_name = "ehdaudio0D2",
405		.codec_dai_name = "intel-hdmi-hifi1",
406		.platform_name = "0000:00:0e.0",
407		.init = broxton_hdmi_init,
408		.dpcm_playback = 1,
409		.no_pcm = 1,
410	},
411	{
412		.name = "iDisp2",
413		.id = 4,
414		.cpu_dai_name = "iDisp2 Pin",
415		.codec_name = "ehdaudio0D2",
416		.codec_dai_name = "intel-hdmi-hifi2",
417		.platform_name = "0000:00:0e.0",
418		.init = broxton_hdmi_init,
419		.dpcm_playback = 1,
420		.no_pcm = 1,
421	},
422	{
423		.name = "iDisp3",
424		.id = 5,
425		.cpu_dai_name = "iDisp3 Pin",
426		.codec_name = "ehdaudio0D2",
427		.codec_dai_name = "intel-hdmi-hifi3",
428		.platform_name = "0000:00:0e.0",
429		.init = broxton_hdmi_init,
430		.dpcm_playback = 1,
431		.no_pcm = 1,
432	},
433};
434
435/* broxton audio machine driver for SPT + RT298S */
436static struct snd_soc_card broxton_rt298 = {
437	.name = "broxton-rt298",
438	.owner = THIS_MODULE,
439	.dai_link = broxton_rt298_dais,
440	.num_links = ARRAY_SIZE(broxton_rt298_dais),
441	.controls = broxton_controls,
442	.num_controls = ARRAY_SIZE(broxton_controls),
443	.dapm_widgets = broxton_widgets,
444	.num_dapm_widgets = ARRAY_SIZE(broxton_widgets),
445	.dapm_routes = broxton_rt298_map,
446	.num_dapm_routes = ARRAY_SIZE(broxton_rt298_map),
447	.fully_routed = true,
448};
449
450static int broxton_audio_probe(struct platform_device *pdev)
451{
452	broxton_rt298.dev = &pdev->dev;
453
454	return devm_snd_soc_register_card(&pdev->dev, &broxton_rt298);
455}
456
457static struct platform_driver broxton_audio = {
458	.probe = broxton_audio_probe,
459	.driver = {
460		.name = "bxt_alc298s_i2s",
461		.pm = &snd_soc_pm_ops,
462	},
463};
464module_platform_driver(broxton_audio)
465
466/* Module information */
467MODULE_AUTHOR("Ramesh Babu <Ramesh.Babu@intel.com>");
468MODULE_AUTHOR("Senthilnathan Veppur <senthilnathanx.veppur@intel.com>");
469MODULE_DESCRIPTION("Intel SST Audio for Broxton");
470MODULE_LICENSE("GPL v2");
471MODULE_ALIAS("platform:bxt_alc298s_i2s");