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