xref: /openbmc/linux/sound/soc/intel/boards/skl_rt286.c (revision 2c684d89)
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 
30 static struct snd_soc_jack skylake_headset;
31 /* Headset jack detection DAPM pins */
32 static 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 
43 static 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 
49 static 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 
57 static 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 
89 static 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 
108 static 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 
124 static 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 
139 static 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 */
144 static 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 */
225 static 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 
239 static 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 
246 static struct platform_driver skylake_audio = {
247 	.probe = skylake_audio_probe,
248 	.driver = {
249 		.name = "skl_alc286s_i2s",
250 	},
251 };
252 
253 module_platform_driver(skylake_audio)
254 
255 /* Module information */
256 MODULE_AUTHOR("Omair Mohammed Abdullah <omair.m.abdullah@intel.com>");
257 MODULE_DESCRIPTION("Intel SST Audio for Skylake");
258 MODULE_LICENSE("GPL v2");
259 MODULE_ALIAS("platform:skl_alc286s_i2s");
260