1b291f42aSCheng-Yi Chiang // SPDX-License-Identifier: GPL-2.0
2b291f42aSCheng-Yi Chiang /*
3727f1c71STzung-Bi Shih * Copyright 2019 Google, Inc.
4727f1c71STzung-Bi Shih *
5727f1c71STzung-Bi Shih * ChromeOS Embedded Controller codec driver.
6b291f42aSCheng-Yi Chiang *
7b291f42aSCheng-Yi Chiang * This driver uses the cros-ec interface to communicate with the ChromeOS
8b291f42aSCheng-Yi Chiang * EC for audio function.
9b291f42aSCheng-Yi Chiang */
10b291f42aSCheng-Yi Chiang
11a24d22b2SEric Biggers #include <crypto/sha2.h>
12877167efSYu-Hsuan Hsu #include <linux/acpi.h>
13b291f42aSCheng-Yi Chiang #include <linux/delay.h>
14b291f42aSCheng-Yi Chiang #include <linux/device.h>
15b6bc07d4STzung-Bi Shih #include <linux/io.h>
16b6bc07d4STzung-Bi Shih #include <linux/jiffies.h>
17b291f42aSCheng-Yi Chiang #include <linux/kernel.h>
18b291f42aSCheng-Yi Chiang #include <linux/module.h>
19b6bc07d4STzung-Bi Shih #include <linux/of.h>
20b6bc07d4STzung-Bi Shih #include <linux/of_address.h>
21840d9f13SEnric Balletbo i Serra #include <linux/platform_data/cros_ec_commands.h>
22840d9f13SEnric Balletbo i Serra #include <linux/platform_data/cros_ec_proto.h>
23b291f42aSCheng-Yi Chiang #include <linux/platform_device.h>
24b291f42aSCheng-Yi Chiang #include <sound/pcm.h>
25b291f42aSCheng-Yi Chiang #include <sound/pcm_params.h>
26b291f42aSCheng-Yi Chiang #include <sound/soc.h>
27b291f42aSCheng-Yi Chiang #include <sound/tlv.h>
28b291f42aSCheng-Yi Chiang
29727f1c71STzung-Bi Shih struct cros_ec_codec_priv {
30b291f42aSCheng-Yi Chiang struct device *dev;
31b291f42aSCheng-Yi Chiang struct cros_ec_device *ec_device;
32b6bc07d4STzung-Bi Shih
33b6bc07d4STzung-Bi Shih /* common */
34b6bc07d4STzung-Bi Shih uint32_t ec_capabilities;
35b6bc07d4STzung-Bi Shih
36b6bc07d4STzung-Bi Shih uint64_t ec_shm_addr;
37b6bc07d4STzung-Bi Shih uint32_t ec_shm_len;
38b6bc07d4STzung-Bi Shih
39b6bc07d4STzung-Bi Shih uint64_t ap_shm_phys_addr;
40b6bc07d4STzung-Bi Shih uint32_t ap_shm_len;
41b6bc07d4STzung-Bi Shih uint64_t ap_shm_addr;
42b6bc07d4STzung-Bi Shih uint64_t ap_shm_last_alloc;
43b6bc07d4STzung-Bi Shih
44b6bc07d4STzung-Bi Shih /* DMIC */
45b6bc07d4STzung-Bi Shih atomic_t dmic_probed;
46b6bc07d4STzung-Bi Shih
4799b4f439SYu-Hsuan Hsu /* I2S_RX */
4899b4f439SYu-Hsuan Hsu uint32_t i2s_rx_bclk_ratio;
4999b4f439SYu-Hsuan Hsu
50b6bc07d4STzung-Bi Shih /* WoV */
51b6bc07d4STzung-Bi Shih bool wov_enabled;
52b6bc07d4STzung-Bi Shih uint8_t *wov_audio_shm_p;
53b6bc07d4STzung-Bi Shih uint32_t wov_audio_shm_len;
54b6bc07d4STzung-Bi Shih uint8_t wov_audio_shm_type;
55b6bc07d4STzung-Bi Shih uint8_t *wov_lang_shm_p;
56b6bc07d4STzung-Bi Shih uint32_t wov_lang_shm_len;
57b6bc07d4STzung-Bi Shih uint8_t wov_lang_shm_type;
58b6bc07d4STzung-Bi Shih
59b6bc07d4STzung-Bi Shih struct mutex wov_dma_lock;
60b6bc07d4STzung-Bi Shih uint8_t wov_buf[64000];
61b6bc07d4STzung-Bi Shih uint32_t wov_rp, wov_wp;
62b6bc07d4STzung-Bi Shih size_t wov_dma_offset;
63b6bc07d4STzung-Bi Shih bool wov_burst_read;
64b6bc07d4STzung-Bi Shih struct snd_pcm_substream *wov_substream;
65b6bc07d4STzung-Bi Shih struct delayed_work wov_copy_work;
66b6bc07d4STzung-Bi Shih struct notifier_block wov_notifier;
67b291f42aSCheng-Yi Chiang };
68b291f42aSCheng-Yi Chiang
ec_codec_capable(struct cros_ec_codec_priv * priv,uint8_t cap)69b6bc07d4STzung-Bi Shih static int ec_codec_capable(struct cros_ec_codec_priv *priv, uint8_t cap)
70b6bc07d4STzung-Bi Shih {
71b6bc07d4STzung-Bi Shih return priv->ec_capabilities & BIT(cap);
72b6bc07d4STzung-Bi Shih }
73b6bc07d4STzung-Bi Shih
send_ec_host_command(struct cros_ec_device * ec_dev,uint32_t cmd,uint8_t * out,size_t outsize,uint8_t * in,size_t insize)74727f1c71STzung-Bi Shih static int send_ec_host_command(struct cros_ec_device *ec_dev, uint32_t cmd,
75727f1c71STzung-Bi Shih uint8_t *out, size_t outsize,
76727f1c71STzung-Bi Shih uint8_t *in, size_t insize)
77b291f42aSCheng-Yi Chiang {
78b291f42aSCheng-Yi Chiang int ret;
79727f1c71STzung-Bi Shih struct cros_ec_command *msg;
80727f1c71STzung-Bi Shih
81727f1c71STzung-Bi Shih msg = kmalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
82727f1c71STzung-Bi Shih if (!msg)
83727f1c71STzung-Bi Shih return -ENOMEM;
84b291f42aSCheng-Yi Chiang
85b291f42aSCheng-Yi Chiang msg->version = 0;
86727f1c71STzung-Bi Shih msg->command = cmd;
87727f1c71STzung-Bi Shih msg->outsize = outsize;
88727f1c71STzung-Bi Shih msg->insize = insize;
89b291f42aSCheng-Yi Chiang
90727f1c71STzung-Bi Shih if (outsize)
91727f1c71STzung-Bi Shih memcpy(msg->data, out, outsize);
92b291f42aSCheng-Yi Chiang
93727f1c71STzung-Bi Shih ret = cros_ec_cmd_xfer_status(ec_dev, msg);
94727f1c71STzung-Bi Shih if (ret < 0)
95727f1c71STzung-Bi Shih goto error;
96b291f42aSCheng-Yi Chiang
97157c4df6SPierre-Louis Bossart if (in && insize)
98727f1c71STzung-Bi Shih memcpy(in, msg->data, insize);
99727f1c71STzung-Bi Shih
100727f1c71STzung-Bi Shih ret = 0;
101727f1c71STzung-Bi Shih error:
102727f1c71STzung-Bi Shih kfree(msg);
103b291f42aSCheng-Yi Chiang return ret;
104b291f42aSCheng-Yi Chiang }
105b291f42aSCheng-Yi Chiang
dmic_get_gain(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)106727f1c71STzung-Bi Shih static int dmic_get_gain(struct snd_kcontrol *kcontrol,
107727f1c71STzung-Bi Shih struct snd_ctl_elem_value *ucontrol)
108b291f42aSCheng-Yi Chiang {
109727f1c71STzung-Bi Shih struct snd_soc_component *component =
110727f1c71STzung-Bi Shih snd_soc_kcontrol_component(kcontrol);
111727f1c71STzung-Bi Shih struct cros_ec_codec_priv *priv =
112b291f42aSCheng-Yi Chiang snd_soc_component_get_drvdata(component);
1138f731d4cSTzung-Bi Shih struct ec_param_ec_codec_dmic p;
114f3e82ad4STzung-Bi Shih struct ec_response_ec_codec_dmic_get_gain_idx r;
115727f1c71STzung-Bi Shih int ret;
116b291f42aSCheng-Yi Chiang
117f3e82ad4STzung-Bi Shih p.cmd = EC_CODEC_DMIC_GET_GAIN_IDX;
118f3e82ad4STzung-Bi Shih p.get_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_0;
1198f731d4cSTzung-Bi Shih ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
120727f1c71STzung-Bi Shih (uint8_t *)&p, sizeof(p),
121727f1c71STzung-Bi Shih (uint8_t *)&r, sizeof(r));
122727f1c71STzung-Bi Shih if (ret < 0)
123727f1c71STzung-Bi Shih return ret;
124f3e82ad4STzung-Bi Shih ucontrol->value.integer.value[0] = r.gain;
125b291f42aSCheng-Yi Chiang
126f3e82ad4STzung-Bi Shih p.cmd = EC_CODEC_DMIC_GET_GAIN_IDX;
127f3e82ad4STzung-Bi Shih p.get_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_1;
128f3e82ad4STzung-Bi Shih ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
129f3e82ad4STzung-Bi Shih (uint8_t *)&p, sizeof(p),
130f3e82ad4STzung-Bi Shih (uint8_t *)&r, sizeof(r));
131f3e82ad4STzung-Bi Shih if (ret < 0)
132f3e82ad4STzung-Bi Shih return ret;
133f3e82ad4STzung-Bi Shih ucontrol->value.integer.value[1] = r.gain;
134b291f42aSCheng-Yi Chiang
135727f1c71STzung-Bi Shih return 0;
136b291f42aSCheng-Yi Chiang }
137b291f42aSCheng-Yi Chiang
dmic_put_gain(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)138727f1c71STzung-Bi Shih static int dmic_put_gain(struct snd_kcontrol *kcontrol,
139727f1c71STzung-Bi Shih struct snd_ctl_elem_value *ucontrol)
140b291f42aSCheng-Yi Chiang {
141727f1c71STzung-Bi Shih struct snd_soc_component *component =
142727f1c71STzung-Bi Shih snd_soc_kcontrol_component(kcontrol);
143727f1c71STzung-Bi Shih struct cros_ec_codec_priv *priv =
144727f1c71STzung-Bi Shih snd_soc_component_get_drvdata(component);
145727f1c71STzung-Bi Shih struct soc_mixer_control *control =
146727f1c71STzung-Bi Shih (struct soc_mixer_control *)kcontrol->private_value;
147727f1c71STzung-Bi Shih int max_dmic_gain = control->max;
148727f1c71STzung-Bi Shih int left = ucontrol->value.integer.value[0];
149727f1c71STzung-Bi Shih int right = ucontrol->value.integer.value[1];
1508f731d4cSTzung-Bi Shih struct ec_param_ec_codec_dmic p;
151f3e82ad4STzung-Bi Shih int ret;
152b291f42aSCheng-Yi Chiang
153727f1c71STzung-Bi Shih if (left > max_dmic_gain || right > max_dmic_gain)
154727f1c71STzung-Bi Shih return -EINVAL;
155b291f42aSCheng-Yi Chiang
156727f1c71STzung-Bi Shih dev_dbg(component->dev, "set mic gain to %u, %u\n", left, right);
157b291f42aSCheng-Yi Chiang
158f3e82ad4STzung-Bi Shih p.cmd = EC_CODEC_DMIC_SET_GAIN_IDX;
159f3e82ad4STzung-Bi Shih p.set_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_0;
160f3e82ad4STzung-Bi Shih p.set_gain_idx_param.gain = left;
161f3e82ad4STzung-Bi Shih ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
162f3e82ad4STzung-Bi Shih (uint8_t *)&p, sizeof(p), NULL, 0);
163f3e82ad4STzung-Bi Shih if (ret < 0)
164f3e82ad4STzung-Bi Shih return ret;
165f3e82ad4STzung-Bi Shih
166f3e82ad4STzung-Bi Shih p.cmd = EC_CODEC_DMIC_SET_GAIN_IDX;
167f3e82ad4STzung-Bi Shih p.set_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_1;
168f3e82ad4STzung-Bi Shih p.set_gain_idx_param.gain = right;
1698f731d4cSTzung-Bi Shih return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
170727f1c71STzung-Bi Shih (uint8_t *)&p, sizeof(p), NULL, 0);
171b291f42aSCheng-Yi Chiang }
172b291f42aSCheng-Yi Chiang
173727f1c71STzung-Bi Shih static const DECLARE_TLV_DB_SCALE(dmic_gain_tlv, 0, 100, 0);
174727f1c71STzung-Bi Shih
175727f1c71STzung-Bi Shih enum {
176727f1c71STzung-Bi Shih DMIC_CTL_GAIN = 0,
177727f1c71STzung-Bi Shih };
178727f1c71STzung-Bi Shih
179727f1c71STzung-Bi Shih static struct snd_kcontrol_new dmic_controls[] = {
180727f1c71STzung-Bi Shih [DMIC_CTL_GAIN] =
181727f1c71STzung-Bi Shih SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM, SND_SOC_NOPM,
182727f1c71STzung-Bi Shih 0, 0, 0, dmic_get_gain, dmic_put_gain,
183727f1c71STzung-Bi Shih dmic_gain_tlv),
184727f1c71STzung-Bi Shih };
185727f1c71STzung-Bi Shih
dmic_probe(struct snd_soc_component * component)1868f731d4cSTzung-Bi Shih static int dmic_probe(struct snd_soc_component *component)
1878f731d4cSTzung-Bi Shih {
1888f731d4cSTzung-Bi Shih struct cros_ec_codec_priv *priv =
1898f731d4cSTzung-Bi Shih snd_soc_component_get_drvdata(component);
1908f731d4cSTzung-Bi Shih struct device *dev = priv->dev;
1918f731d4cSTzung-Bi Shih struct soc_mixer_control *control;
192f3e82ad4STzung-Bi Shih struct ec_param_ec_codec_dmic p;
193f3e82ad4STzung-Bi Shih struct ec_response_ec_codec_dmic_get_max_gain r;
194f3e82ad4STzung-Bi Shih int ret;
1958f731d4cSTzung-Bi Shih
196b6bc07d4STzung-Bi Shih if (!atomic_add_unless(&priv->dmic_probed, 1, 1))
197b6bc07d4STzung-Bi Shih return 0;
198b6bc07d4STzung-Bi Shih
199f3e82ad4STzung-Bi Shih p.cmd = EC_CODEC_DMIC_GET_MAX_GAIN;
200f3e82ad4STzung-Bi Shih
201f3e82ad4STzung-Bi Shih ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
202f3e82ad4STzung-Bi Shih (uint8_t *)&p, sizeof(p),
203f3e82ad4STzung-Bi Shih (uint8_t *)&r, sizeof(r));
204f3e82ad4STzung-Bi Shih if (ret < 0) {
205f3e82ad4STzung-Bi Shih dev_warn(dev, "get_max_gain() unsupported\n");
206f3e82ad4STzung-Bi Shih return 0;
2078f731d4cSTzung-Bi Shih }
2088f731d4cSTzung-Bi Shih
209f3e82ad4STzung-Bi Shih dev_dbg(dev, "max gain = %d\n", r.max_gain);
210f3e82ad4STzung-Bi Shih
2118f731d4cSTzung-Bi Shih control = (struct soc_mixer_control *)
2128f731d4cSTzung-Bi Shih dmic_controls[DMIC_CTL_GAIN].private_value;
213f3e82ad4STzung-Bi Shih control->max = r.max_gain;
214f3e82ad4STzung-Bi Shih control->platform_max = r.max_gain;
2158f731d4cSTzung-Bi Shih
2168f731d4cSTzung-Bi Shih return snd_soc_add_component_controls(component,
2178f731d4cSTzung-Bi Shih &dmic_controls[DMIC_CTL_GAIN], 1);
2188f731d4cSTzung-Bi Shih }
2198f731d4cSTzung-Bi Shih
i2s_rx_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)220727f1c71STzung-Bi Shih static int i2s_rx_hw_params(struct snd_pcm_substream *substream,
221727f1c71STzung-Bi Shih struct snd_pcm_hw_params *params,
222727f1c71STzung-Bi Shih struct snd_soc_dai *dai)
223b291f42aSCheng-Yi Chiang {
224b291f42aSCheng-Yi Chiang struct snd_soc_component *component = dai->component;
225727f1c71STzung-Bi Shih struct cros_ec_codec_priv *priv =
226727f1c71STzung-Bi Shih snd_soc_component_get_drvdata(component);
227727f1c71STzung-Bi Shih struct ec_param_ec_codec_i2s_rx p;
228727f1c71STzung-Bi Shih enum ec_codec_i2s_rx_sample_depth depth;
22999b4f439SYu-Hsuan Hsu uint32_t bclk;
230727f1c71STzung-Bi Shih int ret;
231727f1c71STzung-Bi Shih
232727f1c71STzung-Bi Shih if (params_rate(params) != 48000)
233727f1c71STzung-Bi Shih return -EINVAL;
234727f1c71STzung-Bi Shih
235cfacadbdSCharles Keepax switch (params_width(params)) {
236cfacadbdSCharles Keepax case 16:
237727f1c71STzung-Bi Shih depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_16;
238727f1c71STzung-Bi Shih break;
239cfacadbdSCharles Keepax case 24:
240727f1c71STzung-Bi Shih depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_24;
241727f1c71STzung-Bi Shih break;
242727f1c71STzung-Bi Shih default:
243727f1c71STzung-Bi Shih return -EINVAL;
244727f1c71STzung-Bi Shih }
245727f1c71STzung-Bi Shih
246727f1c71STzung-Bi Shih dev_dbg(component->dev, "set depth to %u\n", depth);
247727f1c71STzung-Bi Shih
248727f1c71STzung-Bi Shih p.cmd = EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH;
249727f1c71STzung-Bi Shih p.set_sample_depth_param.depth = depth;
250727f1c71STzung-Bi Shih ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
251727f1c71STzung-Bi Shih (uint8_t *)&p, sizeof(p), NULL, 0);
252727f1c71STzung-Bi Shih if (ret < 0)
253727f1c71STzung-Bi Shih return ret;
254727f1c71STzung-Bi Shih
25599b4f439SYu-Hsuan Hsu if (priv->i2s_rx_bclk_ratio)
25699b4f439SYu-Hsuan Hsu bclk = params_rate(params) * priv->i2s_rx_bclk_ratio;
25799b4f439SYu-Hsuan Hsu else
25899b4f439SYu-Hsuan Hsu bclk = snd_soc_params_to_bclk(params);
25999b4f439SYu-Hsuan Hsu
26099b4f439SYu-Hsuan Hsu dev_dbg(component->dev, "set bclk to %u\n", bclk);
261727f1c71STzung-Bi Shih
262727f1c71STzung-Bi Shih p.cmd = EC_CODEC_I2S_RX_SET_BCLK;
26399b4f439SYu-Hsuan Hsu p.set_bclk_param.bclk = bclk;
264727f1c71STzung-Bi Shih return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
265727f1c71STzung-Bi Shih (uint8_t *)&p, sizeof(p), NULL, 0);
266727f1c71STzung-Bi Shih }
267727f1c71STzung-Bi Shih
i2s_rx_set_bclk_ratio(struct snd_soc_dai * dai,unsigned int ratio)26899b4f439SYu-Hsuan Hsu static int i2s_rx_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
26999b4f439SYu-Hsuan Hsu {
27099b4f439SYu-Hsuan Hsu struct snd_soc_component *component = dai->component;
27199b4f439SYu-Hsuan Hsu struct cros_ec_codec_priv *priv =
27299b4f439SYu-Hsuan Hsu snd_soc_component_get_drvdata(component);
27399b4f439SYu-Hsuan Hsu
27499b4f439SYu-Hsuan Hsu priv->i2s_rx_bclk_ratio = ratio;
27599b4f439SYu-Hsuan Hsu return 0;
27699b4f439SYu-Hsuan Hsu }
27799b4f439SYu-Hsuan Hsu
i2s_rx_set_fmt(struct snd_soc_dai * dai,unsigned int fmt)278727f1c71STzung-Bi Shih static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
279727f1c71STzung-Bi Shih {
280727f1c71STzung-Bi Shih struct snd_soc_component *component = dai->component;
281727f1c71STzung-Bi Shih struct cros_ec_codec_priv *priv =
282727f1c71STzung-Bi Shih snd_soc_component_get_drvdata(component);
283727f1c71STzung-Bi Shih struct ec_param_ec_codec_i2s_rx p;
284727f1c71STzung-Bi Shih enum ec_codec_i2s_rx_daifmt daifmt;
285b291f42aSCheng-Yi Chiang
28694767044SMark Brown switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
28794767044SMark Brown case SND_SOC_DAIFMT_CBC_CFC:
288b291f42aSCheng-Yi Chiang break;
289b291f42aSCheng-Yi Chiang default:
290b291f42aSCheng-Yi Chiang return -EINVAL;
291b291f42aSCheng-Yi Chiang }
292b291f42aSCheng-Yi Chiang
293b291f42aSCheng-Yi Chiang switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
294b291f42aSCheng-Yi Chiang case SND_SOC_DAIFMT_NB_NF:
295b291f42aSCheng-Yi Chiang break;
296b291f42aSCheng-Yi Chiang default:
297b291f42aSCheng-Yi Chiang return -EINVAL;
298b291f42aSCheng-Yi Chiang }
299b291f42aSCheng-Yi Chiang
300b291f42aSCheng-Yi Chiang switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
301b291f42aSCheng-Yi Chiang case SND_SOC_DAIFMT_I2S:
302727f1c71STzung-Bi Shih daifmt = EC_CODEC_I2S_RX_DAIFMT_I2S;
303b291f42aSCheng-Yi Chiang break;
304b291f42aSCheng-Yi Chiang case SND_SOC_DAIFMT_RIGHT_J:
305727f1c71STzung-Bi Shih daifmt = EC_CODEC_I2S_RX_DAIFMT_RIGHT_J;
306b291f42aSCheng-Yi Chiang break;
307b291f42aSCheng-Yi Chiang case SND_SOC_DAIFMT_LEFT_J:
308727f1c71STzung-Bi Shih daifmt = EC_CODEC_I2S_RX_DAIFMT_LEFT_J;
309b291f42aSCheng-Yi Chiang break;
310b291f42aSCheng-Yi Chiang default:
311b291f42aSCheng-Yi Chiang return -EINVAL;
312b291f42aSCheng-Yi Chiang }
313b291f42aSCheng-Yi Chiang
314727f1c71STzung-Bi Shih dev_dbg(component->dev, "set format to %u\n", daifmt);
315727f1c71STzung-Bi Shih
316727f1c71STzung-Bi Shih p.cmd = EC_CODEC_I2S_RX_SET_DAIFMT;
317727f1c71STzung-Bi Shih p.set_daifmt_param.daifmt = daifmt;
318727f1c71STzung-Bi Shih return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
319727f1c71STzung-Bi Shih (uint8_t *)&p, sizeof(p), NULL, 0);
320b291f42aSCheng-Yi Chiang }
321b291f42aSCheng-Yi Chiang
322727f1c71STzung-Bi Shih static const struct snd_soc_dai_ops i2s_rx_dai_ops = {
323727f1c71STzung-Bi Shih .hw_params = i2s_rx_hw_params,
324727f1c71STzung-Bi Shih .set_fmt = i2s_rx_set_fmt,
32599b4f439SYu-Hsuan Hsu .set_bclk_ratio = i2s_rx_set_bclk_ratio,
326b291f42aSCheng-Yi Chiang };
327b291f42aSCheng-Yi Chiang
i2s_rx_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)328727f1c71STzung-Bi Shih static int i2s_rx_event(struct snd_soc_dapm_widget *w,
329727f1c71STzung-Bi Shih struct snd_kcontrol *kcontrol, int event)
330b291f42aSCheng-Yi Chiang {
331727f1c71STzung-Bi Shih struct snd_soc_component *component =
332727f1c71STzung-Bi Shih snd_soc_dapm_to_component(w->dapm);
333727f1c71STzung-Bi Shih struct cros_ec_codec_priv *priv =
334727f1c71STzung-Bi Shih snd_soc_component_get_drvdata(component);
3357061b8a5SArnd Bergmann struct ec_param_ec_codec_i2s_rx p = {};
336727f1c71STzung-Bi Shih
337727f1c71STzung-Bi Shih switch (event) {
338727f1c71STzung-Bi Shih case SND_SOC_DAPM_PRE_PMU:
339727f1c71STzung-Bi Shih dev_dbg(component->dev, "enable I2S RX\n");
340727f1c71STzung-Bi Shih p.cmd = EC_CODEC_I2S_RX_ENABLE;
341727f1c71STzung-Bi Shih break;
342727f1c71STzung-Bi Shih case SND_SOC_DAPM_PRE_PMD:
343727f1c71STzung-Bi Shih dev_dbg(component->dev, "disable I2S RX\n");
344727f1c71STzung-Bi Shih p.cmd = EC_CODEC_I2S_RX_DISABLE;
345727f1c71STzung-Bi Shih break;
346727f1c71STzung-Bi Shih default:
347727f1c71STzung-Bi Shih return 0;
348727f1c71STzung-Bi Shih }
349727f1c71STzung-Bi Shih
350727f1c71STzung-Bi Shih return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
351727f1c71STzung-Bi Shih (uint8_t *)&p, sizeof(p), NULL, 0);
352727f1c71STzung-Bi Shih }
353727f1c71STzung-Bi Shih
354727f1c71STzung-Bi Shih static struct snd_soc_dapm_widget i2s_rx_dapm_widgets[] = {
355727f1c71STzung-Bi Shih SND_SOC_DAPM_INPUT("DMIC"),
356727f1c71STzung-Bi Shih SND_SOC_DAPM_SUPPLY("I2S RX Enable", SND_SOC_NOPM, 0, 0, i2s_rx_event,
357727f1c71STzung-Bi Shih SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
358727f1c71STzung-Bi Shih SND_SOC_DAPM_AIF_OUT("I2S RX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0),
359727f1c71STzung-Bi Shih };
360727f1c71STzung-Bi Shih
361727f1c71STzung-Bi Shih static struct snd_soc_dapm_route i2s_rx_dapm_routes[] = {
362727f1c71STzung-Bi Shih {"I2S RX", NULL, "DMIC"},
363727f1c71STzung-Bi Shih {"I2S RX", NULL, "I2S RX Enable"},
364727f1c71STzung-Bi Shih };
365727f1c71STzung-Bi Shih
366727f1c71STzung-Bi Shih static struct snd_soc_dai_driver i2s_rx_dai_driver = {
367727f1c71STzung-Bi Shih .name = "EC Codec I2S RX",
368b291f42aSCheng-Yi Chiang .capture = {
369b291f42aSCheng-Yi Chiang .stream_name = "I2S Capture",
370b291f42aSCheng-Yi Chiang .channels_min = 2,
371b291f42aSCheng-Yi Chiang .channels_max = 2,
372b291f42aSCheng-Yi Chiang .rates = SNDRV_PCM_RATE_48000,
373b291f42aSCheng-Yi Chiang .formats = SNDRV_PCM_FMTBIT_S16_LE |
374b291f42aSCheng-Yi Chiang SNDRV_PCM_FMTBIT_S24_LE,
375b291f42aSCheng-Yi Chiang },
376727f1c71STzung-Bi Shih .ops = &i2s_rx_dai_ops,
377b291f42aSCheng-Yi Chiang };
378b291f42aSCheng-Yi Chiang
i2s_rx_probe(struct snd_soc_component * component)379727f1c71STzung-Bi Shih static int i2s_rx_probe(struct snd_soc_component *component)
380b291f42aSCheng-Yi Chiang {
3818f731d4cSTzung-Bi Shih return dmic_probe(component);
382b291f42aSCheng-Yi Chiang }
383b291f42aSCheng-Yi Chiang
384727f1c71STzung-Bi Shih static const struct snd_soc_component_driver i2s_rx_component_driver = {
385727f1c71STzung-Bi Shih .probe = i2s_rx_probe,
386727f1c71STzung-Bi Shih .dapm_widgets = i2s_rx_dapm_widgets,
387727f1c71STzung-Bi Shih .num_dapm_widgets = ARRAY_SIZE(i2s_rx_dapm_widgets),
388727f1c71STzung-Bi Shih .dapm_routes = i2s_rx_dapm_routes,
389727f1c71STzung-Bi Shih .num_dapm_routes = ARRAY_SIZE(i2s_rx_dapm_routes),
390cfacadbdSCharles Keepax .endianness = 1,
391b291f42aSCheng-Yi Chiang };
392b291f42aSCheng-Yi Chiang
wov_map_shm(struct cros_ec_codec_priv * priv,uint8_t shm_id,uint32_t * len,uint8_t * type)393b6bc07d4STzung-Bi Shih static void *wov_map_shm(struct cros_ec_codec_priv *priv,
394b6bc07d4STzung-Bi Shih uint8_t shm_id, uint32_t *len, uint8_t *type)
395b6bc07d4STzung-Bi Shih {
396b6bc07d4STzung-Bi Shih struct ec_param_ec_codec p;
397b6bc07d4STzung-Bi Shih struct ec_response_ec_codec_get_shm_addr r;
398b6bc07d4STzung-Bi Shih uint32_t req, offset;
399b6bc07d4STzung-Bi Shih
400b6bc07d4STzung-Bi Shih p.cmd = EC_CODEC_GET_SHM_ADDR;
401b6bc07d4STzung-Bi Shih p.get_shm_addr_param.shm_id = shm_id;
402b6bc07d4STzung-Bi Shih if (send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC,
403b6bc07d4STzung-Bi Shih (uint8_t *)&p, sizeof(p),
404b6bc07d4STzung-Bi Shih (uint8_t *)&r, sizeof(r)) < 0) {
405b6bc07d4STzung-Bi Shih dev_err(priv->dev, "failed to EC_CODEC_GET_SHM_ADDR\n");
406b6bc07d4STzung-Bi Shih return NULL;
407b6bc07d4STzung-Bi Shih }
408b6bc07d4STzung-Bi Shih
409b6bc07d4STzung-Bi Shih dev_dbg(priv->dev, "phys_addr=%#llx, len=%#x\n", r.phys_addr, r.len);
410b6bc07d4STzung-Bi Shih
411b6bc07d4STzung-Bi Shih *len = r.len;
412b6bc07d4STzung-Bi Shih *type = r.type;
413b6bc07d4STzung-Bi Shih
414b6bc07d4STzung-Bi Shih switch (r.type) {
415b6bc07d4STzung-Bi Shih case EC_CODEC_SHM_TYPE_EC_RAM:
416b6bc07d4STzung-Bi Shih return (void __force *)devm_ioremap_wc(priv->dev,
417b6bc07d4STzung-Bi Shih r.phys_addr + priv->ec_shm_addr, r.len);
418b6bc07d4STzung-Bi Shih case EC_CODEC_SHM_TYPE_SYSTEM_RAM:
419b6bc07d4STzung-Bi Shih if (r.phys_addr) {
420b6bc07d4STzung-Bi Shih dev_err(priv->dev, "unknown status\n");
421b6bc07d4STzung-Bi Shih return NULL;
422b6bc07d4STzung-Bi Shih }
423b6bc07d4STzung-Bi Shih
424b6bc07d4STzung-Bi Shih req = round_up(r.len, PAGE_SIZE);
425b6bc07d4STzung-Bi Shih dev_dbg(priv->dev, "round up from %u to %u\n", r.len, req);
426b6bc07d4STzung-Bi Shih
427b6bc07d4STzung-Bi Shih if (priv->ap_shm_last_alloc + req >
428b6bc07d4STzung-Bi Shih priv->ap_shm_phys_addr + priv->ap_shm_len) {
429b6bc07d4STzung-Bi Shih dev_err(priv->dev, "insufficient space for AP SHM\n");
430b6bc07d4STzung-Bi Shih return NULL;
431b6bc07d4STzung-Bi Shih }
432b6bc07d4STzung-Bi Shih
433b6bc07d4STzung-Bi Shih dev_dbg(priv->dev, "alloc AP SHM addr=%#llx, len=%#x\n",
434b6bc07d4STzung-Bi Shih priv->ap_shm_last_alloc, req);
435b6bc07d4STzung-Bi Shih
436b6bc07d4STzung-Bi Shih p.cmd = EC_CODEC_SET_SHM_ADDR;
437b6bc07d4STzung-Bi Shih p.set_shm_addr_param.phys_addr = priv->ap_shm_last_alloc;
438b6bc07d4STzung-Bi Shih p.set_shm_addr_param.len = req;
439b6bc07d4STzung-Bi Shih p.set_shm_addr_param.shm_id = shm_id;
440b6bc07d4STzung-Bi Shih if (send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC,
441b6bc07d4STzung-Bi Shih (uint8_t *)&p, sizeof(p),
442b6bc07d4STzung-Bi Shih NULL, 0) < 0) {
443b6bc07d4STzung-Bi Shih dev_err(priv->dev, "failed to EC_CODEC_SET_SHM_ADDR\n");
444b6bc07d4STzung-Bi Shih return NULL;
445b6bc07d4STzung-Bi Shih }
446b6bc07d4STzung-Bi Shih
447b6bc07d4STzung-Bi Shih /*
448b6bc07d4STzung-Bi Shih * Note: EC codec only requests for `r.len' but we allocate
449b6bc07d4STzung-Bi Shih * round up PAGE_SIZE `req'.
450b6bc07d4STzung-Bi Shih */
451b6bc07d4STzung-Bi Shih offset = priv->ap_shm_last_alloc - priv->ap_shm_phys_addr;
452b6bc07d4STzung-Bi Shih priv->ap_shm_last_alloc += req;
453b6bc07d4STzung-Bi Shih
454b6bc07d4STzung-Bi Shih return (void *)(uintptr_t)(priv->ap_shm_addr + offset);
455b6bc07d4STzung-Bi Shih default:
456b6bc07d4STzung-Bi Shih return NULL;
457b6bc07d4STzung-Bi Shih }
458b6bc07d4STzung-Bi Shih }
459b6bc07d4STzung-Bi Shih
wov_queue_full(struct cros_ec_codec_priv * priv)460b6bc07d4STzung-Bi Shih static bool wov_queue_full(struct cros_ec_codec_priv *priv)
461b6bc07d4STzung-Bi Shih {
462b6bc07d4STzung-Bi Shih return ((priv->wov_wp + 1) % sizeof(priv->wov_buf)) == priv->wov_rp;
463b6bc07d4STzung-Bi Shih }
464b6bc07d4STzung-Bi Shih
wov_queue_size(struct cros_ec_codec_priv * priv)465b6bc07d4STzung-Bi Shih static size_t wov_queue_size(struct cros_ec_codec_priv *priv)
466b6bc07d4STzung-Bi Shih {
467b6bc07d4STzung-Bi Shih if (priv->wov_wp >= priv->wov_rp)
468b6bc07d4STzung-Bi Shih return priv->wov_wp - priv->wov_rp;
469b6bc07d4STzung-Bi Shih else
470b6bc07d4STzung-Bi Shih return sizeof(priv->wov_buf) - priv->wov_rp + priv->wov_wp;
471b6bc07d4STzung-Bi Shih }
472b6bc07d4STzung-Bi Shih
wov_queue_dequeue(struct cros_ec_codec_priv * priv,size_t len)473b6bc07d4STzung-Bi Shih static void wov_queue_dequeue(struct cros_ec_codec_priv *priv, size_t len)
474b6bc07d4STzung-Bi Shih {
475b6bc07d4STzung-Bi Shih struct snd_pcm_runtime *runtime = priv->wov_substream->runtime;
476b6bc07d4STzung-Bi Shih size_t req;
477b6bc07d4STzung-Bi Shih
478b6bc07d4STzung-Bi Shih while (len) {
479b6bc07d4STzung-Bi Shih req = min(len, runtime->dma_bytes - priv->wov_dma_offset);
480b6bc07d4STzung-Bi Shih if (priv->wov_wp >= priv->wov_rp)
481b6bc07d4STzung-Bi Shih req = min(req, (size_t)priv->wov_wp - priv->wov_rp);
482b6bc07d4STzung-Bi Shih else
483b6bc07d4STzung-Bi Shih req = min(req, sizeof(priv->wov_buf) - priv->wov_rp);
484b6bc07d4STzung-Bi Shih
485b6bc07d4STzung-Bi Shih memcpy(runtime->dma_area + priv->wov_dma_offset,
486b6bc07d4STzung-Bi Shih priv->wov_buf + priv->wov_rp, req);
487b6bc07d4STzung-Bi Shih
488b6bc07d4STzung-Bi Shih priv->wov_dma_offset += req;
489b6bc07d4STzung-Bi Shih if (priv->wov_dma_offset == runtime->dma_bytes)
490b6bc07d4STzung-Bi Shih priv->wov_dma_offset = 0;
491b6bc07d4STzung-Bi Shih
492b6bc07d4STzung-Bi Shih priv->wov_rp += req;
493b6bc07d4STzung-Bi Shih if (priv->wov_rp == sizeof(priv->wov_buf))
494b6bc07d4STzung-Bi Shih priv->wov_rp = 0;
495b6bc07d4STzung-Bi Shih
496b6bc07d4STzung-Bi Shih len -= req;
497b6bc07d4STzung-Bi Shih }
498b6bc07d4STzung-Bi Shih
499b6bc07d4STzung-Bi Shih snd_pcm_period_elapsed(priv->wov_substream);
500b6bc07d4STzung-Bi Shih }
501b6bc07d4STzung-Bi Shih
wov_queue_try_dequeue(struct cros_ec_codec_priv * priv)502b6bc07d4STzung-Bi Shih static void wov_queue_try_dequeue(struct cros_ec_codec_priv *priv)
503b6bc07d4STzung-Bi Shih {
504b6bc07d4STzung-Bi Shih size_t period_bytes = snd_pcm_lib_period_bytes(priv->wov_substream);
505b6bc07d4STzung-Bi Shih
506b6bc07d4STzung-Bi Shih while (period_bytes && wov_queue_size(priv) >= period_bytes) {
507b6bc07d4STzung-Bi Shih wov_queue_dequeue(priv, period_bytes);
508b6bc07d4STzung-Bi Shih period_bytes = snd_pcm_lib_period_bytes(priv->wov_substream);
509b6bc07d4STzung-Bi Shih }
510b6bc07d4STzung-Bi Shih }
511b6bc07d4STzung-Bi Shih
wov_queue_enqueue(struct cros_ec_codec_priv * priv,uint8_t * addr,size_t len,bool iomem)512b6bc07d4STzung-Bi Shih static void wov_queue_enqueue(struct cros_ec_codec_priv *priv,
513b6bc07d4STzung-Bi Shih uint8_t *addr, size_t len, bool iomem)
514b6bc07d4STzung-Bi Shih {
515b6bc07d4STzung-Bi Shih size_t req;
516b6bc07d4STzung-Bi Shih
517b6bc07d4STzung-Bi Shih while (len) {
518b6bc07d4STzung-Bi Shih if (wov_queue_full(priv)) {
519b6bc07d4STzung-Bi Shih wov_queue_try_dequeue(priv);
520b6bc07d4STzung-Bi Shih
521b6bc07d4STzung-Bi Shih if (wov_queue_full(priv)) {
522b6bc07d4STzung-Bi Shih dev_err(priv->dev, "overrun detected\n");
523b6bc07d4STzung-Bi Shih return;
524b6bc07d4STzung-Bi Shih }
525b6bc07d4STzung-Bi Shih }
526b6bc07d4STzung-Bi Shih
527b6bc07d4STzung-Bi Shih if (priv->wov_wp >= priv->wov_rp)
528b6bc07d4STzung-Bi Shih req = sizeof(priv->wov_buf) - priv->wov_wp;
529b6bc07d4STzung-Bi Shih else
530b6bc07d4STzung-Bi Shih /* Note: waste 1-byte to differentiate full and empty */
531b6bc07d4STzung-Bi Shih req = priv->wov_rp - priv->wov_wp - 1;
532b6bc07d4STzung-Bi Shih req = min(req, len);
533b6bc07d4STzung-Bi Shih
534b6bc07d4STzung-Bi Shih if (iomem)
535b6bc07d4STzung-Bi Shih memcpy_fromio(priv->wov_buf + priv->wov_wp,
536b6bc07d4STzung-Bi Shih (void __force __iomem *)addr, req);
537b6bc07d4STzung-Bi Shih else
538b6bc07d4STzung-Bi Shih memcpy(priv->wov_buf + priv->wov_wp, addr, req);
539b6bc07d4STzung-Bi Shih
540b6bc07d4STzung-Bi Shih priv->wov_wp += req;
541b6bc07d4STzung-Bi Shih if (priv->wov_wp == sizeof(priv->wov_buf))
542b6bc07d4STzung-Bi Shih priv->wov_wp = 0;
543b6bc07d4STzung-Bi Shih
544b6bc07d4STzung-Bi Shih addr += req;
545b6bc07d4STzung-Bi Shih len -= req;
546b6bc07d4STzung-Bi Shih }
547b6bc07d4STzung-Bi Shih
548b6bc07d4STzung-Bi Shih wov_queue_try_dequeue(priv);
549b6bc07d4STzung-Bi Shih }
550b6bc07d4STzung-Bi Shih
wov_read_audio_shm(struct cros_ec_codec_priv * priv)551b6bc07d4STzung-Bi Shih static int wov_read_audio_shm(struct cros_ec_codec_priv *priv)
552b6bc07d4STzung-Bi Shih {
553b6bc07d4STzung-Bi Shih struct ec_param_ec_codec_wov p;
554b6bc07d4STzung-Bi Shih struct ec_response_ec_codec_wov_read_audio_shm r;
555b6bc07d4STzung-Bi Shih int ret;
556b6bc07d4STzung-Bi Shih
557b6bc07d4STzung-Bi Shih p.cmd = EC_CODEC_WOV_READ_AUDIO_SHM;
558b6bc07d4STzung-Bi Shih ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
559b6bc07d4STzung-Bi Shih (uint8_t *)&p, sizeof(p),
560b6bc07d4STzung-Bi Shih (uint8_t *)&r, sizeof(r));
561b6bc07d4STzung-Bi Shih if (ret) {
562b6bc07d4STzung-Bi Shih dev_err(priv->dev, "failed to EC_CODEC_WOV_READ_AUDIO_SHM\n");
563b6bc07d4STzung-Bi Shih return ret;
564b6bc07d4STzung-Bi Shih }
565b6bc07d4STzung-Bi Shih
566b6bc07d4STzung-Bi Shih if (!r.len)
567b6bc07d4STzung-Bi Shih dev_dbg(priv->dev, "no data, sleep\n");
568b6bc07d4STzung-Bi Shih else
569b6bc07d4STzung-Bi Shih wov_queue_enqueue(priv, priv->wov_audio_shm_p + r.offset, r.len,
570b6bc07d4STzung-Bi Shih priv->wov_audio_shm_type == EC_CODEC_SHM_TYPE_EC_RAM);
571b6bc07d4STzung-Bi Shih return -EAGAIN;
572b6bc07d4STzung-Bi Shih }
573b6bc07d4STzung-Bi Shih
wov_read_audio(struct cros_ec_codec_priv * priv)574b6bc07d4STzung-Bi Shih static int wov_read_audio(struct cros_ec_codec_priv *priv)
575b6bc07d4STzung-Bi Shih {
576b6bc07d4STzung-Bi Shih struct ec_param_ec_codec_wov p;
577b6bc07d4STzung-Bi Shih struct ec_response_ec_codec_wov_read_audio r;
578b6bc07d4STzung-Bi Shih int remain = priv->wov_burst_read ? 16000 : 320;
579b6bc07d4STzung-Bi Shih int ret;
580b6bc07d4STzung-Bi Shih
581b6bc07d4STzung-Bi Shih while (remain >= 0) {
582b6bc07d4STzung-Bi Shih p.cmd = EC_CODEC_WOV_READ_AUDIO;
583b6bc07d4STzung-Bi Shih ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
584b6bc07d4STzung-Bi Shih (uint8_t *)&p, sizeof(p),
585b6bc07d4STzung-Bi Shih (uint8_t *)&r, sizeof(r));
586b6bc07d4STzung-Bi Shih if (ret) {
587b6bc07d4STzung-Bi Shih dev_err(priv->dev,
588b6bc07d4STzung-Bi Shih "failed to EC_CODEC_WOV_READ_AUDIO\n");
589b6bc07d4STzung-Bi Shih return ret;
590b6bc07d4STzung-Bi Shih }
591b6bc07d4STzung-Bi Shih
592b6bc07d4STzung-Bi Shih if (!r.len) {
593b6bc07d4STzung-Bi Shih dev_dbg(priv->dev, "no data, sleep\n");
594b6bc07d4STzung-Bi Shih priv->wov_burst_read = false;
595b6bc07d4STzung-Bi Shih break;
596b6bc07d4STzung-Bi Shih }
597b6bc07d4STzung-Bi Shih
598b6bc07d4STzung-Bi Shih wov_queue_enqueue(priv, r.buf, r.len, false);
599b6bc07d4STzung-Bi Shih remain -= r.len;
600b6bc07d4STzung-Bi Shih }
601b6bc07d4STzung-Bi Shih
602b6bc07d4STzung-Bi Shih return -EAGAIN;
603b6bc07d4STzung-Bi Shih }
604b6bc07d4STzung-Bi Shih
wov_copy_work(struct work_struct * w)605b6bc07d4STzung-Bi Shih static void wov_copy_work(struct work_struct *w)
606b6bc07d4STzung-Bi Shih {
607b6bc07d4STzung-Bi Shih struct cros_ec_codec_priv *priv =
608b6bc07d4STzung-Bi Shih container_of(w, struct cros_ec_codec_priv, wov_copy_work.work);
609b6bc07d4STzung-Bi Shih int ret;
610b6bc07d4STzung-Bi Shih
611b6bc07d4STzung-Bi Shih mutex_lock(&priv->wov_dma_lock);
612b6bc07d4STzung-Bi Shih if (!priv->wov_substream) {
613b6bc07d4STzung-Bi Shih dev_warn(priv->dev, "no pcm substream\n");
614b6bc07d4STzung-Bi Shih goto leave;
615b6bc07d4STzung-Bi Shih }
616b6bc07d4STzung-Bi Shih
617b6bc07d4STzung-Bi Shih if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM))
618b6bc07d4STzung-Bi Shih ret = wov_read_audio_shm(priv);
619b6bc07d4STzung-Bi Shih else
620b6bc07d4STzung-Bi Shih ret = wov_read_audio(priv);
621b6bc07d4STzung-Bi Shih
622b6bc07d4STzung-Bi Shih if (ret == -EAGAIN)
623b6bc07d4STzung-Bi Shih schedule_delayed_work(&priv->wov_copy_work,
624b6bc07d4STzung-Bi Shih msecs_to_jiffies(10));
625b6bc07d4STzung-Bi Shih else if (ret)
626b6bc07d4STzung-Bi Shih dev_err(priv->dev, "failed to read audio data\n");
627b6bc07d4STzung-Bi Shih leave:
628b6bc07d4STzung-Bi Shih mutex_unlock(&priv->wov_dma_lock);
629b6bc07d4STzung-Bi Shih }
630b6bc07d4STzung-Bi Shih
wov_enable_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)631b6bc07d4STzung-Bi Shih static int wov_enable_get(struct snd_kcontrol *kcontrol,
632b6bc07d4STzung-Bi Shih struct snd_ctl_elem_value *ucontrol)
633b6bc07d4STzung-Bi Shih {
634b6bc07d4STzung-Bi Shih struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
635b6bc07d4STzung-Bi Shih struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(c);
636b6bc07d4STzung-Bi Shih
637b6bc07d4STzung-Bi Shih ucontrol->value.integer.value[0] = priv->wov_enabled;
638b6bc07d4STzung-Bi Shih return 0;
639b6bc07d4STzung-Bi Shih }
640b6bc07d4STzung-Bi Shih
wov_enable_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)641b6bc07d4STzung-Bi Shih static int wov_enable_put(struct snd_kcontrol *kcontrol,
642b6bc07d4STzung-Bi Shih struct snd_ctl_elem_value *ucontrol)
643b6bc07d4STzung-Bi Shih {
644b6bc07d4STzung-Bi Shih struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
645b6bc07d4STzung-Bi Shih struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(c);
646b6bc07d4STzung-Bi Shih int enabled = ucontrol->value.integer.value[0];
647b6bc07d4STzung-Bi Shih struct ec_param_ec_codec_wov p;
648b6bc07d4STzung-Bi Shih int ret;
649b6bc07d4STzung-Bi Shih
650b6bc07d4STzung-Bi Shih if (priv->wov_enabled != enabled) {
651b6bc07d4STzung-Bi Shih if (enabled)
652b6bc07d4STzung-Bi Shih p.cmd = EC_CODEC_WOV_ENABLE;
653b6bc07d4STzung-Bi Shih else
654b6bc07d4STzung-Bi Shih p.cmd = EC_CODEC_WOV_DISABLE;
655b6bc07d4STzung-Bi Shih
656b6bc07d4STzung-Bi Shih ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
657b6bc07d4STzung-Bi Shih (uint8_t *)&p, sizeof(p), NULL, 0);
658b6bc07d4STzung-Bi Shih if (ret) {
659b6bc07d4STzung-Bi Shih dev_err(priv->dev, "failed to %s wov\n",
660b6bc07d4STzung-Bi Shih enabled ? "enable" : "disable");
661b6bc07d4STzung-Bi Shih return ret;
662b6bc07d4STzung-Bi Shih }
663b6bc07d4STzung-Bi Shih
664b6bc07d4STzung-Bi Shih priv->wov_enabled = enabled;
665b6bc07d4STzung-Bi Shih }
666b6bc07d4STzung-Bi Shih
667b6bc07d4STzung-Bi Shih return 0;
668b6bc07d4STzung-Bi Shih }
669b6bc07d4STzung-Bi Shih
wov_set_lang_shm(struct cros_ec_codec_priv * priv,uint8_t * buf,size_t size,uint8_t * digest)670b6bc07d4STzung-Bi Shih static int wov_set_lang_shm(struct cros_ec_codec_priv *priv,
671b6bc07d4STzung-Bi Shih uint8_t *buf, size_t size, uint8_t *digest)
672b6bc07d4STzung-Bi Shih {
673b6bc07d4STzung-Bi Shih struct ec_param_ec_codec_wov p;
674b6bc07d4STzung-Bi Shih struct ec_param_ec_codec_wov_set_lang_shm *pp = &p.set_lang_shm_param;
675b6bc07d4STzung-Bi Shih int ret;
676b6bc07d4STzung-Bi Shih
677b6bc07d4STzung-Bi Shih if (size > priv->wov_lang_shm_len) {
678b6bc07d4STzung-Bi Shih dev_err(priv->dev, "no enough SHM size: %d\n",
679b6bc07d4STzung-Bi Shih priv->wov_lang_shm_len);
680b6bc07d4STzung-Bi Shih return -EIO;
681b6bc07d4STzung-Bi Shih }
682b6bc07d4STzung-Bi Shih
683b6bc07d4STzung-Bi Shih switch (priv->wov_lang_shm_type) {
684b6bc07d4STzung-Bi Shih case EC_CODEC_SHM_TYPE_EC_RAM:
685b6bc07d4STzung-Bi Shih memcpy_toio((void __force __iomem *)priv->wov_lang_shm_p,
686b6bc07d4STzung-Bi Shih buf, size);
687b6bc07d4STzung-Bi Shih memset_io((void __force __iomem *)priv->wov_lang_shm_p + size,
688b6bc07d4STzung-Bi Shih 0, priv->wov_lang_shm_len - size);
689b6bc07d4STzung-Bi Shih break;
690b6bc07d4STzung-Bi Shih case EC_CODEC_SHM_TYPE_SYSTEM_RAM:
691b6bc07d4STzung-Bi Shih memcpy(priv->wov_lang_shm_p, buf, size);
692b6bc07d4STzung-Bi Shih memset(priv->wov_lang_shm_p + size, 0,
693b6bc07d4STzung-Bi Shih priv->wov_lang_shm_len - size);
694b6bc07d4STzung-Bi Shih
695b6bc07d4STzung-Bi Shih /* make sure write to memory before calling host command */
696b6bc07d4STzung-Bi Shih wmb();
697b6bc07d4STzung-Bi Shih break;
698b6bc07d4STzung-Bi Shih }
699b6bc07d4STzung-Bi Shih
700b6bc07d4STzung-Bi Shih p.cmd = EC_CODEC_WOV_SET_LANG_SHM;
701b6bc07d4STzung-Bi Shih memcpy(pp->hash, digest, SHA256_DIGEST_SIZE);
702b6bc07d4STzung-Bi Shih pp->total_len = size;
703b6bc07d4STzung-Bi Shih ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
704b6bc07d4STzung-Bi Shih (uint8_t *)&p, sizeof(p), NULL, 0);
705b6bc07d4STzung-Bi Shih if (ret) {
706b6bc07d4STzung-Bi Shih dev_err(priv->dev, "failed to EC_CODEC_WOV_SET_LANG_SHM\n");
707b6bc07d4STzung-Bi Shih return ret;
708b6bc07d4STzung-Bi Shih }
709b6bc07d4STzung-Bi Shih
710b6bc07d4STzung-Bi Shih return 0;
711b6bc07d4STzung-Bi Shih }
712b6bc07d4STzung-Bi Shih
wov_set_lang(struct cros_ec_codec_priv * priv,uint8_t * buf,size_t size,uint8_t * digest)713b6bc07d4STzung-Bi Shih static int wov_set_lang(struct cros_ec_codec_priv *priv,
714b6bc07d4STzung-Bi Shih uint8_t *buf, size_t size, uint8_t *digest)
715b6bc07d4STzung-Bi Shih {
716b6bc07d4STzung-Bi Shih struct ec_param_ec_codec_wov p;
717b6bc07d4STzung-Bi Shih struct ec_param_ec_codec_wov_set_lang *pp = &p.set_lang_param;
718b6bc07d4STzung-Bi Shih size_t i, req;
719b6bc07d4STzung-Bi Shih int ret;
720b6bc07d4STzung-Bi Shih
721b6bc07d4STzung-Bi Shih for (i = 0; i < size; i += req) {
722b6bc07d4STzung-Bi Shih req = min(size - i, ARRAY_SIZE(pp->buf));
723b6bc07d4STzung-Bi Shih
724b6bc07d4STzung-Bi Shih p.cmd = EC_CODEC_WOV_SET_LANG;
725b6bc07d4STzung-Bi Shih memcpy(pp->hash, digest, SHA256_DIGEST_SIZE);
726b6bc07d4STzung-Bi Shih pp->total_len = size;
727b6bc07d4STzung-Bi Shih pp->offset = i;
728b6bc07d4STzung-Bi Shih memcpy(pp->buf, buf + i, req);
729b6bc07d4STzung-Bi Shih pp->len = req;
730b6bc07d4STzung-Bi Shih ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
731b6bc07d4STzung-Bi Shih (uint8_t *)&p, sizeof(p), NULL, 0);
732b6bc07d4STzung-Bi Shih if (ret) {
733b6bc07d4STzung-Bi Shih dev_err(priv->dev, "failed to EC_CODEC_WOV_SET_LANG\n");
734b6bc07d4STzung-Bi Shih return ret;
735b6bc07d4STzung-Bi Shih }
736b6bc07d4STzung-Bi Shih }
737b6bc07d4STzung-Bi Shih
738b6bc07d4STzung-Bi Shih return 0;
739b6bc07d4STzung-Bi Shih }
740b6bc07d4STzung-Bi Shih
wov_hotword_model_put(struct snd_kcontrol * kcontrol,const unsigned int __user * bytes,unsigned int size)741b6bc07d4STzung-Bi Shih static int wov_hotword_model_put(struct snd_kcontrol *kcontrol,
742b6bc07d4STzung-Bi Shih const unsigned int __user *bytes,
743b6bc07d4STzung-Bi Shih unsigned int size)
744b6bc07d4STzung-Bi Shih {
745b6bc07d4STzung-Bi Shih struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
746b6bc07d4STzung-Bi Shih struct cros_ec_codec_priv *priv =
747b6bc07d4STzung-Bi Shih snd_soc_component_get_drvdata(component);
748b6bc07d4STzung-Bi Shih struct ec_param_ec_codec_wov p;
749b6bc07d4STzung-Bi Shih struct ec_response_ec_codec_wov_get_lang r;
750b6bc07d4STzung-Bi Shih uint8_t digest[SHA256_DIGEST_SIZE];
751b6bc07d4STzung-Bi Shih uint8_t *buf;
752b6bc07d4STzung-Bi Shih int ret;
753b6bc07d4STzung-Bi Shih
754b6bc07d4STzung-Bi Shih /* Skips the TLV header. */
755b6bc07d4STzung-Bi Shih bytes += 2;
756b6bc07d4STzung-Bi Shih size -= 8;
757b6bc07d4STzung-Bi Shih
758b6bc07d4STzung-Bi Shih dev_dbg(priv->dev, "%s: size=%d\n", __func__, size);
759b6bc07d4STzung-Bi Shih
760b6bc07d4STzung-Bi Shih buf = memdup_user(bytes, size);
761b6bc07d4STzung-Bi Shih if (IS_ERR(buf))
762b6bc07d4STzung-Bi Shih return PTR_ERR(buf);
763b6bc07d4STzung-Bi Shih
7649d4cafa5SEric Biggers sha256(buf, size, digest);
7659d4cafa5SEric Biggers dev_dbg(priv->dev, "hash=%*phN\n", SHA256_DIGEST_SIZE, digest);
766b6bc07d4STzung-Bi Shih
767b6bc07d4STzung-Bi Shih p.cmd = EC_CODEC_WOV_GET_LANG;
768b6bc07d4STzung-Bi Shih ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
769b6bc07d4STzung-Bi Shih (uint8_t *)&p, sizeof(p),
770b6bc07d4STzung-Bi Shih (uint8_t *)&r, sizeof(r));
771b6bc07d4STzung-Bi Shih if (ret)
772b6bc07d4STzung-Bi Shih goto leave;
773b6bc07d4STzung-Bi Shih
774b6bc07d4STzung-Bi Shih if (memcmp(digest, r.hash, SHA256_DIGEST_SIZE) == 0) {
775b6bc07d4STzung-Bi Shih dev_dbg(priv->dev, "not updated");
776b6bc07d4STzung-Bi Shih goto leave;
777b6bc07d4STzung-Bi Shih }
778b6bc07d4STzung-Bi Shih
779b6bc07d4STzung-Bi Shih if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_LANG_SHM))
780b6bc07d4STzung-Bi Shih ret = wov_set_lang_shm(priv, buf, size, digest);
781b6bc07d4STzung-Bi Shih else
782b6bc07d4STzung-Bi Shih ret = wov_set_lang(priv, buf, size, digest);
783b6bc07d4STzung-Bi Shih
784b6bc07d4STzung-Bi Shih leave:
785b6bc07d4STzung-Bi Shih kfree(buf);
786b6bc07d4STzung-Bi Shih return ret;
787b6bc07d4STzung-Bi Shih }
788b6bc07d4STzung-Bi Shih
789b6bc07d4STzung-Bi Shih static struct snd_kcontrol_new wov_controls[] = {
790b6bc07d4STzung-Bi Shih SOC_SINGLE_BOOL_EXT("Wake-on-Voice Switch", 0,
791b6bc07d4STzung-Bi Shih wov_enable_get, wov_enable_put),
792b6bc07d4STzung-Bi Shih SND_SOC_BYTES_TLV("Hotword Model", 0x11000, NULL,
793b6bc07d4STzung-Bi Shih wov_hotword_model_put),
794b6bc07d4STzung-Bi Shih };
795b6bc07d4STzung-Bi Shih
796b6bc07d4STzung-Bi Shih static struct snd_soc_dai_driver wov_dai_driver = {
797b6bc07d4STzung-Bi Shih .name = "Wake on Voice",
798b6bc07d4STzung-Bi Shih .capture = {
799b6bc07d4STzung-Bi Shih .stream_name = "WoV Capture",
800b6bc07d4STzung-Bi Shih .channels_min = 1,
801b6bc07d4STzung-Bi Shih .channels_max = 1,
802b6bc07d4STzung-Bi Shih .rates = SNDRV_PCM_RATE_16000,
803b6bc07d4STzung-Bi Shih .formats = SNDRV_PCM_FMTBIT_S16_LE,
804b6bc07d4STzung-Bi Shih },
805b6bc07d4STzung-Bi Shih };
806b6bc07d4STzung-Bi Shih
wov_host_event(struct notifier_block * nb,unsigned long queued_during_suspend,void * notify)807b6bc07d4STzung-Bi Shih static int wov_host_event(struct notifier_block *nb,
808b6bc07d4STzung-Bi Shih unsigned long queued_during_suspend, void *notify)
809b6bc07d4STzung-Bi Shih {
810b6bc07d4STzung-Bi Shih struct cros_ec_codec_priv *priv =
811b6bc07d4STzung-Bi Shih container_of(nb, struct cros_ec_codec_priv, wov_notifier);
812b6bc07d4STzung-Bi Shih u32 host_event;
813b6bc07d4STzung-Bi Shih
814b6bc07d4STzung-Bi Shih dev_dbg(priv->dev, "%s\n", __func__);
815b6bc07d4STzung-Bi Shih
816b6bc07d4STzung-Bi Shih host_event = cros_ec_get_host_event(priv->ec_device);
817b6bc07d4STzung-Bi Shih if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_WOV)) {
818b6bc07d4STzung-Bi Shih schedule_delayed_work(&priv->wov_copy_work, 0);
819b6bc07d4STzung-Bi Shih return NOTIFY_OK;
820b6bc07d4STzung-Bi Shih } else {
821b6bc07d4STzung-Bi Shih return NOTIFY_DONE;
822b6bc07d4STzung-Bi Shih }
823b6bc07d4STzung-Bi Shih }
824b6bc07d4STzung-Bi Shih
wov_probe(struct snd_soc_component * component)825b6bc07d4STzung-Bi Shih static int wov_probe(struct snd_soc_component *component)
826b6bc07d4STzung-Bi Shih {
827b6bc07d4STzung-Bi Shih struct cros_ec_codec_priv *priv =
828b6bc07d4STzung-Bi Shih snd_soc_component_get_drvdata(component);
829b6bc07d4STzung-Bi Shih int ret;
830b6bc07d4STzung-Bi Shih
831b6bc07d4STzung-Bi Shih mutex_init(&priv->wov_dma_lock);
832b6bc07d4STzung-Bi Shih INIT_DELAYED_WORK(&priv->wov_copy_work, wov_copy_work);
833b6bc07d4STzung-Bi Shih
834b6bc07d4STzung-Bi Shih priv->wov_notifier.notifier_call = wov_host_event;
835b6bc07d4STzung-Bi Shih ret = blocking_notifier_chain_register(
836b6bc07d4STzung-Bi Shih &priv->ec_device->event_notifier, &priv->wov_notifier);
837b6bc07d4STzung-Bi Shih if (ret)
838b6bc07d4STzung-Bi Shih return ret;
839b6bc07d4STzung-Bi Shih
840b6bc07d4STzung-Bi Shih if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_LANG_SHM)) {
841b6bc07d4STzung-Bi Shih priv->wov_lang_shm_p = wov_map_shm(priv,
842b6bc07d4STzung-Bi Shih EC_CODEC_SHM_ID_WOV_LANG,
843b6bc07d4STzung-Bi Shih &priv->wov_lang_shm_len,
844b6bc07d4STzung-Bi Shih &priv->wov_lang_shm_type);
845b6bc07d4STzung-Bi Shih if (!priv->wov_lang_shm_p)
846b6bc07d4STzung-Bi Shih return -EFAULT;
847b6bc07d4STzung-Bi Shih }
848b6bc07d4STzung-Bi Shih
849b6bc07d4STzung-Bi Shih if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM)) {
850b6bc07d4STzung-Bi Shih priv->wov_audio_shm_p = wov_map_shm(priv,
851b6bc07d4STzung-Bi Shih EC_CODEC_SHM_ID_WOV_AUDIO,
852b6bc07d4STzung-Bi Shih &priv->wov_audio_shm_len,
853b6bc07d4STzung-Bi Shih &priv->wov_audio_shm_type);
854b6bc07d4STzung-Bi Shih if (!priv->wov_audio_shm_p)
855b6bc07d4STzung-Bi Shih return -EFAULT;
856b6bc07d4STzung-Bi Shih }
857b6bc07d4STzung-Bi Shih
858b6bc07d4STzung-Bi Shih return dmic_probe(component);
859b6bc07d4STzung-Bi Shih }
860b6bc07d4STzung-Bi Shih
wov_remove(struct snd_soc_component * component)861b6bc07d4STzung-Bi Shih static void wov_remove(struct snd_soc_component *component)
862b6bc07d4STzung-Bi Shih {
863b6bc07d4STzung-Bi Shih struct cros_ec_codec_priv *priv =
864b6bc07d4STzung-Bi Shih snd_soc_component_get_drvdata(component);
865b6bc07d4STzung-Bi Shih
866b6bc07d4STzung-Bi Shih blocking_notifier_chain_unregister(
867b6bc07d4STzung-Bi Shih &priv->ec_device->event_notifier, &priv->wov_notifier);
868b6bc07d4STzung-Bi Shih }
869b6bc07d4STzung-Bi Shih
wov_pcm_open(struct snd_soc_component * component,struct snd_pcm_substream * substream)870b6bc07d4STzung-Bi Shih static int wov_pcm_open(struct snd_soc_component *component,
871b6bc07d4STzung-Bi Shih struct snd_pcm_substream *substream)
872b6bc07d4STzung-Bi Shih {
873b6bc07d4STzung-Bi Shih static const struct snd_pcm_hardware hw_param = {
874b6bc07d4STzung-Bi Shih .info = SNDRV_PCM_INFO_MMAP |
875b6bc07d4STzung-Bi Shih SNDRV_PCM_INFO_INTERLEAVED |
876b6bc07d4STzung-Bi Shih SNDRV_PCM_INFO_MMAP_VALID,
877b6bc07d4STzung-Bi Shih .formats = SNDRV_PCM_FMTBIT_S16_LE,
878b6bc07d4STzung-Bi Shih .rates = SNDRV_PCM_RATE_16000,
879b6bc07d4STzung-Bi Shih .channels_min = 1,
880b6bc07d4STzung-Bi Shih .channels_max = 1,
881b6bc07d4STzung-Bi Shih .period_bytes_min = PAGE_SIZE,
882b6bc07d4STzung-Bi Shih .period_bytes_max = 0x20000 / 8,
883b6bc07d4STzung-Bi Shih .periods_min = 8,
884b6bc07d4STzung-Bi Shih .periods_max = 8,
885b6bc07d4STzung-Bi Shih .buffer_bytes_max = 0x20000,
886b6bc07d4STzung-Bi Shih };
887b6bc07d4STzung-Bi Shih
888b6bc07d4STzung-Bi Shih return snd_soc_set_runtime_hwparams(substream, &hw_param);
889b6bc07d4STzung-Bi Shih }
890b6bc07d4STzung-Bi Shih
wov_pcm_hw_params(struct snd_soc_component * component,struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)891b6bc07d4STzung-Bi Shih static int wov_pcm_hw_params(struct snd_soc_component *component,
892b6bc07d4STzung-Bi Shih struct snd_pcm_substream *substream,
893b6bc07d4STzung-Bi Shih struct snd_pcm_hw_params *hw_params)
894b6bc07d4STzung-Bi Shih {
895b6bc07d4STzung-Bi Shih struct cros_ec_codec_priv *priv =
896b6bc07d4STzung-Bi Shih snd_soc_component_get_drvdata(component);
897b6bc07d4STzung-Bi Shih
898b6bc07d4STzung-Bi Shih mutex_lock(&priv->wov_dma_lock);
899b6bc07d4STzung-Bi Shih priv->wov_substream = substream;
900b6bc07d4STzung-Bi Shih priv->wov_rp = priv->wov_wp = 0;
901b6bc07d4STzung-Bi Shih priv->wov_dma_offset = 0;
902b6bc07d4STzung-Bi Shih priv->wov_burst_read = true;
903b6bc07d4STzung-Bi Shih mutex_unlock(&priv->wov_dma_lock);
904b6bc07d4STzung-Bi Shih
90566b3621bSTakashi Iwai return 0;
906b6bc07d4STzung-Bi Shih }
907b6bc07d4STzung-Bi Shih
wov_pcm_hw_free(struct snd_soc_component * component,struct snd_pcm_substream * substream)908b6bc07d4STzung-Bi Shih static int wov_pcm_hw_free(struct snd_soc_component *component,
909b6bc07d4STzung-Bi Shih struct snd_pcm_substream *substream)
910b6bc07d4STzung-Bi Shih {
911b6bc07d4STzung-Bi Shih struct cros_ec_codec_priv *priv =
912b6bc07d4STzung-Bi Shih snd_soc_component_get_drvdata(component);
913b6bc07d4STzung-Bi Shih
914b6bc07d4STzung-Bi Shih mutex_lock(&priv->wov_dma_lock);
915b6bc07d4STzung-Bi Shih wov_queue_dequeue(priv, wov_queue_size(priv));
916b6bc07d4STzung-Bi Shih priv->wov_substream = NULL;
917b6bc07d4STzung-Bi Shih mutex_unlock(&priv->wov_dma_lock);
918b6bc07d4STzung-Bi Shih
919b6bc07d4STzung-Bi Shih cancel_delayed_work_sync(&priv->wov_copy_work);
920b6bc07d4STzung-Bi Shih
92166b3621bSTakashi Iwai return 0;
922b6bc07d4STzung-Bi Shih }
923b6bc07d4STzung-Bi Shih
wov_pcm_pointer(struct snd_soc_component * component,struct snd_pcm_substream * substream)924b6bc07d4STzung-Bi Shih static snd_pcm_uframes_t wov_pcm_pointer(struct snd_soc_component *component,
925b6bc07d4STzung-Bi Shih struct snd_pcm_substream *substream)
926b6bc07d4STzung-Bi Shih {
927b6bc07d4STzung-Bi Shih struct snd_pcm_runtime *runtime = substream->runtime;
928b6bc07d4STzung-Bi Shih struct cros_ec_codec_priv *priv =
929b6bc07d4STzung-Bi Shih snd_soc_component_get_drvdata(component);
930b6bc07d4STzung-Bi Shih
931b6bc07d4STzung-Bi Shih return bytes_to_frames(runtime, priv->wov_dma_offset);
932b6bc07d4STzung-Bi Shih }
933b6bc07d4STzung-Bi Shih
wov_pcm_new(struct snd_soc_component * component,struct snd_soc_pcm_runtime * rtd)934e610748aSTakashi Iwai static int wov_pcm_new(struct snd_soc_component *component,
935e610748aSTakashi Iwai struct snd_soc_pcm_runtime *rtd)
936b6bc07d4STzung-Bi Shih {
93766b3621bSTakashi Iwai snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC,
938e610748aSTakashi Iwai NULL, 0, 0);
939e610748aSTakashi Iwai return 0;
940b6bc07d4STzung-Bi Shih }
941b6bc07d4STzung-Bi Shih
942b6bc07d4STzung-Bi Shih static const struct snd_soc_component_driver wov_component_driver = {
943b6bc07d4STzung-Bi Shih .probe = wov_probe,
944b6bc07d4STzung-Bi Shih .remove = wov_remove,
945b6bc07d4STzung-Bi Shih .controls = wov_controls,
946b6bc07d4STzung-Bi Shih .num_controls = ARRAY_SIZE(wov_controls),
947b6bc07d4STzung-Bi Shih .open = wov_pcm_open,
948b6bc07d4STzung-Bi Shih .hw_params = wov_pcm_hw_params,
949b6bc07d4STzung-Bi Shih .hw_free = wov_pcm_hw_free,
950b6bc07d4STzung-Bi Shih .pointer = wov_pcm_pointer,
951e610748aSTakashi Iwai .pcm_construct = wov_pcm_new,
952b6bc07d4STzung-Bi Shih };
953b6bc07d4STzung-Bi Shih
cros_ec_codec_platform_probe(struct platform_device * pdev)954727f1c71STzung-Bi Shih static int cros_ec_codec_platform_probe(struct platform_device *pdev)
955b291f42aSCheng-Yi Chiang {
956727f1c71STzung-Bi Shih struct device *dev = &pdev->dev;
957727f1c71STzung-Bi Shih struct cros_ec_device *ec_device = dev_get_drvdata(pdev->dev.parent);
958727f1c71STzung-Bi Shih struct cros_ec_codec_priv *priv;
959b6bc07d4STzung-Bi Shih struct ec_param_ec_codec p;
960b6bc07d4STzung-Bi Shih struct ec_response_ec_codec_get_capabilities r;
961b6bc07d4STzung-Bi Shih int ret;
962b6bc07d4STzung-Bi Shih #ifdef CONFIG_OF
963b6bc07d4STzung-Bi Shih struct device_node *node;
964b6bc07d4STzung-Bi Shih struct resource res;
965b6bc07d4STzung-Bi Shih u64 ec_shm_size;
966b6bc07d4STzung-Bi Shih const __be32 *regaddr_p;
967b6bc07d4STzung-Bi Shih #endif
968b291f42aSCheng-Yi Chiang
969727f1c71STzung-Bi Shih priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
970727f1c71STzung-Bi Shih if (!priv)
971b291f42aSCheng-Yi Chiang return -ENOMEM;
972b291f42aSCheng-Yi Chiang
973b6bc07d4STzung-Bi Shih #ifdef CONFIG_OF
974b6bc07d4STzung-Bi Shih regaddr_p = of_get_address(dev->of_node, 0, &ec_shm_size, NULL);
975b6bc07d4STzung-Bi Shih if (regaddr_p) {
976b6bc07d4STzung-Bi Shih priv->ec_shm_addr = of_read_number(regaddr_p, 2);
977b6bc07d4STzung-Bi Shih priv->ec_shm_len = ec_shm_size;
978b6bc07d4STzung-Bi Shih
979b6bc07d4STzung-Bi Shih dev_dbg(dev, "ec_shm_addr=%#llx len=%#x\n",
980b6bc07d4STzung-Bi Shih priv->ec_shm_addr, priv->ec_shm_len);
981b6bc07d4STzung-Bi Shih }
982b6bc07d4STzung-Bi Shih
983b6bc07d4STzung-Bi Shih node = of_parse_phandle(dev->of_node, "memory-region", 0);
984b6bc07d4STzung-Bi Shih if (node) {
985b6bc07d4STzung-Bi Shih ret = of_address_to_resource(node, 0, &res);
986b6bc07d4STzung-Bi Shih if (!ret) {
987b6bc07d4STzung-Bi Shih priv->ap_shm_phys_addr = res.start;
988b6bc07d4STzung-Bi Shih priv->ap_shm_len = resource_size(&res);
989b6bc07d4STzung-Bi Shih priv->ap_shm_addr =
990b6bc07d4STzung-Bi Shih (uint64_t)(uintptr_t)devm_ioremap_wc(
991b6bc07d4STzung-Bi Shih dev, priv->ap_shm_phys_addr,
992b6bc07d4STzung-Bi Shih priv->ap_shm_len);
993b6bc07d4STzung-Bi Shih priv->ap_shm_last_alloc = priv->ap_shm_phys_addr;
994b6bc07d4STzung-Bi Shih
995b6bc07d4STzung-Bi Shih dev_dbg(dev, "ap_shm_phys_addr=%#llx len=%#x\n",
996b6bc07d4STzung-Bi Shih priv->ap_shm_phys_addr, priv->ap_shm_len);
997b6bc07d4STzung-Bi Shih }
998*0a034d93SMiaoqian Lin of_node_put(node);
999b6bc07d4STzung-Bi Shih }
1000b6bc07d4STzung-Bi Shih #endif
1001b6bc07d4STzung-Bi Shih
1002727f1c71STzung-Bi Shih priv->dev = dev;
1003727f1c71STzung-Bi Shih priv->ec_device = ec_device;
1004b6bc07d4STzung-Bi Shih atomic_set(&priv->dmic_probed, 0);
1005b6bc07d4STzung-Bi Shih
1006b6bc07d4STzung-Bi Shih p.cmd = EC_CODEC_GET_CAPABILITIES;
1007b6bc07d4STzung-Bi Shih ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC,
1008b6bc07d4STzung-Bi Shih (uint8_t *)&p, sizeof(p),
1009b6bc07d4STzung-Bi Shih (uint8_t *)&r, sizeof(r));
1010b6bc07d4STzung-Bi Shih if (ret) {
1011b6bc07d4STzung-Bi Shih dev_err(dev, "failed to EC_CODEC_GET_CAPABILITIES\n");
1012b6bc07d4STzung-Bi Shih return ret;
1013b6bc07d4STzung-Bi Shih }
1014b6bc07d4STzung-Bi Shih priv->ec_capabilities = r.capabilities;
1015b291f42aSCheng-Yi Chiang
10167f1f7ae1SYu-Hsuan Hsu /* Reset EC codec i2s rx. */
10177f1f7ae1SYu-Hsuan Hsu p.cmd = EC_CODEC_I2S_RX_RESET;
10187f1f7ae1SYu-Hsuan Hsu ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
10197f1f7ae1SYu-Hsuan Hsu (uint8_t *)&p, sizeof(p), NULL, 0);
10207f1f7ae1SYu-Hsuan Hsu if (ret == -ENOPROTOOPT) {
10217f1f7ae1SYu-Hsuan Hsu dev_info(dev,
10227f1f7ae1SYu-Hsuan Hsu "Missing reset command. Please update EC firmware.\n");
10237f1f7ae1SYu-Hsuan Hsu } else if (ret) {
10247f1f7ae1SYu-Hsuan Hsu dev_err(dev, "failed to EC_CODEC_I2S_RESET: %d\n", ret);
10257f1f7ae1SYu-Hsuan Hsu return ret;
10267f1f7ae1SYu-Hsuan Hsu }
10277f1f7ae1SYu-Hsuan Hsu
1028727f1c71STzung-Bi Shih platform_set_drvdata(pdev, priv);
1029b291f42aSCheng-Yi Chiang
1030b6bc07d4STzung-Bi Shih ret = devm_snd_soc_register_component(dev, &i2s_rx_component_driver,
1031727f1c71STzung-Bi Shih &i2s_rx_dai_driver, 1);
1032b6bc07d4STzung-Bi Shih if (ret)
1033b6bc07d4STzung-Bi Shih return ret;
1034b6bc07d4STzung-Bi Shih
1035b6bc07d4STzung-Bi Shih return devm_snd_soc_register_component(dev, &wov_component_driver,
1036b6bc07d4STzung-Bi Shih &wov_dai_driver, 1);
1037b291f42aSCheng-Yi Chiang }
1038b291f42aSCheng-Yi Chiang
1039b291f42aSCheng-Yi Chiang #ifdef CONFIG_OF
1040b291f42aSCheng-Yi Chiang static const struct of_device_id cros_ec_codec_of_match[] = {
1041b291f42aSCheng-Yi Chiang { .compatible = "google,cros-ec-codec" },
1042b291f42aSCheng-Yi Chiang {},
1043b291f42aSCheng-Yi Chiang };
1044b291f42aSCheng-Yi Chiang MODULE_DEVICE_TABLE(of, cros_ec_codec_of_match);
1045b291f42aSCheng-Yi Chiang #endif
1046b291f42aSCheng-Yi Chiang
1047fbcde4ffSPierre-Louis Bossart #ifdef CONFIG_ACPI
1048877167efSYu-Hsuan Hsu static const struct acpi_device_id cros_ec_codec_acpi_id[] = {
1049877167efSYu-Hsuan Hsu { "GOOG0013", 0 },
1050877167efSYu-Hsuan Hsu { }
1051877167efSYu-Hsuan Hsu };
1052877167efSYu-Hsuan Hsu MODULE_DEVICE_TABLE(acpi, cros_ec_codec_acpi_id);
1053fbcde4ffSPierre-Louis Bossart #endif
1054877167efSYu-Hsuan Hsu
1055b291f42aSCheng-Yi Chiang static struct platform_driver cros_ec_codec_platform_driver = {
1056b291f42aSCheng-Yi Chiang .driver = {
1057727f1c71STzung-Bi Shih .name = "cros-ec-codec",
1058b291f42aSCheng-Yi Chiang .of_match_table = of_match_ptr(cros_ec_codec_of_match),
1059877167efSYu-Hsuan Hsu .acpi_match_table = ACPI_PTR(cros_ec_codec_acpi_id),
1060b291f42aSCheng-Yi Chiang },
1061b291f42aSCheng-Yi Chiang .probe = cros_ec_codec_platform_probe,
1062b291f42aSCheng-Yi Chiang };
1063b291f42aSCheng-Yi Chiang
1064b291f42aSCheng-Yi Chiang module_platform_driver(cros_ec_codec_platform_driver);
1065b291f42aSCheng-Yi Chiang
1066b291f42aSCheng-Yi Chiang MODULE_LICENSE("GPL v2");
1067b291f42aSCheng-Yi Chiang MODULE_DESCRIPTION("ChromeOS EC codec driver");
1068b291f42aSCheng-Yi Chiang MODULE_AUTHOR("Cheng-Yi Chiang <cychiang@chromium.org>");
1069727f1c71STzung-Bi Shih MODULE_ALIAS("platform:cros-ec-codec");
1070