1/*
2 * Intel Skylake I2S Machine Driver
3 *
4 * Copyright (C) 2014-2015, Intel Corporation. All rights reserved.
5 *
6 * Modified from:
7 *   Intel Broadwell Wildcatpoint SST Audio
8 *
9 *   Copyright (C) 2013, Intel Corporation. All rights reserved.
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License version
13 * 2 as published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 * GNU General Public License for more details.
19 */
20
21#include <linux/module.h>
22#include <linux/platform_device.h>
23#include <sound/core.h>
24#include <sound/pcm.h>
25#include <sound/soc.h>
26#include <sound/jack.h>
27#include <sound/pcm_params.h>
28#include "../../codecs/rt286.h"
29
30static struct snd_soc_jack skylake_headset;
31/* Headset jack detection DAPM pins */
32static struct snd_soc_jack_pin skylake_headset_pins[] = {
33	{
34		.pin = "Mic Jack",
35		.mask = SND_JACK_MICROPHONE,
36	},
37	{
38		.pin = "Headphone Jack",
39		.mask = SND_JACK_HEADPHONE,
40	},
41};
42
43static const struct snd_kcontrol_new skylake_controls[] = {
44	SOC_DAPM_PIN_SWITCH("Speaker"),
45	SOC_DAPM_PIN_SWITCH("Headphone Jack"),
46	SOC_DAPM_PIN_SWITCH("Mic Jack"),
47};
48
49static const struct snd_soc_dapm_widget skylake_widgets[] = {
50	SND_SOC_DAPM_HP("Headphone Jack", NULL),
51	SND_SOC_DAPM_SPK("Speaker", NULL),
52	SND_SOC_DAPM_MIC("Mic Jack", NULL),
53	SND_SOC_DAPM_MIC("DMIC2", NULL),
54	SND_SOC_DAPM_MIC("SoC DMIC", NULL),
55};
56
57static const struct snd_soc_dapm_route skylake_rt286_map[] = {
58	/* speaker */
59	{"Speaker", NULL, "SPOR"},
60	{"Speaker", NULL, "SPOL"},
61
62	/* HP jack connectors - unknown if we have jack deteck */
63	{"Headphone Jack", NULL, "HPO Pin"},
64
65	/* other jacks */
66	{"MIC1", NULL, "Mic Jack"},
67
68	/* digital mics */
69	{"DMIC1 Pin", NULL, "DMIC2"},
70	{"DMIC AIF", NULL, "SoC DMIC"},
71
72	/* CODEC BE connections */
73	{ "AIF1 Playback", NULL, "ssp0 Tx"},
74	{ "ssp0 Tx", NULL, "codec0_out"},
75	{ "ssp0 Tx", NULL, "codec1_out"},
76
77	{ "codec0_in", NULL, "ssp0 Rx" },
78	{ "codec1_in", NULL, "ssp0 Rx" },
79	{ "ssp0 Rx", NULL, "AIF1 Capture" },
80
81	{ "dmic01_hifi", NULL, "DMIC01 Rx" },
82	{ "DMIC01 Rx", NULL, "Capture" },
83
84	{ "hif1", NULL, "iDisp Tx"},
85	{ "iDisp Tx", NULL, "iDisp_out"},
86
87};
88
89static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
90{
91	struct snd_soc_codec *codec = rtd->codec;
92	int ret;
93
94	ret = snd_soc_card_jack_new(rtd->card, "Headset",
95		SND_JACK_HEADSET | SND_JACK_BTN_0,
96		&skylake_headset,
97		skylake_headset_pins, ARRAY_SIZE(skylake_headset_pins));
98
99	if (ret)
100		return ret;
101
102	rt286_mic_detect(codec, &skylake_headset);
103
104	return 0;
105}
106
107
108static int skylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
109			struct snd_pcm_hw_params *params)
110{
111	struct snd_interval *rate = hw_param_interval(params,
112			SNDRV_PCM_HW_PARAM_RATE);
113	struct snd_interval *channels = hw_param_interval(params,
114						SNDRV_PCM_HW_PARAM_CHANNELS);
115
116	/* The output is 48KHz, stereo, 16bits */
117	rate->min = rate->max = 48000;
118	channels->min = channels->max = 2;
119	params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
120
121	return 0;
122}
123
124static int skylake_rt286_hw_params(struct snd_pcm_substream *substream,
125	struct snd_pcm_hw_params *params)
126{
127	struct snd_soc_pcm_runtime *rtd = substream->private_data;
128	struct snd_soc_dai *codec_dai = rtd->codec_dai;
129	int ret;
130
131	ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000,
132		SND_SOC_CLOCK_IN);
133	if (ret < 0)
134		dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret);
135
136	return ret;
137}
138
139static struct snd_soc_ops skylake_rt286_ops = {
140	.hw_params = skylake_rt286_hw_params,
141};
142
143/* skylake digital audio interface glue - connects codec <--> CPU */
144static struct snd_soc_dai_link skylake_rt286_dais[] = {
145	/* Front End DAI links */
146	{
147		.name = "Skl Audio Port",
148		.stream_name = "Audio",
149		.cpu_dai_name = "System Pin",
150		.platform_name = "0000:00:1f.3",
151		.nonatomic = 1,
152		.dynamic = 1,
153		.codec_name = "snd-soc-dummy",
154		.codec_dai_name = "snd-soc-dummy-dai",
155		.trigger = {
156			SND_SOC_DPCM_TRIGGER_POST,
157			SND_SOC_DPCM_TRIGGER_POST
158		},
159		.dpcm_playback = 1,
160	},
161	{
162		.name = "Skl Audio Capture Port",
163		.stream_name = "Audio Record",
164		.cpu_dai_name = "System Pin",
165		.platform_name = "0000:00:1f.3",
166		.nonatomic = 1,
167		.dynamic = 1,
168		.codec_name = "snd-soc-dummy",
169		.codec_dai_name = "snd-soc-dummy-dai",
170		.trigger = {
171			SND_SOC_DPCM_TRIGGER_POST,
172			SND_SOC_DPCM_TRIGGER_POST
173		},
174		.dpcm_capture = 1,
175	},
176	{
177		.name = "Skl Audio Reference cap",
178		.stream_name = "refcap",
179		.cpu_dai_name = "Reference Pin",
180		.codec_name = "snd-soc-dummy",
181		.codec_dai_name = "snd-soc-dummy-dai",
182		.platform_name = "0000:00:1f.3",
183		.init = NULL,
184		.dpcm_capture = 1,
185		.ignore_suspend = 1,
186		.nonatomic = 1,
187		.dynamic = 1,
188	},
189
190	/* Back End DAI links */
191	{
192		/* SSP0 - Codec */
193		.name = "SSP0-Codec",
194		.be_id = 0,
195		.cpu_dai_name = "SSP0 Pin",
196		.platform_name = "0000:00:1f.3",
197		.no_pcm = 1,
198		.codec_name = "i2c-INT343A:00",
199		.codec_dai_name = "rt286-aif1",
200		.init = skylake_rt286_codec_init,
201		.dai_fmt = SND_SOC_DAIFMT_I2S |
202			SND_SOC_DAIFMT_NB_NF |
203			SND_SOC_DAIFMT_CBS_CFS,
204		.ignore_suspend = 1,
205		.ignore_pmdown_time = 1,
206		.be_hw_params_fixup = skylake_ssp0_fixup,
207		.ops = &skylake_rt286_ops,
208		.dpcm_playback = 1,
209		.dpcm_capture = 1,
210	},
211	{
212		.name = "dmic01",
213		.be_id = 1,
214		.cpu_dai_name = "DMIC01 Pin",
215		.codec_name = "dmic-codec",
216		.codec_dai_name = "dmic-hifi",
217		.platform_name = "0000:00:1f.3",
218		.ignore_suspend = 1,
219		.dpcm_capture = 1,
220		.no_pcm = 1,
221	},
222};
223
224/* skylake audio machine driver for SPT + RT286S */
225static struct snd_soc_card skylake_rt286 = {
226	.name = "skylake-rt286",
227	.owner = THIS_MODULE,
228	.dai_link = skylake_rt286_dais,
229	.num_links = ARRAY_SIZE(skylake_rt286_dais),
230	.controls = skylake_controls,
231	.num_controls = ARRAY_SIZE(skylake_controls),
232	.dapm_widgets = skylake_widgets,
233	.num_dapm_widgets = ARRAY_SIZE(skylake_widgets),
234	.dapm_routes = skylake_rt286_map,
235	.num_dapm_routes = ARRAY_SIZE(skylake_rt286_map),
236	.fully_routed = true,
237};
238
239static int skylake_audio_probe(struct platform_device *pdev)
240{
241	skylake_rt286.dev = &pdev->dev;
242
243	return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286);
244}
245
246static struct platform_driver skylake_audio = {
247	.probe = skylake_audio_probe,
248	.driver = {
249		.name = "skl_alc286s_i2s",
250	},
251};
252
253module_platform_driver(skylake_audio)
254
255/* Module information */
256MODULE_AUTHOR("Omair Mohammed Abdullah <omair.m.abdullah@intel.com>");
257MODULE_DESCRIPTION("Intel SST Audio for Skylake");
258MODULE_LICENSE("GPL v2");
259MODULE_ALIAS("platform:skl_alc286s_i2s");
260