ASoC: cros_ec_codec: refactor I2S RX
[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);
727f1c71
TBS
67 struct ec_param_ec_codec_i2s_rx p;
68 struct ec_response_ec_codec_i2s_rx_get_gain r;
69 int ret;
b291f42a 70
727f1c71
TBS
71 p.cmd = EC_CODEC_I2S_RX_GET_GAIN;
72 ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
73 (uint8_t *)&p, sizeof(p),
74 (uint8_t *)&r, sizeof(r));
75 if (ret < 0)
76 return ret;
b291f42a 77
727f1c71
TBS
78 ucontrol->value.integer.value[0] = r.left;
79 ucontrol->value.integer.value[1] = r.right;
b291f42a 80
727f1c71 81 return 0;
b291f42a
CYC
82}
83
727f1c71
TBS
84static int dmic_put_gain(struct snd_kcontrol *kcontrol,
85 struct snd_ctl_elem_value *ucontrol)
b291f42a 86{
727f1c71
TBS
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_i2s_rx p;
b291f42a 97
727f1c71 98 if (left > max_dmic_gain || right > max_dmic_gain)
b291f42a 99 return -EINVAL;
b291f42a 100
727f1c71 101 dev_dbg(component->dev, "set mic gain to %u, %u\n", left, right);
b291f42a 102
727f1c71
TBS
103 p.cmd = EC_CODEC_I2S_RX_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_I2S_RX,
107 (uint8_t *)&p, sizeof(p), NULL, 0);
b291f42a
CYC
108}
109
727f1c71 110static const DECLARE_TLV_DB_SCALE(dmic_gain_tlv, 0, 100, 0);
b291f42a 111
727f1c71
TBS
112enum {
113 DMIC_CTL_GAIN = 0,
114};
b291f42a 115
727f1c71
TBS
116static 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};
b291f42a 122
727f1c71
TBS
123static int i2s_rx_hw_params(struct snd_pcm_substream *substream,
124 struct snd_pcm_hw_params *params,
125 struct snd_soc_dai *dai)
b291f42a
CYC
126{
127 struct snd_soc_component *component = dai->component;
727f1c71
TBS
128 struct cros_ec_codec_priv *priv =
129 snd_soc_component_get_drvdata(component);
130 struct ec_param_ec_codec_i2s_rx p;
131 enum ec_codec_i2s_rx_sample_depth depth;
b291f42a
CYC
132 int ret;
133
727f1c71 134 if (params_rate(params) != 48000)
b291f42a
CYC
135 return -EINVAL;
136
137 switch (params_format(params)) {
138 case SNDRV_PCM_FORMAT_S16_LE:
727f1c71 139 depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_16;
b291f42a
CYC
140 break;
141 case SNDRV_PCM_FORMAT_S24_LE:
727f1c71 142 depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_24;
b291f42a
CYC
143 break;
144 default:
145 return -EINVAL;
146 }
b291f42a 147
727f1c71 148 dev_dbg(component->dev, "set depth to %u\n", depth);
b291f42a 149
727f1c71
TBS
150 p.cmd = EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH;
151 p.set_sample_depth_param.depth = depth;
152 ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
153 (uint8_t *)&p, sizeof(p), NULL, 0);
b291f42a
CYC
154 if (ret < 0)
155 return ret;
156
727f1c71
TBS
157 dev_dbg(component->dev, "set bclk to %u\n",
158 snd_soc_params_to_bclk(params));
b291f42a 159
727f1c71
TBS
160 p.cmd = EC_CODEC_I2S_RX_SET_BCLK;
161 p.set_bclk_param.bclk = snd_soc_params_to_bclk(params);
162 return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
163 (uint8_t *)&p, sizeof(p), NULL, 0);
b291f42a
CYC
164}
165
727f1c71 166static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
b291f42a 167{
727f1c71
TBS
168 struct snd_soc_component *component = dai->component;
169 struct cros_ec_codec_priv *priv =
b291f42a 170 snd_soc_component_get_drvdata(component);
727f1c71
TBS
171 struct ec_param_ec_codec_i2s_rx p;
172 enum ec_codec_i2s_rx_daifmt daifmt;
b291f42a 173
727f1c71
TBS
174 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
175 case SND_SOC_DAIFMT_CBS_CFS:
176 break;
177 default:
b291f42a 178 return -EINVAL;
727f1c71 179 }
b291f42a 180
727f1c71
TBS
181 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
182 case SND_SOC_DAIFMT_NB_NF:
183 break;
184 default:
185 return -EINVAL;
186 }
b291f42a 187
727f1c71
TBS
188 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
189 case SND_SOC_DAIFMT_I2S:
190 daifmt = EC_CODEC_I2S_RX_DAIFMT_I2S;
191 break;
192 case SND_SOC_DAIFMT_RIGHT_J:
193 daifmt = EC_CODEC_I2S_RX_DAIFMT_RIGHT_J;
194 break;
195 case SND_SOC_DAIFMT_LEFT_J:
196 daifmt = EC_CODEC_I2S_RX_DAIFMT_LEFT_J;
197 break;
198 default:
199 return -EINVAL;
200 }
b291f42a 201
727f1c71 202 dev_dbg(component->dev, "set format to %u\n", daifmt);
b291f42a 203
727f1c71
TBS
204 p.cmd = EC_CODEC_I2S_RX_SET_DAIFMT;
205 p.set_daifmt_param.daifmt = daifmt;
206 return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
207 (uint8_t *)&p, sizeof(p), NULL, 0);
b291f42a
CYC
208}
209
727f1c71
TBS
210static const struct snd_soc_dai_ops i2s_rx_dai_ops = {
211 .hw_params = i2s_rx_hw_params,
212 .set_fmt = i2s_rx_set_fmt,
213};
214
215static int i2s_rx_event(struct snd_soc_dapm_widget *w,
216 struct snd_kcontrol *kcontrol, int event)
b291f42a
CYC
217{
218 struct snd_soc_component *component =
219 snd_soc_dapm_to_component(w->dapm);
727f1c71
TBS
220 struct cros_ec_codec_priv *priv =
221 snd_soc_component_get_drvdata(component);
222 struct ec_param_ec_codec_i2s_rx p;
b291f42a
CYC
223
224 switch (event) {
225 case SND_SOC_DAPM_PRE_PMU:
727f1c71
TBS
226 dev_dbg(component->dev, "enable I2S RX\n");
227 p.cmd = EC_CODEC_I2S_RX_ENABLE;
228 break;
b291f42a 229 case SND_SOC_DAPM_PRE_PMD:
727f1c71
TBS
230 dev_dbg(component->dev, "disable I2S RX\n");
231 p.cmd = EC_CODEC_I2S_RX_DISABLE;
232 break;
233 default:
234 return 0;
b291f42a
CYC
235 }
236
727f1c71
TBS
237 return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
238 (uint8_t *)&p, sizeof(p), NULL, 0);
b291f42a
CYC
239}
240
727f1c71 241static struct snd_soc_dapm_widget i2s_rx_dapm_widgets[] = {
b291f42a 242 SND_SOC_DAPM_INPUT("DMIC"),
727f1c71 243 SND_SOC_DAPM_SUPPLY("I2S RX Enable", SND_SOC_NOPM, 0, 0, i2s_rx_event,
b291f42a 244 SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
727f1c71
TBS
245 SND_SOC_DAPM_AIF_OUT("I2S RX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0),
246};
b291f42a 247
727f1c71
TBS
248static struct snd_soc_dapm_route i2s_rx_dapm_routes[] = {
249 {"I2S RX", NULL, "DMIC"},
250 {"I2S RX", NULL, "I2S RX Enable"},
b291f42a
CYC
251};
252
727f1c71
TBS
253static struct snd_soc_dai_driver i2s_rx_dai_driver = {
254 .name = "EC Codec I2S RX",
255 .capture = {
256 .stream_name = "I2S Capture",
257 .channels_min = 2,
258 .channels_max = 2,
259 .rates = SNDRV_PCM_RATE_48000,
260 .formats = SNDRV_PCM_FMTBIT_S16_LE |
261 SNDRV_PCM_FMTBIT_S24_LE,
262 },
263 .ops = &i2s_rx_dai_ops,
b291f42a
CYC
264};
265
727f1c71 266static int i2s_rx_probe(struct snd_soc_component *component)
b291f42a 267{
727f1c71
TBS
268 struct cros_ec_codec_priv *priv =
269 snd_soc_component_get_drvdata(component);
270 struct device *dev = priv->dev;
271 int ret, val;
b291f42a 272 struct soc_mixer_control *control;
b291f42a 273
727f1c71
TBS
274 ret = device_property_read_u32(dev, "max-dmic-gain", &val);
275 if (ret) {
276 dev_err(dev, "Failed to read 'max-dmic-gain'\n");
277 return ret;
278 }
b291f42a
CYC
279
280 control = (struct soc_mixer_control *)
727f1c71
TBS
281 dmic_controls[DMIC_CTL_GAIN].private_value;
282 control->max = val;
283 control->platform_max = val;
b291f42a 284
727f1c71
TBS
285 return snd_soc_add_component_controls(component,
286 &dmic_controls[DMIC_CTL_GAIN], 1);
b291f42a
CYC
287}
288
727f1c71
TBS
289static const struct snd_soc_component_driver i2s_rx_component_driver = {
290 .probe = i2s_rx_probe,
291 .dapm_widgets = i2s_rx_dapm_widgets,
292 .num_dapm_widgets = ARRAY_SIZE(i2s_rx_dapm_widgets),
293 .dapm_routes = i2s_rx_dapm_routes,
294 .num_dapm_routes = ARRAY_SIZE(i2s_rx_dapm_routes),
b291f42a
CYC
295};
296
727f1c71 297static int cros_ec_codec_platform_probe(struct platform_device *pdev)
b291f42a 298{
727f1c71
TBS
299 struct device *dev = &pdev->dev;
300 struct cros_ec_device *ec_device = dev_get_drvdata(pdev->dev.parent);
301 struct cros_ec_codec_priv *priv;
b291f42a 302
727f1c71
TBS
303 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
304 if (!priv)
b291f42a
CYC
305 return -ENOMEM;
306
727f1c71
TBS
307 priv->dev = dev;
308 priv->ec_device = ec_device;
b291f42a 309
727f1c71 310 platform_set_drvdata(pdev, priv);
b291f42a 311
727f1c71
TBS
312 return devm_snd_soc_register_component(dev, &i2s_rx_component_driver,
313 &i2s_rx_dai_driver, 1);
b291f42a
CYC
314}
315
316#ifdef CONFIG_OF
317static const struct of_device_id cros_ec_codec_of_match[] = {
318 { .compatible = "google,cros-ec-codec" },
319 {},
320};
321MODULE_DEVICE_TABLE(of, cros_ec_codec_of_match);
322#endif
323
324static struct platform_driver cros_ec_codec_platform_driver = {
325 .driver = {
727f1c71 326 .name = "cros-ec-codec",
b291f42a
CYC
327 .of_match_table = of_match_ptr(cros_ec_codec_of_match),
328 },
329 .probe = cros_ec_codec_platform_probe,
330};
331
332module_platform_driver(cros_ec_codec_platform_driver);
333
334MODULE_LICENSE("GPL v2");
335MODULE_DESCRIPTION("ChromeOS EC codec driver");
336MODULE_AUTHOR("Cheng-Yi Chiang <cychiang@chromium.org>");
727f1c71 337MODULE_ALIAS("platform:cros-ec-codec");