xref: /openbmc/linux/sound/soc/codecs/cros_ec_codec.c (revision 7a2eb736)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Driver for ChromeOS Embedded Controller codec.
4  *
5  * This driver uses the cros-ec interface to communicate with the ChromeOS
6  * EC for audio function.
7  */
8 
9 #include <linux/delay.h>
10 #include <linux/device.h>
11 #include <linux/kernel.h>
12 #include <linux/mfd/cros_ec.h>
13 #include <linux/mfd/cros_ec_commands.h>
14 #include <linux/module.h>
15 #include <linux/platform_device.h>
16 #include <sound/pcm.h>
17 #include <sound/pcm_params.h>
18 #include <sound/soc.h>
19 #include <sound/tlv.h>
20 
21 #define DRV_NAME "cros-ec-codec"
22 
23 /**
24  * struct cros_ec_codec_data - ChromeOS EC codec driver data.
25  * @dev:		Device structure used in sysfs.
26  * @ec_device:		cros_ec_device structure to talk to the physical device.
27  * @component:		Pointer to the component.
28  * @max_dmic_gain:	Maximum gain in dB supported by EC codec.
29  */
30 struct cros_ec_codec_data {
31 	struct device *dev;
32 	struct cros_ec_device *ec_device;
33 	struct snd_soc_component *component;
34 	unsigned int max_dmic_gain;
35 };
36 
37 static const DECLARE_TLV_DB_SCALE(ec_mic_gain_tlv, 0, 100, 0);
38 
39 static int ec_command_get_gain(struct snd_soc_component *component,
40 			       struct ec_param_codec_i2s *param,
41 			       struct ec_codec_i2s_gain *resp)
42 {
43 	struct cros_ec_codec_data *codec_data =
44 		snd_soc_component_get_drvdata(component);
45 	struct cros_ec_device *ec_device = codec_data->ec_device;
46 	u8 buffer[sizeof(struct cros_ec_command) +
47 		  max(sizeof(struct ec_param_codec_i2s),
48 		      sizeof(struct ec_codec_i2s_gain))];
49 	struct cros_ec_command *msg = (struct cros_ec_command *)&buffer;
50 	int ret;
51 
52 	msg->version = 0;
53 	msg->command = EC_CMD_CODEC_I2S;
54 	msg->outsize = sizeof(struct ec_param_codec_i2s);
55 	msg->insize = sizeof(struct ec_codec_i2s_gain);
56 
57 	memcpy(msg->data, param, msg->outsize);
58 
59 	ret = cros_ec_cmd_xfer_status(ec_device, msg);
60 	if (ret > 0)
61 		memcpy(resp, msg->data, msg->insize);
62 
63 	return ret;
64 }
65 
66 /*
67  * Wrapper for EC command without response.
68  */
69 static int ec_command_no_resp(struct snd_soc_component *component,
70 			      struct ec_param_codec_i2s *param)
71 {
72 	struct cros_ec_codec_data *codec_data =
73 		snd_soc_component_get_drvdata(component);
74 	struct cros_ec_device *ec_device = codec_data->ec_device;
75 	u8 buffer[sizeof(struct cros_ec_command) +
76 		  sizeof(struct ec_param_codec_i2s)];
77 	struct cros_ec_command *msg = (struct cros_ec_command *)&buffer;
78 
79 	msg->version = 0;
80 	msg->command = EC_CMD_CODEC_I2S;
81 	msg->outsize = sizeof(struct ec_param_codec_i2s);
82 	msg->insize = 0;
83 
84 	memcpy(msg->data, param, msg->outsize);
85 
86 	return cros_ec_cmd_xfer_status(ec_device, msg);
87 }
88 
89 static int set_i2s_config(struct snd_soc_component *component,
90 			  enum ec_i2s_config i2s_config)
91 {
92 	struct ec_param_codec_i2s param;
93 
94 	dev_dbg(component->dev, "%s set I2S format to %u\n", __func__,
95 		i2s_config);
96 
97 	param.cmd = EC_CODEC_I2S_SET_CONFIG;
98 	param.i2s_config = i2s_config;
99 
100 	return ec_command_no_resp(component, &param);
101 }
102 
103 static int cros_ec_i2s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
104 {
105 	struct snd_soc_component *component = dai->component;
106 	enum ec_i2s_config i2s_config;
107 
108 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
109 	case SND_SOC_DAIFMT_CBS_CFS:
110 		break;
111 	default:
112 		return -EINVAL;
113 	}
114 
115 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
116 	case SND_SOC_DAIFMT_NB_NF:
117 		break;
118 	default:
119 		return -EINVAL;
120 	}
121 
122 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
123 	case SND_SOC_DAIFMT_I2S:
124 		i2s_config = EC_DAI_FMT_I2S;
125 		break;
126 
127 	case SND_SOC_DAIFMT_RIGHT_J:
128 		i2s_config = EC_DAI_FMT_RIGHT_J;
129 		break;
130 
131 	case SND_SOC_DAIFMT_LEFT_J:
132 		i2s_config = EC_DAI_FMT_LEFT_J;
133 		break;
134 
135 	case SND_SOC_DAIFMT_DSP_A:
136 		i2s_config = EC_DAI_FMT_PCM_A;
137 		break;
138 
139 	case SND_SOC_DAIFMT_DSP_B:
140 		i2s_config = EC_DAI_FMT_PCM_B;
141 		break;
142 
143 	default:
144 		return -EINVAL;
145 	}
146 
147 	return set_i2s_config(component, i2s_config);
148 }
149 
150 static int set_i2s_sample_depth(struct snd_soc_component *component,
151 				enum ec_sample_depth_value depth)
152 {
153 	struct ec_param_codec_i2s param;
154 
155 	dev_dbg(component->dev, "%s set depth to %u\n", __func__, depth);
156 
157 	param.cmd = EC_CODEC_SET_SAMPLE_DEPTH;
158 	param.depth = depth;
159 
160 	return ec_command_no_resp(component, &param);
161 }
162 
163 static int set_i2s_bclk(struct snd_soc_component *component, uint32_t bclk)
164 {
165 	struct ec_param_codec_i2s param;
166 
167 	dev_dbg(component->dev, "%s set i2s bclk to %u\n", __func__, bclk);
168 
169 	param.cmd = EC_CODEC_I2S_SET_BCLK;
170 	param.bclk = bclk;
171 
172 	return ec_command_no_resp(component, &param);
173 }
174 
175 static int cros_ec_i2s_hw_params(struct snd_pcm_substream *substream,
176 				 struct snd_pcm_hw_params *params,
177 				 struct snd_soc_dai *dai)
178 {
179 	struct snd_soc_component *component = dai->component;
180 	unsigned int rate, bclk;
181 	int ret;
182 
183 	rate = params_rate(params);
184 	if (rate != 48000)
185 		return -EINVAL;
186 
187 	switch (params_format(params)) {
188 	case SNDRV_PCM_FORMAT_S16_LE:
189 		ret = set_i2s_sample_depth(component, EC_CODEC_SAMPLE_DEPTH_16);
190 		break;
191 	case SNDRV_PCM_FORMAT_S24_LE:
192 		ret = set_i2s_sample_depth(component, EC_CODEC_SAMPLE_DEPTH_24);
193 		break;
194 	default:
195 		return -EINVAL;
196 	}
197 	if (ret < 0)
198 		return ret;
199 
200 	bclk = snd_soc_params_to_bclk(params);
201 	return set_i2s_bclk(component, bclk);
202 }
203 
204 static const struct snd_soc_dai_ops cros_ec_i2s_dai_ops = {
205 	.hw_params = cros_ec_i2s_hw_params,
206 	.set_fmt = cros_ec_i2s_set_dai_fmt,
207 };
208 
209 static struct snd_soc_dai_driver cros_ec_dai[] = {
210 	{
211 		.name = "cros_ec_codec I2S",
212 		.id = 0,
213 		.capture = {
214 			.stream_name = "I2S Capture",
215 			.channels_min = 2,
216 			.channels_max = 2,
217 			.rates = SNDRV_PCM_RATE_48000,
218 			.formats = SNDRV_PCM_FMTBIT_S16_LE |
219 				   SNDRV_PCM_FMTBIT_S24_LE,
220 		},
221 		.ops = &cros_ec_i2s_dai_ops,
222 	}
223 };
224 
225 static int get_ec_mic_gain(struct snd_soc_component *component,
226 			   u8 *left, u8 *right)
227 {
228 	struct ec_param_codec_i2s param;
229 	struct ec_codec_i2s_gain resp;
230 	int ret;
231 
232 	param.cmd = EC_CODEC_GET_GAIN;
233 
234 	ret = ec_command_get_gain(component, &param, &resp);
235 	if (ret < 0)
236 		return ret;
237 
238 	*left = resp.left;
239 	*right = resp.right;
240 
241 	return 0;
242 }
243 
244 static int mic_gain_get(struct snd_kcontrol *kcontrol,
245 			struct snd_ctl_elem_value *ucontrol)
246 {
247 	struct snd_soc_component *component =
248 		snd_soc_kcontrol_component(kcontrol);
249 	u8 left, right;
250 	int ret;
251 
252 	ret = get_ec_mic_gain(component, &left, &right);
253 	if (ret)
254 		return ret;
255 
256 	ucontrol->value.integer.value[0] = left;
257 	ucontrol->value.integer.value[1] = right;
258 
259 	return 0;
260 }
261 
262 static int set_ec_mic_gain(struct snd_soc_component *component,
263 			   u8 left, u8 right)
264 {
265 	struct ec_param_codec_i2s param;
266 
267 	dev_dbg(component->dev, "%s set mic gain to %u, %u\n",
268 		__func__, left, right);
269 
270 	param.cmd = EC_CODEC_SET_GAIN;
271 	param.gain.left = left;
272 	param.gain.right = right;
273 
274 	return ec_command_no_resp(component, &param);
275 }
276 
277 static int mic_gain_put(struct snd_kcontrol *kcontrol,
278 			struct snd_ctl_elem_value *ucontrol)
279 {
280 	struct snd_soc_component *component =
281 		snd_soc_kcontrol_component(kcontrol);
282 	struct cros_ec_codec_data *codec_data =
283 		snd_soc_component_get_drvdata(component);
284 	int left = ucontrol->value.integer.value[0];
285 	int right = ucontrol->value.integer.value[1];
286 	unsigned int max_dmic_gain = codec_data->max_dmic_gain;
287 
288 	if (left > max_dmic_gain || right > max_dmic_gain)
289 		return -EINVAL;
290 
291 	return set_ec_mic_gain(component, (u8)left, (u8)right);
292 }
293 
294 static struct snd_kcontrol_new mic_gain_control =
295 	SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM, SND_SOC_NOPM, 0, 0, 0,
296 			   mic_gain_get, mic_gain_put, ec_mic_gain_tlv);
297 
298 static int enable_i2s(struct snd_soc_component *component, int enable)
299 {
300 	struct ec_param_codec_i2s param;
301 
302 	dev_dbg(component->dev, "%s set i2s to %u\n", __func__, enable);
303 
304 	param.cmd = EC_CODEC_I2S_ENABLE;
305 	param.i2s_enable = enable;
306 
307 	return ec_command_no_resp(component, &param);
308 }
309 
310 static int cros_ec_i2s_enable_event(struct snd_soc_dapm_widget *w,
311 				    struct snd_kcontrol *kcontrol, int event)
312 {
313 	struct snd_soc_component *component =
314 		snd_soc_dapm_to_component(w->dapm);
315 
316 	switch (event) {
317 	case SND_SOC_DAPM_PRE_PMU:
318 		dev_dbg(component->dev,
319 			"%s got SND_SOC_DAPM_PRE_PMU event\n", __func__);
320 		return enable_i2s(component, 1);
321 
322 	case SND_SOC_DAPM_PRE_PMD:
323 		dev_dbg(component->dev,
324 			"%s got SND_SOC_DAPM_PRE_PMD event\n", __func__);
325 		return enable_i2s(component, 0);
326 	}
327 
328 	return 0;
329 }
330 
331 /*
332  * The goal of this DAPM route is to turn on/off I2S using EC
333  * host command when capture stream is started/stopped.
334  */
335 static const struct snd_soc_dapm_widget cros_ec_codec_dapm_widgets[] = {
336 	SND_SOC_DAPM_INPUT("DMIC"),
337 
338 	/*
339 	 * Control EC to enable/disable I2S.
340 	 */
341 	SND_SOC_DAPM_SUPPLY("I2S Enable", SND_SOC_NOPM,
342 			    0, 0, cros_ec_i2s_enable_event,
343 			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
344 
345 	SND_SOC_DAPM_AIF_OUT("I2STX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0),
346 };
347 
348 static const struct snd_soc_dapm_route cros_ec_codec_dapm_routes[] = {
349 	{ "I2STX", NULL, "DMIC" },
350 	{ "I2STX", NULL, "I2S Enable" },
351 };
352 
353 /*
354  * Read maximum gain from device property and set it to mixer control.
355  */
356 static int cros_ec_set_gain_range(struct device *dev)
357 {
358 	struct soc_mixer_control *control;
359 	struct cros_ec_codec_data *codec_data = dev_get_drvdata(dev);
360 	int rc;
361 
362 	rc = device_property_read_u32(dev, "max-dmic-gain",
363 				      &codec_data->max_dmic_gain);
364 	if (rc)
365 		return rc;
366 
367 	control = (struct soc_mixer_control *)
368 				mic_gain_control.private_value;
369 	control->max = codec_data->max_dmic_gain;
370 	control->platform_max = codec_data->max_dmic_gain;
371 
372 	return 0;
373 }
374 
375 static int cros_ec_codec_probe(struct snd_soc_component *component)
376 {
377 	int rc;
378 
379 	struct cros_ec_codec_data *codec_data =
380 		snd_soc_component_get_drvdata(component);
381 
382 	rc = cros_ec_set_gain_range(codec_data->dev);
383 	if (rc)
384 		return rc;
385 
386 	return snd_soc_add_component_controls(component, &mic_gain_control, 1);
387 }
388 
389 static const struct snd_soc_component_driver cros_ec_component_driver = {
390 	.probe			= cros_ec_codec_probe,
391 	.dapm_widgets		= cros_ec_codec_dapm_widgets,
392 	.num_dapm_widgets	= ARRAY_SIZE(cros_ec_codec_dapm_widgets),
393 	.dapm_routes		= cros_ec_codec_dapm_routes,
394 	.num_dapm_routes	= ARRAY_SIZE(cros_ec_codec_dapm_routes),
395 };
396 
397 /*
398  * Platform device and platform driver fro cros-ec-codec.
399  */
400 static int cros_ec_codec_platform_probe(struct platform_device *pd)
401 {
402 	struct device *dev = &pd->dev;
403 	struct cros_ec_device *ec_device = dev_get_drvdata(pd->dev.parent);
404 	struct cros_ec_codec_data *codec_data;
405 
406 	codec_data = devm_kzalloc(dev, sizeof(struct cros_ec_codec_data),
407 				  GFP_KERNEL);
408 	if (!codec_data)
409 		return -ENOMEM;
410 
411 	codec_data->dev = dev;
412 	codec_data->ec_device = ec_device;
413 
414 	platform_set_drvdata(pd, codec_data);
415 
416 	return devm_snd_soc_register_component(dev, &cros_ec_component_driver,
417 					  cros_ec_dai, ARRAY_SIZE(cros_ec_dai));
418 }
419 
420 #ifdef CONFIG_OF
421 static const struct of_device_id cros_ec_codec_of_match[] = {
422 	{ .compatible = "google,cros-ec-codec" },
423 	{},
424 };
425 MODULE_DEVICE_TABLE(of, cros_ec_codec_of_match);
426 #endif
427 
428 static struct platform_driver cros_ec_codec_platform_driver = {
429 	.driver = {
430 		.name = DRV_NAME,
431 		.of_match_table = of_match_ptr(cros_ec_codec_of_match),
432 	},
433 	.probe = cros_ec_codec_platform_probe,
434 };
435 
436 module_platform_driver(cros_ec_codec_platform_driver);
437 
438 MODULE_LICENSE("GPL v2");
439 MODULE_DESCRIPTION("ChromeOS EC codec driver");
440 MODULE_AUTHOR("Cheng-Yi Chiang <cychiang@chromium.org>");
441 MODULE_ALIAS("platform:" DRV_NAME);
442