ASoC: cros_ec_codec: read max DMIC gain from EC codec
[linux-2.6-block.git] / sound / soc / codecs / cros_ec_codec.c
CommitLineData
b291f42a
CYC
1// SPDX-License-Identifier: GPL-2.0
2/*
727f1c71
TBS
3 * Copyright 2019 Google, Inc.
4 *
5 * ChromeOS Embedded Controller codec driver.
b291f42a
CYC
6 *
7 * This driver uses the cros-ec interface to communicate with the ChromeOS
8 * EC for audio function.
9 */
10
11#include <linux/delay.h>
12#include <linux/device.h>
13#include <linux/kernel.h>
b291f42a 14#include <linux/module.h>
840d9f13
EBS
15#include <linux/platform_data/cros_ec_commands.h>
16#include <linux/platform_data/cros_ec_proto.h>
b291f42a
CYC
17#include <linux/platform_device.h>
18#include <sound/pcm.h>
19#include <sound/pcm_params.h>
20#include <sound/soc.h>
21#include <sound/tlv.h>
22
727f1c71 23struct cros_ec_codec_priv {
b291f42a
CYC
24 struct device *dev;
25 struct cros_ec_device *ec_device;
b291f42a
CYC
26};
27
727f1c71
TBS
28static int send_ec_host_command(struct cros_ec_device *ec_dev, uint32_t cmd,
29 uint8_t *out, size_t outsize,
30 uint8_t *in, size_t insize)
b291f42a 31{
b291f42a 32 int ret;
727f1c71
TBS
33 struct cros_ec_command *msg;
34
35 msg = kmalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
36 if (!msg)
37 return -ENOMEM;
b291f42a
CYC
38
39 msg->version = 0;
727f1c71
TBS
40 msg->command = cmd;
41 msg->outsize = outsize;
42 msg->insize = insize;
43
44 if (outsize)
45 memcpy(msg->data, out, outsize);
b291f42a 46
727f1c71
TBS
47 ret = cros_ec_cmd_xfer_status(ec_dev, msg);
48 if (ret < 0)
49 goto error;
b291f42a 50
727f1c71
TBS
51 if (insize)
52 memcpy(in, msg->data, insize);
b291f42a 53
727f1c71
TBS
54 ret = 0;
55error:
56 kfree(msg);
b291f42a
CYC
57 return ret;
58}
59
727f1c71
TBS
60static int dmic_get_gain(struct snd_kcontrol *kcontrol,
61 struct snd_ctl_elem_value *ucontrol)
b291f42a 62{
727f1c71
TBS
63 struct snd_soc_component *component =
64 snd_soc_kcontrol_component(kcontrol);
65 struct cros_ec_codec_priv *priv =
b291f42a 66 snd_soc_component_get_drvdata(component);
8f731d4c 67 struct ec_param_ec_codec_dmic p;
f3e82ad4 68 struct ec_response_ec_codec_dmic_get_gain_idx r;
727f1c71 69 int ret;
b291f42a 70
f3e82ad4
TBS
71 p.cmd = EC_CODEC_DMIC_GET_GAIN_IDX;
72 p.get_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_0;
8f731d4c 73 ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
727f1c71
TBS
74 (uint8_t *)&p, sizeof(p),
75 (uint8_t *)&r, sizeof(r));
76 if (ret < 0)
77 return ret;
f3e82ad4 78 ucontrol->value.integer.value[0] = r.gain;
b291f42a 79
f3e82ad4
TBS
80 p.cmd = EC_CODEC_DMIC_GET_GAIN_IDX;
81 p.get_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_1;
82 ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
83 (uint8_t *)&p, sizeof(p),
84 (uint8_t *)&r, sizeof(r));
85 if (ret < 0)
86 return ret;
87 ucontrol->value.integer.value[1] = r.gain;
b291f42a 88
727f1c71 89 return 0;
b291f42a
CYC
90}
91
727f1c71
TBS
92static int dmic_put_gain(struct snd_kcontrol *kcontrol,
93 struct snd_ctl_elem_value *ucontrol)
b291f42a 94{
727f1c71
TBS
95 struct snd_soc_component *component =
96 snd_soc_kcontrol_component(kcontrol);
97 struct cros_ec_codec_priv *priv =
98 snd_soc_component_get_drvdata(component);
99 struct soc_mixer_control *control =
100 (struct soc_mixer_control *)kcontrol->private_value;
101 int max_dmic_gain = control->max;
102 int left = ucontrol->value.integer.value[0];
103 int right = ucontrol->value.integer.value[1];
8f731d4c 104 struct ec_param_ec_codec_dmic p;
f3e82ad4 105 int ret;
b291f42a 106
727f1c71 107 if (left > max_dmic_gain || right > max_dmic_gain)
b291f42a 108 return -EINVAL;
b291f42a 109
727f1c71 110 dev_dbg(component->dev, "set mic gain to %u, %u\n", left, right);
b291f42a 111
f3e82ad4
TBS
112 p.cmd = EC_CODEC_DMIC_SET_GAIN_IDX;
113 p.set_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_0;
114 p.set_gain_idx_param.gain = left;
115 ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
116 (uint8_t *)&p, sizeof(p), NULL, 0);
117 if (ret < 0)
118 return ret;
119
120 p.cmd = EC_CODEC_DMIC_SET_GAIN_IDX;
121 p.set_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_1;
122 p.set_gain_idx_param.gain = right;
8f731d4c 123 return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
727f1c71 124 (uint8_t *)&p, sizeof(p), NULL, 0);
b291f42a
CYC
125}
126
727f1c71 127static const DECLARE_TLV_DB_SCALE(dmic_gain_tlv, 0, 100, 0);
b291f42a 128
727f1c71
TBS
129enum {
130 DMIC_CTL_GAIN = 0,
131};
b291f42a 132
727f1c71
TBS
133static struct snd_kcontrol_new dmic_controls[] = {
134 [DMIC_CTL_GAIN] =
135 SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM, SND_SOC_NOPM,
136 0, 0, 0, dmic_get_gain, dmic_put_gain,
137 dmic_gain_tlv),
138};
b291f42a 139
8f731d4c
TBS
140static int dmic_probe(struct snd_soc_component *component)
141{
142 struct cros_ec_codec_priv *priv =
143 snd_soc_component_get_drvdata(component);
144 struct device *dev = priv->dev;
8f731d4c 145 struct soc_mixer_control *control;
f3e82ad4
TBS
146 struct ec_param_ec_codec_dmic p;
147 struct ec_response_ec_codec_dmic_get_max_gain r;
148 int ret;
8f731d4c 149
f3e82ad4
TBS
150 p.cmd = EC_CODEC_DMIC_GET_MAX_GAIN;
151
152 ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
153 (uint8_t *)&p, sizeof(p),
154 (uint8_t *)&r, sizeof(r));
155 if (ret < 0) {
156 dev_warn(dev, "get_max_gain() unsupported\n");
157 return 0;
8f731d4c
TBS
158 }
159
f3e82ad4
TBS
160 dev_dbg(dev, "max gain = %d\n", r.max_gain);
161
8f731d4c
TBS
162 control = (struct soc_mixer_control *)
163 dmic_controls[DMIC_CTL_GAIN].private_value;
f3e82ad4
TBS
164 control->max = r.max_gain;
165 control->platform_max = r.max_gain;
8f731d4c
TBS
166
167 return snd_soc_add_component_controls(component,
168 &dmic_controls[DMIC_CTL_GAIN], 1);
169}
170
727f1c71
TBS
171static int i2s_rx_hw_params(struct snd_pcm_substream *substream,
172 struct snd_pcm_hw_params *params,
173 struct snd_soc_dai *dai)
b291f42a
CYC
174{
175 struct snd_soc_component *component = dai->component;
727f1c71
TBS
176 struct cros_ec_codec_priv *priv =
177 snd_soc_component_get_drvdata(component);
178 struct ec_param_ec_codec_i2s_rx p;
179 enum ec_codec_i2s_rx_sample_depth depth;
b291f42a
CYC
180 int ret;
181
727f1c71 182 if (params_rate(params) != 48000)
b291f42a
CYC
183 return -EINVAL;
184
185 switch (params_format(params)) {
186 case SNDRV_PCM_FORMAT_S16_LE:
727f1c71 187 depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_16;
b291f42a
CYC
188 break;
189 case SNDRV_PCM_FORMAT_S24_LE:
727f1c71 190 depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_24;
b291f42a
CYC
191 break;
192 default:
193 return -EINVAL;
194 }
b291f42a 195
727f1c71 196 dev_dbg(component->dev, "set depth to %u\n", depth);
b291f42a 197
727f1c71
TBS
198 p.cmd = EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH;
199 p.set_sample_depth_param.depth = depth;
200 ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
201 (uint8_t *)&p, sizeof(p), NULL, 0);
b291f42a
CYC
202 if (ret < 0)
203 return ret;
204
727f1c71
TBS
205 dev_dbg(component->dev, "set bclk to %u\n",
206 snd_soc_params_to_bclk(params));
b291f42a 207
727f1c71
TBS
208 p.cmd = EC_CODEC_I2S_RX_SET_BCLK;
209 p.set_bclk_param.bclk = snd_soc_params_to_bclk(params);
210 return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
211 (uint8_t *)&p, sizeof(p), NULL, 0);
b291f42a
CYC
212}
213
727f1c71 214static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
b291f42a 215{
727f1c71
TBS
216 struct snd_soc_component *component = dai->component;
217 struct cros_ec_codec_priv *priv =
b291f42a 218 snd_soc_component_get_drvdata(component);
727f1c71
TBS
219 struct ec_param_ec_codec_i2s_rx p;
220 enum ec_codec_i2s_rx_daifmt daifmt;
b291f42a 221
727f1c71
TBS
222 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
223 case SND_SOC_DAIFMT_CBS_CFS:
224 break;
225 default:
b291f42a 226 return -EINVAL;
727f1c71 227 }
b291f42a 228
727f1c71
TBS
229 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
230 case SND_SOC_DAIFMT_NB_NF:
231 break;
232 default:
233 return -EINVAL;
234 }
b291f42a 235
727f1c71
TBS
236 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
237 case SND_SOC_DAIFMT_I2S:
238 daifmt = EC_CODEC_I2S_RX_DAIFMT_I2S;
239 break;
240 case SND_SOC_DAIFMT_RIGHT_J:
241 daifmt = EC_CODEC_I2S_RX_DAIFMT_RIGHT_J;
242 break;
243 case SND_SOC_DAIFMT_LEFT_J:
244 daifmt = EC_CODEC_I2S_RX_DAIFMT_LEFT_J;
245 break;
246 default:
247 return -EINVAL;
248 }
b291f42a 249
727f1c71 250 dev_dbg(component->dev, "set format to %u\n", daifmt);
b291f42a 251
727f1c71
TBS
252 p.cmd = EC_CODEC_I2S_RX_SET_DAIFMT;
253 p.set_daifmt_param.daifmt = daifmt;
254 return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
255 (uint8_t *)&p, sizeof(p), NULL, 0);
b291f42a
CYC
256}
257
727f1c71
TBS
258static const struct snd_soc_dai_ops i2s_rx_dai_ops = {
259 .hw_params = i2s_rx_hw_params,
260 .set_fmt = i2s_rx_set_fmt,
261};
262
263static int i2s_rx_event(struct snd_soc_dapm_widget *w,
264 struct snd_kcontrol *kcontrol, int event)
b291f42a
CYC
265{
266 struct snd_soc_component *component =
267 snd_soc_dapm_to_component(w->dapm);
727f1c71
TBS
268 struct cros_ec_codec_priv *priv =
269 snd_soc_component_get_drvdata(component);
270 struct ec_param_ec_codec_i2s_rx p;
b291f42a
CYC
271
272 switch (event) {
273 case SND_SOC_DAPM_PRE_PMU:
727f1c71
TBS
274 dev_dbg(component->dev, "enable I2S RX\n");
275 p.cmd = EC_CODEC_I2S_RX_ENABLE;
276 break;
b291f42a 277 case SND_SOC_DAPM_PRE_PMD:
727f1c71
TBS
278 dev_dbg(component->dev, "disable I2S RX\n");
279 p.cmd = EC_CODEC_I2S_RX_DISABLE;
280 break;
281 default:
282 return 0;
b291f42a
CYC
283 }
284
727f1c71
TBS
285 return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
286 (uint8_t *)&p, sizeof(p), NULL, 0);
b291f42a
CYC
287}
288
727f1c71 289static struct snd_soc_dapm_widget i2s_rx_dapm_widgets[] = {
b291f42a 290 SND_SOC_DAPM_INPUT("DMIC"),
727f1c71 291 SND_SOC_DAPM_SUPPLY("I2S RX Enable", SND_SOC_NOPM, 0, 0, i2s_rx_event,
b291f42a 292 SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
727f1c71
TBS
293 SND_SOC_DAPM_AIF_OUT("I2S RX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0),
294};
b291f42a 295
727f1c71
TBS
296static struct snd_soc_dapm_route i2s_rx_dapm_routes[] = {
297 {"I2S RX", NULL, "DMIC"},
298 {"I2S RX", NULL, "I2S RX Enable"},
b291f42a
CYC
299};
300
727f1c71
TBS
301static struct snd_soc_dai_driver i2s_rx_dai_driver = {
302 .name = "EC Codec I2S RX",
303 .capture = {
304 .stream_name = "I2S Capture",
305 .channels_min = 2,
306 .channels_max = 2,
307 .rates = SNDRV_PCM_RATE_48000,
308 .formats = SNDRV_PCM_FMTBIT_S16_LE |
309 SNDRV_PCM_FMTBIT_S24_LE,
310 },
311 .ops = &i2s_rx_dai_ops,
b291f42a
CYC
312};
313
727f1c71 314static int i2s_rx_probe(struct snd_soc_component *component)
b291f42a 315{
8f731d4c 316 return dmic_probe(component);
b291f42a
CYC
317}
318
727f1c71
TBS
319static const struct snd_soc_component_driver i2s_rx_component_driver = {
320 .probe = i2s_rx_probe,
321 .dapm_widgets = i2s_rx_dapm_widgets,
322 .num_dapm_widgets = ARRAY_SIZE(i2s_rx_dapm_widgets),
323 .dapm_routes = i2s_rx_dapm_routes,
324 .num_dapm_routes = ARRAY_SIZE(i2s_rx_dapm_routes),
b291f42a
CYC
325};
326
727f1c71 327static int cros_ec_codec_platform_probe(struct platform_device *pdev)
b291f42a 328{
727f1c71
TBS
329 struct device *dev = &pdev->dev;
330 struct cros_ec_device *ec_device = dev_get_drvdata(pdev->dev.parent);
331 struct cros_ec_codec_priv *priv;
b291f42a 332
727f1c71
TBS
333 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
334 if (!priv)
b291f42a
CYC
335 return -ENOMEM;
336
727f1c71
TBS
337 priv->dev = dev;
338 priv->ec_device = ec_device;
b291f42a 339
727f1c71 340 platform_set_drvdata(pdev, priv);
b291f42a 341
727f1c71
TBS
342 return devm_snd_soc_register_component(dev, &i2s_rx_component_driver,
343 &i2s_rx_dai_driver, 1);
b291f42a
CYC
344}
345
346#ifdef CONFIG_OF
347static const struct of_device_id cros_ec_codec_of_match[] = {
348 { .compatible = "google,cros-ec-codec" },
349 {},
350};
351MODULE_DEVICE_TABLE(of, cros_ec_codec_of_match);
352#endif
353
354static struct platform_driver cros_ec_codec_platform_driver = {
355 .driver = {
727f1c71 356 .name = "cros-ec-codec",
b291f42a
CYC
357 .of_match_table = of_match_ptr(cros_ec_codec_of_match),
358 },
359 .probe = cros_ec_codec_platform_probe,
360};
361
362module_platform_driver(cros_ec_codec_platform_driver);
363
364MODULE_LICENSE("GPL v2");
365MODULE_DESCRIPTION("ChromeOS EC codec driver");
366MODULE_AUTHOR("Cheng-Yi Chiang <cychiang@chromium.org>");
727f1c71 367MODULE_ALIAS("platform:cros-ec-codec");