1 // SPDX-License-Identifier: GPL-2.0
3 * Driver for ChromeOS Embedded Controller codec.
5 * This driver uses the cros-ec interface to communicate with the ChromeOS
6 * EC for audio function.
9 #include <linux/delay.h>
10 #include <linux/device.h>
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/platform_data/cros_ec_commands.h>
14 #include <linux/platform_data/cros_ec_proto.h>
15 #include <linux/platform_device.h>
16 #include <sound/pcm.h>
17 #include <sound/pcm_params.h>
18 #include <sound/soc.h>
19 #include <sound/tlv.h>
21 #define DRV_NAME "cros-ec-codec"
24 * struct cros_ec_codec_data - ChromeOS EC codec driver data.
25 * @dev: Device structure used in sysfs.
26 * @ec_device: cros_ec_device structure to talk to the physical device.
27 * @component: Pointer to the component.
28 * @max_dmic_gain: Maximum gain in dB supported by EC codec.
30 struct cros_ec_codec_data
{
32 struct cros_ec_device
*ec_device
;
33 struct snd_soc_component
*component
;
34 unsigned int max_dmic_gain
;
37 static const DECLARE_TLV_DB_SCALE(ec_mic_gain_tlv
, 0, 100, 0);
39 static int ec_command_get_gain(struct snd_soc_component
*component
,
40 struct ec_param_codec_i2s
*param
,
41 struct ec_codec_i2s_gain
*resp
)
43 struct cros_ec_codec_data
*codec_data
=
44 snd_soc_component_get_drvdata(component
);
45 struct cros_ec_device
*ec_device
= codec_data
->ec_device
;
46 u8 buffer
[sizeof(struct cros_ec_command
) +
47 max(sizeof(struct ec_param_codec_i2s
),
48 sizeof(struct ec_codec_i2s_gain
))];
49 struct cros_ec_command
*msg
= (struct cros_ec_command
*)&buffer
;
53 msg
->command
= EC_CMD_CODEC_I2S
;
54 msg
->outsize
= sizeof(struct ec_param_codec_i2s
);
55 msg
->insize
= sizeof(struct ec_codec_i2s_gain
);
57 memcpy(msg
->data
, param
, msg
->outsize
);
59 ret
= cros_ec_cmd_xfer_status(ec_device
, msg
);
61 memcpy(resp
, msg
->data
, msg
->insize
);
67 * Wrapper for EC command without response.
69 static int ec_command_no_resp(struct snd_soc_component
*component
,
70 struct ec_param_codec_i2s
*param
)
72 struct cros_ec_codec_data
*codec_data
=
73 snd_soc_component_get_drvdata(component
);
74 struct cros_ec_device
*ec_device
= codec_data
->ec_device
;
75 u8 buffer
[sizeof(struct cros_ec_command
) +
76 sizeof(struct ec_param_codec_i2s
)];
77 struct cros_ec_command
*msg
= (struct cros_ec_command
*)&buffer
;
80 msg
->command
= EC_CMD_CODEC_I2S
;
81 msg
->outsize
= sizeof(struct ec_param_codec_i2s
);
84 memcpy(msg
->data
, param
, msg
->outsize
);
86 return cros_ec_cmd_xfer_status(ec_device
, msg
);
89 static int set_i2s_config(struct snd_soc_component
*component
,
90 enum ec_i2s_config i2s_config
)
92 struct ec_param_codec_i2s param
;
94 dev_dbg(component
->dev
, "%s set I2S format to %u\n", __func__
,
97 param
.cmd
= EC_CODEC_I2S_SET_CONFIG
;
98 param
.i2s_config
= i2s_config
;
100 return ec_command_no_resp(component
, ¶m
);
103 static int cros_ec_i2s_set_dai_fmt(struct snd_soc_dai
*dai
, unsigned int fmt
)
105 struct snd_soc_component
*component
= dai
->component
;
106 enum ec_i2s_config i2s_config
;
108 switch (fmt
& SND_SOC_DAIFMT_MASTER_MASK
) {
109 case SND_SOC_DAIFMT_CBS_CFS
:
115 switch (fmt
& SND_SOC_DAIFMT_INV_MASK
) {
116 case SND_SOC_DAIFMT_NB_NF
:
122 switch (fmt
& SND_SOC_DAIFMT_FORMAT_MASK
) {
123 case SND_SOC_DAIFMT_I2S
:
124 i2s_config
= EC_DAI_FMT_I2S
;
127 case SND_SOC_DAIFMT_RIGHT_J
:
128 i2s_config
= EC_DAI_FMT_RIGHT_J
;
131 case SND_SOC_DAIFMT_LEFT_J
:
132 i2s_config
= EC_DAI_FMT_LEFT_J
;
135 case SND_SOC_DAIFMT_DSP_A
:
136 i2s_config
= EC_DAI_FMT_PCM_A
;
139 case SND_SOC_DAIFMT_DSP_B
:
140 i2s_config
= EC_DAI_FMT_PCM_B
;
147 return set_i2s_config(component
, i2s_config
);
150 static int set_i2s_sample_depth(struct snd_soc_component
*component
,
151 enum ec_sample_depth_value depth
)
153 struct ec_param_codec_i2s param
;
155 dev_dbg(component
->dev
, "%s set depth to %u\n", __func__
, depth
);
157 param
.cmd
= EC_CODEC_SET_SAMPLE_DEPTH
;
160 return ec_command_no_resp(component
, ¶m
);
163 static int set_i2s_bclk(struct snd_soc_component
*component
, uint32_t bclk
)
165 struct ec_param_codec_i2s param
;
167 dev_dbg(component
->dev
, "%s set i2s bclk to %u\n", __func__
, bclk
);
169 param
.cmd
= EC_CODEC_I2S_SET_BCLK
;
172 return ec_command_no_resp(component
, ¶m
);
175 static int cros_ec_i2s_hw_params(struct snd_pcm_substream
*substream
,
176 struct snd_pcm_hw_params
*params
,
177 struct snd_soc_dai
*dai
)
179 struct snd_soc_component
*component
= dai
->component
;
180 unsigned int rate
, bclk
;
183 rate
= params_rate(params
);
187 switch (params_format(params
)) {
188 case SNDRV_PCM_FORMAT_S16_LE
:
189 ret
= set_i2s_sample_depth(component
, EC_CODEC_SAMPLE_DEPTH_16
);
191 case SNDRV_PCM_FORMAT_S24_LE
:
192 ret
= set_i2s_sample_depth(component
, EC_CODEC_SAMPLE_DEPTH_24
);
200 bclk
= snd_soc_params_to_bclk(params
);
201 return set_i2s_bclk(component
, bclk
);
204 static const struct snd_soc_dai_ops cros_ec_i2s_dai_ops
= {
205 .hw_params
= cros_ec_i2s_hw_params
,
206 .set_fmt
= cros_ec_i2s_set_dai_fmt
,
209 static struct snd_soc_dai_driver cros_ec_dai
[] = {
211 .name
= "cros_ec_codec I2S",
214 .stream_name
= "I2S Capture",
217 .rates
= SNDRV_PCM_RATE_48000
,
218 .formats
= SNDRV_PCM_FMTBIT_S16_LE
|
219 SNDRV_PCM_FMTBIT_S24_LE
,
221 .ops
= &cros_ec_i2s_dai_ops
,
225 static int get_ec_mic_gain(struct snd_soc_component
*component
,
228 struct ec_param_codec_i2s param
;
229 struct ec_codec_i2s_gain resp
;
232 param
.cmd
= EC_CODEC_GET_GAIN
;
234 ret
= ec_command_get_gain(component
, ¶m
, &resp
);
244 static int mic_gain_get(struct snd_kcontrol
*kcontrol
,
245 struct snd_ctl_elem_value
*ucontrol
)
247 struct snd_soc_component
*component
=
248 snd_soc_kcontrol_component(kcontrol
);
252 ret
= get_ec_mic_gain(component
, &left
, &right
);
256 ucontrol
->value
.integer
.value
[0] = left
;
257 ucontrol
->value
.integer
.value
[1] = right
;
262 static int set_ec_mic_gain(struct snd_soc_component
*component
,
265 struct ec_param_codec_i2s param
;
267 dev_dbg(component
->dev
, "%s set mic gain to %u, %u\n",
268 __func__
, left
, right
);
270 param
.cmd
= EC_CODEC_SET_GAIN
;
271 param
.gain
.left
= left
;
272 param
.gain
.right
= right
;
274 return ec_command_no_resp(component
, ¶m
);
277 static int mic_gain_put(struct snd_kcontrol
*kcontrol
,
278 struct snd_ctl_elem_value
*ucontrol
)
280 struct snd_soc_component
*component
=
281 snd_soc_kcontrol_component(kcontrol
);
282 struct cros_ec_codec_data
*codec_data
=
283 snd_soc_component_get_drvdata(component
);
284 int left
= ucontrol
->value
.integer
.value
[0];
285 int right
= ucontrol
->value
.integer
.value
[1];
286 unsigned int max_dmic_gain
= codec_data
->max_dmic_gain
;
288 if (left
> max_dmic_gain
|| right
> max_dmic_gain
)
291 return set_ec_mic_gain(component
, (u8
)left
, (u8
)right
);
294 static struct snd_kcontrol_new mic_gain_control
=
295 SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM
, SND_SOC_NOPM
, 0, 0, 0,
296 mic_gain_get
, mic_gain_put
, ec_mic_gain_tlv
);
298 static int enable_i2s(struct snd_soc_component
*component
, int enable
)
300 struct ec_param_codec_i2s param
;
302 dev_dbg(component
->dev
, "%s set i2s to %u\n", __func__
, enable
);
304 param
.cmd
= EC_CODEC_I2S_ENABLE
;
305 param
.i2s_enable
= enable
;
307 return ec_command_no_resp(component
, ¶m
);
310 static int cros_ec_i2s_enable_event(struct snd_soc_dapm_widget
*w
,
311 struct snd_kcontrol
*kcontrol
, int event
)
313 struct snd_soc_component
*component
=
314 snd_soc_dapm_to_component(w
->dapm
);
317 case SND_SOC_DAPM_PRE_PMU
:
318 dev_dbg(component
->dev
,
319 "%s got SND_SOC_DAPM_PRE_PMU event\n", __func__
);
320 return enable_i2s(component
, 1);
322 case SND_SOC_DAPM_PRE_PMD
:
323 dev_dbg(component
->dev
,
324 "%s got SND_SOC_DAPM_PRE_PMD event\n", __func__
);
325 return enable_i2s(component
, 0);
332 * The goal of this DAPM route is to turn on/off I2S using EC
333 * host command when capture stream is started/stopped.
335 static const struct snd_soc_dapm_widget cros_ec_codec_dapm_widgets
[] = {
336 SND_SOC_DAPM_INPUT("DMIC"),
339 * Control EC to enable/disable I2S.
341 SND_SOC_DAPM_SUPPLY("I2S Enable", SND_SOC_NOPM
,
342 0, 0, cros_ec_i2s_enable_event
,
343 SND_SOC_DAPM_PRE_PMU
| SND_SOC_DAPM_PRE_PMD
),
345 SND_SOC_DAPM_AIF_OUT("I2STX", "I2S Capture", 0, SND_SOC_NOPM
, 0, 0),
348 static const struct snd_soc_dapm_route cros_ec_codec_dapm_routes
[] = {
349 { "I2STX", NULL
, "DMIC" },
350 { "I2STX", NULL
, "I2S Enable" },
354 * Read maximum gain from device property and set it to mixer control.
356 static int cros_ec_set_gain_range(struct device
*dev
)
358 struct soc_mixer_control
*control
;
359 struct cros_ec_codec_data
*codec_data
= dev_get_drvdata(dev
);
362 rc
= device_property_read_u32(dev
, "max-dmic-gain",
363 &codec_data
->max_dmic_gain
);
367 control
= (struct soc_mixer_control
*)
368 mic_gain_control
.private_value
;
369 control
->max
= codec_data
->max_dmic_gain
;
370 control
->platform_max
= codec_data
->max_dmic_gain
;
375 static int cros_ec_codec_probe(struct snd_soc_component
*component
)
379 struct cros_ec_codec_data
*codec_data
=
380 snd_soc_component_get_drvdata(component
);
382 rc
= cros_ec_set_gain_range(codec_data
->dev
);
386 return snd_soc_add_component_controls(component
, &mic_gain_control
, 1);
389 static const struct snd_soc_component_driver cros_ec_component_driver
= {
390 .probe
= cros_ec_codec_probe
,
391 .dapm_widgets
= cros_ec_codec_dapm_widgets
,
392 .num_dapm_widgets
= ARRAY_SIZE(cros_ec_codec_dapm_widgets
),
393 .dapm_routes
= cros_ec_codec_dapm_routes
,
394 .num_dapm_routes
= ARRAY_SIZE(cros_ec_codec_dapm_routes
),
398 * Platform device and platform driver fro cros-ec-codec.
400 static int cros_ec_codec_platform_probe(struct platform_device
*pd
)
402 struct device
*dev
= &pd
->dev
;
403 struct cros_ec_device
*ec_device
= dev_get_drvdata(pd
->dev
.parent
);
404 struct cros_ec_codec_data
*codec_data
;
406 codec_data
= devm_kzalloc(dev
, sizeof(struct cros_ec_codec_data
),
411 codec_data
->dev
= dev
;
412 codec_data
->ec_device
= ec_device
;
414 platform_set_drvdata(pd
, codec_data
);
416 return devm_snd_soc_register_component(dev
, &cros_ec_component_driver
,
417 cros_ec_dai
, ARRAY_SIZE(cros_ec_dai
));
421 static const struct of_device_id cros_ec_codec_of_match
[] = {
422 { .compatible
= "google,cros-ec-codec" },
425 MODULE_DEVICE_TABLE(of
, cros_ec_codec_of_match
);
428 static struct platform_driver cros_ec_codec_platform_driver
= {
431 .of_match_table
= of_match_ptr(cros_ec_codec_of_match
),
433 .probe
= cros_ec_codec_platform_probe
,
436 module_platform_driver(cros_ec_codec_platform_driver
);
438 MODULE_LICENSE("GPL v2");
439 MODULE_DESCRIPTION("ChromeOS EC codec driver");
440 MODULE_AUTHOR("Cheng-Yi Chiang <cychiang@chromium.org>");
441 MODULE_ALIAS("platform:" DRV_NAME
);