[alsa-devel] [PATCH v3 00/10] ASoC: mediatek: mt8183-mt6358-ts3a227-max98357: support WoV
This series makes mt6358, cros_ec_codec, and mt8183-mt6358-ts3a227-max98357 support WoV (wake on voice).
The first 3 commits are some cleanups and refactors. It looks like breaking the existing interface. But please be noticed that, the cros_ec_codec has not used by any real device yet. The refactor is very necessary to keep the style consistent and for easier to further extend and maintain. platform/chrome: cros_ec: remove unused EC feature ASoC: cros_ec_codec: refactor I2S RX ASoC: cros_ec_codec: extract DMIC EC command from I2S RX
The 4th commit extends the feature offered from EC codec. platform/chrome: cros_ec: add common commands for EC codec
The 5th commit changes the behavior of setting and getting DMIC gains. ASoC: cros_ec_codec: read max DMIC gain from EC codec
The 6th and 7th commit make cros_ec_codec support WoV. ASoC: dt-bindings: cros_ec_codec: add SHM bindings ASoC: cros_ec_codec: support WoV
The 8th commit sets necessary registers on mt6358 to support WoV. ASoC: mediatek: mt6358: support WoV
The last 2 commit make machine driver mt8183-mt6358-ts3a227-max98357 support WoV if ec-codec is in DTS. ASoC: dt-bindings: mt8183: add ec-codec ASoC: mediatek: mt8183: support WoV
Changes from v1: - fix a compile error and make kbuild bot happy https://mailman.alsa-project.org/pipermail/alsa-devel/2019-October/156315.ht... Changes from v2: - rebase upon to "don't use snd_pcm_ops" series https://mailman.alsa-project.org/pipermail/alsa-devel/2019-October/156170.ht... - fix sparse errors https://mailman.alsa-project.org/pipermail/alsa-devel/2019-October/156328.ht... - use "reg" for SHM binding https://mailman.alsa-project.org/pipermail/alsa-devel/2019-October/156657.ht...
Tzung-Bi Shih (10): WIP: platform/chrome: cros_ec: remove unused EC feature WIP: ASoC: cros_ec_codec: refactor I2S RX WIP: ASoC: cros_ec_codec: extract DMIC EC command from I2S RX WIP: platform/chrome: cros_ec: add common commands for EC codec WIP: ASoC: cros_ec_codec: read max DMIC gain from EC codec WIP: ASoC: dt-bindings: cros_ec_codec: add SHM bindings WIP: ASoC: cros_ec_codec: support WoV WIP: ASoC: mediatek: mt6358: support WoV WIP: ASoC: dt-bindings: mt8183: add ec-codec WIP: ASoC: mediatek: mt8183: support WoV
.../bindings/sound/google,cros-ec-codec.txt | 24 +- .../sound/mt8183-mt6358-ts3a227-max98357.txt | 3 + drivers/platform/chrome/cros_ec_trace.c | 5 +- .../linux/platform_data/cros_ec_commands.h | 285 ++++- sound/soc/codecs/cros_ec_codec.c | 1128 +++++++++++++---- sound/soc/codecs/mt6358.c | 105 ++ sound/soc/mediatek/Kconfig | 1 + .../mt8183/mt8183-mt6358-ts3a227-max98357.c | 70 +- 8 files changed, 1296 insertions(+), 325 deletions(-)
Remove unused EC_FEATURE_AUDIO_CODEC.
Signed-off-by: Tzung-Bi Shih tzungbi@google.com --- include/linux/platform_data/cros_ec_commands.h | 2 -- 1 file changed, 2 deletions(-)
diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 98415686cbfa..43b8f7dae4cc 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -1277,8 +1277,6 @@ enum ec_feature_code { * MOTIONSENSE_CMD_TABLET_MODE_LID_ANGLE. */ EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS = 37, - /* EC supports audio codec. */ - EC_FEATURE_AUDIO_CODEC = 38, /* The MCU is a System Companion Processor (SCP). */ EC_FEATURE_SCP = 39, /* The MCU is an Integrated Sensor Hub */
On Mon, Oct 14, 2019 at 06:20:13PM +0800, Tzung-Bi Shih wrote:
Remove unused EC_FEATURE_AUDIO_CODEC.
What's the route to getting these platform/chrome changes reviewed? They don't seem to have got any attention thus far and this one is right at the start of the series.
On Tue, Oct 15, 2019 at 7:49 PM Mark Brown broonie@kernel.org wrote:
On Mon, Oct 14, 2019 at 06:20:13PM +0800, Tzung-Bi Shih wrote:
Remove unused EC_FEATURE_AUDIO_CODEC.
What's the route to getting these platform/chrome changes reviewed? They don't seem to have got any attention thus far and this one is right at the start of the series.
Enric, could you help to review the "platform/chrome" changes in this series? All changes have merged in the EC firmware code (https://chromium.googlesource.com/chromiumos/platform/ec/+/refs/heads/master...).
Hi Tzung-Bi,
On Mon, Oct 14, 2019 at 06:20:13PM +0800, Tzung-Bi Shih wrote:
Remove unused EC_FEATURE_AUDIO_CODEC.
Signed-off-by: Tzung-Bi Shih tzungbi@google.com
Acked-By: Benson Leung bleung@chromium.org
Thanks, Benson
include/linux/platform_data/cros_ec_commands.h | 2 -- 1 file changed, 2 deletions(-)
diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 98415686cbfa..43b8f7dae4cc 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -1277,8 +1277,6 @@ enum ec_feature_code { * MOTIONSENSE_CMD_TABLET_MODE_LID_ANGLE. */ EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS = 37,
- /* EC supports audio codec. */
- EC_FEATURE_AUDIO_CODEC = 38, /* The MCU is a System Companion Processor (SCP). */ EC_FEATURE_SCP = 39, /* The MCU is an Integrated Sensor Hub */
-- 2.23.0.700.g56cf767bdb-goog
The patch
platform/chrome: cros_ec: remove unused EC feature
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-5.5
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 036beb0e85f8772acb635c30df573876103b0c21 Mon Sep 17 00:00:00 2001
From: Tzung-Bi Shih tzungbi@google.com Date: Mon, 14 Oct 2019 18:20:13 +0800 Subject: [PATCH] platform/chrome: cros_ec: remove unused EC feature
Remove unused EC_FEATURE_AUDIO_CODEC.
Signed-off-by: Tzung-Bi Shih tzungbi@google.com Acked-By: Benson Leung bleung@chromium.org Link: https://lore.kernel.org/r/20191014180059.01.I374c311eaca0d47944a37b07acbe48f... Signed-off-by: Mark Brown broonie@kernel.org --- include/linux/platform_data/cros_ec_commands.h | 2 -- 1 file changed, 2 deletions(-)
diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 98415686cbfa..43b8f7dae4cc 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -1277,8 +1277,6 @@ enum ec_feature_code { * MOTIONSENSE_CMD_TABLET_MODE_LID_ANGLE. */ EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS = 37, - /* EC supports audio codec. */ - EC_FEATURE_AUDIO_CODEC = 38, /* The MCU is a System Companion Processor (SCP). */ EC_FEATURE_SCP = 39, /* The MCU is an Integrated Sensor Hub */
Refactor by the following items: - reformat copyright declaration - use more specific name "i2s rx" - use verbose symbol names to separate namespaces - make some short functions inline - remove unused TDM-related code
Signed-off-by: Tzung-Bi Shih tzungbi@google.com --- drivers/platform/chrome/cros_ec_trace.c | 2 +- .../linux/platform_data/cros_ec_commands.h | 120 ++--- sound/soc/codecs/cros_ec_codec.c | 502 +++++++----------- 3 files changed, 251 insertions(+), 373 deletions(-)
diff --git a/drivers/platform/chrome/cros_ec_trace.c b/drivers/platform/chrome/cros_ec_trace.c index 6f80ff4532ae..901850004b2b 100644 --- a/drivers/platform/chrome/cros_ec_trace.c +++ b/drivers/platform/chrome/cros_ec_trace.c @@ -98,7 +98,7 @@ TRACE_SYMBOL(EC_CMD_SB_READ_BLOCK), \ TRACE_SYMBOL(EC_CMD_SB_WRITE_BLOCK), \ TRACE_SYMBOL(EC_CMD_BATTERY_VENDOR_PARAM), \ - TRACE_SYMBOL(EC_CMD_CODEC_I2S), \ + TRACE_SYMBOL(EC_CMD_EC_CODEC_I2S_RX), \ TRACE_SYMBOL(EC_CMD_REBOOT_EC), \ TRACE_SYMBOL(EC_CMD_GET_PANIC_INFO), \ TRACE_SYMBOL(EC_CMD_ACPI_READ), \ diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 43b8f7dae4cc..261ac83bd007 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -4466,92 +4466,74 @@ enum mkbp_cec_event {
/*****************************************************************************/
-/* Commands for I2S recording on audio codec. */ - -#define EC_CMD_CODEC_I2S 0x00BC -#define EC_WOV_I2S_SAMPLE_RATE 48000 - -enum ec_codec_i2s_subcmd { - EC_CODEC_SET_SAMPLE_DEPTH = 0x0, - EC_CODEC_SET_GAIN = 0x1, - EC_CODEC_GET_GAIN = 0x2, - EC_CODEC_I2S_ENABLE = 0x3, - EC_CODEC_I2S_SET_CONFIG = 0x4, - EC_CODEC_I2S_SET_TDM_CONFIG = 0x5, - EC_CODEC_I2S_SET_BCLK = 0x6, - EC_CODEC_I2S_SUBCMD_COUNT = 0x7, +/* Commands for I2S RX on audio codec. */ + +#define EC_CMD_EC_CODEC_I2S_RX 0x00BC + +enum ec_codec_i2s_rx_subcmd { + EC_CODEC_I2S_RX_ENABLE = 0x0, + EC_CODEC_I2S_RX_DISABLE = 0x1, + EC_CODEC_I2S_RX_SET_GAIN = 0x2, + EC_CODEC_I2S_RX_GET_GAIN = 0x3, + EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH = 0x4, + EC_CODEC_I2S_RX_SET_DAIFMT = 0x5, + EC_CODEC_I2S_RX_SET_BCLK = 0x6, + EC_CODEC_I2S_RX_SUBCMD_COUNT, };
-enum ec_sample_depth_value { - EC_CODEC_SAMPLE_DEPTH_16 = 0, - EC_CODEC_SAMPLE_DEPTH_24 = 1, +enum ec_codec_i2s_rx_sample_depth { + EC_CODEC_I2S_RX_SAMPLE_DEPTH_16 = 0x0, + EC_CODEC_I2S_RX_SAMPLE_DEPTH_24 = 0x1, + EC_CODEC_I2S_RX_SAMPLE_DEPTH_COUNT, };
-enum ec_i2s_config { - EC_DAI_FMT_I2S = 0, - EC_DAI_FMT_RIGHT_J = 1, - EC_DAI_FMT_LEFT_J = 2, - EC_DAI_FMT_PCM_A = 3, - EC_DAI_FMT_PCM_B = 4, - EC_DAI_FMT_PCM_TDM = 5, +enum ec_codec_i2s_rx_daifmt { + EC_CODEC_I2S_RX_DAIFMT_I2S = 0x0, + EC_CODEC_I2S_RX_DAIFMT_RIGHT_J = 0x1, + EC_CODEC_I2S_RX_DAIFMT_LEFT_J = 0x2, + EC_CODEC_I2S_RX_DAIFMT_COUNT, };
-/* - * For subcommand EC_CODEC_GET_GAIN. - */ -struct __ec_align1 ec_codec_i2s_gain { +struct __ec_align1 ec_param_ec_codec_i2s_rx_set_sample_depth { + uint8_t depth; + uint8_t reserved[3]; +}; + +struct __ec_align1 ec_param_ec_codec_i2s_rx_set_gain { uint8_t left; uint8_t right; + uint8_t reserved[2]; };
-struct __ec_todo_unpacked ec_param_codec_i2s_tdm { - int16_t ch0_delay; /* 0 to 496 */ - int16_t ch1_delay; /* -1 to 496 */ - uint8_t adjacent_to_ch0; - uint8_t adjacent_to_ch1; +struct __ec_align1 ec_param_ec_codec_i2s_rx_set_daifmt { + uint8_t daifmt; + uint8_t reserved[3]; };
-struct __ec_todo_packed ec_param_codec_i2s { - /* enum ec_codec_i2s_subcmd */ - uint8_t cmd; - union { - /* - * EC_CODEC_SET_SAMPLE_DEPTH - * Value should be one of ec_sample_depth_value. - */ - uint8_t depth; - - /* - * EC_CODEC_SET_GAIN - * Value should be 0~43 for both channels. - */ - struct ec_codec_i2s_gain gain; - - /* - * EC_CODEC_I2S_ENABLE - * 1 to enable, 0 to disable. - */ - uint8_t i2s_enable; - - /* - * EC_CODEC_I2S_SET_CONFIG - * Value should be one of ec_i2s_config. - */ - uint8_t i2s_config; +struct __ec_align4 ec_param_ec_codec_i2s_rx_set_bclk { + uint32_t bclk; +};
- /* - * EC_CODEC_I2S_SET_TDM_CONFIG - * Value should be one of ec_i2s_config. - */ - struct ec_param_codec_i2s_tdm tdm_param; +struct __ec_align4 ec_param_ec_codec_i2s_rx { + uint8_t cmd; /* enum ec_codec_i2s_rx_subcmd */ + uint8_t reserved[3];
- /* - * EC_CODEC_I2S_SET_BCLK - */ - uint32_t bclk; + union { + struct ec_param_ec_codec_i2s_rx_set_sample_depth + set_sample_depth_param; + struct ec_param_ec_codec_i2s_rx_set_gain + set_gain_param; + struct ec_param_ec_codec_i2s_rx_set_daifmt + set_daifmt_param; + struct ec_param_ec_codec_i2s_rx_set_bclk + set_bclk_param; }; };
+struct __ec_align1 ec_response_ec_codec_i2s_rx_get_gain { + uint8_t left; + uint8_t right; +};
/*****************************************************************************/ /* System commands */ diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index 3c1bd24a1057..179fa77291cd 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Driver for ChromeOS Embedded Controller codec. + * Copyright 2019 Google, Inc. + * + * ChromeOS Embedded Controller codec driver. * * This driver uses the cros-ec interface to communicate with the ChromeOS * EC for audio function. @@ -18,403 +20,297 @@ #include <sound/soc.h> #include <sound/tlv.h>
-#define DRV_NAME "cros-ec-codec" - -/** - * struct cros_ec_codec_data - ChromeOS EC codec driver data. - * @dev: Device structure used in sysfs. - * @ec_device: cros_ec_device structure to talk to the physical device. - * @component: Pointer to the component. - * @max_dmic_gain: Maximum gain in dB supported by EC codec. - */ -struct cros_ec_codec_data { +struct cros_ec_codec_priv { struct device *dev; struct cros_ec_device *ec_device; - struct snd_soc_component *component; - unsigned int max_dmic_gain; };
-static const DECLARE_TLV_DB_SCALE(ec_mic_gain_tlv, 0, 100, 0); - -static int ec_command_get_gain(struct snd_soc_component *component, - struct ec_param_codec_i2s *param, - struct ec_codec_i2s_gain *resp) +static int 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) { - struct cros_ec_codec_data *codec_data = - snd_soc_component_get_drvdata(component); - struct cros_ec_device *ec_device = codec_data->ec_device; - u8 buffer[sizeof(struct cros_ec_command) + - max(sizeof(struct ec_param_codec_i2s), - sizeof(struct ec_codec_i2s_gain))]; - struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; int ret; + struct cros_ec_command *msg; + + msg = kmalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL); + if (!msg) + return -ENOMEM;
msg->version = 0; - msg->command = EC_CMD_CODEC_I2S; - msg->outsize = sizeof(struct ec_param_codec_i2s); - msg->insize = sizeof(struct ec_codec_i2s_gain); + msg->command = cmd; + msg->outsize = outsize; + msg->insize = insize; + + if (outsize) + memcpy(msg->data, out, outsize);
- memcpy(msg->data, param, msg->outsize); + ret = cros_ec_cmd_xfer_status(ec_dev, msg); + if (ret < 0) + goto error;
- ret = cros_ec_cmd_xfer_status(ec_device, msg); - if (ret > 0) - memcpy(resp, msg->data, msg->insize); + if (insize) + memcpy(in, msg->data, insize);
+ ret = 0; +error: + kfree(msg); return ret; }
-/* - * Wrapper for EC command without response. - */ -static int ec_command_no_resp(struct snd_soc_component *component, - struct ec_param_codec_i2s *param) +static int dmic_get_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - struct cros_ec_codec_data *codec_data = + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component); - struct cros_ec_device *ec_device = codec_data->ec_device; - u8 buffer[sizeof(struct cros_ec_command) + - sizeof(struct ec_param_codec_i2s)]; - struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; - - msg->version = 0; - msg->command = EC_CMD_CODEC_I2S; - msg->outsize = sizeof(struct ec_param_codec_i2s); - msg->insize = 0; - - memcpy(msg->data, param, msg->outsize); - - return cros_ec_cmd_xfer_status(ec_device, msg); -} - -static int set_i2s_config(struct snd_soc_component *component, - enum ec_i2s_config i2s_config) -{ - struct ec_param_codec_i2s param; + struct ec_param_ec_codec_i2s_rx p; + struct ec_response_ec_codec_i2s_rx_get_gain r; + int ret;
- dev_dbg(component->dev, "%s set I2S format to %u\n", __func__, - i2s_config); + p.cmd = EC_CODEC_I2S_RX_GET_GAIN; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret < 0) + return ret;
- param.cmd = EC_CODEC_I2S_SET_CONFIG; - param.i2s_config = i2s_config; + ucontrol->value.integer.value[0] = r.left; + ucontrol->value.integer.value[1] = r.right;
- return ec_command_no_resp(component, ¶m); + return 0; }
-static int cros_ec_i2s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +static int dmic_put_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = dai->component; - enum ec_i2s_config i2s_config; - - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - break; - default: - return -EINVAL; - } - - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - break; - default: - return -EINVAL; - } - - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - i2s_config = EC_DAI_FMT_I2S; - break; - - case SND_SOC_DAIFMT_RIGHT_J: - i2s_config = EC_DAI_FMT_RIGHT_J; - break; - - case SND_SOC_DAIFMT_LEFT_J: - i2s_config = EC_DAI_FMT_LEFT_J; - break; - - case SND_SOC_DAIFMT_DSP_A: - i2s_config = EC_DAI_FMT_PCM_A; - break; - - case SND_SOC_DAIFMT_DSP_B: - i2s_config = EC_DAI_FMT_PCM_B; - break; + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct soc_mixer_control *control = + (struct soc_mixer_control *)kcontrol->private_value; + int max_dmic_gain = control->max; + int left = ucontrol->value.integer.value[0]; + int right = ucontrol->value.integer.value[1]; + struct ec_param_ec_codec_i2s_rx p;
- default: + if (left > max_dmic_gain || right > max_dmic_gain) return -EINVAL; - }
- return set_i2s_config(component, i2s_config); -} - -static int set_i2s_sample_depth(struct snd_soc_component *component, - enum ec_sample_depth_value depth) -{ - struct ec_param_codec_i2s param; - - dev_dbg(component->dev, "%s set depth to %u\n", __func__, depth); - - param.cmd = EC_CODEC_SET_SAMPLE_DEPTH; - param.depth = depth; + dev_dbg(component->dev, "set mic gain to %u, %u\n", left, right);
- return ec_command_no_resp(component, ¶m); + p.cmd = EC_CODEC_I2S_RX_SET_GAIN; + p.set_gain_param.left = left; + p.set_gain_param.right = right; + return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), NULL, 0); }
-static int set_i2s_bclk(struct snd_soc_component *component, uint32_t bclk) -{ - struct ec_param_codec_i2s param; - - dev_dbg(component->dev, "%s set i2s bclk to %u\n", __func__, bclk); +static const DECLARE_TLV_DB_SCALE(dmic_gain_tlv, 0, 100, 0);
- param.cmd = EC_CODEC_I2S_SET_BCLK; - param.bclk = bclk; +enum { + DMIC_CTL_GAIN = 0, +};
- return ec_command_no_resp(component, ¶m); -} +static struct snd_kcontrol_new dmic_controls[] = { + [DMIC_CTL_GAIN] = + SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM, SND_SOC_NOPM, + 0, 0, 0, dmic_get_gain, dmic_put_gain, + dmic_gain_tlv), +};
-static int cros_ec_i2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) +static int i2s_rx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; - unsigned int rate, bclk; + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct ec_param_ec_codec_i2s_rx p; + enum ec_codec_i2s_rx_sample_depth depth; int ret;
- rate = params_rate(params); - if (rate != 48000) + if (params_rate(params) != 48000) return -EINVAL;
switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: - ret = set_i2s_sample_depth(component, EC_CODEC_SAMPLE_DEPTH_16); + depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_16; break; case SNDRV_PCM_FORMAT_S24_LE: - ret = set_i2s_sample_depth(component, EC_CODEC_SAMPLE_DEPTH_24); + depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_24; break; default: return -EINVAL; } - if (ret < 0) - return ret; - - bclk = snd_soc_params_to_bclk(params); - return set_i2s_bclk(component, bclk); -}
-static const struct snd_soc_dai_ops cros_ec_i2s_dai_ops = { - .hw_params = cros_ec_i2s_hw_params, - .set_fmt = cros_ec_i2s_set_dai_fmt, -}; + dev_dbg(component->dev, "set depth to %u\n", depth);
-static struct snd_soc_dai_driver cros_ec_dai[] = { - { - .name = "cros_ec_codec I2S", - .id = 0, - .capture = { - .stream_name = "I2S Capture", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - }, - .ops = &cros_ec_i2s_dai_ops, - } -}; - -static int get_ec_mic_gain(struct snd_soc_component *component, - u8 *left, u8 *right) -{ - struct ec_param_codec_i2s param; - struct ec_codec_i2s_gain resp; - int ret; - - param.cmd = EC_CODEC_GET_GAIN; - - ret = ec_command_get_gain(component, ¶m, &resp); + p.cmd = EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH; + p.set_sample_depth_param.depth = depth; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), NULL, 0); if (ret < 0) return ret;
- *left = resp.left; - *right = resp.right; - - return 0; -} - -static int mic_gain_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = - snd_soc_kcontrol_component(kcontrol); - u8 left, right; - int ret; - - ret = get_ec_mic_gain(component, &left, &right); - if (ret) - return ret; - - ucontrol->value.integer.value[0] = left; - ucontrol->value.integer.value[1] = right; - - return 0; -} - -static int set_ec_mic_gain(struct snd_soc_component *component, - u8 left, u8 right) -{ - struct ec_param_codec_i2s param; - - dev_dbg(component->dev, "%s set mic gain to %u, %u\n", - __func__, left, right); + dev_dbg(component->dev, "set bclk to %u\n", + snd_soc_params_to_bclk(params));
- param.cmd = EC_CODEC_SET_GAIN; - param.gain.left = left; - param.gain.right = right; - - return ec_command_no_resp(component, ¶m); + p.cmd = EC_CODEC_I2S_RX_SET_BCLK; + p.set_bclk_param.bclk = snd_soc_params_to_bclk(params); + return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), NULL, 0); }
-static int mic_gain_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { - struct snd_soc_component *component = - snd_soc_kcontrol_component(kcontrol); - struct cros_ec_codec_data *codec_data = + struct snd_soc_component *component = dai->component; + struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component); - int left = ucontrol->value.integer.value[0]; - int right = ucontrol->value.integer.value[1]; - unsigned int max_dmic_gain = codec_data->max_dmic_gain; + struct ec_param_ec_codec_i2s_rx p; + enum ec_codec_i2s_rx_daifmt daifmt;
- if (left > max_dmic_gain || right > max_dmic_gain) + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: return -EINVAL; + }
- return set_ec_mic_gain(component, (u8)left, (u8)right); -} - -static struct snd_kcontrol_new mic_gain_control = - SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM, SND_SOC_NOPM, 0, 0, 0, - mic_gain_get, mic_gain_put, ec_mic_gain_tlv); - -static int enable_i2s(struct snd_soc_component *component, int enable) -{ - struct ec_param_codec_i2s param; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + }
- dev_dbg(component->dev, "%s set i2s to %u\n", __func__, enable); + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + daifmt = EC_CODEC_I2S_RX_DAIFMT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + daifmt = EC_CODEC_I2S_RX_DAIFMT_RIGHT_J; + break; + case SND_SOC_DAIFMT_LEFT_J: + daifmt = EC_CODEC_I2S_RX_DAIFMT_LEFT_J; + break; + default: + return -EINVAL; + }
- param.cmd = EC_CODEC_I2S_ENABLE; - param.i2s_enable = enable; + dev_dbg(component->dev, "set format to %u\n", daifmt);
- return ec_command_no_resp(component, ¶m); + p.cmd = EC_CODEC_I2S_RX_SET_DAIFMT; + p.set_daifmt_param.daifmt = daifmt; + return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), NULL, 0); }
-static int cros_ec_i2s_enable_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +static const struct snd_soc_dai_ops i2s_rx_dai_ops = { + .hw_params = i2s_rx_hw_params, + .set_fmt = i2s_rx_set_fmt, +}; + +static int i2s_rx_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct ec_param_ec_codec_i2s_rx p;
switch (event) { case SND_SOC_DAPM_PRE_PMU: - dev_dbg(component->dev, - "%s got SND_SOC_DAPM_PRE_PMU event\n", __func__); - return enable_i2s(component, 1); - + dev_dbg(component->dev, "enable I2S RX\n"); + p.cmd = EC_CODEC_I2S_RX_ENABLE; + break; case SND_SOC_DAPM_PRE_PMD: - dev_dbg(component->dev, - "%s got SND_SOC_DAPM_PRE_PMD event\n", __func__); - return enable_i2s(component, 0); + dev_dbg(component->dev, "disable I2S RX\n"); + p.cmd = EC_CODEC_I2S_RX_DISABLE; + break; + default: + return 0; }
- return 0; + return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), NULL, 0); }
-/* - * The goal of this DAPM route is to turn on/off I2S using EC - * host command when capture stream is started/stopped. - */ -static const struct snd_soc_dapm_widget cros_ec_codec_dapm_widgets[] = { +static struct snd_soc_dapm_widget i2s_rx_dapm_widgets[] = { SND_SOC_DAPM_INPUT("DMIC"), - - /* - * Control EC to enable/disable I2S. - */ - SND_SOC_DAPM_SUPPLY("I2S Enable", SND_SOC_NOPM, - 0, 0, cros_ec_i2s_enable_event, + SND_SOC_DAPM_SUPPLY("I2S RX Enable", SND_SOC_NOPM, 0, 0, i2s_rx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_AIF_OUT("I2S RX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0), +};
- SND_SOC_DAPM_AIF_OUT("I2STX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0), +static struct snd_soc_dapm_route i2s_rx_dapm_routes[] = { + {"I2S RX", NULL, "DMIC"}, + {"I2S RX", NULL, "I2S RX Enable"}, };
-static const struct snd_soc_dapm_route cros_ec_codec_dapm_routes[] = { - { "I2STX", NULL, "DMIC" }, - { "I2STX", NULL, "I2S Enable" }, +static struct snd_soc_dai_driver i2s_rx_dai_driver = { + .name = "EC Codec I2S RX", + .capture = { + .stream_name = "I2S Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &i2s_rx_dai_ops, };
-/* - * Read maximum gain from device property and set it to mixer control. - */ -static int cros_ec_set_gain_range(struct device *dev) +static int i2s_rx_probe(struct snd_soc_component *component) { + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct device *dev = priv->dev; + int ret, val; struct soc_mixer_control *control; - struct cros_ec_codec_data *codec_data = dev_get_drvdata(dev); - int rc;
- rc = device_property_read_u32(dev, "max-dmic-gain", - &codec_data->max_dmic_gain); - if (rc) - return rc; + ret = device_property_read_u32(dev, "max-dmic-gain", &val); + if (ret) { + dev_err(dev, "Failed to read 'max-dmic-gain'\n"); + return ret; + }
control = (struct soc_mixer_control *) - mic_gain_control.private_value; - control->max = codec_data->max_dmic_gain; - control->platform_max = codec_data->max_dmic_gain; + dmic_controls[DMIC_CTL_GAIN].private_value; + control->max = val; + control->platform_max = val;
- return 0; -} - -static int cros_ec_codec_probe(struct snd_soc_component *component) -{ - int rc; - - struct cros_ec_codec_data *codec_data = - snd_soc_component_get_drvdata(component); - - rc = cros_ec_set_gain_range(codec_data->dev); - if (rc) - return rc; - - return snd_soc_add_component_controls(component, &mic_gain_control, 1); + return snd_soc_add_component_controls(component, + &dmic_controls[DMIC_CTL_GAIN], 1); }
-static const struct snd_soc_component_driver cros_ec_component_driver = { - .probe = cros_ec_codec_probe, - .dapm_widgets = cros_ec_codec_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(cros_ec_codec_dapm_widgets), - .dapm_routes = cros_ec_codec_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(cros_ec_codec_dapm_routes), +static const struct snd_soc_component_driver i2s_rx_component_driver = { + .probe = i2s_rx_probe, + .dapm_widgets = i2s_rx_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(i2s_rx_dapm_widgets), + .dapm_routes = i2s_rx_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(i2s_rx_dapm_routes), };
-/* - * Platform device and platform driver fro cros-ec-codec. - */ -static int cros_ec_codec_platform_probe(struct platform_device *pd) +static int cros_ec_codec_platform_probe(struct platform_device *pdev) { - struct device *dev = &pd->dev; - struct cros_ec_device *ec_device = dev_get_drvdata(pd->dev.parent); - struct cros_ec_codec_data *codec_data; + struct device *dev = &pdev->dev; + struct cros_ec_device *ec_device = dev_get_drvdata(pdev->dev.parent); + struct cros_ec_codec_priv *priv;
- codec_data = devm_kzalloc(dev, sizeof(struct cros_ec_codec_data), - GFP_KERNEL); - if (!codec_data) + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) return -ENOMEM;
- codec_data->dev = dev; - codec_data->ec_device = ec_device; + priv->dev = dev; + priv->ec_device = ec_device;
- platform_set_drvdata(pd, codec_data); + platform_set_drvdata(pdev, priv);
- return devm_snd_soc_register_component(dev, &cros_ec_component_driver, - cros_ec_dai, ARRAY_SIZE(cros_ec_dai)); + return devm_snd_soc_register_component(dev, &i2s_rx_component_driver, + &i2s_rx_dai_driver, 1); }
#ifdef CONFIG_OF @@ -427,7 +323,7 @@ MODULE_DEVICE_TABLE(of, cros_ec_codec_of_match);
static struct platform_driver cros_ec_codec_platform_driver = { .driver = { - .name = DRV_NAME, + .name = "cros-ec-codec", .of_match_table = of_match_ptr(cros_ec_codec_of_match), }, .probe = cros_ec_codec_platform_probe, @@ -438,4 +334,4 @@ module_platform_driver(cros_ec_codec_platform_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("ChromeOS EC codec driver"); MODULE_AUTHOR("Cheng-Yi Chiang cychiang@chromium.org"); -MODULE_ALIAS("platform:" DRV_NAME); +MODULE_ALIAS("platform:cros-ec-codec");
Hi Tzung-Bi,
On Mon, Oct 14, 2019 at 06:20:14PM +0800, Tzung-Bi Shih wrote:
Refactor by the following items:
- reformat copyright declaration
- use more specific name "i2s rx"
- use verbose symbol names to separate namespaces
- make some short functions inline
- remove unused TDM-related code
Signed-off-by: Tzung-Bi Shih tzungbi@google.com
Acked-By: Benson Leung bleung@chromium.org
Thanks, Benson
drivers/platform/chrome/cros_ec_trace.c | 2 +- .../linux/platform_data/cros_ec_commands.h | 120 ++--- sound/soc/codecs/cros_ec_codec.c | 502 +++++++----------- 3 files changed, 251 insertions(+), 373 deletions(-)
diff --git a/drivers/platform/chrome/cros_ec_trace.c b/drivers/platform/chrome/cros_ec_trace.c index 6f80ff4532ae..901850004b2b 100644 --- a/drivers/platform/chrome/cros_ec_trace.c +++ b/drivers/platform/chrome/cros_ec_trace.c @@ -98,7 +98,7 @@ TRACE_SYMBOL(EC_CMD_SB_READ_BLOCK), \ TRACE_SYMBOL(EC_CMD_SB_WRITE_BLOCK), \ TRACE_SYMBOL(EC_CMD_BATTERY_VENDOR_PARAM), \
- TRACE_SYMBOL(EC_CMD_CODEC_I2S), \
- TRACE_SYMBOL(EC_CMD_EC_CODEC_I2S_RX), \ TRACE_SYMBOL(EC_CMD_REBOOT_EC), \ TRACE_SYMBOL(EC_CMD_GET_PANIC_INFO), \ TRACE_SYMBOL(EC_CMD_ACPI_READ), \
diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 43b8f7dae4cc..261ac83bd007 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -4466,92 +4466,74 @@ enum mkbp_cec_event {
/*****************************************************************************/
-/* Commands for I2S recording on audio codec. */
-#define EC_CMD_CODEC_I2S 0x00BC -#define EC_WOV_I2S_SAMPLE_RATE 48000
-enum ec_codec_i2s_subcmd {
- EC_CODEC_SET_SAMPLE_DEPTH = 0x0,
- EC_CODEC_SET_GAIN = 0x1,
- EC_CODEC_GET_GAIN = 0x2,
- EC_CODEC_I2S_ENABLE = 0x3,
- EC_CODEC_I2S_SET_CONFIG = 0x4,
- EC_CODEC_I2S_SET_TDM_CONFIG = 0x5,
- EC_CODEC_I2S_SET_BCLK = 0x6,
- EC_CODEC_I2S_SUBCMD_COUNT = 0x7,
+/* Commands for I2S RX on audio codec. */
+#define EC_CMD_EC_CODEC_I2S_RX 0x00BC
+enum ec_codec_i2s_rx_subcmd {
- EC_CODEC_I2S_RX_ENABLE = 0x0,
- EC_CODEC_I2S_RX_DISABLE = 0x1,
- EC_CODEC_I2S_RX_SET_GAIN = 0x2,
- EC_CODEC_I2S_RX_GET_GAIN = 0x3,
- EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH = 0x4,
- EC_CODEC_I2S_RX_SET_DAIFMT = 0x5,
- EC_CODEC_I2S_RX_SET_BCLK = 0x6,
- EC_CODEC_I2S_RX_SUBCMD_COUNT,
};
-enum ec_sample_depth_value {
- EC_CODEC_SAMPLE_DEPTH_16 = 0,
- EC_CODEC_SAMPLE_DEPTH_24 = 1,
+enum ec_codec_i2s_rx_sample_depth {
- EC_CODEC_I2S_RX_SAMPLE_DEPTH_16 = 0x0,
- EC_CODEC_I2S_RX_SAMPLE_DEPTH_24 = 0x1,
- EC_CODEC_I2S_RX_SAMPLE_DEPTH_COUNT,
};
-enum ec_i2s_config {
- EC_DAI_FMT_I2S = 0,
- EC_DAI_FMT_RIGHT_J = 1,
- EC_DAI_FMT_LEFT_J = 2,
- EC_DAI_FMT_PCM_A = 3,
- EC_DAI_FMT_PCM_B = 4,
- EC_DAI_FMT_PCM_TDM = 5,
+enum ec_codec_i2s_rx_daifmt {
- EC_CODEC_I2S_RX_DAIFMT_I2S = 0x0,
- EC_CODEC_I2S_RX_DAIFMT_RIGHT_J = 0x1,
- EC_CODEC_I2S_RX_DAIFMT_LEFT_J = 0x2,
- EC_CODEC_I2S_RX_DAIFMT_COUNT,
};
-/*
- For subcommand EC_CODEC_GET_GAIN.
- */
-struct __ec_align1 ec_codec_i2s_gain { +struct __ec_align1 ec_param_ec_codec_i2s_rx_set_sample_depth {
- uint8_t depth;
- uint8_t reserved[3];
+};
+struct __ec_align1 ec_param_ec_codec_i2s_rx_set_gain { uint8_t left; uint8_t right;
- uint8_t reserved[2];
};
-struct __ec_todo_unpacked ec_param_codec_i2s_tdm {
- int16_t ch0_delay; /* 0 to 496 */
- int16_t ch1_delay; /* -1 to 496 */
- uint8_t adjacent_to_ch0;
- uint8_t adjacent_to_ch1;
+struct __ec_align1 ec_param_ec_codec_i2s_rx_set_daifmt {
- uint8_t daifmt;
- uint8_t reserved[3];
};
-struct __ec_todo_packed ec_param_codec_i2s {
- /* enum ec_codec_i2s_subcmd */
- uint8_t cmd;
- union {
/*
* EC_CODEC_SET_SAMPLE_DEPTH
* Value should be one of ec_sample_depth_value.
*/
uint8_t depth;
/*
* EC_CODEC_SET_GAIN
* Value should be 0~43 for both channels.
*/
struct ec_codec_i2s_gain gain;
/*
* EC_CODEC_I2S_ENABLE
* 1 to enable, 0 to disable.
*/
uint8_t i2s_enable;
/*
* EC_CODEC_I2S_SET_CONFIG
* Value should be one of ec_i2s_config.
*/
uint8_t i2s_config;
+struct __ec_align4 ec_param_ec_codec_i2s_rx_set_bclk {
- uint32_t bclk;
+};
/*
* EC_CODEC_I2S_SET_TDM_CONFIG
* Value should be one of ec_i2s_config.
*/
struct ec_param_codec_i2s_tdm tdm_param;
+struct __ec_align4 ec_param_ec_codec_i2s_rx {
- uint8_t cmd; /* enum ec_codec_i2s_rx_subcmd */
- uint8_t reserved[3];
/*
* EC_CODEC_I2S_SET_BCLK
*/
uint32_t bclk;
- union {
struct ec_param_ec_codec_i2s_rx_set_sample_depth
set_sample_depth_param;
struct ec_param_ec_codec_i2s_rx_set_gain
set_gain_param;
struct ec_param_ec_codec_i2s_rx_set_daifmt
set_daifmt_param;
struct ec_param_ec_codec_i2s_rx_set_bclk
};set_bclk_param;
};
+struct __ec_align1 ec_response_ec_codec_i2s_rx_get_gain {
- uint8_t left;
- uint8_t right;
+};
/*****************************************************************************/ /* System commands */ diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index 3c1bd24a1057..179fa77291cd 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /*
- Driver for ChromeOS Embedded Controller codec.
- Copyright 2019 Google, Inc.
- ChromeOS Embedded Controller codec driver.
- This driver uses the cros-ec interface to communicate with the ChromeOS
- EC for audio function.
@@ -18,403 +20,297 @@ #include <sound/soc.h> #include <sound/tlv.h>
-#define DRV_NAME "cros-ec-codec"
-/**
- struct cros_ec_codec_data - ChromeOS EC codec driver data.
- @dev: Device structure used in sysfs.
- @ec_device: cros_ec_device structure to talk to the physical device.
- @component: Pointer to the component.
- @max_dmic_gain: Maximum gain in dB supported by EC codec.
- */
-struct cros_ec_codec_data { +struct cros_ec_codec_priv { struct device *dev; struct cros_ec_device *ec_device;
- struct snd_soc_component *component;
- unsigned int max_dmic_gain;
};
-static const DECLARE_TLV_DB_SCALE(ec_mic_gain_tlv, 0, 100, 0);
-static int ec_command_get_gain(struct snd_soc_component *component,
struct ec_param_codec_i2s *param,
struct ec_codec_i2s_gain *resp)
+static int 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)
{
- struct cros_ec_codec_data *codec_data =
snd_soc_component_get_drvdata(component);
- struct cros_ec_device *ec_device = codec_data->ec_device;
- u8 buffer[sizeof(struct cros_ec_command) +
max(sizeof(struct ec_param_codec_i2s),
sizeof(struct ec_codec_i2s_gain))];
- struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; int ret;
struct cros_ec_command *msg;
msg = kmalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
if (!msg)
return -ENOMEM;
msg->version = 0;
- msg->command = EC_CMD_CODEC_I2S;
- msg->outsize = sizeof(struct ec_param_codec_i2s);
- msg->insize = sizeof(struct ec_codec_i2s_gain);
- msg->command = cmd;
- msg->outsize = outsize;
- msg->insize = insize;
- if (outsize)
memcpy(msg->data, out, outsize);
- memcpy(msg->data, param, msg->outsize);
- ret = cros_ec_cmd_xfer_status(ec_dev, msg);
- if (ret < 0)
goto error;
- ret = cros_ec_cmd_xfer_status(ec_device, msg);
- if (ret > 0)
memcpy(resp, msg->data, msg->insize);
if (insize)
memcpy(in, msg->data, insize);
ret = 0;
+error:
- kfree(msg); return ret;
}
-/*
- Wrapper for EC command without response.
- */
-static int ec_command_no_resp(struct snd_soc_component *component,
struct ec_param_codec_i2s *param)
+static int dmic_get_gain(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct cros_ec_codec_data *codec_data =
- struct snd_soc_component *component =
snd_soc_kcontrol_component(kcontrol);
- struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component);
- struct cros_ec_device *ec_device = codec_data->ec_device;
- u8 buffer[sizeof(struct cros_ec_command) +
sizeof(struct ec_param_codec_i2s)];
- struct cros_ec_command *msg = (struct cros_ec_command *)&buffer;
- msg->version = 0;
- msg->command = EC_CMD_CODEC_I2S;
- msg->outsize = sizeof(struct ec_param_codec_i2s);
- msg->insize = 0;
- memcpy(msg->data, param, msg->outsize);
- return cros_ec_cmd_xfer_status(ec_device, msg);
-}
-static int set_i2s_config(struct snd_soc_component *component,
enum ec_i2s_config i2s_config)
-{
- struct ec_param_codec_i2s param;
- struct ec_param_ec_codec_i2s_rx p;
- struct ec_response_ec_codec_i2s_rx_get_gain r;
- int ret;
- dev_dbg(component->dev, "%s set I2S format to %u\n", __func__,
i2s_config);
- p.cmd = EC_CODEC_I2S_RX_GET_GAIN;
- ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
(uint8_t *)&p, sizeof(p),
(uint8_t *)&r, sizeof(r));
- if (ret < 0)
return ret;
- param.cmd = EC_CODEC_I2S_SET_CONFIG;
- param.i2s_config = i2s_config;
- ucontrol->value.integer.value[0] = r.left;
- ucontrol->value.integer.value[1] = r.right;
- return ec_command_no_resp(component, ¶m);
- return 0;
}
-static int cros_ec_i2s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +static int dmic_put_gain(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_component *component = dai->component;
- enum ec_i2s_config i2s_config;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
break;
- default:
return -EINVAL;
- }
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
break;
- default:
return -EINVAL;
- }
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
i2s_config = EC_DAI_FMT_I2S;
break;
- case SND_SOC_DAIFMT_RIGHT_J:
i2s_config = EC_DAI_FMT_RIGHT_J;
break;
- case SND_SOC_DAIFMT_LEFT_J:
i2s_config = EC_DAI_FMT_LEFT_J;
break;
- case SND_SOC_DAIFMT_DSP_A:
i2s_config = EC_DAI_FMT_PCM_A;
break;
- case SND_SOC_DAIFMT_DSP_B:
i2s_config = EC_DAI_FMT_PCM_B;
break;
- struct snd_soc_component *component =
snd_soc_kcontrol_component(kcontrol);
- struct cros_ec_codec_priv *priv =
snd_soc_component_get_drvdata(component);
- struct soc_mixer_control *control =
(struct soc_mixer_control *)kcontrol->private_value;
- int max_dmic_gain = control->max;
- int left = ucontrol->value.integer.value[0];
- int right = ucontrol->value.integer.value[1];
- struct ec_param_ec_codec_i2s_rx p;
- default:
- if (left > max_dmic_gain || right > max_dmic_gain) return -EINVAL;
}
return set_i2s_config(component, i2s_config);
-}
-static int set_i2s_sample_depth(struct snd_soc_component *component,
enum ec_sample_depth_value depth)
-{
- struct ec_param_codec_i2s param;
- dev_dbg(component->dev, "%s set depth to %u\n", __func__, depth);
- param.cmd = EC_CODEC_SET_SAMPLE_DEPTH;
- param.depth = depth;
- dev_dbg(component->dev, "set mic gain to %u, %u\n", left, right);
- return ec_command_no_resp(component, ¶m);
- p.cmd = EC_CODEC_I2S_RX_SET_GAIN;
- p.set_gain_param.left = left;
- p.set_gain_param.right = right;
- return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
(uint8_t *)&p, sizeof(p), NULL, 0);
}
-static int set_i2s_bclk(struct snd_soc_component *component, uint32_t bclk) -{
- struct ec_param_codec_i2s param;
- dev_dbg(component->dev, "%s set i2s bclk to %u\n", __func__, bclk);
+static const DECLARE_TLV_DB_SCALE(dmic_gain_tlv, 0, 100, 0);
- param.cmd = EC_CODEC_I2S_SET_BCLK;
- param.bclk = bclk;
+enum {
- DMIC_CTL_GAIN = 0,
+};
- return ec_command_no_resp(component, ¶m);
-} +static struct snd_kcontrol_new dmic_controls[] = {
- [DMIC_CTL_GAIN] =
SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM, SND_SOC_NOPM,
0, 0, 0, dmic_get_gain, dmic_put_gain,
dmic_gain_tlv),
+};
-static int cros_ec_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
+static int i2s_rx_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{ struct snd_soc_component *component = dai->component;
- unsigned int rate, bclk;
- struct cros_ec_codec_priv *priv =
snd_soc_component_get_drvdata(component);
- struct ec_param_ec_codec_i2s_rx p;
- enum ec_codec_i2s_rx_sample_depth depth; int ret;
- rate = params_rate(params);
- if (rate != 48000)
if (params_rate(params) != 48000) return -EINVAL;
switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE:
ret = set_i2s_sample_depth(component, EC_CODEC_SAMPLE_DEPTH_16);
break; case SNDRV_PCM_FORMAT_S24_LE:depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_16;
ret = set_i2s_sample_depth(component, EC_CODEC_SAMPLE_DEPTH_24);
break; default: return -EINVAL; }depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_24;
- if (ret < 0)
return ret;
- bclk = snd_soc_params_to_bclk(params);
- return set_i2s_bclk(component, bclk);
-}
-static const struct snd_soc_dai_ops cros_ec_i2s_dai_ops = {
- .hw_params = cros_ec_i2s_hw_params,
- .set_fmt = cros_ec_i2s_set_dai_fmt,
-};
- dev_dbg(component->dev, "set depth to %u\n", depth);
-static struct snd_soc_dai_driver cros_ec_dai[] = {
- {
.name = "cros_ec_codec I2S",
.id = 0,
.capture = {
.stream_name = "I2S Capture",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
},
.ops = &cros_ec_i2s_dai_ops,
- }
-};
-static int get_ec_mic_gain(struct snd_soc_component *component,
u8 *left, u8 *right)
-{
- struct ec_param_codec_i2s param;
- struct ec_codec_i2s_gain resp;
- int ret;
- param.cmd = EC_CODEC_GET_GAIN;
- ret = ec_command_get_gain(component, ¶m, &resp);
- p.cmd = EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH;
- p.set_sample_depth_param.depth = depth;
- ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
if (ret < 0) return ret;(uint8_t *)&p, sizeof(p), NULL, 0);
- *left = resp.left;
- *right = resp.right;
- return 0;
-}
-static int mic_gain_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component =
snd_soc_kcontrol_component(kcontrol);
- u8 left, right;
- int ret;
- ret = get_ec_mic_gain(component, &left, &right);
- if (ret)
return ret;
- ucontrol->value.integer.value[0] = left;
- ucontrol->value.integer.value[1] = right;
- return 0;
-}
-static int set_ec_mic_gain(struct snd_soc_component *component,
u8 left, u8 right)
-{
- struct ec_param_codec_i2s param;
- dev_dbg(component->dev, "%s set mic gain to %u, %u\n",
__func__, left, right);
- dev_dbg(component->dev, "set bclk to %u\n",
snd_soc_params_to_bclk(params));
- param.cmd = EC_CODEC_SET_GAIN;
- param.gain.left = left;
- param.gain.right = right;
- return ec_command_no_resp(component, ¶m);
- p.cmd = EC_CODEC_I2S_RX_SET_BCLK;
- p.set_bclk_param.bclk = snd_soc_params_to_bclk(params);
- return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
(uint8_t *)&p, sizeof(p), NULL, 0);
}
-static int mic_gain_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
+static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) {
- struct snd_soc_component *component =
snd_soc_kcontrol_component(kcontrol);
- struct cros_ec_codec_data *codec_data =
- struct snd_soc_component *component = dai->component;
- struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component);
- int left = ucontrol->value.integer.value[0];
- int right = ucontrol->value.integer.value[1];
- unsigned int max_dmic_gain = codec_data->max_dmic_gain;
- struct ec_param_ec_codec_i2s_rx p;
- enum ec_codec_i2s_rx_daifmt daifmt;
- if (left > max_dmic_gain || right > max_dmic_gain)
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
break;
- default: return -EINVAL;
- }
- return set_ec_mic_gain(component, (u8)left, (u8)right);
-}
-static struct snd_kcontrol_new mic_gain_control =
- SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM, SND_SOC_NOPM, 0, 0, 0,
mic_gain_get, mic_gain_put, ec_mic_gain_tlv);
-static int enable_i2s(struct snd_soc_component *component, int enable) -{
- struct ec_param_codec_i2s param;
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
break;
- default:
return -EINVAL;
- }
- dev_dbg(component->dev, "%s set i2s to %u\n", __func__, enable);
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
daifmt = EC_CODEC_I2S_RX_DAIFMT_I2S;
break;
- case SND_SOC_DAIFMT_RIGHT_J:
daifmt = EC_CODEC_I2S_RX_DAIFMT_RIGHT_J;
break;
- case SND_SOC_DAIFMT_LEFT_J:
daifmt = EC_CODEC_I2S_RX_DAIFMT_LEFT_J;
break;
- default:
return -EINVAL;
- }
- param.cmd = EC_CODEC_I2S_ENABLE;
- param.i2s_enable = enable;
- dev_dbg(component->dev, "set format to %u\n", daifmt);
- return ec_command_no_resp(component, ¶m);
- p.cmd = EC_CODEC_I2S_RX_SET_DAIFMT;
- p.set_daifmt_param.daifmt = daifmt;
- return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
(uint8_t *)&p, sizeof(p), NULL, 0);
}
-static int cros_ec_i2s_enable_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
+static const struct snd_soc_dai_ops i2s_rx_dai_ops = {
- .hw_params = i2s_rx_hw_params,
- .set_fmt = i2s_rx_set_fmt,
+};
+static int i2s_rx_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct cros_ec_codec_priv *priv =
snd_soc_component_get_drvdata(component);
struct ec_param_ec_codec_i2s_rx p;
switch (event) { case SND_SOC_DAPM_PRE_PMU:
dev_dbg(component->dev,
"%s got SND_SOC_DAPM_PRE_PMU event\n", __func__);
return enable_i2s(component, 1);
dev_dbg(component->dev, "enable I2S RX\n");
p.cmd = EC_CODEC_I2S_RX_ENABLE;
case SND_SOC_DAPM_PRE_PMD:break;
dev_dbg(component->dev,
"%s got SND_SOC_DAPM_PRE_PMD event\n", __func__);
return enable_i2s(component, 0);
dev_dbg(component->dev, "disable I2S RX\n");
p.cmd = EC_CODEC_I2S_RX_DISABLE;
break;
- default:
}return 0;
- return 0;
- return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
(uint8_t *)&p, sizeof(p), NULL, 0);
}
-/*
- The goal of this DAPM route is to turn on/off I2S using EC
- host command when capture stream is started/stopped.
- */
-static const struct snd_soc_dapm_widget cros_ec_codec_dapm_widgets[] = { +static struct snd_soc_dapm_widget i2s_rx_dapm_widgets[] = { SND_SOC_DAPM_INPUT("DMIC"),
- /*
* Control EC to enable/disable I2S.
*/
- SND_SOC_DAPM_SUPPLY("I2S Enable", SND_SOC_NOPM,
0, 0, cros_ec_i2s_enable_event,
- SND_SOC_DAPM_SUPPLY("I2S RX Enable", SND_SOC_NOPM, 0, 0, i2s_rx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
- SND_SOC_DAPM_AIF_OUT("I2S RX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0),
+};
- SND_SOC_DAPM_AIF_OUT("I2STX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0),
+static struct snd_soc_dapm_route i2s_rx_dapm_routes[] = {
- {"I2S RX", NULL, "DMIC"},
- {"I2S RX", NULL, "I2S RX Enable"},
};
-static const struct snd_soc_dapm_route cros_ec_codec_dapm_routes[] = {
- { "I2STX", NULL, "DMIC" },
- { "I2STX", NULL, "I2S Enable" },
+static struct snd_soc_dai_driver i2s_rx_dai_driver = {
- .name = "EC Codec I2S RX",
- .capture = {
.stream_name = "I2S Capture",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
- },
- .ops = &i2s_rx_dai_ops,
};
-/*
- Read maximum gain from device property and set it to mixer control.
- */
-static int cros_ec_set_gain_range(struct device *dev) +static int i2s_rx_probe(struct snd_soc_component *component) {
- struct cros_ec_codec_priv *priv =
snd_soc_component_get_drvdata(component);
- struct device *dev = priv->dev;
- int ret, val; struct soc_mixer_control *control;
struct cros_ec_codec_data *codec_data = dev_get_drvdata(dev);
int rc;
rc = device_property_read_u32(dev, "max-dmic-gain",
&codec_data->max_dmic_gain);
if (rc)
return rc;
ret = device_property_read_u32(dev, "max-dmic-gain", &val);
if (ret) {
dev_err(dev, "Failed to read 'max-dmic-gain'\n");
return ret;
}
control = (struct soc_mixer_control *)
mic_gain_control.private_value;
- control->max = codec_data->max_dmic_gain;
- control->platform_max = codec_data->max_dmic_gain;
dmic_controls[DMIC_CTL_GAIN].private_value;
- control->max = val;
- control->platform_max = val;
- return 0;
-}
-static int cros_ec_codec_probe(struct snd_soc_component *component) -{
- int rc;
- struct cros_ec_codec_data *codec_data =
snd_soc_component_get_drvdata(component);
- rc = cros_ec_set_gain_range(codec_data->dev);
- if (rc)
return rc;
- return snd_soc_add_component_controls(component, &mic_gain_control, 1);
- return snd_soc_add_component_controls(component,
&dmic_controls[DMIC_CTL_GAIN], 1);
}
-static const struct snd_soc_component_driver cros_ec_component_driver = {
- .probe = cros_ec_codec_probe,
- .dapm_widgets = cros_ec_codec_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(cros_ec_codec_dapm_widgets),
- .dapm_routes = cros_ec_codec_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(cros_ec_codec_dapm_routes),
+static const struct snd_soc_component_driver i2s_rx_component_driver = {
- .probe = i2s_rx_probe,
- .dapm_widgets = i2s_rx_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(i2s_rx_dapm_widgets),
- .dapm_routes = i2s_rx_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(i2s_rx_dapm_routes),
};
-/*
- Platform device and platform driver fro cros-ec-codec.
- */
-static int cros_ec_codec_platform_probe(struct platform_device *pd) +static int cros_ec_codec_platform_probe(struct platform_device *pdev) {
- struct device *dev = &pd->dev;
- struct cros_ec_device *ec_device = dev_get_drvdata(pd->dev.parent);
- struct cros_ec_codec_data *codec_data;
- struct device *dev = &pdev->dev;
- struct cros_ec_device *ec_device = dev_get_drvdata(pdev->dev.parent);
- struct cros_ec_codec_priv *priv;
- codec_data = devm_kzalloc(dev, sizeof(struct cros_ec_codec_data),
GFP_KERNEL);
- if (!codec_data)
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv) return -ENOMEM;
- codec_data->dev = dev;
- codec_data->ec_device = ec_device;
- priv->dev = dev;
- priv->ec_device = ec_device;
- platform_set_drvdata(pd, codec_data);
- platform_set_drvdata(pdev, priv);
- return devm_snd_soc_register_component(dev, &cros_ec_component_driver,
cros_ec_dai, ARRAY_SIZE(cros_ec_dai));
- return devm_snd_soc_register_component(dev, &i2s_rx_component_driver,
&i2s_rx_dai_driver, 1);
}
#ifdef CONFIG_OF @@ -427,7 +323,7 @@ MODULE_DEVICE_TABLE(of, cros_ec_codec_of_match);
static struct platform_driver cros_ec_codec_platform_driver = { .driver = {
.name = DRV_NAME,
.of_match_table = of_match_ptr(cros_ec_codec_of_match), }, .probe = cros_ec_codec_platform_probe,.name = "cros-ec-codec",
@@ -438,4 +334,4 @@ module_platform_driver(cros_ec_codec_platform_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("ChromeOS EC codec driver"); MODULE_AUTHOR("Cheng-Yi Chiang cychiang@chromium.org"); -MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_ALIAS("platform:cros-ec-codec");
2.23.0.700.g56cf767bdb-goog
The patch
ASoC: cros_ec_codec: refactor I2S RX
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-5.5
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 727f1c71c780789aeb8f3da2596c65ae008d5d6c Mon Sep 17 00:00:00 2001
From: Tzung-Bi Shih tzungbi@google.com Date: Mon, 14 Oct 2019 18:20:14 +0800 Subject: [PATCH] ASoC: cros_ec_codec: refactor I2S RX
Refactor by the following items: - reformat copyright declaration - use more specific name "i2s rx" - use verbose symbol names to separate namespaces - make some short functions inline - remove unused TDM-related code
Signed-off-by: Tzung-Bi Shih tzungbi@google.com Acked-By: Benson Leung bleung@chromium.org Link: https://lore.kernel.org/r/20191014180059.02.I43373b9a66dbb70196b3f216b3aa861... Signed-off-by: Mark Brown broonie@kernel.org --- drivers/platform/chrome/cros_ec_trace.c | 2 +- .../linux/platform_data/cros_ec_commands.h | 120 ++--- sound/soc/codecs/cros_ec_codec.c | 502 +++++++----------- 3 files changed, 251 insertions(+), 373 deletions(-)
diff --git a/drivers/platform/chrome/cros_ec_trace.c b/drivers/platform/chrome/cros_ec_trace.c index 6f80ff4532ae..901850004b2b 100644 --- a/drivers/platform/chrome/cros_ec_trace.c +++ b/drivers/platform/chrome/cros_ec_trace.c @@ -98,7 +98,7 @@ TRACE_SYMBOL(EC_CMD_SB_READ_BLOCK), \ TRACE_SYMBOL(EC_CMD_SB_WRITE_BLOCK), \ TRACE_SYMBOL(EC_CMD_BATTERY_VENDOR_PARAM), \ - TRACE_SYMBOL(EC_CMD_CODEC_I2S), \ + TRACE_SYMBOL(EC_CMD_EC_CODEC_I2S_RX), \ TRACE_SYMBOL(EC_CMD_REBOOT_EC), \ TRACE_SYMBOL(EC_CMD_GET_PANIC_INFO), \ TRACE_SYMBOL(EC_CMD_ACPI_READ), \ diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 43b8f7dae4cc..261ac83bd007 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -4466,92 +4466,74 @@ enum mkbp_cec_event {
/*****************************************************************************/
-/* Commands for I2S recording on audio codec. */ - -#define EC_CMD_CODEC_I2S 0x00BC -#define EC_WOV_I2S_SAMPLE_RATE 48000 - -enum ec_codec_i2s_subcmd { - EC_CODEC_SET_SAMPLE_DEPTH = 0x0, - EC_CODEC_SET_GAIN = 0x1, - EC_CODEC_GET_GAIN = 0x2, - EC_CODEC_I2S_ENABLE = 0x3, - EC_CODEC_I2S_SET_CONFIG = 0x4, - EC_CODEC_I2S_SET_TDM_CONFIG = 0x5, - EC_CODEC_I2S_SET_BCLK = 0x6, - EC_CODEC_I2S_SUBCMD_COUNT = 0x7, +/* Commands for I2S RX on audio codec. */ + +#define EC_CMD_EC_CODEC_I2S_RX 0x00BC + +enum ec_codec_i2s_rx_subcmd { + EC_CODEC_I2S_RX_ENABLE = 0x0, + EC_CODEC_I2S_RX_DISABLE = 0x1, + EC_CODEC_I2S_RX_SET_GAIN = 0x2, + EC_CODEC_I2S_RX_GET_GAIN = 0x3, + EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH = 0x4, + EC_CODEC_I2S_RX_SET_DAIFMT = 0x5, + EC_CODEC_I2S_RX_SET_BCLK = 0x6, + EC_CODEC_I2S_RX_SUBCMD_COUNT, };
-enum ec_sample_depth_value { - EC_CODEC_SAMPLE_DEPTH_16 = 0, - EC_CODEC_SAMPLE_DEPTH_24 = 1, +enum ec_codec_i2s_rx_sample_depth { + EC_CODEC_I2S_RX_SAMPLE_DEPTH_16 = 0x0, + EC_CODEC_I2S_RX_SAMPLE_DEPTH_24 = 0x1, + EC_CODEC_I2S_RX_SAMPLE_DEPTH_COUNT, };
-enum ec_i2s_config { - EC_DAI_FMT_I2S = 0, - EC_DAI_FMT_RIGHT_J = 1, - EC_DAI_FMT_LEFT_J = 2, - EC_DAI_FMT_PCM_A = 3, - EC_DAI_FMT_PCM_B = 4, - EC_DAI_FMT_PCM_TDM = 5, +enum ec_codec_i2s_rx_daifmt { + EC_CODEC_I2S_RX_DAIFMT_I2S = 0x0, + EC_CODEC_I2S_RX_DAIFMT_RIGHT_J = 0x1, + EC_CODEC_I2S_RX_DAIFMT_LEFT_J = 0x2, + EC_CODEC_I2S_RX_DAIFMT_COUNT, };
-/* - * For subcommand EC_CODEC_GET_GAIN. - */ -struct __ec_align1 ec_codec_i2s_gain { +struct __ec_align1 ec_param_ec_codec_i2s_rx_set_sample_depth { + uint8_t depth; + uint8_t reserved[3]; +}; + +struct __ec_align1 ec_param_ec_codec_i2s_rx_set_gain { uint8_t left; uint8_t right; + uint8_t reserved[2]; };
-struct __ec_todo_unpacked ec_param_codec_i2s_tdm { - int16_t ch0_delay; /* 0 to 496 */ - int16_t ch1_delay; /* -1 to 496 */ - uint8_t adjacent_to_ch0; - uint8_t adjacent_to_ch1; +struct __ec_align1 ec_param_ec_codec_i2s_rx_set_daifmt { + uint8_t daifmt; + uint8_t reserved[3]; };
-struct __ec_todo_packed ec_param_codec_i2s { - /* enum ec_codec_i2s_subcmd */ - uint8_t cmd; - union { - /* - * EC_CODEC_SET_SAMPLE_DEPTH - * Value should be one of ec_sample_depth_value. - */ - uint8_t depth; - - /* - * EC_CODEC_SET_GAIN - * Value should be 0~43 for both channels. - */ - struct ec_codec_i2s_gain gain; - - /* - * EC_CODEC_I2S_ENABLE - * 1 to enable, 0 to disable. - */ - uint8_t i2s_enable; - - /* - * EC_CODEC_I2S_SET_CONFIG - * Value should be one of ec_i2s_config. - */ - uint8_t i2s_config; +struct __ec_align4 ec_param_ec_codec_i2s_rx_set_bclk { + uint32_t bclk; +};
- /* - * EC_CODEC_I2S_SET_TDM_CONFIG - * Value should be one of ec_i2s_config. - */ - struct ec_param_codec_i2s_tdm tdm_param; +struct __ec_align4 ec_param_ec_codec_i2s_rx { + uint8_t cmd; /* enum ec_codec_i2s_rx_subcmd */ + uint8_t reserved[3];
- /* - * EC_CODEC_I2S_SET_BCLK - */ - uint32_t bclk; + union { + struct ec_param_ec_codec_i2s_rx_set_sample_depth + set_sample_depth_param; + struct ec_param_ec_codec_i2s_rx_set_gain + set_gain_param; + struct ec_param_ec_codec_i2s_rx_set_daifmt + set_daifmt_param; + struct ec_param_ec_codec_i2s_rx_set_bclk + set_bclk_param; }; };
+struct __ec_align1 ec_response_ec_codec_i2s_rx_get_gain { + uint8_t left; + uint8_t right; +};
/*****************************************************************************/ /* System commands */ diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index 3c1bd24a1057..179fa77291cd 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Driver for ChromeOS Embedded Controller codec. + * Copyright 2019 Google, Inc. + * + * ChromeOS Embedded Controller codec driver. * * This driver uses the cros-ec interface to communicate with the ChromeOS * EC for audio function. @@ -18,403 +20,297 @@ #include <sound/soc.h> #include <sound/tlv.h>
-#define DRV_NAME "cros-ec-codec" - -/** - * struct cros_ec_codec_data - ChromeOS EC codec driver data. - * @dev: Device structure used in sysfs. - * @ec_device: cros_ec_device structure to talk to the physical device. - * @component: Pointer to the component. - * @max_dmic_gain: Maximum gain in dB supported by EC codec. - */ -struct cros_ec_codec_data { +struct cros_ec_codec_priv { struct device *dev; struct cros_ec_device *ec_device; - struct snd_soc_component *component; - unsigned int max_dmic_gain; };
-static const DECLARE_TLV_DB_SCALE(ec_mic_gain_tlv, 0, 100, 0); - -static int ec_command_get_gain(struct snd_soc_component *component, - struct ec_param_codec_i2s *param, - struct ec_codec_i2s_gain *resp) +static int 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) { - struct cros_ec_codec_data *codec_data = - snd_soc_component_get_drvdata(component); - struct cros_ec_device *ec_device = codec_data->ec_device; - u8 buffer[sizeof(struct cros_ec_command) + - max(sizeof(struct ec_param_codec_i2s), - sizeof(struct ec_codec_i2s_gain))]; - struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; int ret; + struct cros_ec_command *msg; + + msg = kmalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL); + if (!msg) + return -ENOMEM;
msg->version = 0; - msg->command = EC_CMD_CODEC_I2S; - msg->outsize = sizeof(struct ec_param_codec_i2s); - msg->insize = sizeof(struct ec_codec_i2s_gain); + msg->command = cmd; + msg->outsize = outsize; + msg->insize = insize; + + if (outsize) + memcpy(msg->data, out, outsize);
- memcpy(msg->data, param, msg->outsize); + ret = cros_ec_cmd_xfer_status(ec_dev, msg); + if (ret < 0) + goto error;
- ret = cros_ec_cmd_xfer_status(ec_device, msg); - if (ret > 0) - memcpy(resp, msg->data, msg->insize); + if (insize) + memcpy(in, msg->data, insize);
+ ret = 0; +error: + kfree(msg); return ret; }
-/* - * Wrapper for EC command without response. - */ -static int ec_command_no_resp(struct snd_soc_component *component, - struct ec_param_codec_i2s *param) +static int dmic_get_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - struct cros_ec_codec_data *codec_data = + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component); - struct cros_ec_device *ec_device = codec_data->ec_device; - u8 buffer[sizeof(struct cros_ec_command) + - sizeof(struct ec_param_codec_i2s)]; - struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; - - msg->version = 0; - msg->command = EC_CMD_CODEC_I2S; - msg->outsize = sizeof(struct ec_param_codec_i2s); - msg->insize = 0; - - memcpy(msg->data, param, msg->outsize); - - return cros_ec_cmd_xfer_status(ec_device, msg); -} - -static int set_i2s_config(struct snd_soc_component *component, - enum ec_i2s_config i2s_config) -{ - struct ec_param_codec_i2s param; + struct ec_param_ec_codec_i2s_rx p; + struct ec_response_ec_codec_i2s_rx_get_gain r; + int ret;
- dev_dbg(component->dev, "%s set I2S format to %u\n", __func__, - i2s_config); + p.cmd = EC_CODEC_I2S_RX_GET_GAIN; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret < 0) + return ret;
- param.cmd = EC_CODEC_I2S_SET_CONFIG; - param.i2s_config = i2s_config; + ucontrol->value.integer.value[0] = r.left; + ucontrol->value.integer.value[1] = r.right;
- return ec_command_no_resp(component, ¶m); + return 0; }
-static int cros_ec_i2s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +static int dmic_put_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = dai->component; - enum ec_i2s_config i2s_config; - - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - break; - default: - return -EINVAL; - } - - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - break; - default: - return -EINVAL; - } - - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - i2s_config = EC_DAI_FMT_I2S; - break; - - case SND_SOC_DAIFMT_RIGHT_J: - i2s_config = EC_DAI_FMT_RIGHT_J; - break; - - case SND_SOC_DAIFMT_LEFT_J: - i2s_config = EC_DAI_FMT_LEFT_J; - break; - - case SND_SOC_DAIFMT_DSP_A: - i2s_config = EC_DAI_FMT_PCM_A; - break; - - case SND_SOC_DAIFMT_DSP_B: - i2s_config = EC_DAI_FMT_PCM_B; - break; + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct soc_mixer_control *control = + (struct soc_mixer_control *)kcontrol->private_value; + int max_dmic_gain = control->max; + int left = ucontrol->value.integer.value[0]; + int right = ucontrol->value.integer.value[1]; + struct ec_param_ec_codec_i2s_rx p;
- default: + if (left > max_dmic_gain || right > max_dmic_gain) return -EINVAL; - }
- return set_i2s_config(component, i2s_config); -} - -static int set_i2s_sample_depth(struct snd_soc_component *component, - enum ec_sample_depth_value depth) -{ - struct ec_param_codec_i2s param; - - dev_dbg(component->dev, "%s set depth to %u\n", __func__, depth); - - param.cmd = EC_CODEC_SET_SAMPLE_DEPTH; - param.depth = depth; + dev_dbg(component->dev, "set mic gain to %u, %u\n", left, right);
- return ec_command_no_resp(component, ¶m); + p.cmd = EC_CODEC_I2S_RX_SET_GAIN; + p.set_gain_param.left = left; + p.set_gain_param.right = right; + return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), NULL, 0); }
-static int set_i2s_bclk(struct snd_soc_component *component, uint32_t bclk) -{ - struct ec_param_codec_i2s param; - - dev_dbg(component->dev, "%s set i2s bclk to %u\n", __func__, bclk); +static const DECLARE_TLV_DB_SCALE(dmic_gain_tlv, 0, 100, 0);
- param.cmd = EC_CODEC_I2S_SET_BCLK; - param.bclk = bclk; +enum { + DMIC_CTL_GAIN = 0, +};
- return ec_command_no_resp(component, ¶m); -} +static struct snd_kcontrol_new dmic_controls[] = { + [DMIC_CTL_GAIN] = + SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM, SND_SOC_NOPM, + 0, 0, 0, dmic_get_gain, dmic_put_gain, + dmic_gain_tlv), +};
-static int cros_ec_i2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) +static int i2s_rx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; - unsigned int rate, bclk; + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct ec_param_ec_codec_i2s_rx p; + enum ec_codec_i2s_rx_sample_depth depth; int ret;
- rate = params_rate(params); - if (rate != 48000) + if (params_rate(params) != 48000) return -EINVAL;
switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: - ret = set_i2s_sample_depth(component, EC_CODEC_SAMPLE_DEPTH_16); + depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_16; break; case SNDRV_PCM_FORMAT_S24_LE: - ret = set_i2s_sample_depth(component, EC_CODEC_SAMPLE_DEPTH_24); + depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_24; break; default: return -EINVAL; } - if (ret < 0) - return ret; - - bclk = snd_soc_params_to_bclk(params); - return set_i2s_bclk(component, bclk); -}
-static const struct snd_soc_dai_ops cros_ec_i2s_dai_ops = { - .hw_params = cros_ec_i2s_hw_params, - .set_fmt = cros_ec_i2s_set_dai_fmt, -}; + dev_dbg(component->dev, "set depth to %u\n", depth);
-static struct snd_soc_dai_driver cros_ec_dai[] = { - { - .name = "cros_ec_codec I2S", - .id = 0, - .capture = { - .stream_name = "I2S Capture", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - }, - .ops = &cros_ec_i2s_dai_ops, - } -}; - -static int get_ec_mic_gain(struct snd_soc_component *component, - u8 *left, u8 *right) -{ - struct ec_param_codec_i2s param; - struct ec_codec_i2s_gain resp; - int ret; - - param.cmd = EC_CODEC_GET_GAIN; - - ret = ec_command_get_gain(component, ¶m, &resp); + p.cmd = EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH; + p.set_sample_depth_param.depth = depth; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), NULL, 0); if (ret < 0) return ret;
- *left = resp.left; - *right = resp.right; - - return 0; -} - -static int mic_gain_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = - snd_soc_kcontrol_component(kcontrol); - u8 left, right; - int ret; - - ret = get_ec_mic_gain(component, &left, &right); - if (ret) - return ret; - - ucontrol->value.integer.value[0] = left; - ucontrol->value.integer.value[1] = right; - - return 0; -} - -static int set_ec_mic_gain(struct snd_soc_component *component, - u8 left, u8 right) -{ - struct ec_param_codec_i2s param; - - dev_dbg(component->dev, "%s set mic gain to %u, %u\n", - __func__, left, right); + dev_dbg(component->dev, "set bclk to %u\n", + snd_soc_params_to_bclk(params));
- param.cmd = EC_CODEC_SET_GAIN; - param.gain.left = left; - param.gain.right = right; - - return ec_command_no_resp(component, ¶m); + p.cmd = EC_CODEC_I2S_RX_SET_BCLK; + p.set_bclk_param.bclk = snd_soc_params_to_bclk(params); + return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), NULL, 0); }
-static int mic_gain_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { - struct snd_soc_component *component = - snd_soc_kcontrol_component(kcontrol); - struct cros_ec_codec_data *codec_data = + struct snd_soc_component *component = dai->component; + struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component); - int left = ucontrol->value.integer.value[0]; - int right = ucontrol->value.integer.value[1]; - unsigned int max_dmic_gain = codec_data->max_dmic_gain; + struct ec_param_ec_codec_i2s_rx p; + enum ec_codec_i2s_rx_daifmt daifmt;
- if (left > max_dmic_gain || right > max_dmic_gain) + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: return -EINVAL; + }
- return set_ec_mic_gain(component, (u8)left, (u8)right); -} - -static struct snd_kcontrol_new mic_gain_control = - SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM, SND_SOC_NOPM, 0, 0, 0, - mic_gain_get, mic_gain_put, ec_mic_gain_tlv); - -static int enable_i2s(struct snd_soc_component *component, int enable) -{ - struct ec_param_codec_i2s param; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + }
- dev_dbg(component->dev, "%s set i2s to %u\n", __func__, enable); + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + daifmt = EC_CODEC_I2S_RX_DAIFMT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + daifmt = EC_CODEC_I2S_RX_DAIFMT_RIGHT_J; + break; + case SND_SOC_DAIFMT_LEFT_J: + daifmt = EC_CODEC_I2S_RX_DAIFMT_LEFT_J; + break; + default: + return -EINVAL; + }
- param.cmd = EC_CODEC_I2S_ENABLE; - param.i2s_enable = enable; + dev_dbg(component->dev, "set format to %u\n", daifmt);
- return ec_command_no_resp(component, ¶m); + p.cmd = EC_CODEC_I2S_RX_SET_DAIFMT; + p.set_daifmt_param.daifmt = daifmt; + return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), NULL, 0); }
-static int cros_ec_i2s_enable_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +static const struct snd_soc_dai_ops i2s_rx_dai_ops = { + .hw_params = i2s_rx_hw_params, + .set_fmt = i2s_rx_set_fmt, +}; + +static int i2s_rx_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct ec_param_ec_codec_i2s_rx p;
switch (event) { case SND_SOC_DAPM_PRE_PMU: - dev_dbg(component->dev, - "%s got SND_SOC_DAPM_PRE_PMU event\n", __func__); - return enable_i2s(component, 1); - + dev_dbg(component->dev, "enable I2S RX\n"); + p.cmd = EC_CODEC_I2S_RX_ENABLE; + break; case SND_SOC_DAPM_PRE_PMD: - dev_dbg(component->dev, - "%s got SND_SOC_DAPM_PRE_PMD event\n", __func__); - return enable_i2s(component, 0); + dev_dbg(component->dev, "disable I2S RX\n"); + p.cmd = EC_CODEC_I2S_RX_DISABLE; + break; + default: + return 0; }
- return 0; + return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), NULL, 0); }
-/* - * The goal of this DAPM route is to turn on/off I2S using EC - * host command when capture stream is started/stopped. - */ -static const struct snd_soc_dapm_widget cros_ec_codec_dapm_widgets[] = { +static struct snd_soc_dapm_widget i2s_rx_dapm_widgets[] = { SND_SOC_DAPM_INPUT("DMIC"), - - /* - * Control EC to enable/disable I2S. - */ - SND_SOC_DAPM_SUPPLY("I2S Enable", SND_SOC_NOPM, - 0, 0, cros_ec_i2s_enable_event, + SND_SOC_DAPM_SUPPLY("I2S RX Enable", SND_SOC_NOPM, 0, 0, i2s_rx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_AIF_OUT("I2S RX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0), +};
- SND_SOC_DAPM_AIF_OUT("I2STX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0), +static struct snd_soc_dapm_route i2s_rx_dapm_routes[] = { + {"I2S RX", NULL, "DMIC"}, + {"I2S RX", NULL, "I2S RX Enable"}, };
-static const struct snd_soc_dapm_route cros_ec_codec_dapm_routes[] = { - { "I2STX", NULL, "DMIC" }, - { "I2STX", NULL, "I2S Enable" }, +static struct snd_soc_dai_driver i2s_rx_dai_driver = { + .name = "EC Codec I2S RX", + .capture = { + .stream_name = "I2S Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &i2s_rx_dai_ops, };
-/* - * Read maximum gain from device property and set it to mixer control. - */ -static int cros_ec_set_gain_range(struct device *dev) +static int i2s_rx_probe(struct snd_soc_component *component) { + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct device *dev = priv->dev; + int ret, val; struct soc_mixer_control *control; - struct cros_ec_codec_data *codec_data = dev_get_drvdata(dev); - int rc;
- rc = device_property_read_u32(dev, "max-dmic-gain", - &codec_data->max_dmic_gain); - if (rc) - return rc; + ret = device_property_read_u32(dev, "max-dmic-gain", &val); + if (ret) { + dev_err(dev, "Failed to read 'max-dmic-gain'\n"); + return ret; + }
control = (struct soc_mixer_control *) - mic_gain_control.private_value; - control->max = codec_data->max_dmic_gain; - control->platform_max = codec_data->max_dmic_gain; + dmic_controls[DMIC_CTL_GAIN].private_value; + control->max = val; + control->platform_max = val;
- return 0; -} - -static int cros_ec_codec_probe(struct snd_soc_component *component) -{ - int rc; - - struct cros_ec_codec_data *codec_data = - snd_soc_component_get_drvdata(component); - - rc = cros_ec_set_gain_range(codec_data->dev); - if (rc) - return rc; - - return snd_soc_add_component_controls(component, &mic_gain_control, 1); + return snd_soc_add_component_controls(component, + &dmic_controls[DMIC_CTL_GAIN], 1); }
-static const struct snd_soc_component_driver cros_ec_component_driver = { - .probe = cros_ec_codec_probe, - .dapm_widgets = cros_ec_codec_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(cros_ec_codec_dapm_widgets), - .dapm_routes = cros_ec_codec_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(cros_ec_codec_dapm_routes), +static const struct snd_soc_component_driver i2s_rx_component_driver = { + .probe = i2s_rx_probe, + .dapm_widgets = i2s_rx_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(i2s_rx_dapm_widgets), + .dapm_routes = i2s_rx_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(i2s_rx_dapm_routes), };
-/* - * Platform device and platform driver fro cros-ec-codec. - */ -static int cros_ec_codec_platform_probe(struct platform_device *pd) +static int cros_ec_codec_platform_probe(struct platform_device *pdev) { - struct device *dev = &pd->dev; - struct cros_ec_device *ec_device = dev_get_drvdata(pd->dev.parent); - struct cros_ec_codec_data *codec_data; + struct device *dev = &pdev->dev; + struct cros_ec_device *ec_device = dev_get_drvdata(pdev->dev.parent); + struct cros_ec_codec_priv *priv;
- codec_data = devm_kzalloc(dev, sizeof(struct cros_ec_codec_data), - GFP_KERNEL); - if (!codec_data) + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) return -ENOMEM;
- codec_data->dev = dev; - codec_data->ec_device = ec_device; + priv->dev = dev; + priv->ec_device = ec_device;
- platform_set_drvdata(pd, codec_data); + platform_set_drvdata(pdev, priv);
- return devm_snd_soc_register_component(dev, &cros_ec_component_driver, - cros_ec_dai, ARRAY_SIZE(cros_ec_dai)); + return devm_snd_soc_register_component(dev, &i2s_rx_component_driver, + &i2s_rx_dai_driver, 1); }
#ifdef CONFIG_OF @@ -427,7 +323,7 @@ MODULE_DEVICE_TABLE(of, cros_ec_codec_of_match);
static struct platform_driver cros_ec_codec_platform_driver = { .driver = { - .name = DRV_NAME, + .name = "cros-ec-codec", .of_match_table = of_match_ptr(cros_ec_codec_of_match), }, .probe = cros_ec_codec_platform_probe, @@ -438,4 +334,4 @@ module_platform_driver(cros_ec_codec_platform_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("ChromeOS EC codec driver"); MODULE_AUTHOR("Cheng-Yi Chiang cychiang@chromium.org"); -MODULE_ALIAS("platform:" DRV_NAME); +MODULE_ALIAS("platform:cros-ec-codec");
Extract DMIC EC command from I2S RX. Setting and getting microphone gains is common features.
Signed-off-by: Tzung-Bi Shih tzungbi@google.com --- drivers/platform/chrome/cros_ec_trace.c | 1 + .../linux/platform_data/cros_ec_commands.h | 49 +++++++++++----- sound/soc/codecs/cros_ec_codec.c | 57 ++++++++++--------- 3 files changed, 68 insertions(+), 39 deletions(-)
diff --git a/drivers/platform/chrome/cros_ec_trace.c b/drivers/platform/chrome/cros_ec_trace.c index 901850004b2b..e73bb6a8b00e 100644 --- a/drivers/platform/chrome/cros_ec_trace.c +++ b/drivers/platform/chrome/cros_ec_trace.c @@ -98,6 +98,7 @@ TRACE_SYMBOL(EC_CMD_SB_READ_BLOCK), \ TRACE_SYMBOL(EC_CMD_SB_WRITE_BLOCK), \ TRACE_SYMBOL(EC_CMD_BATTERY_VENDOR_PARAM), \ + TRACE_SYMBOL(EC_CMD_EC_CODEC_DMIC), \ TRACE_SYMBOL(EC_CMD_EC_CODEC_I2S_RX), \ TRACE_SYMBOL(EC_CMD_REBOOT_EC), \ TRACE_SYMBOL(EC_CMD_GET_PANIC_INFO), \ diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 261ac83bd007..58e460c015ef 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -4466,18 +4466,48 @@ enum mkbp_cec_event {
/*****************************************************************************/
+/* Commands for DMIC on audio codec. */ +#define EC_CMD_EC_CODEC_DMIC 0x00BC + +enum ec_codec_dmic_subcmd { + EC_CODEC_DMIC_SET_GAIN = 0x0, + EC_CODEC_DMIC_GET_GAIN = 0x1, + EC_CODEC_DMIC_SUBCMD_COUNT, +}; + +struct __ec_align1 ec_param_ec_codec_dmic_set_gain { + uint8_t left; + uint8_t right; + uint8_t reserved[2]; +}; + +struct __ec_align4 ec_param_ec_codec_dmic { + uint8_t cmd; /* enum ec_codec_dmic_subcmd */ + uint8_t reserved[3]; + + union { + struct ec_param_ec_codec_dmic_set_gain + set_gain_param; + }; +}; + +struct __ec_align1 ec_response_ec_codec_dmic_get_gain { + uint8_t left; + uint8_t right; +}; + +/*****************************************************************************/ + /* Commands for I2S RX on audio codec. */
-#define EC_CMD_EC_CODEC_I2S_RX 0x00BC +#define EC_CMD_EC_CODEC_I2S_RX 0x00BD
enum ec_codec_i2s_rx_subcmd { EC_CODEC_I2S_RX_ENABLE = 0x0, EC_CODEC_I2S_RX_DISABLE = 0x1, - EC_CODEC_I2S_RX_SET_GAIN = 0x2, - EC_CODEC_I2S_RX_GET_GAIN = 0x3, - EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH = 0x4, - EC_CODEC_I2S_RX_SET_DAIFMT = 0x5, - EC_CODEC_I2S_RX_SET_BCLK = 0x6, + EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH = 0x2, + EC_CODEC_I2S_RX_SET_DAIFMT = 0x3, + EC_CODEC_I2S_RX_SET_BCLK = 0x4, EC_CODEC_I2S_RX_SUBCMD_COUNT, };
@@ -4521,8 +4551,6 @@ struct __ec_align4 ec_param_ec_codec_i2s_rx { union { struct ec_param_ec_codec_i2s_rx_set_sample_depth set_sample_depth_param; - struct ec_param_ec_codec_i2s_rx_set_gain - set_gain_param; struct ec_param_ec_codec_i2s_rx_set_daifmt set_daifmt_param; struct ec_param_ec_codec_i2s_rx_set_bclk @@ -4530,11 +4558,6 @@ struct __ec_align4 ec_param_ec_codec_i2s_rx { }; };
-struct __ec_align1 ec_response_ec_codec_i2s_rx_get_gain { - uint8_t left; - uint8_t right; -}; - /*****************************************************************************/ /* System commands */
diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index 179fa77291cd..c19c7fe42e2e 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -64,12 +64,12 @@ static int dmic_get_gain(struct snd_kcontrol *kcontrol, snd_soc_kcontrol_component(kcontrol); struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component); - struct ec_param_ec_codec_i2s_rx p; - struct ec_response_ec_codec_i2s_rx_get_gain r; + struct ec_param_ec_codec_dmic p; + struct ec_response_ec_codec_dmic_get_gain r; int ret;
- p.cmd = EC_CODEC_I2S_RX_GET_GAIN; - ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + p.cmd = EC_CODEC_DMIC_GET_GAIN; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, (uint8_t *)&p, sizeof(p), (uint8_t *)&r, sizeof(r)); if (ret < 0) @@ -93,17 +93,17 @@ static int dmic_put_gain(struct snd_kcontrol *kcontrol, int max_dmic_gain = control->max; int left = ucontrol->value.integer.value[0]; int right = ucontrol->value.integer.value[1]; - struct ec_param_ec_codec_i2s_rx p; + struct ec_param_ec_codec_dmic p;
if (left > max_dmic_gain || right > max_dmic_gain) return -EINVAL;
dev_dbg(component->dev, "set mic gain to %u, %u\n", left, right);
- p.cmd = EC_CODEC_I2S_RX_SET_GAIN; + p.cmd = EC_CODEC_DMIC_SET_GAIN; p.set_gain_param.left = left; p.set_gain_param.right = right; - return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, (uint8_t *)&p, sizeof(p), NULL, 0); }
@@ -120,6 +120,29 @@ static struct snd_kcontrol_new dmic_controls[] = { dmic_gain_tlv), };
+static int dmic_probe(struct snd_soc_component *component) +{ + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct device *dev = priv->dev; + int ret, val; + struct soc_mixer_control *control; + + ret = device_property_read_u32(dev, "max-dmic-gain", &val); + if (ret) { + dev_err(dev, "Failed to read 'max-dmic-gain'\n"); + return ret; + } + + control = (struct soc_mixer_control *) + dmic_controls[DMIC_CTL_GAIN].private_value; + control->max = val; + control->platform_max = val; + + return snd_soc_add_component_controls(component, + &dmic_controls[DMIC_CTL_GAIN], 1); +} + static int i2s_rx_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -265,25 +288,7 @@ static struct snd_soc_dai_driver i2s_rx_dai_driver = {
static int i2s_rx_probe(struct snd_soc_component *component) { - struct cros_ec_codec_priv *priv = - snd_soc_component_get_drvdata(component); - struct device *dev = priv->dev; - int ret, val; - struct soc_mixer_control *control; - - ret = device_property_read_u32(dev, "max-dmic-gain", &val); - if (ret) { - dev_err(dev, "Failed to read 'max-dmic-gain'\n"); - return ret; - } - - control = (struct soc_mixer_control *) - dmic_controls[DMIC_CTL_GAIN].private_value; - control->max = val; - control->platform_max = val; - - return snd_soc_add_component_controls(component, - &dmic_controls[DMIC_CTL_GAIN], 1); + return dmic_probe(component); }
static const struct snd_soc_component_driver i2s_rx_component_driver = {
Hi Tzung-Bi,
On Mon, Oct 14, 2019 at 06:20:15PM +0800, Tzung-Bi Shih wrote:
Extract DMIC EC command from I2S RX. Setting and getting microphone gains is common features.
Signed-off-by: Tzung-Bi Shih tzungbi@google.com
Acked-By: Benson Leung bleung@chromium.org
Thanks, Benson
drivers/platform/chrome/cros_ec_trace.c | 1 + .../linux/platform_data/cros_ec_commands.h | 49 +++++++++++----- sound/soc/codecs/cros_ec_codec.c | 57 ++++++++++--------- 3 files changed, 68 insertions(+), 39 deletions(-)
diff --git a/drivers/platform/chrome/cros_ec_trace.c b/drivers/platform/chrome/cros_ec_trace.c index 901850004b2b..e73bb6a8b00e 100644 --- a/drivers/platform/chrome/cros_ec_trace.c +++ b/drivers/platform/chrome/cros_ec_trace.c @@ -98,6 +98,7 @@ TRACE_SYMBOL(EC_CMD_SB_READ_BLOCK), \ TRACE_SYMBOL(EC_CMD_SB_WRITE_BLOCK), \ TRACE_SYMBOL(EC_CMD_BATTERY_VENDOR_PARAM), \
- TRACE_SYMBOL(EC_CMD_EC_CODEC_DMIC), \ TRACE_SYMBOL(EC_CMD_EC_CODEC_I2S_RX), \ TRACE_SYMBOL(EC_CMD_REBOOT_EC), \ TRACE_SYMBOL(EC_CMD_GET_PANIC_INFO), \
diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 261ac83bd007..58e460c015ef 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -4466,18 +4466,48 @@ enum mkbp_cec_event {
/*****************************************************************************/
+/* Commands for DMIC on audio codec. */ +#define EC_CMD_EC_CODEC_DMIC 0x00BC
+enum ec_codec_dmic_subcmd {
- EC_CODEC_DMIC_SET_GAIN = 0x0,
- EC_CODEC_DMIC_GET_GAIN = 0x1,
- EC_CODEC_DMIC_SUBCMD_COUNT,
+};
+struct __ec_align1 ec_param_ec_codec_dmic_set_gain {
- uint8_t left;
- uint8_t right;
- uint8_t reserved[2];
+};
+struct __ec_align4 ec_param_ec_codec_dmic {
- uint8_t cmd; /* enum ec_codec_dmic_subcmd */
- uint8_t reserved[3];
- union {
struct ec_param_ec_codec_dmic_set_gain
set_gain_param;
- };
+};
+struct __ec_align1 ec_response_ec_codec_dmic_get_gain {
- uint8_t left;
- uint8_t right;
+};
+/*****************************************************************************/
/* Commands for I2S RX on audio codec. */
-#define EC_CMD_EC_CODEC_I2S_RX 0x00BC +#define EC_CMD_EC_CODEC_I2S_RX 0x00BD
enum ec_codec_i2s_rx_subcmd { EC_CODEC_I2S_RX_ENABLE = 0x0, EC_CODEC_I2S_RX_DISABLE = 0x1,
- EC_CODEC_I2S_RX_SET_GAIN = 0x2,
- EC_CODEC_I2S_RX_GET_GAIN = 0x3,
- EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH = 0x4,
- EC_CODEC_I2S_RX_SET_DAIFMT = 0x5,
- EC_CODEC_I2S_RX_SET_BCLK = 0x6,
- EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH = 0x2,
- EC_CODEC_I2S_RX_SET_DAIFMT = 0x3,
- EC_CODEC_I2S_RX_SET_BCLK = 0x4, EC_CODEC_I2S_RX_SUBCMD_COUNT,
};
@@ -4521,8 +4551,6 @@ struct __ec_align4 ec_param_ec_codec_i2s_rx { union { struct ec_param_ec_codec_i2s_rx_set_sample_depth set_sample_depth_param;
struct ec_param_ec_codec_i2s_rx_set_gain
struct ec_param_ec_codec_i2s_rx_set_daifmt set_daifmt_param; struct ec_param_ec_codec_i2s_rx_set_bclkset_gain_param;
@@ -4530,11 +4558,6 @@ struct __ec_align4 ec_param_ec_codec_i2s_rx { }; };
-struct __ec_align1 ec_response_ec_codec_i2s_rx_get_gain {
- uint8_t left;
- uint8_t right;
-};
/*****************************************************************************/ /* System commands */
diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index 179fa77291cd..c19c7fe42e2e 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -64,12 +64,12 @@ static int dmic_get_gain(struct snd_kcontrol *kcontrol, snd_soc_kcontrol_component(kcontrol); struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component);
- struct ec_param_ec_codec_i2s_rx p;
- struct ec_response_ec_codec_i2s_rx_get_gain r;
- struct ec_param_ec_codec_dmic p;
- struct ec_response_ec_codec_dmic_get_gain r; int ret;
- p.cmd = EC_CODEC_I2S_RX_GET_GAIN;
- ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
- p.cmd = EC_CODEC_DMIC_GET_GAIN;
- ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, (uint8_t *)&p, sizeof(p), (uint8_t *)&r, sizeof(r)); if (ret < 0)
@@ -93,17 +93,17 @@ static int dmic_put_gain(struct snd_kcontrol *kcontrol, int max_dmic_gain = control->max; int left = ucontrol->value.integer.value[0]; int right = ucontrol->value.integer.value[1];
- struct ec_param_ec_codec_i2s_rx p;
struct ec_param_ec_codec_dmic p;
if (left > max_dmic_gain || right > max_dmic_gain) return -EINVAL;
dev_dbg(component->dev, "set mic gain to %u, %u\n", left, right);
- p.cmd = EC_CODEC_I2S_RX_SET_GAIN;
- p.cmd = EC_CODEC_DMIC_SET_GAIN; p.set_gain_param.left = left; p.set_gain_param.right = right;
- return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
- return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, (uint8_t *)&p, sizeof(p), NULL, 0);
}
@@ -120,6 +120,29 @@ static struct snd_kcontrol_new dmic_controls[] = { dmic_gain_tlv), };
+static int dmic_probe(struct snd_soc_component *component) +{
- struct cros_ec_codec_priv *priv =
snd_soc_component_get_drvdata(component);
- struct device *dev = priv->dev;
- int ret, val;
- struct soc_mixer_control *control;
- ret = device_property_read_u32(dev, "max-dmic-gain", &val);
- if (ret) {
dev_err(dev, "Failed to read 'max-dmic-gain'\n");
return ret;
- }
- control = (struct soc_mixer_control *)
dmic_controls[DMIC_CTL_GAIN].private_value;
- control->max = val;
- control->platform_max = val;
- return snd_soc_add_component_controls(component,
&dmic_controls[DMIC_CTL_GAIN], 1);
+}
static int i2s_rx_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -265,25 +288,7 @@ static struct snd_soc_dai_driver i2s_rx_dai_driver = {
static int i2s_rx_probe(struct snd_soc_component *component) {
- struct cros_ec_codec_priv *priv =
snd_soc_component_get_drvdata(component);
- struct device *dev = priv->dev;
- int ret, val;
- struct soc_mixer_control *control;
- ret = device_property_read_u32(dev, "max-dmic-gain", &val);
- if (ret) {
dev_err(dev, "Failed to read 'max-dmic-gain'\n");
return ret;
- }
- control = (struct soc_mixer_control *)
dmic_controls[DMIC_CTL_GAIN].private_value;
- control->max = val;
- control->platform_max = val;
- return snd_soc_add_component_controls(component,
&dmic_controls[DMIC_CTL_GAIN], 1);
- return dmic_probe(component);
}
static const struct snd_soc_component_driver i2s_rx_component_driver = {
2.23.0.700.g56cf767bdb-goog
The patch
ASoC: cros_ec_codec: extract DMIC EC command from I2S RX
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-5.5
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 8f731d4c92c2ef9434d4d7f84882c6429754164b Mon Sep 17 00:00:00 2001
From: Tzung-Bi Shih tzungbi@google.com Date: Mon, 14 Oct 2019 18:20:15 +0800 Subject: [PATCH] ASoC: cros_ec_codec: extract DMIC EC command from I2S RX
Extract DMIC EC command from I2S RX. Setting and getting microphone gains is common features.
Signed-off-by: Tzung-Bi Shih tzungbi@google.com Acked-By: Benson Leung bleung@chromium.org Link: https://lore.kernel.org/r/20191014180059.03.I93d9c65964f3c30f85a36d228e31150... Signed-off-by: Mark Brown broonie@kernel.org --- drivers/platform/chrome/cros_ec_trace.c | 1 + .../linux/platform_data/cros_ec_commands.h | 49 +++++++++++----- sound/soc/codecs/cros_ec_codec.c | 57 ++++++++++--------- 3 files changed, 68 insertions(+), 39 deletions(-)
diff --git a/drivers/platform/chrome/cros_ec_trace.c b/drivers/platform/chrome/cros_ec_trace.c index 901850004b2b..e73bb6a8b00e 100644 --- a/drivers/platform/chrome/cros_ec_trace.c +++ b/drivers/platform/chrome/cros_ec_trace.c @@ -98,6 +98,7 @@ TRACE_SYMBOL(EC_CMD_SB_READ_BLOCK), \ TRACE_SYMBOL(EC_CMD_SB_WRITE_BLOCK), \ TRACE_SYMBOL(EC_CMD_BATTERY_VENDOR_PARAM), \ + TRACE_SYMBOL(EC_CMD_EC_CODEC_DMIC), \ TRACE_SYMBOL(EC_CMD_EC_CODEC_I2S_RX), \ TRACE_SYMBOL(EC_CMD_REBOOT_EC), \ TRACE_SYMBOL(EC_CMD_GET_PANIC_INFO), \ diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 261ac83bd007..58e460c015ef 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -4466,18 +4466,48 @@ enum mkbp_cec_event {
/*****************************************************************************/
+/* Commands for DMIC on audio codec. */ +#define EC_CMD_EC_CODEC_DMIC 0x00BC + +enum ec_codec_dmic_subcmd { + EC_CODEC_DMIC_SET_GAIN = 0x0, + EC_CODEC_DMIC_GET_GAIN = 0x1, + EC_CODEC_DMIC_SUBCMD_COUNT, +}; + +struct __ec_align1 ec_param_ec_codec_dmic_set_gain { + uint8_t left; + uint8_t right; + uint8_t reserved[2]; +}; + +struct __ec_align4 ec_param_ec_codec_dmic { + uint8_t cmd; /* enum ec_codec_dmic_subcmd */ + uint8_t reserved[3]; + + union { + struct ec_param_ec_codec_dmic_set_gain + set_gain_param; + }; +}; + +struct __ec_align1 ec_response_ec_codec_dmic_get_gain { + uint8_t left; + uint8_t right; +}; + +/*****************************************************************************/ + /* Commands for I2S RX on audio codec. */
-#define EC_CMD_EC_CODEC_I2S_RX 0x00BC +#define EC_CMD_EC_CODEC_I2S_RX 0x00BD
enum ec_codec_i2s_rx_subcmd { EC_CODEC_I2S_RX_ENABLE = 0x0, EC_CODEC_I2S_RX_DISABLE = 0x1, - EC_CODEC_I2S_RX_SET_GAIN = 0x2, - EC_CODEC_I2S_RX_GET_GAIN = 0x3, - EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH = 0x4, - EC_CODEC_I2S_RX_SET_DAIFMT = 0x5, - EC_CODEC_I2S_RX_SET_BCLK = 0x6, + EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH = 0x2, + EC_CODEC_I2S_RX_SET_DAIFMT = 0x3, + EC_CODEC_I2S_RX_SET_BCLK = 0x4, EC_CODEC_I2S_RX_SUBCMD_COUNT, };
@@ -4521,8 +4551,6 @@ struct __ec_align4 ec_param_ec_codec_i2s_rx { union { struct ec_param_ec_codec_i2s_rx_set_sample_depth set_sample_depth_param; - struct ec_param_ec_codec_i2s_rx_set_gain - set_gain_param; struct ec_param_ec_codec_i2s_rx_set_daifmt set_daifmt_param; struct ec_param_ec_codec_i2s_rx_set_bclk @@ -4530,11 +4558,6 @@ struct __ec_align4 ec_param_ec_codec_i2s_rx { }; };
-struct __ec_align1 ec_response_ec_codec_i2s_rx_get_gain { - uint8_t left; - uint8_t right; -}; - /*****************************************************************************/ /* System commands */
diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index 179fa77291cd..c19c7fe42e2e 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -64,12 +64,12 @@ static int dmic_get_gain(struct snd_kcontrol *kcontrol, snd_soc_kcontrol_component(kcontrol); struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component); - struct ec_param_ec_codec_i2s_rx p; - struct ec_response_ec_codec_i2s_rx_get_gain r; + struct ec_param_ec_codec_dmic p; + struct ec_response_ec_codec_dmic_get_gain r; int ret;
- p.cmd = EC_CODEC_I2S_RX_GET_GAIN; - ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + p.cmd = EC_CODEC_DMIC_GET_GAIN; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, (uint8_t *)&p, sizeof(p), (uint8_t *)&r, sizeof(r)); if (ret < 0) @@ -93,17 +93,17 @@ static int dmic_put_gain(struct snd_kcontrol *kcontrol, int max_dmic_gain = control->max; int left = ucontrol->value.integer.value[0]; int right = ucontrol->value.integer.value[1]; - struct ec_param_ec_codec_i2s_rx p; + struct ec_param_ec_codec_dmic p;
if (left > max_dmic_gain || right > max_dmic_gain) return -EINVAL;
dev_dbg(component->dev, "set mic gain to %u, %u\n", left, right);
- p.cmd = EC_CODEC_I2S_RX_SET_GAIN; + p.cmd = EC_CODEC_DMIC_SET_GAIN; p.set_gain_param.left = left; p.set_gain_param.right = right; - return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, (uint8_t *)&p, sizeof(p), NULL, 0); }
@@ -120,6 +120,29 @@ static struct snd_kcontrol_new dmic_controls[] = { dmic_gain_tlv), };
+static int dmic_probe(struct snd_soc_component *component) +{ + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct device *dev = priv->dev; + int ret, val; + struct soc_mixer_control *control; + + ret = device_property_read_u32(dev, "max-dmic-gain", &val); + if (ret) { + dev_err(dev, "Failed to read 'max-dmic-gain'\n"); + return ret; + } + + control = (struct soc_mixer_control *) + dmic_controls[DMIC_CTL_GAIN].private_value; + control->max = val; + control->platform_max = val; + + return snd_soc_add_component_controls(component, + &dmic_controls[DMIC_CTL_GAIN], 1); +} + static int i2s_rx_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -265,25 +288,7 @@ static struct snd_soc_dai_driver i2s_rx_dai_driver = {
static int i2s_rx_probe(struct snd_soc_component *component) { - struct cros_ec_codec_priv *priv = - snd_soc_component_get_drvdata(component); - struct device *dev = priv->dev; - int ret, val; - struct soc_mixer_control *control; - - ret = device_property_read_u32(dev, "max-dmic-gain", &val); - if (ret) { - dev_err(dev, "Failed to read 'max-dmic-gain'\n"); - return ret; - } - - control = (struct soc_mixer_control *) - dmic_controls[DMIC_CTL_GAIN].private_value; - control->max = val; - control->platform_max = val; - - return snd_soc_add_component_controls(component, - &dmic_controls[DMIC_CTL_GAIN], 1); + return dmic_probe(component); }
static const struct snd_soc_component_driver i2s_rx_component_driver = {
Add the following common commands: - GET_CAPABILITIES - GET_SHM_ADDR - SET_SHM_ADDR
Signed-off-by: Tzung-Bi Shih tzungbi@google.com --- drivers/platform/chrome/cros_ec_trace.c | 1 + .../linux/platform_data/cros_ec_commands.h | 64 ++++++++++++++++++- 2 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/drivers/platform/chrome/cros_ec_trace.c b/drivers/platform/chrome/cros_ec_trace.c index e73bb6a8b00e..2ea0d4e0d54d 100644 --- a/drivers/platform/chrome/cros_ec_trace.c +++ b/drivers/platform/chrome/cros_ec_trace.c @@ -98,6 +98,7 @@ TRACE_SYMBOL(EC_CMD_SB_READ_BLOCK), \ TRACE_SYMBOL(EC_CMD_SB_WRITE_BLOCK), \ TRACE_SYMBOL(EC_CMD_BATTERY_VENDOR_PARAM), \ + TRACE_SYMBOL(EC_CMD_EC_CODEC), \ TRACE_SYMBOL(EC_CMD_EC_CODEC_DMIC), \ TRACE_SYMBOL(EC_CMD_EC_CODEC_I2S_RX), \ TRACE_SYMBOL(EC_CMD_REBOOT_EC), \ diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 58e460c015ef..3ca0fa9e92a7 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -4466,8 +4466,68 @@ enum mkbp_cec_event {
/*****************************************************************************/
+/* Commands for audio codec. */ +#define EC_CMD_EC_CODEC 0x00BC + +enum ec_codec_subcmd { + EC_CODEC_GET_CAPABILITIES = 0x0, + EC_CODEC_GET_SHM_ADDR = 0x1, + EC_CODEC_SET_SHM_ADDR = 0x2, + EC_CODEC_SUBCMD_COUNT, +}; + +enum ec_codec_cap { + EC_CODEC_CAP_LAST = 32, +}; + +enum ec_codec_shm_id { + EC_CODEC_SHM_ID_LAST, +}; + +enum ec_codec_shm_type { + EC_CODEC_SHM_TYPE_EC_RAM = 0x0, + EC_CODEC_SHM_TYPE_SYSTEM_RAM = 0x1, +}; + +struct __ec_align1 ec_param_ec_codec_get_shm_addr { + uint8_t shm_id; + uint8_t reserved[3]; +}; + +struct __ec_align4 ec_param_ec_codec_set_shm_addr { + uint64_t phys_addr; + uint32_t len; + uint8_t shm_id; + uint8_t reserved[3]; +}; + +struct __ec_align4 ec_param_ec_codec { + uint8_t cmd; /* enum ec_codec_subcmd */ + uint8_t reserved[3]; + + union { + struct ec_param_ec_codec_get_shm_addr + get_shm_addr_param; + struct ec_param_ec_codec_set_shm_addr + set_shm_addr_param; + }; +}; + +struct __ec_align4 ec_response_ec_codec_get_capabilities { + uint32_t capabilities; +}; + +struct __ec_align4 ec_response_ec_codec_get_shm_addr { + uint64_t phys_addr; + uint32_t len; + uint8_t type; + uint8_t reserved[3]; +}; + +/*****************************************************************************/ + /* Commands for DMIC on audio codec. */ -#define EC_CMD_EC_CODEC_DMIC 0x00BC +#define EC_CMD_EC_CODEC_DMIC 0x00BD
enum ec_codec_dmic_subcmd { EC_CODEC_DMIC_SET_GAIN = 0x0, @@ -4500,7 +4560,7 @@ struct __ec_align1 ec_response_ec_codec_dmic_get_gain {
/* Commands for I2S RX on audio codec. */
-#define EC_CMD_EC_CODEC_I2S_RX 0x00BD +#define EC_CMD_EC_CODEC_I2S_RX 0x00BE
enum ec_codec_i2s_rx_subcmd { EC_CODEC_I2S_RX_ENABLE = 0x0,
Hi Tzung-Bi,
On Mon, Oct 14, 2019 at 06:20:16PM +0800, Tzung-Bi Shih wrote:
Add the following common commands:
- GET_CAPABILITIES
- GET_SHM_ADDR
- SET_SHM_ADDR
Signed-off-by: Tzung-Bi Shih tzungbi@google.com
Acked-By: Benson Leung bleung@chromium.org
Thanks, Benson
drivers/platform/chrome/cros_ec_trace.c | 1 + .../linux/platform_data/cros_ec_commands.h | 64 ++++++++++++++++++- 2 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/drivers/platform/chrome/cros_ec_trace.c b/drivers/platform/chrome/cros_ec_trace.c index e73bb6a8b00e..2ea0d4e0d54d 100644 --- a/drivers/platform/chrome/cros_ec_trace.c +++ b/drivers/platform/chrome/cros_ec_trace.c @@ -98,6 +98,7 @@ TRACE_SYMBOL(EC_CMD_SB_READ_BLOCK), \ TRACE_SYMBOL(EC_CMD_SB_WRITE_BLOCK), \ TRACE_SYMBOL(EC_CMD_BATTERY_VENDOR_PARAM), \
- TRACE_SYMBOL(EC_CMD_EC_CODEC), \ TRACE_SYMBOL(EC_CMD_EC_CODEC_DMIC), \ TRACE_SYMBOL(EC_CMD_EC_CODEC_I2S_RX), \ TRACE_SYMBOL(EC_CMD_REBOOT_EC), \
diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 58e460c015ef..3ca0fa9e92a7 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -4466,8 +4466,68 @@ enum mkbp_cec_event {
/*****************************************************************************/
+/* Commands for audio codec. */ +#define EC_CMD_EC_CODEC 0x00BC
+enum ec_codec_subcmd {
- EC_CODEC_GET_CAPABILITIES = 0x0,
- EC_CODEC_GET_SHM_ADDR = 0x1,
- EC_CODEC_SET_SHM_ADDR = 0x2,
- EC_CODEC_SUBCMD_COUNT,
+};
+enum ec_codec_cap {
- EC_CODEC_CAP_LAST = 32,
+};
+enum ec_codec_shm_id {
- EC_CODEC_SHM_ID_LAST,
+};
+enum ec_codec_shm_type {
- EC_CODEC_SHM_TYPE_EC_RAM = 0x0,
- EC_CODEC_SHM_TYPE_SYSTEM_RAM = 0x1,
+};
+struct __ec_align1 ec_param_ec_codec_get_shm_addr {
- uint8_t shm_id;
- uint8_t reserved[3];
+};
+struct __ec_align4 ec_param_ec_codec_set_shm_addr {
- uint64_t phys_addr;
- uint32_t len;
- uint8_t shm_id;
- uint8_t reserved[3];
+};
+struct __ec_align4 ec_param_ec_codec {
- uint8_t cmd; /* enum ec_codec_subcmd */
- uint8_t reserved[3];
- union {
struct ec_param_ec_codec_get_shm_addr
get_shm_addr_param;
struct ec_param_ec_codec_set_shm_addr
set_shm_addr_param;
- };
+};
+struct __ec_align4 ec_response_ec_codec_get_capabilities {
- uint32_t capabilities;
+};
+struct __ec_align4 ec_response_ec_codec_get_shm_addr {
- uint64_t phys_addr;
- uint32_t len;
- uint8_t type;
- uint8_t reserved[3];
+};
+/*****************************************************************************/
/* Commands for DMIC on audio codec. */ -#define EC_CMD_EC_CODEC_DMIC 0x00BC +#define EC_CMD_EC_CODEC_DMIC 0x00BD
enum ec_codec_dmic_subcmd { EC_CODEC_DMIC_SET_GAIN = 0x0, @@ -4500,7 +4560,7 @@ struct __ec_align1 ec_response_ec_codec_dmic_get_gain {
/* Commands for I2S RX on audio codec. */
-#define EC_CMD_EC_CODEC_I2S_RX 0x00BD +#define EC_CMD_EC_CODEC_I2S_RX 0x00BE
enum ec_codec_i2s_rx_subcmd { EC_CODEC_I2S_RX_ENABLE = 0x0, -- 2.23.0.700.g56cf767bdb-goog
Read max DMIC gain from EC codec instead of DTS. Also removes the dt-binding of max-dmic-gain.
Acked-by: Rob Herring robh@kernel.org Signed-off-by: Tzung-Bi Shih tzungbi@google.com --- .../bindings/sound/google,cros-ec-codec.txt | 4 +- .../linux/platform_data/cros_ec_commands.h | 43 +++++++++++---- sound/soc/codecs/cros_ec_codec.c | 53 ++++++++++++++----- 3 files changed, 73 insertions(+), 27 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt index 1084f7f22eea..0ce9fafc78e2 100644 --- a/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt +++ b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt @@ -1,4 +1,4 @@ -* Audio codec controlled by ChromeOS EC +Audio codec controlled by ChromeOS EC
Google's ChromeOS EC codec is a digital mic codec provided by the Embedded Controller (EC) and is controlled via a host-command interface. @@ -9,7 +9,6 @@ Documentation/devicetree/bindings/mfd/cros-ec.txt). Required properties: - compatible: Must contain "google,cros-ec-codec" - #sound-dai-cells: Should be 1. The cell specifies number of DAIs. -- max-dmic-gain: A number for maximum gain in dB on digital microphone.
Example:
@@ -21,6 +20,5 @@ cros-ec@0 { cros_ec_codec: ec-codec { compatible = "google,cros-ec-codec"; #sound-dai-cells = <1>; - max-dmic-gain = <43>; }; }; diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 3ca0fa9e92a7..21db0d4d4025 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -4530,30 +4530,53 @@ struct __ec_align4 ec_response_ec_codec_get_shm_addr { #define EC_CMD_EC_CODEC_DMIC 0x00BD
enum ec_codec_dmic_subcmd { - EC_CODEC_DMIC_SET_GAIN = 0x0, - EC_CODEC_DMIC_GET_GAIN = 0x1, + EC_CODEC_DMIC_GET_MAX_GAIN = 0x0, + EC_CODEC_DMIC_SET_GAIN_IDX = 0x1, + EC_CODEC_DMIC_GET_GAIN_IDX = 0x2, EC_CODEC_DMIC_SUBCMD_COUNT, };
-struct __ec_align1 ec_param_ec_codec_dmic_set_gain { - uint8_t left; - uint8_t right; +enum ec_codec_dmic_channel { + EC_CODEC_DMIC_CHANNEL_0 = 0x0, + EC_CODEC_DMIC_CHANNEL_1 = 0x1, + EC_CODEC_DMIC_CHANNEL_2 = 0x2, + EC_CODEC_DMIC_CHANNEL_3 = 0x3, + EC_CODEC_DMIC_CHANNEL_4 = 0x4, + EC_CODEC_DMIC_CHANNEL_5 = 0x5, + EC_CODEC_DMIC_CHANNEL_6 = 0x6, + EC_CODEC_DMIC_CHANNEL_7 = 0x7, + EC_CODEC_DMIC_CHANNEL_COUNT, +}; + +struct __ec_align1 ec_param_ec_codec_dmic_set_gain_idx { + uint8_t channel; /* enum ec_codec_dmic_channel */ + uint8_t gain; uint8_t reserved[2]; };
+struct __ec_align1 ec_param_ec_codec_dmic_get_gain_idx { + uint8_t channel; /* enum ec_codec_dmic_channel */ + uint8_t reserved[3]; +}; + struct __ec_align4 ec_param_ec_codec_dmic { uint8_t cmd; /* enum ec_codec_dmic_subcmd */ uint8_t reserved[3];
union { - struct ec_param_ec_codec_dmic_set_gain - set_gain_param; + struct ec_param_ec_codec_dmic_set_gain_idx + set_gain_idx_param; + struct ec_param_ec_codec_dmic_get_gain_idx + get_gain_idx_param; }; };
-struct __ec_align1 ec_response_ec_codec_dmic_get_gain { - uint8_t left; - uint8_t right; +struct __ec_align1 ec_response_ec_codec_dmic_get_max_gain { + uint8_t max_gain; +}; + +struct __ec_align1 ec_response_ec_codec_dmic_get_gain_idx { + uint8_t gain; };
/*****************************************************************************/ diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index c19c7fe42e2e..3d4f9e82d6e9 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -65,18 +65,26 @@ static int dmic_get_gain(struct snd_kcontrol *kcontrol, struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component); struct ec_param_ec_codec_dmic p; - struct ec_response_ec_codec_dmic_get_gain r; + struct ec_response_ec_codec_dmic_get_gain_idx r; int ret;
- p.cmd = EC_CODEC_DMIC_GET_GAIN; + p.cmd = EC_CODEC_DMIC_GET_GAIN_IDX; + p.get_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_0; ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, (uint8_t *)&p, sizeof(p), (uint8_t *)&r, sizeof(r)); if (ret < 0) return ret; + ucontrol->value.integer.value[0] = r.gain;
- ucontrol->value.integer.value[0] = r.left; - ucontrol->value.integer.value[1] = r.right; + p.cmd = EC_CODEC_DMIC_GET_GAIN_IDX; + p.get_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_1; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret < 0) + return ret; + ucontrol->value.integer.value[1] = r.gain;
return 0; } @@ -94,15 +102,24 @@ static int dmic_put_gain(struct snd_kcontrol *kcontrol, int left = ucontrol->value.integer.value[0]; int right = ucontrol->value.integer.value[1]; struct ec_param_ec_codec_dmic p; + int ret;
if (left > max_dmic_gain || right > max_dmic_gain) return -EINVAL;
dev_dbg(component->dev, "set mic gain to %u, %u\n", left, right);
- p.cmd = EC_CODEC_DMIC_SET_GAIN; - p.set_gain_param.left = left; - p.set_gain_param.right = right; + p.cmd = EC_CODEC_DMIC_SET_GAIN_IDX; + p.set_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_0; + p.set_gain_idx_param.gain = left; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, + (uint8_t *)&p, sizeof(p), NULL, 0); + if (ret < 0) + return ret; + + p.cmd = EC_CODEC_DMIC_SET_GAIN_IDX; + p.set_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_1; + p.set_gain_idx_param.gain = right; return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, (uint8_t *)&p, sizeof(p), NULL, 0); } @@ -125,19 +142,27 @@ static int dmic_probe(struct snd_soc_component *component) struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component); struct device *dev = priv->dev; - int ret, val; struct soc_mixer_control *control; + struct ec_param_ec_codec_dmic p; + struct ec_response_ec_codec_dmic_get_max_gain r; + int ret;
- ret = device_property_read_u32(dev, "max-dmic-gain", &val); - if (ret) { - dev_err(dev, "Failed to read 'max-dmic-gain'\n"); - return ret; + p.cmd = EC_CODEC_DMIC_GET_MAX_GAIN; + + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret < 0) { + dev_warn(dev, "get_max_gain() unsupported\n"); + return 0; }
+ dev_dbg(dev, "max gain = %d\n", r.max_gain); + control = (struct soc_mixer_control *) dmic_controls[DMIC_CTL_GAIN].private_value; - control->max = val; - control->platform_max = val; + control->max = r.max_gain; + control->platform_max = r.max_gain;
return snd_soc_add_component_controls(component, &dmic_controls[DMIC_CTL_GAIN], 1);
On Mon, Oct 14, 2019 at 06:20:17PM +0800, Tzung-Bi Shih wrote:
Read max DMIC gain from EC codec instead of DTS. Also removes the dt-binding of max-dmic-gain.
Acked-by: Rob Herring robh@kernel.org Signed-off-by: Tzung-Bi Shih tzungbi@google.com
Acked-By: Benson Leung bleung@chromium.org
.../bindings/sound/google,cros-ec-codec.txt | 4 +- .../linux/platform_data/cros_ec_commands.h | 43 +++++++++++---- sound/soc/codecs/cros_ec_codec.c | 53 ++++++++++++++----- 3 files changed, 73 insertions(+), 27 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt index 1084f7f22eea..0ce9fafc78e2 100644 --- a/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt +++ b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt @@ -1,4 +1,4 @@ -* Audio codec controlled by ChromeOS EC +Audio codec controlled by ChromeOS EC
Google's ChromeOS EC codec is a digital mic codec provided by the Embedded Controller (EC) and is controlled via a host-command interface. @@ -9,7 +9,6 @@ Documentation/devicetree/bindings/mfd/cros-ec.txt). Required properties:
- compatible: Must contain "google,cros-ec-codec"
- #sound-dai-cells: Should be 1. The cell specifies number of DAIs.
-- max-dmic-gain: A number for maximum gain in dB on digital microphone.
Example:
@@ -21,6 +20,5 @@ cros-ec@0 { cros_ec_codec: ec-codec { compatible = "google,cros-ec-codec"; #sound-dai-cells = <1>;
};max-dmic-gain = <43>;
}; diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 3ca0fa9e92a7..21db0d4d4025 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -4530,30 +4530,53 @@ struct __ec_align4 ec_response_ec_codec_get_shm_addr { #define EC_CMD_EC_CODEC_DMIC 0x00BD
enum ec_codec_dmic_subcmd {
- EC_CODEC_DMIC_SET_GAIN = 0x0,
- EC_CODEC_DMIC_GET_GAIN = 0x1,
- EC_CODEC_DMIC_GET_MAX_GAIN = 0x0,
- EC_CODEC_DMIC_SET_GAIN_IDX = 0x1,
- EC_CODEC_DMIC_GET_GAIN_IDX = 0x2, EC_CODEC_DMIC_SUBCMD_COUNT,
};
-struct __ec_align1 ec_param_ec_codec_dmic_set_gain {
- uint8_t left;
- uint8_t right;
+enum ec_codec_dmic_channel {
- EC_CODEC_DMIC_CHANNEL_0 = 0x0,
- EC_CODEC_DMIC_CHANNEL_1 = 0x1,
- EC_CODEC_DMIC_CHANNEL_2 = 0x2,
- EC_CODEC_DMIC_CHANNEL_3 = 0x3,
- EC_CODEC_DMIC_CHANNEL_4 = 0x4,
- EC_CODEC_DMIC_CHANNEL_5 = 0x5,
- EC_CODEC_DMIC_CHANNEL_6 = 0x6,
- EC_CODEC_DMIC_CHANNEL_7 = 0x7,
- EC_CODEC_DMIC_CHANNEL_COUNT,
+};
+struct __ec_align1 ec_param_ec_codec_dmic_set_gain_idx {
- uint8_t channel; /* enum ec_codec_dmic_channel */
- uint8_t gain; uint8_t reserved[2];
};
+struct __ec_align1 ec_param_ec_codec_dmic_get_gain_idx {
- uint8_t channel; /* enum ec_codec_dmic_channel */
- uint8_t reserved[3];
+};
struct __ec_align4 ec_param_ec_codec_dmic { uint8_t cmd; /* enum ec_codec_dmic_subcmd */ uint8_t reserved[3];
union {
struct ec_param_ec_codec_dmic_set_gain
set_gain_param;
struct ec_param_ec_codec_dmic_set_gain_idx
set_gain_idx_param;
struct ec_param_ec_codec_dmic_get_gain_idx
};get_gain_idx_param;
};
-struct __ec_align1 ec_response_ec_codec_dmic_get_gain {
- uint8_t left;
- uint8_t right;
+struct __ec_align1 ec_response_ec_codec_dmic_get_max_gain {
- uint8_t max_gain;
+};
+struct __ec_align1 ec_response_ec_codec_dmic_get_gain_idx {
- uint8_t gain;
};
/*****************************************************************************/ diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index c19c7fe42e2e..3d4f9e82d6e9 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -65,18 +65,26 @@ static int dmic_get_gain(struct snd_kcontrol *kcontrol, struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component); struct ec_param_ec_codec_dmic p;
- struct ec_response_ec_codec_dmic_get_gain r;
- struct ec_response_ec_codec_dmic_get_gain_idx r; int ret;
- p.cmd = EC_CODEC_DMIC_GET_GAIN;
- p.cmd = EC_CODEC_DMIC_GET_GAIN_IDX;
- p.get_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_0; ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, (uint8_t *)&p, sizeof(p), (uint8_t *)&r, sizeof(r)); if (ret < 0) return ret;
- ucontrol->value.integer.value[0] = r.gain;
- ucontrol->value.integer.value[0] = r.left;
- ucontrol->value.integer.value[1] = r.right;
p.cmd = EC_CODEC_DMIC_GET_GAIN_IDX;
p.get_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_1;
ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
(uint8_t *)&p, sizeof(p),
(uint8_t *)&r, sizeof(r));
if (ret < 0)
return ret;
ucontrol->value.integer.value[1] = r.gain;
return 0;
} @@ -94,15 +102,24 @@ static int dmic_put_gain(struct snd_kcontrol *kcontrol, int left = ucontrol->value.integer.value[0]; int right = ucontrol->value.integer.value[1]; struct ec_param_ec_codec_dmic p;
int ret;
if (left > max_dmic_gain || right > max_dmic_gain) return -EINVAL;
dev_dbg(component->dev, "set mic gain to %u, %u\n", left, right);
- p.cmd = EC_CODEC_DMIC_SET_GAIN;
- p.set_gain_param.left = left;
- p.set_gain_param.right = right;
- p.cmd = EC_CODEC_DMIC_SET_GAIN_IDX;
- p.set_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_0;
- p.set_gain_idx_param.gain = left;
- ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
(uint8_t *)&p, sizeof(p), NULL, 0);
- if (ret < 0)
return ret;
- p.cmd = EC_CODEC_DMIC_SET_GAIN_IDX;
- p.set_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_1;
- p.set_gain_idx_param.gain = right; return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, (uint8_t *)&p, sizeof(p), NULL, 0);
} @@ -125,19 +142,27 @@ static int dmic_probe(struct snd_soc_component *component) struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component); struct device *dev = priv->dev;
- int ret, val; struct soc_mixer_control *control;
- struct ec_param_ec_codec_dmic p;
- struct ec_response_ec_codec_dmic_get_max_gain r;
- int ret;
- ret = device_property_read_u32(dev, "max-dmic-gain", &val);
- if (ret) {
dev_err(dev, "Failed to read 'max-dmic-gain'\n");
return ret;
p.cmd = EC_CODEC_DMIC_GET_MAX_GAIN;
ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
(uint8_t *)&p, sizeof(p),
(uint8_t *)&r, sizeof(r));
if (ret < 0) {
dev_warn(dev, "get_max_gain() unsupported\n");
return 0;
}
dev_dbg(dev, "max gain = %d\n", r.max_gain);
control = (struct soc_mixer_control *) dmic_controls[DMIC_CTL_GAIN].private_value;
- control->max = val;
- control->platform_max = val;
control->max = r.max_gain;
control->platform_max = r.max_gain;
return snd_soc_add_component_controls(component, &dmic_controls[DMIC_CTL_GAIN], 1);
-- 2.23.0.700.g56cf767bdb-goog
- Add "reg" for binding to shared memory exposed by EC. - Add "memory-region" for binding to memory region shared by AP.
Signed-off-by: Tzung-Bi Shih tzungbi@google.com --- .../bindings/sound/google,cros-ec-codec.txt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt index 0ce9fafc78e2..8ca52dcc5572 100644 --- a/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt +++ b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt @@ -10,8 +10,26 @@ Required properties: - compatible: Must contain "google,cros-ec-codec" - #sound-dai-cells: Should be 1. The cell specifies number of DAIs.
+Optional properties: +- reg: Pysical base address and length of shared memory region from EC. + It contains 3 unsigned 32-bit integer. The first 2 integers + combine to become an unsigned 64-bit physical address. The last + one integer is length of the shared memory. +- memory-region: Shared memory region to EC. A "shared-dma-pool". See + ../reserved-memory/reserved-memory.txt for details. + Example:
+{ + ... + + reserved_mem: reserved_mem { + compatible = "shared-dma-pool"; + reg = <0 0x52800000 0 0x100000>; + no-map; + }; +} + cros-ec@0 { compatible = "google,cros-ec-spi";
@@ -20,5 +38,7 @@ cros-ec@0 { cros_ec_codec: ec-codec { compatible = "google,cros-ec-codec"; #sound-dai-cells = <1>; + reg = <0x0 0x10500000 0x80000>; + memory-region = <&reserved_mem>; }; };
On Mon, 14 Oct 2019 18:20:18 +0800, Tzung-Bi Shih wrote:
- Add "reg" for binding to shared memory exposed by EC.
- Add "memory-region" for binding to memory region shared by AP.
Signed-off-by: Tzung-Bi Shih tzungbi@google.com
.../bindings/sound/google,cros-ec-codec.txt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+)
Acked-by: Rob Herring robh@kernel.org
On Mon, Oct 14, 2019 at 06:20:18PM +0800, Tzung-Bi Shih wrote:
- Add "reg" for binding to shared memory exposed by EC.
- Add "memory-region" for binding to memory region shared by AP.
Signed-off-by: Tzung-Bi Shih tzungbi@google.com
Acked-By: Benson Leung bleung@chromium.org
.../bindings/sound/google,cros-ec-codec.txt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt index 0ce9fafc78e2..8ca52dcc5572 100644 --- a/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt +++ b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt @@ -10,8 +10,26 @@ Required properties:
- compatible: Must contain "google,cros-ec-codec"
- #sound-dai-cells: Should be 1. The cell specifies number of DAIs.
+Optional properties: +- reg: Pysical base address and length of shared memory region from EC.
It contains 3 unsigned 32-bit integer. The first 2 integers
combine to become an unsigned 64-bit physical address. The last
one integer is length of the shared memory.
+- memory-region: Shared memory region to EC. A "shared-dma-pool". See
../reserved-memory/reserved-memory.txt for details.
Example:
+{
- ...
- reserved_mem: reserved_mem {
compatible = "shared-dma-pool";
reg = <0 0x52800000 0 0x100000>;
no-map;
- };
+}
cros-ec@0 { compatible = "google,cros-ec-spi";
@@ -20,5 +38,7 @@ cros-ec@0 { cros_ec_codec: ec-codec { compatible = "google,cros-ec-codec"; #sound-dai-cells = <1>;
reg = <0x0 0x10500000 0x80000>;
};memory-region = <&reserved_mem>;
};
2.23.0.700.g56cf767bdb-goog
1. Get EC codec's capabilities. 2. Get and set SHM address if any. 3. Transmit language model to EC codec if needed. 4. Start to read audio data from EC codec if receives host event.
Signed-off-by: Tzung-Bi Shih tzungbi@google.com --- drivers/platform/chrome/cros_ec_trace.c | 1 + .../linux/platform_data/cros_ec_commands.h | 69 ++ sound/soc/codecs/cros_ec_codec.c | 700 +++++++++++++++++- 3 files changed, 768 insertions(+), 2 deletions(-)
diff --git a/drivers/platform/chrome/cros_ec_trace.c b/drivers/platform/chrome/cros_ec_trace.c index 2ea0d4e0d54d..5af1d66d9eca 100644 --- a/drivers/platform/chrome/cros_ec_trace.c +++ b/drivers/platform/chrome/cros_ec_trace.c @@ -101,6 +101,7 @@ TRACE_SYMBOL(EC_CMD_EC_CODEC), \ TRACE_SYMBOL(EC_CMD_EC_CODEC_DMIC), \ TRACE_SYMBOL(EC_CMD_EC_CODEC_I2S_RX), \ + TRACE_SYMBOL(EC_CMD_EC_CODEC_WOV), \ TRACE_SYMBOL(EC_CMD_REBOOT_EC), \ TRACE_SYMBOL(EC_CMD_GET_PANIC_INFO), \ TRACE_SYMBOL(EC_CMD_ACPI_READ), \ diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 21db0d4d4025..69210881ebac 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -556,6 +556,9 @@ enum host_event_code { /* Keyboard recovery combo with hardware reinitialization */ EC_HOST_EVENT_KEYBOARD_RECOVERY_HW_REINIT = 30,
+ /* WoV */ + EC_HOST_EVENT_WOV = 31, + /* * The high bit of the event mask is not used as a host event code. If * it reads back as set, then the entire event mask should be @@ -4477,10 +4480,14 @@ enum ec_codec_subcmd { };
enum ec_codec_cap { + EC_CODEC_CAP_WOV_AUDIO_SHM = 0, + EC_CODEC_CAP_WOV_LANG_SHM = 1, EC_CODEC_CAP_LAST = 32, };
enum ec_codec_shm_id { + EC_CODEC_SHM_ID_WOV_AUDIO = 0x0, + EC_CODEC_SHM_ID_WOV_LANG = 0x1, EC_CODEC_SHM_ID_LAST, };
@@ -4641,6 +4648,68 @@ struct __ec_align4 ec_param_ec_codec_i2s_rx { }; };
+/*****************************************************************************/ +/* Commands for WoV on audio codec. */ + +#define EC_CMD_EC_CODEC_WOV 0x00BF + +enum ec_codec_wov_subcmd { + EC_CODEC_WOV_SET_LANG = 0x0, + EC_CODEC_WOV_SET_LANG_SHM = 0x1, + EC_CODEC_WOV_GET_LANG = 0x2, + EC_CODEC_WOV_ENABLE = 0x3, + EC_CODEC_WOV_DISABLE = 0x4, + EC_CODEC_WOV_READ_AUDIO = 0x5, + EC_CODEC_WOV_READ_AUDIO_SHM = 0x6, + EC_CODEC_WOV_SUBCMD_COUNT, +}; + +/* + * @hash is SHA256 of the whole language model. + * @total_len indicates the length of whole language model. + * @offset is the cursor from the beginning of the model. + * @buf is the packet buffer. + * @len denotes how many bytes in the buf. + */ +struct __ec_align4 ec_param_ec_codec_wov_set_lang { + uint8_t hash[32]; + uint32_t total_len; + uint32_t offset; + uint8_t buf[128]; + uint32_t len; +}; + +struct __ec_align4 ec_param_ec_codec_wov_set_lang_shm { + uint8_t hash[32]; + uint32_t total_len; +}; + +struct __ec_align4 ec_param_ec_codec_wov { + uint8_t cmd; /* enum ec_codec_wov_subcmd */ + uint8_t reserved[3]; + + union { + struct ec_param_ec_codec_wov_set_lang + set_lang_param; + struct ec_param_ec_codec_wov_set_lang_shm + set_lang_shm_param; + }; +}; + +struct __ec_align4 ec_response_ec_codec_wov_get_lang { + uint8_t hash[32]; +}; + +struct __ec_align4 ec_response_ec_codec_wov_read_audio { + uint8_t buf[128]; + uint32_t len; +}; + +struct __ec_align4 ec_response_ec_codec_wov_read_audio_shm { + uint32_t offset; + uint32_t len; +}; + /*****************************************************************************/ /* System commands */
diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index 3d4f9e82d6e9..bd9295548f03 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -8,10 +8,15 @@ * EC for audio function. */
+#include <crypto/hash.h> +#include <crypto/sha.h> #include <linux/delay.h> #include <linux/device.h> +#include <linux/io.h> +#include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of_address.h> #include <linux/platform_data/cros_ec_commands.h> #include <linux/platform_data/cros_ec_proto.h> #include <linux/platform_device.h> @@ -23,8 +28,45 @@ struct cros_ec_codec_priv { struct device *dev; struct cros_ec_device *ec_device; + + /* common */ + uint32_t ec_capabilities; + + uint64_t ec_shm_addr; + uint32_t ec_shm_len; + + uint64_t ap_shm_phys_addr; + uint32_t ap_shm_len; + uint64_t ap_shm_addr; + uint64_t ap_shm_last_alloc; + + /* DMIC */ + atomic_t dmic_probed; + + /* WoV */ + bool wov_enabled; + uint8_t *wov_audio_shm_p; + uint32_t wov_audio_shm_len; + uint8_t wov_audio_shm_type; + uint8_t *wov_lang_shm_p; + uint32_t wov_lang_shm_len; + uint8_t wov_lang_shm_type; + + struct mutex wov_dma_lock; + uint8_t wov_buf[64000]; + uint32_t wov_rp, wov_wp; + size_t wov_dma_offset; + bool wov_burst_read; + struct snd_pcm_substream *wov_substream; + struct delayed_work wov_copy_work; + struct notifier_block wov_notifier; };
+static int ec_codec_capable(struct cros_ec_codec_priv *priv, uint8_t cap) +{ + return priv->ec_capabilities & BIT(cap); +} + static int 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) @@ -57,6 +99,41 @@ static int send_ec_host_command(struct cros_ec_device *ec_dev, uint32_t cmd, return ret; }
+static int calculate_sha256(struct cros_ec_codec_priv *priv, + uint8_t *buf, uint32_t size, uint8_t *digest) +{ + struct crypto_shash *tfm; + + tfm = crypto_alloc_shash("sha256", CRYPTO_ALG_TYPE_SHASH, 0); + if (IS_ERR(tfm)) { + dev_err(priv->dev, "can't alloc shash\n"); + return PTR_ERR(tfm); + } + + { + SHASH_DESC_ON_STACK(desc, tfm); + + desc->tfm = tfm; + + crypto_shash_digest(desc, buf, size, digest); + shash_desc_zero(desc); + } + + crypto_free_shash(tfm); + +#ifdef DEBUG + { + char digest_str[65]; + + bin2hex(digest_str, digest, 32); + digest_str[64] = 0; + dev_dbg(priv->dev, "hash=%s\n", digest_str); + } +#endif + + return 0; +} + static int dmic_get_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -147,6 +224,9 @@ static int dmic_probe(struct snd_soc_component *component) struct ec_response_ec_codec_dmic_get_max_gain r; int ret;
+ if (!atomic_add_unless(&priv->dmic_probed, 1, 1)) + return 0; + p.cmd = EC_CODEC_DMIC_GET_MAX_GAIN;
ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, @@ -324,23 +404,639 @@ static const struct snd_soc_component_driver i2s_rx_component_driver = { .num_dapm_routes = ARRAY_SIZE(i2s_rx_dapm_routes), };
+static void *wov_map_shm(struct cros_ec_codec_priv *priv, + uint8_t shm_id, uint32_t *len, uint8_t *type) +{ + struct ec_param_ec_codec p; + struct ec_response_ec_codec_get_shm_addr r; + uint32_t req, offset; + + p.cmd = EC_CODEC_GET_SHM_ADDR; + p.get_shm_addr_param.shm_id = shm_id; + if (send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)) < 0) { + dev_err(priv->dev, "failed to EC_CODEC_GET_SHM_ADDR\n"); + return NULL; + } + + dev_dbg(priv->dev, "phys_addr=%#llx, len=%#x\n", r.phys_addr, r.len); + + *len = r.len; + *type = r.type; + + switch (r.type) { + case EC_CODEC_SHM_TYPE_EC_RAM: + return (void __force *)devm_ioremap_wc(priv->dev, + r.phys_addr + priv->ec_shm_addr, r.len); + case EC_CODEC_SHM_TYPE_SYSTEM_RAM: + if (r.phys_addr) { + dev_err(priv->dev, "unknown status\n"); + return NULL; + } + + req = round_up(r.len, PAGE_SIZE); + dev_dbg(priv->dev, "round up from %u to %u\n", r.len, req); + + if (priv->ap_shm_last_alloc + req > + priv->ap_shm_phys_addr + priv->ap_shm_len) { + dev_err(priv->dev, "insufficient space for AP SHM\n"); + return NULL; + } + + dev_dbg(priv->dev, "alloc AP SHM addr=%#llx, len=%#x\n", + priv->ap_shm_last_alloc, req); + + p.cmd = EC_CODEC_SET_SHM_ADDR; + p.set_shm_addr_param.phys_addr = priv->ap_shm_last_alloc; + p.set_shm_addr_param.len = req; + p.set_shm_addr_param.shm_id = shm_id; + if (send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC, + (uint8_t *)&p, sizeof(p), + NULL, 0) < 0) { + dev_err(priv->dev, "failed to EC_CODEC_SET_SHM_ADDR\n"); + return NULL; + } + + /* + * Note: EC codec only requests for `r.len' but we allocate + * round up PAGE_SIZE `req'. + */ + offset = priv->ap_shm_last_alloc - priv->ap_shm_phys_addr; + priv->ap_shm_last_alloc += req; + + return (void *)(uintptr_t)(priv->ap_shm_addr + offset); + default: + return NULL; + } +} + +static bool wov_queue_full(struct cros_ec_codec_priv *priv) +{ + return ((priv->wov_wp + 1) % sizeof(priv->wov_buf)) == priv->wov_rp; +} + +static size_t wov_queue_size(struct cros_ec_codec_priv *priv) +{ + if (priv->wov_wp >= priv->wov_rp) + return priv->wov_wp - priv->wov_rp; + else + return sizeof(priv->wov_buf) - priv->wov_rp + priv->wov_wp; +} + +static void wov_queue_dequeue(struct cros_ec_codec_priv *priv, size_t len) +{ + struct snd_pcm_runtime *runtime = priv->wov_substream->runtime; + size_t req; + + while (len) { + req = min(len, runtime->dma_bytes - priv->wov_dma_offset); + if (priv->wov_wp >= priv->wov_rp) + req = min(req, (size_t)priv->wov_wp - priv->wov_rp); + else + req = min(req, sizeof(priv->wov_buf) - priv->wov_rp); + + memcpy(runtime->dma_area + priv->wov_dma_offset, + priv->wov_buf + priv->wov_rp, req); + + priv->wov_dma_offset += req; + if (priv->wov_dma_offset == runtime->dma_bytes) + priv->wov_dma_offset = 0; + + priv->wov_rp += req; + if (priv->wov_rp == sizeof(priv->wov_buf)) + priv->wov_rp = 0; + + len -= req; + } + + snd_pcm_period_elapsed(priv->wov_substream); +} + +static void wov_queue_try_dequeue(struct cros_ec_codec_priv *priv) +{ + size_t period_bytes = snd_pcm_lib_period_bytes(priv->wov_substream); + + while (period_bytes && wov_queue_size(priv) >= period_bytes) { + wov_queue_dequeue(priv, period_bytes); + period_bytes = snd_pcm_lib_period_bytes(priv->wov_substream); + } +} + +static void wov_queue_enqueue(struct cros_ec_codec_priv *priv, + uint8_t *addr, size_t len, bool iomem) +{ + size_t req; + + while (len) { + if (wov_queue_full(priv)) { + wov_queue_try_dequeue(priv); + + if (wov_queue_full(priv)) { + dev_err(priv->dev, "overrun detected\n"); + return; + } + } + + if (priv->wov_wp >= priv->wov_rp) + req = sizeof(priv->wov_buf) - priv->wov_wp; + else + /* Note: waste 1-byte to differentiate full and empty */ + req = priv->wov_rp - priv->wov_wp - 1; + req = min(req, len); + + if (iomem) + memcpy_fromio(priv->wov_buf + priv->wov_wp, + (void __force __iomem *)addr, req); + else + memcpy(priv->wov_buf + priv->wov_wp, addr, req); + + priv->wov_wp += req; + if (priv->wov_wp == sizeof(priv->wov_buf)) + priv->wov_wp = 0; + + addr += req; + len -= req; + } + + wov_queue_try_dequeue(priv); +} + +static int wov_read_audio_shm(struct cros_ec_codec_priv *priv) +{ + struct ec_param_ec_codec_wov p; + struct ec_response_ec_codec_wov_read_audio_shm r; + int ret; + + p.cmd = EC_CODEC_WOV_READ_AUDIO_SHM; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret) { + dev_err(priv->dev, "failed to EC_CODEC_WOV_READ_AUDIO_SHM\n"); + return ret; + } + + if (!r.len) + dev_dbg(priv->dev, "no data, sleep\n"); + else + wov_queue_enqueue(priv, priv->wov_audio_shm_p + r.offset, r.len, + priv->wov_audio_shm_type == EC_CODEC_SHM_TYPE_EC_RAM); + return -EAGAIN; +} + +static int wov_read_audio(struct cros_ec_codec_priv *priv) +{ + struct ec_param_ec_codec_wov p; + struct ec_response_ec_codec_wov_read_audio r; + int remain = priv->wov_burst_read ? 16000 : 320; + int ret; + + while (remain >= 0) { + p.cmd = EC_CODEC_WOV_READ_AUDIO; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret) { + dev_err(priv->dev, + "failed to EC_CODEC_WOV_READ_AUDIO\n"); + return ret; + } + + if (!r.len) { + dev_dbg(priv->dev, "no data, sleep\n"); + priv->wov_burst_read = false; + break; + } + + wov_queue_enqueue(priv, r.buf, r.len, false); + remain -= r.len; + } + + return -EAGAIN; +} + +static void wov_copy_work(struct work_struct *w) +{ + struct cros_ec_codec_priv *priv = + container_of(w, struct cros_ec_codec_priv, wov_copy_work.work); + int ret; + + mutex_lock(&priv->wov_dma_lock); + if (!priv->wov_substream) { + dev_warn(priv->dev, "no pcm substream\n"); + goto leave; + } + + if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM)) + ret = wov_read_audio_shm(priv); + else + ret = wov_read_audio(priv); + + if (ret == -EAGAIN) + schedule_delayed_work(&priv->wov_copy_work, + msecs_to_jiffies(10)); + else if (ret) + dev_err(priv->dev, "failed to read audio data\n"); +leave: + mutex_unlock(&priv->wov_dma_lock); +} + +static int wov_enable_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(c); + + ucontrol->value.integer.value[0] = priv->wov_enabled; + return 0; +} + +static int wov_enable_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(c); + int enabled = ucontrol->value.integer.value[0]; + struct ec_param_ec_codec_wov p; + int ret; + + if (priv->wov_enabled != enabled) { + if (enabled) + p.cmd = EC_CODEC_WOV_ENABLE; + else + p.cmd = EC_CODEC_WOV_DISABLE; + + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV, + (uint8_t *)&p, sizeof(p), NULL, 0); + if (ret) { + dev_err(priv->dev, "failed to %s wov\n", + enabled ? "enable" : "disable"); + return ret; + } + + priv->wov_enabled = enabled; + } + + return 0; +} + +static int wov_set_lang_shm(struct cros_ec_codec_priv *priv, + uint8_t *buf, size_t size, uint8_t *digest) +{ + struct ec_param_ec_codec_wov p; + struct ec_param_ec_codec_wov_set_lang_shm *pp = &p.set_lang_shm_param; + int ret; + + if (size > priv->wov_lang_shm_len) { + dev_err(priv->dev, "no enough SHM size: %d\n", + priv->wov_lang_shm_len); + return -EIO; + } + + switch (priv->wov_lang_shm_type) { + case EC_CODEC_SHM_TYPE_EC_RAM: + memcpy_toio((void __force __iomem *)priv->wov_lang_shm_p, + buf, size); + memset_io((void __force __iomem *)priv->wov_lang_shm_p + size, + 0, priv->wov_lang_shm_len - size); + break; + case EC_CODEC_SHM_TYPE_SYSTEM_RAM: + memcpy(priv->wov_lang_shm_p, buf, size); + memset(priv->wov_lang_shm_p + size, 0, + priv->wov_lang_shm_len - size); + + /* make sure write to memory before calling host command */ + wmb(); + break; + } + + p.cmd = EC_CODEC_WOV_SET_LANG_SHM; + memcpy(pp->hash, digest, SHA256_DIGEST_SIZE); + pp->total_len = size; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV, + (uint8_t *)&p, sizeof(p), NULL, 0); + if (ret) { + dev_err(priv->dev, "failed to EC_CODEC_WOV_SET_LANG_SHM\n"); + return ret; + } + + return 0; +} + +static int wov_set_lang(struct cros_ec_codec_priv *priv, + uint8_t *buf, size_t size, uint8_t *digest) +{ + struct ec_param_ec_codec_wov p; + struct ec_param_ec_codec_wov_set_lang *pp = &p.set_lang_param; + size_t i, req; + int ret; + + for (i = 0; i < size; i += req) { + req = min(size - i, ARRAY_SIZE(pp->buf)); + + p.cmd = EC_CODEC_WOV_SET_LANG; + memcpy(pp->hash, digest, SHA256_DIGEST_SIZE); + pp->total_len = size; + pp->offset = i; + memcpy(pp->buf, buf + i, req); + pp->len = req; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV, + (uint8_t *)&p, sizeof(p), NULL, 0); + if (ret) { + dev_err(priv->dev, "failed to EC_CODEC_WOV_SET_LANG\n"); + return ret; + } + } + + return 0; +} + +static int wov_hotword_model_put(struct snd_kcontrol *kcontrol, + const unsigned int __user *bytes, + unsigned int size) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct ec_param_ec_codec_wov p; + struct ec_response_ec_codec_wov_get_lang r; + uint8_t digest[SHA256_DIGEST_SIZE]; + uint8_t *buf; + int ret; + + /* Skips the TLV header. */ + bytes += 2; + size -= 8; + + dev_dbg(priv->dev, "%s: size=%d\n", __func__, size); + + buf = kmalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, bytes, size)) { + ret = -EFAULT; + goto leave; + } + + ret = calculate_sha256(priv, buf, size, digest); + if (ret) + goto leave; + + p.cmd = EC_CODEC_WOV_GET_LANG; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret) + goto leave; + + if (memcmp(digest, r.hash, SHA256_DIGEST_SIZE) == 0) { + dev_dbg(priv->dev, "not updated"); + goto leave; + } + + if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_LANG_SHM)) + ret = wov_set_lang_shm(priv, buf, size, digest); + else + ret = wov_set_lang(priv, buf, size, digest); + +leave: + kfree(buf); + return ret; +} + +static struct snd_kcontrol_new wov_controls[] = { + SOC_SINGLE_BOOL_EXT("Wake-on-Voice Switch", 0, + wov_enable_get, wov_enable_put), + SND_SOC_BYTES_TLV("Hotword Model", 0x11000, NULL, + wov_hotword_model_put), +}; + +static struct snd_soc_dai_driver wov_dai_driver = { + .name = "Wake on Voice", + .capture = { + .stream_name = "WoV Capture", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}; + +static int wov_host_event(struct notifier_block *nb, + unsigned long queued_during_suspend, void *notify) +{ + struct cros_ec_codec_priv *priv = + container_of(nb, struct cros_ec_codec_priv, wov_notifier); + u32 host_event; + + dev_dbg(priv->dev, "%s\n", __func__); + + host_event = cros_ec_get_host_event(priv->ec_device); + if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_WOV)) { + schedule_delayed_work(&priv->wov_copy_work, 0); + return NOTIFY_OK; + } else { + return NOTIFY_DONE; + } +} + +static int wov_probe(struct snd_soc_component *component) +{ + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + int ret; + + mutex_init(&priv->wov_dma_lock); + INIT_DELAYED_WORK(&priv->wov_copy_work, wov_copy_work); + + priv->wov_notifier.notifier_call = wov_host_event; + ret = blocking_notifier_chain_register( + &priv->ec_device->event_notifier, &priv->wov_notifier); + if (ret) + return ret; + + if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_LANG_SHM)) { + priv->wov_lang_shm_p = wov_map_shm(priv, + EC_CODEC_SHM_ID_WOV_LANG, + &priv->wov_lang_shm_len, + &priv->wov_lang_shm_type); + if (!priv->wov_lang_shm_p) + return -EFAULT; + } + + if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM)) { + priv->wov_audio_shm_p = wov_map_shm(priv, + EC_CODEC_SHM_ID_WOV_AUDIO, + &priv->wov_audio_shm_len, + &priv->wov_audio_shm_type); + if (!priv->wov_audio_shm_p) + return -EFAULT; + } + + return dmic_probe(component); +} + +static void wov_remove(struct snd_soc_component *component) +{ + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + + blocking_notifier_chain_unregister( + &priv->ec_device->event_notifier, &priv->wov_notifier); +} + +static int wov_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + static const struct snd_pcm_hardware hw_param = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_16000, + .channels_min = 1, + .channels_max = 1, + .period_bytes_min = PAGE_SIZE, + .period_bytes_max = 0x20000 / 8, + .periods_min = 8, + .periods_max = 8, + .buffer_bytes_max = 0x20000, + }; + + return snd_soc_set_runtime_hwparams(substream, &hw_param); +} + +static int wov_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + + mutex_lock(&priv->wov_dma_lock); + priv->wov_substream = substream; + priv->wov_rp = priv->wov_wp = 0; + priv->wov_dma_offset = 0; + priv->wov_burst_read = true; + mutex_unlock(&priv->wov_dma_lock); + + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); +} + +static int wov_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + + mutex_lock(&priv->wov_dma_lock); + wov_queue_dequeue(priv, wov_queue_size(priv)); + priv->wov_substream = NULL; + mutex_unlock(&priv->wov_dma_lock); + + cancel_delayed_work_sync(&priv->wov_copy_work); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static snd_pcm_uframes_t wov_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + + return bytes_to_frames(runtime, priv->wov_dma_offset); +} + +static struct page *wov_pcm_page(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + unsigned long offset) +{ + return snd_pcm_lib_get_vmalloc_page(substream, offset); +} + +static const struct snd_soc_component_driver wov_component_driver = { + .probe = wov_probe, + .remove = wov_remove, + .controls = wov_controls, + .num_controls = ARRAY_SIZE(wov_controls), + .open = wov_pcm_open, + .hw_params = wov_pcm_hw_params, + .hw_free = wov_pcm_hw_free, + .pointer = wov_pcm_pointer, + .page = wov_pcm_page, +}; + static int cros_ec_codec_platform_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct cros_ec_device *ec_device = dev_get_drvdata(pdev->dev.parent); struct cros_ec_codec_priv *priv; + struct device_node *node; + struct resource res; + struct ec_param_ec_codec p; + struct ec_response_ec_codec_get_capabilities r; + int ret; + u64 ec_shm_size; + const __be32 *regaddr_p;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM;
+ regaddr_p = of_get_address(dev->of_node, 0, &ec_shm_size, NULL); + if (regaddr_p) { + priv->ec_shm_addr = of_read_number(regaddr_p, 2); + priv->ec_shm_len = ec_shm_size; + + dev_dbg(dev, "ec_shm_addr=%#llx len=%#x\n", + priv->ec_shm_addr, priv->ec_shm_len); + } + + node = of_parse_phandle(dev->of_node, "memory-region", 0); + if (node) { + ret = of_address_to_resource(node, 0, &res); + if (!ret) { + priv->ap_shm_phys_addr = res.start; + priv->ap_shm_len = resource_size(&res); + priv->ap_shm_addr = + (uint64_t)(uintptr_t)devm_ioremap_wc( + dev, priv->ap_shm_phys_addr, + priv->ap_shm_len); + priv->ap_shm_last_alloc = priv->ap_shm_phys_addr; + + dev_dbg(dev, "ap_shm_phys_addr=%#llx len=%#x\n", + priv->ap_shm_phys_addr, priv->ap_shm_len); + } + } + priv->dev = dev; priv->ec_device = ec_device; + atomic_set(&priv->dmic_probed, 0); + + p.cmd = EC_CODEC_GET_CAPABILITIES; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret) { + dev_err(dev, "failed to EC_CODEC_GET_CAPABILITIES\n"); + return ret; + } + priv->ec_capabilities = r.capabilities;
platform_set_drvdata(pdev, priv);
- return devm_snd_soc_register_component(dev, &i2s_rx_component_driver, - &i2s_rx_dai_driver, 1); + ret = devm_snd_soc_register_component(dev, &i2s_rx_component_driver, + &i2s_rx_dai_driver, 1); + if (ret) + return ret; + + return devm_snd_soc_register_component(dev, &wov_component_driver, + &wov_dai_driver, 1); }
#ifdef CONFIG_OF
Hi Tzung-Bi,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on asoc/for-next] [cannot apply to v5.4-rc3 next-20191014] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Tzung-Bi-Shih/ASoC-mediatek-mt8183-... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next config: x86_64-randconfig-g002-201941 (attached as .config) compiler: gcc-7 (Debian 7.4.0-13) 7.4.0 reproduce: # save the attached .config to linux build tree make ARCH=x86_64
If you fix the issue, kindly add following tag Reported-by: kbuild test robot lkp@intel.com
All errors (new ones prefixed by >>):
sound/soc/codecs/cros_ec_codec.c: In function 'cros_ec_codec_platform_probe':
sound/soc/codecs/cros_ec_codec.c:993:23: error: implicit declaration of function 'of_read_number'; did you mean 'wov_read_audio'? [-Werror=implicit-function-declaration]
priv->ec_shm_addr = of_read_number(regaddr_p, 2); ^~~~~~~~~~~~~~ wov_read_audio cc1: some warnings being treated as errors
vim +993 sound/soc/codecs/cros_ec_codec.c
973 974 static int cros_ec_codec_platform_probe(struct platform_device *pdev) 975 { 976 struct device *dev = &pdev->dev; 977 struct cros_ec_device *ec_device = dev_get_drvdata(pdev->dev.parent); 978 struct cros_ec_codec_priv *priv; 979 struct device_node *node; 980 struct resource res; 981 struct ec_param_ec_codec p; 982 struct ec_response_ec_codec_get_capabilities r; 983 int ret; 984 u64 ec_shm_size; 985 const __be32 *regaddr_p; 986 987 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 988 if (!priv) 989 return -ENOMEM; 990 991 regaddr_p = of_get_address(dev->of_node, 0, &ec_shm_size, NULL); 992 if (regaddr_p) {
993 priv->ec_shm_addr = of_read_number(regaddr_p, 2);
994 priv->ec_shm_len = ec_shm_size; 995 996 dev_dbg(dev, "ec_shm_addr=%#llx len=%#x\n", 997 priv->ec_shm_addr, priv->ec_shm_len); 998 } 999 1000 node = of_parse_phandle(dev->of_node, "memory-region", 0); 1001 if (node) { 1002 ret = of_address_to_resource(node, 0, &res); 1003 if (!ret) { 1004 priv->ap_shm_phys_addr = res.start; 1005 priv->ap_shm_len = resource_size(&res); 1006 priv->ap_shm_addr = 1007 (uint64_t)(uintptr_t)devm_ioremap_wc( 1008 dev, priv->ap_shm_phys_addr, 1009 priv->ap_shm_len); 1010 priv->ap_shm_last_alloc = priv->ap_shm_phys_addr; 1011 1012 dev_dbg(dev, "ap_shm_phys_addr=%#llx len=%#x\n", 1013 priv->ap_shm_phys_addr, priv->ap_shm_len); 1014 } 1015 } 1016 1017 priv->dev = dev; 1018 priv->ec_device = ec_device; 1019 atomic_set(&priv->dmic_probed, 0); 1020 1021 p.cmd = EC_CODEC_GET_CAPABILITIES; 1022 ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC, 1023 (uint8_t *)&p, sizeof(p), 1024 (uint8_t *)&r, sizeof(r)); 1025 if (ret) { 1026 dev_err(dev, "failed to EC_CODEC_GET_CAPABILITIES\n"); 1027 return ret; 1028 } 1029 priv->ec_capabilities = r.capabilities; 1030 1031 platform_set_drvdata(pdev, priv); 1032 1033 ret = devm_snd_soc_register_component(dev, &i2s_rx_component_driver, 1034 &i2s_rx_dai_driver, 1); 1035 if (ret) 1036 return ret; 1037 1038 return devm_snd_soc_register_component(dev, &wov_component_driver, 1039 &wov_dai_driver, 1); 1040 } 1041
--- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
On Tue, Oct 15, 2019 at 9:34 AM kbuild test robot lkp@intel.com wrote:
Hi Tzung-Bi,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on asoc/for-next] [cannot apply to v5.4-rc3 next-20191014] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Tzung-Bi-Shih/ASoC-mediatek-mt8183-... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next config: x86_64-randconfig-g002-201941 (attached as .config) compiler: gcc-7 (Debian 7.4.0-13) 7.4.0 reproduce: # save the attached .config to linux build tree make ARCH=x86_64
If you fix the issue, kindly add following tag Reported-by: kbuild test robot lkp@intel.com
All errors (new ones prefixed by >>):
sound/soc/codecs/cros_ec_codec.c: In function 'cros_ec_codec_platform_probe':
sound/soc/codecs/cros_ec_codec.c:993:23: error: implicit declaration of function 'of_read_number'; did you mean 'wov_read_audio'? [-Werror=implicit-function-declaration]
priv->ec_shm_addr = of_read_number(regaddr_p, 2); ^~~~~~~~~~~~~~ wov_read_audio
cc1: some warnings being treated as errors
The error is caused by EC_CODEC=y but CONFIG_OF is not set: $ grep -e 'CONFIG_OF.*' -e 'EC_CODEC' config # CONFIG_OF is not set CONFIG_SND_SOC_CROS_EC_CODEC=y
vim +993 sound/soc/codecs/cros_ec_codec.c
973 974 static int cros_ec_codec_platform_probe(struct platform_device *pdev) 975 { 976 struct device *dev = &pdev->dev; 977 struct cros_ec_device *ec_device = dev_get_drvdata(pdev->dev.parent); 978 struct cros_ec_codec_priv *priv; 979 struct device_node *node; 980 struct resource res; 981 struct ec_param_ec_codec p; 982 struct ec_response_ec_codec_get_capabilities r; 983 int ret; 984 u64 ec_shm_size; 985 const __be32 *regaddr_p; 986 987 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 988 if (!priv) 989 return -ENOMEM; 990 991 regaddr_p = of_get_address(dev->of_node, 0, &ec_shm_size, NULL); 992 if (regaddr_p) {
993 priv->ec_shm_addr = of_read_number(regaddr_p, 2);
In the case, the of_get_address( ) will get a dummy implementation (https://elixir.bootlin.com/linux/v5.3.6/source/include/linux/of_address.h#L8...) while of_read_number( ) won't (https://elixir.bootlin.com/linux/v5.3.6/source/include/linux/of.h#L234).
Rob, what do you think if we add a dummy implementation for of_read_number( ) if !CONFIG_OF (i.e. after https://elixir.bootlin.com/linux/v5.3.6/source/include/linux/of.h#L559)? Does it make sense?
994 priv->ec_shm_len = ec_shm_size; 995 996 dev_dbg(dev, "ec_shm_addr=%#llx len=%#x\n", 997 priv->ec_shm_addr, priv->ec_shm_len); 998 } 999 1000 node = of_parse_phandle(dev->of_node, "memory-region", 0); 1001 if (node) { 1002 ret = of_address_to_resource(node, 0, &res); 1003 if (!ret) { 1004 priv->ap_shm_phys_addr = res.start; 1005 priv->ap_shm_len = resource_size(&res); 1006 priv->ap_shm_addr = 1007 (uint64_t)(uintptr_t)devm_ioremap_wc( 1008 dev, priv->ap_shm_phys_addr, 1009 priv->ap_shm_len); 1010 priv->ap_shm_last_alloc = priv->ap_shm_phys_addr; 1011 1012 dev_dbg(dev, "ap_shm_phys_addr=%#llx len=%#x\n", 1013 priv->ap_shm_phys_addr, priv->ap_shm_len); 1014 } 1015 } 1016 1017 priv->dev = dev; 1018 priv->ec_device = ec_device; 1019 atomic_set(&priv->dmic_probed, 0); 1020 1021 p.cmd = EC_CODEC_GET_CAPABILITIES; 1022 ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC, 1023 (uint8_t *)&p, sizeof(p), 1024 (uint8_t *)&r, sizeof(r)); 1025 if (ret) { 1026 dev_err(dev, "failed to EC_CODEC_GET_CAPABILITIES\n"); 1027 return ret; 1028 } 1029 priv->ec_capabilities = r.capabilities; 1030 1031 platform_set_drvdata(pdev, priv); 1032 1033 ret = devm_snd_soc_register_component(dev, &i2s_rx_component_driver, 1034 &i2s_rx_dai_driver, 1); 1035 if (ret) 1036 return ret; 1037 1038 return devm_snd_soc_register_component(dev, &wov_component_driver, 1039 &wov_dai_driver, 1); 1040 } 1041
0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
On Tue, Oct 15, 2019 at 02:49:41PM +0800, Tzung-Bi Shih wrote:
On Tue, Oct 15, 2019 at 9:34 AM kbuild test robot lkp@intel.com wrote:
priv->ec_shm_addr = of_read_number(regaddr_p, 2); ^~~~~~~~~~~~~~ wov_read_audio
cc1: some warnings being treated as errors
The error is caused by EC_CODEC=y but CONFIG_OF is not set: $ grep -e 'CONFIG_OF.*' -e 'EC_CODEC' config # CONFIG_OF is not set CONFIG_SND_SOC_CROS_EC_CODEC=y
If that can happen there's missing dependencies in Kconfig or missing ifdefs in the code.
Hi Tzung-Bi,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on asoc/for-next] [cannot apply to v5.4-rc3 next-20191014] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Tzung-Bi-Shih/ASoC-mediatek-mt8183-... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
If you fix the issue, kindly add following tag Reported-by: kbuild test robot lkp@intel.com
coccinelle warnings: (new ones prefixed by >>)
sound/soc/codecs/cros_ec_codec.c:774:7-14: WARNING opportunity for memdup_user
vim +774 sound/soc/codecs/cros_ec_codec.c
754 755 static int wov_hotword_model_put(struct snd_kcontrol *kcontrol, 756 const unsigned int __user *bytes, 757 unsigned int size) 758 { 759 struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); 760 struct cros_ec_codec_priv *priv = 761 snd_soc_component_get_drvdata(component); 762 struct ec_param_ec_codec_wov p; 763 struct ec_response_ec_codec_wov_get_lang r; 764 uint8_t digest[SHA256_DIGEST_SIZE]; 765 uint8_t *buf; 766 int ret; 767 768 /* Skips the TLV header. */ 769 bytes += 2; 770 size -= 8; 771 772 dev_dbg(priv->dev, "%s: size=%d\n", __func__, size); 773
774 buf = kmalloc(size, GFP_KERNEL);
775 if (!buf) 776 return -ENOMEM; 777 778 if (copy_from_user(buf, bytes, size)) { 779 ret = -EFAULT; 780 goto leave; 781 } 782 783 ret = calculate_sha256(priv, buf, size, digest); 784 if (ret) 785 goto leave; 786 787 p.cmd = EC_CODEC_WOV_GET_LANG; 788 ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV, 789 (uint8_t *)&p, sizeof(p), 790 (uint8_t *)&r, sizeof(r)); 791 if (ret) 792 goto leave; 793 794 if (memcmp(digest, r.hash, SHA256_DIGEST_SIZE) == 0) { 795 dev_dbg(priv->dev, "not updated"); 796 goto leave; 797 } 798 799 if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_LANG_SHM)) 800 ret = wov_set_lang_shm(priv, buf, size, digest); 801 else 802 ret = wov_set_lang(priv, buf, size, digest); 803 804 leave: 805 kfree(buf); 806 return ret; 807 } 808
--- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
Switch mono DMIC on to support wake-on-voice.
Signed-off-by: Tzung-Bi Shih tzungbi@google.com --- sound/soc/codecs/mt6358.c | 105 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+)
diff --git a/sound/soc/codecs/mt6358.c b/sound/soc/codecs/mt6358.c index bb737fd678cc..1b830ea4f6ed 100644 --- a/sound/soc/codecs/mt6358.c +++ b/sound/soc/codecs/mt6358.c @@ -93,6 +93,8 @@ struct mt6358_priv { int mtkaif_protocol;
struct regulator *avdd_reg; + + int wov_enabled; };
int mt6358_set_mtkaif_protocol(struct snd_soc_component *cmpnt, @@ -464,6 +466,106 @@ static int mt6358_put_volsw(struct snd_kcontrol *kcontrol, return ret; }
+static void mt6358_restore_pga(struct mt6358_priv *priv); + +static int mt6358_enable_wov_phase2(struct mt6358_priv *priv) +{ + /* analog */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, + 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_DCXO_CW14, 0xffff, 0xa2b5); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + 0xffff, 0x0800); + mt6358_restore_pga(priv); + + regmap_update_bits(priv->regmap, MT6358_DCXO_CW13, 0xffff, 0x9929); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9, + 0xffff, 0x0025); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON8, + 0xffff, 0x0005); + + /* digital */ + regmap_update_bits(priv->regmap, MT6358_AUD_TOP_CKPDN_CON0, + 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3, 0xffff, 0x0120); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG0, 0xffff, 0xffff); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG1, 0xffff, 0x0200); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG2, 0xffff, 0x2424); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG3, 0xffff, 0xdbac); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG4, 0xffff, 0x029e); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG5, 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_POSDIV_CFG0, + 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_HPF_CFG0, + 0xffff, 0x0451); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_TOP, 0xffff, 0x68d1); + + return 0; +} + +static int mt6358_disable_wov_phase2(struct mt6358_priv *priv) +{ + /* digital */ + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_TOP, 0xffff, 0xc000); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_HPF_CFG0, + 0xffff, 0x0450); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_POSDIV_CFG0, + 0xffff, 0x0c00); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG5, 0xffff, 0x0100); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG4, 0xffff, 0x006c); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG3, 0xffff, 0xa879); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG2, 0xffff, 0x2323); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG1, 0xffff, 0x0400); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG0, 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3, 0xffff, 0x02d8); + regmap_update_bits(priv->regmap, MT6358_AUD_TOP_CKPDN_CON0, + 0xffff, 0x0000); + + /* analog */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON8, + 0xffff, 0x0004); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9, + 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_DCXO_CW13, 0xffff, 0x9829); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + 0xffff, 0x0000); + mt6358_restore_pga(priv); + regmap_update_bits(priv->regmap, MT6358_DCXO_CW14, 0xffff, 0xa2b5); + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, + 0xffff, 0x0010); + + return 0; +} + +static int mt6358_get_wov(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(c); + + ucontrol->value.integer.value[0] = priv->wov_enabled; + return 0; +} + +static int mt6358_put_wov(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(c); + int enabled = ucontrol->value.integer.value[0]; + + if (priv->wov_enabled != enabled) { + if (enabled) + mt6358_enable_wov_phase2(priv); + else + mt6358_disable_wov_phase2(priv); + + priv->wov_enabled = enabled; + } + + return 0; +} + static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0); static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 600, 0);
@@ -483,6 +585,9 @@ static const struct snd_kcontrol_new mt6358_snd_controls[] = { MT6358_AUDENC_ANA_CON0, MT6358_AUDENC_ANA_CON1, 8, 4, 0, snd_soc_get_volsw, mt6358_put_volsw, pga_tlv), + + SOC_SINGLE_BOOL_EXT("Wake-on-Voice Phase2 Switch", 0, + mt6358_get_wov, mt6358_put_wov), };
/* MUX */
Add an optional property "ec-codec". If specified, mt8183 could use the "wake on voice" feature offered by EC codec.
Signed-off-by: Tzung-Bi Shih tzungbi@google.com --- .../bindings/sound/mt8183-mt6358-ts3a227-max98357.txt | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/mt8183-mt6358-ts3a227-max98357.txt b/Documentation/devicetree/bindings/sound/mt8183-mt6358-ts3a227-max98357.txt index 17ff3892f439..decaa013a07e 100644 --- a/Documentation/devicetree/bindings/sound/mt8183-mt6358-ts3a227-max98357.txt +++ b/Documentation/devicetree/bindings/sound/mt8183-mt6358-ts3a227-max98357.txt @@ -6,12 +6,15 @@ Required properties:
Optional properties: - mediatek,headset-codec: the phandles of ts3a227 codecs +- mediatek,ec-codec: the phandle of EC codecs. + See google,cros-ec-codec.txt for more details.
Example:
sound { compatible = "mediatek,mt8183_mt6358_ts3a227_max98357"; mediatek,headset-codec = <&ts3a227>; + mediatek,ec-codec = <&ec_codec>; mediatek,platform = <&afe>; };
On Mon, 14 Oct 2019 18:20:21 +0800, Tzung-Bi Shih wrote:
Add an optional property "ec-codec". If specified, mt8183 could use the "wake on voice" feature offered by EC codec.
Signed-off-by: Tzung-Bi Shih tzungbi@google.com
.../bindings/sound/mt8183-mt6358-ts3a227-max98357.txt | 3 +++ 1 file changed, 3 insertions(+)
Acked-by: Rob Herring robh@kernel.org
Add DAI link and pin muxing for wake on voice.
Signed-off-by: Tzung-Bi Shih tzungbi@google.com --- sound/soc/mediatek/Kconfig | 1 + .../mt8183/mt8183-mt6358-ts3a227-max98357.c | 70 ++++++++++++++++++- 2 files changed, 68 insertions(+), 3 deletions(-)
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 111e44b64b38..8b29f3979899 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -125,6 +125,7 @@ config SND_SOC_MT8183_MT6358_TS3A227E_MAX98357A select SND_SOC_MAX98357A select SND_SOC_BT_SCO select SND_SOC_TS3A227E + select SND_SOC_CROS_EC_CODEC help This adds ASoC driver for Mediatek MT8183 boards with the MT6358 TS3A227E MAX98357A audio codec. diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c index bb9cdc0d6552..0555f7d73d05 100644 --- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c @@ -19,11 +19,12 @@ enum PINCTRL_PIN_STATE { PIN_STATE_DEFAULT = 0, PIN_TDM_OUT_ON, PIN_TDM_OUT_OFF, + PIN_WOV, PIN_STATE_MAX };
static const char * const mt8183_pin_str[PIN_STATE_MAX] = { - "default", "aud_tdm_out_on", "aud_tdm_out_off", + "default", "aud_tdm_out_on", "aud_tdm_out_off", "wov", };
struct mt8183_mt6358_ts3a227_max98357_priv { @@ -142,6 +143,11 @@ SND_SOC_DAILINK_DEFS(playback_hdmi, DAILINK_COMP_ARRAY(COMP_DUMMY()), DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(wake_on_voice, + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + /* BE */ SND_SOC_DAILINK_DEFS(primary_codec, DAILINK_COMP_ARRAY(COMP_CPU("ADDA")), @@ -229,6 +235,41 @@ static struct snd_soc_ops mt8183_mt6358_tdm_ops = { .shutdown = mt8183_mt6358_tdm_shutdown, };
+static int +mt8183_mt6358_ts3a227_max98357_wov_startup( + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct mt8183_mt6358_ts3a227_max98357_priv *priv = + snd_soc_card_get_drvdata(card); + + return pinctrl_select_state(priv->pinctrl, + priv->pin_states[PIN_WOV]); +} + +static void +mt8183_mt6358_ts3a227_max98357_wov_shutdown( + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct mt8183_mt6358_ts3a227_max98357_priv *priv = + snd_soc_card_get_drvdata(card); + int ret; + + ret = pinctrl_select_state(priv->pinctrl, + priv->pin_states[PIN_STATE_DEFAULT]); + if (ret) + dev_err(card->dev, "%s failed to select state %d\n", + __func__, ret); +} + +static const struct snd_soc_ops mt8183_mt6358_ts3a227_max98357_wov_ops = { + .startup = mt8183_mt6358_ts3a227_max98357_wov_startup, + .shutdown = mt8183_mt6358_ts3a227_max98357_wov_shutdown, +}; + static struct snd_soc_dai_link mt8183_mt6358_ts3a227_max98357_dai_links[] = { /* FE */ @@ -306,6 +347,15 @@ mt8183_mt6358_ts3a227_max98357_dai_links[] = { .dpcm_playback = 1, SND_SOC_DAILINK_REG(playback_hdmi), }, + { + .name = "Wake on Voice", + .stream_name = "Wake on Voice", + .ignore_suspend = 1, + .ignore = 1, + SND_SOC_DAILINK_REG(wake_on_voice), + .ops = &mt8183_mt6358_ts3a227_max98357_wov_ops, + }, + /* BE */ { .name = "Primary Codec", @@ -429,7 +479,7 @@ static int mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev) { struct snd_soc_card *card = &mt8183_mt6358_ts3a227_max98357_card; - struct device_node *platform_node; + struct device_node *platform_node, *ec_codec; struct snd_soc_dai_link *dai_link; struct mt8183_mt6358_ts3a227_max98357_priv *priv; int ret; @@ -444,10 +494,24 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev) return -EINVAL; }
+ ec_codec = of_parse_phandle(pdev->dev.of_node, "mediatek,ec-codec", 0); + for_each_card_prelinks(card, i, dai_link) { if (dai_link->platforms->name) continue; - dai_link->platforms->of_node = platform_node; + + if (ec_codec && strcmp(dai_link->name, "Wake on Voice") == 0) { + dai_link->cpus[0].name = NULL; + dai_link->cpus[0].of_node = ec_codec; + dai_link->cpus[0].dai_name = NULL; + dai_link->codecs[0].name = NULL; + dai_link->codecs[0].of_node = ec_codec; + dai_link->codecs[0].dai_name = "Wake on Voice"; + dai_link->platforms[0].of_node = ec_codec; + dai_link->ignore = 0; + } else { + dai_link->platforms->of_node = platform_node; + } }
mt8183_mt6358_ts3a227_max98357_headset_dev.dlc.of_node =
On Mon, Oct 14, 2019 at 6:20 PM Tzung-Bi Shih tzungbi@google.com wrote:
This series makes mt6358, cros_ec_codec, and mt8183-mt6358-ts3a227-max98357 support WoV (wake on voice).
The first 3 commits are some cleanups and refactors. It looks like breaking the existing interface. But please be noticed that, the cros_ec_codec has not used by any real device yet. The refactor is very necessary to keep the style consistent and for easier to further extend and maintain. platform/chrome: cros_ec: remove unused EC feature ASoC: cros_ec_codec: refactor I2S RX ASoC: cros_ec_codec: extract DMIC EC command from I2S RX
The 4th commit extends the feature offered from EC codec. platform/chrome: cros_ec: add common commands for EC codec
The 5th commit changes the behavior of setting and getting DMIC gains. ASoC: cros_ec_codec: read max DMIC gain from EC codec
The 6th and 7th commit make cros_ec_codec support WoV. ASoC: dt-bindings: cros_ec_codec: add SHM bindings ASoC: cros_ec_codec: support WoV
The 8th commit sets necessary registers on mt6358 to support WoV. ASoC: mediatek: mt6358: support WoV
The last 2 commit make machine driver mt8183-mt6358-ts3a227-max98357 support WoV if ec-codec is in DTS. ASoC: dt-bindings: mt8183: add ec-codec ASoC: mediatek: mt8183: support WoV
Changes from v1:
- fix a compile error and make kbuild bot happy
https://mailman.alsa-project.org/pipermail/alsa-devel/2019-October/156315.ht... Changes from v2:
- rebase upon to "don't use snd_pcm_ops" series
https://mailman.alsa-project.org/pipermail/alsa-devel/2019-October/156170.ht...
- fix sparse errors
https://mailman.alsa-project.org/pipermail/alsa-devel/2019-October/156328.ht...
- use "reg" for SHM binding
https://mailman.alsa-project.org/pipermail/alsa-devel/2019-October/156657.ht...
Tzung-Bi Shih (10): WIP: platform/chrome: cros_ec: remove unused EC feature WIP: ASoC: cros_ec_codec: refactor I2S RX WIP: ASoC: cros_ec_codec: extract DMIC EC command from I2S RX WIP: platform/chrome: cros_ec: add common commands for EC codec WIP: ASoC: cros_ec_codec: read max DMIC gain from EC codec WIP: ASoC: dt-bindings: cros_ec_codec: add SHM bindings WIP: ASoC: cros_ec_codec: support WoV WIP: ASoC: mediatek: mt6358: support WoV WIP: ASoC: dt-bindings: mt8183: add ec-codec WIP: ASoC: mediatek: mt8183: support WoV
Ha..I noticed here are some "WIP" prefixes forgot to remove. To not generate too much flood, will fix in later versions.
.../bindings/sound/google,cros-ec-codec.txt | 24 +- .../sound/mt8183-mt6358-ts3a227-max98357.txt | 3 + drivers/platform/chrome/cros_ec_trace.c | 5 +- .../linux/platform_data/cros_ec_commands.h | 285 ++++- sound/soc/codecs/cros_ec_codec.c | 1128 +++++++++++++---- sound/soc/codecs/mt6358.c | 105 ++ sound/soc/mediatek/Kconfig | 1 + .../mt8183/mt8183-mt6358-ts3a227-max98357.c | 70 +- 8 files changed, 1296 insertions(+), 325 deletions(-)
-- 2.23.0.700.g56cf767bdb-goog
participants (5)
-
Benson Leung
-
kbuild test robot
-
Mark Brown
-
Rob Herring
-
Tzung-Bi Shih