| From f21a9df3f7cb0005947679d7b9237c90574e229a Mon Sep 17 00:00:00 2001 |
| From: Sameer Pujar <spujar@nvidia.com> |
| Date: Thu, 18 Nov 2021 12:37:03 +0530 |
| Subject: ASoC: tegra: Fix kcontrol put callback in I2S |
| |
| From: Sameer Pujar <spujar@nvidia.com> |
| |
| commit f21a9df3f7cb0005947679d7b9237c90574e229a upstream. |
| |
| The kcontrol put callback is expected to return 1 when there is change |
| in HW or when the update is acknowledged by driver. This would ensure |
| that change notifications are sent to subscribed applications. Update |
| the I2S driver accordingly. |
| |
| Fixes: c0bfa98349d1 ("ASoC: tegra: Add Tegra210 based I2S driver") |
| Suggested-by: Jaroslav Kysela <perex@perex.cz> |
| Suggested-by: Mark Brown <broonie@kernel.org> |
| Signed-off-by: Sameer Pujar <spujar@nvidia.com> |
| Reviewed-by: Takashi Iwai <tiwai@suse.de> |
| Link: https://lore.kernel.org/r/1637219231-406-9-git-send-email-spujar@nvidia.com |
| Signed-off-by: Mark Brown <broonie@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| sound/soc/tegra/tegra210_i2s.c | 322 ++++++++++++++++++++++++++++++----------- |
| 1 file changed, 236 insertions(+), 86 deletions(-) |
| |
| --- a/sound/soc/tegra/tegra210_i2s.c |
| +++ b/sound/soc/tegra/tegra210_i2s.c |
| @@ -302,91 +302,235 @@ static int tegra210_i2s_set_tdm_slot(str |
| return 0; |
| } |
| |
| -static int tegra210_i2s_set_dai_bclk_ratio(struct snd_soc_dai *dai, |
| - unsigned int ratio) |
| +static int tegra210_i2s_get_loopback(struct snd_kcontrol *kcontrol, |
| + struct snd_ctl_elem_value *ucontrol) |
| { |
| - struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
| + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); |
| + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); |
| |
| - i2s->bclk_ratio = ratio; |
| + ucontrol->value.integer.value[0] = i2s->loopback; |
| |
| return 0; |
| } |
| |
| -static int tegra210_i2s_get_control(struct snd_kcontrol *kcontrol, |
| - struct snd_ctl_elem_value *ucontrol) |
| +static int tegra210_i2s_put_loopback(struct snd_kcontrol *kcontrol, |
| + struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); |
| struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); |
| + int value = ucontrol->value.integer.value[0]; |
| |
| - if (strstr(kcontrol->id.name, "Loopback")) |
| - ucontrol->value.integer.value[0] = i2s->loopback; |
| - else if (strstr(kcontrol->id.name, "FSYNC Width")) |
| - ucontrol->value.integer.value[0] = i2s->fsync_width; |
| - else if (strstr(kcontrol->id.name, "Capture Stereo To Mono")) |
| - ucontrol->value.enumerated.item[0] = |
| - i2s->stereo_to_mono[I2S_TX_PATH]; |
| - else if (strstr(kcontrol->id.name, "Capture Mono To Stereo")) |
| - ucontrol->value.enumerated.item[0] = |
| - i2s->mono_to_stereo[I2S_TX_PATH]; |
| - else if (strstr(kcontrol->id.name, "Playback Stereo To Mono")) |
| - ucontrol->value.enumerated.item[0] = |
| - i2s->stereo_to_mono[I2S_RX_PATH]; |
| - else if (strstr(kcontrol->id.name, "Playback Mono To Stereo")) |
| - ucontrol->value.enumerated.item[0] = |
| - i2s->mono_to_stereo[I2S_RX_PATH]; |
| - else if (strstr(kcontrol->id.name, "Playback FIFO Threshold")) |
| - ucontrol->value.integer.value[0] = i2s->rx_fifo_th; |
| - else if (strstr(kcontrol->id.name, "BCLK Ratio")) |
| - ucontrol->value.integer.value[0] = i2s->bclk_ratio; |
| - |
| - return 0; |
| -} |
| - |
| -static int tegra210_i2s_put_control(struct snd_kcontrol *kcontrol, |
| - struct snd_ctl_elem_value *ucontrol) |
| -{ |
| - struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); |
| - struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); |
| - |
| - if (strstr(kcontrol->id.name, "Loopback")) { |
| - i2s->loopback = ucontrol->value.integer.value[0]; |
| - |
| - regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL, |
| - I2S_CTRL_LPBK_MASK, |
| - i2s->loopback << I2S_CTRL_LPBK_SHIFT); |
| - |
| - } else if (strstr(kcontrol->id.name, "FSYNC Width")) { |
| - /* |
| - * Frame sync width is used only for FSYNC modes and not |
| - * applicable for LRCK modes. Reset value for this field is "0", |
| - * which means the width is one bit clock wide. |
| - * The width requirement may depend on the codec and in such |
| - * cases mixer control is used to update custom values. A value |
| - * of "N" here means, width is "N + 1" bit clock wide. |
| - */ |
| - i2s->fsync_width = ucontrol->value.integer.value[0]; |
| - |
| - regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL, |
| - I2S_CTRL_FSYNC_WIDTH_MASK, |
| - i2s->fsync_width << I2S_FSYNC_WIDTH_SHIFT); |
| - |
| - } else if (strstr(kcontrol->id.name, "Capture Stereo To Mono")) { |
| - i2s->stereo_to_mono[I2S_TX_PATH] = |
| - ucontrol->value.enumerated.item[0]; |
| - } else if (strstr(kcontrol->id.name, "Capture Mono To Stereo")) { |
| - i2s->mono_to_stereo[I2S_TX_PATH] = |
| - ucontrol->value.enumerated.item[0]; |
| - } else if (strstr(kcontrol->id.name, "Playback Stereo To Mono")) { |
| - i2s->stereo_to_mono[I2S_RX_PATH] = |
| - ucontrol->value.enumerated.item[0]; |
| - } else if (strstr(kcontrol->id.name, "Playback Mono To Stereo")) { |
| - i2s->mono_to_stereo[I2S_RX_PATH] = |
| - ucontrol->value.enumerated.item[0]; |
| - } else if (strstr(kcontrol->id.name, "Playback FIFO Threshold")) { |
| - i2s->rx_fifo_th = ucontrol->value.integer.value[0]; |
| - } else if (strstr(kcontrol->id.name, "BCLK Ratio")) { |
| - i2s->bclk_ratio = ucontrol->value.integer.value[0]; |
| - } |
| + if (value == i2s->loopback) |
| + return 0; |
| + |
| + i2s->loopback = value; |
| + |
| + regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL, I2S_CTRL_LPBK_MASK, |
| + i2s->loopback << I2S_CTRL_LPBK_SHIFT); |
| + |
| + return 1; |
| +} |
| + |
| +static int tegra210_i2s_get_fsync_width(struct snd_kcontrol *kcontrol, |
| + struct snd_ctl_elem_value *ucontrol) |
| +{ |
| + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); |
| + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); |
| + |
| + ucontrol->value.integer.value[0] = i2s->fsync_width; |
| + |
| + return 0; |
| +} |
| + |
| +static int tegra210_i2s_put_fsync_width(struct snd_kcontrol *kcontrol, |
| + struct snd_ctl_elem_value *ucontrol) |
| +{ |
| + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); |
| + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); |
| + int value = ucontrol->value.integer.value[0]; |
| + |
| + if (value == i2s->fsync_width) |
| + return 0; |
| + |
| + i2s->fsync_width = value; |
| + |
| + /* |
| + * Frame sync width is used only for FSYNC modes and not |
| + * applicable for LRCK modes. Reset value for this field is "0", |
| + * which means the width is one bit clock wide. |
| + * The width requirement may depend on the codec and in such |
| + * cases mixer control is used to update custom values. A value |
| + * of "N" here means, width is "N + 1" bit clock wide. |
| + */ |
| + regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL, |
| + I2S_CTRL_FSYNC_WIDTH_MASK, |
| + i2s->fsync_width << I2S_FSYNC_WIDTH_SHIFT); |
| + |
| + return 1; |
| +} |
| + |
| +static int tegra210_i2s_cget_stereo_to_mono(struct snd_kcontrol *kcontrol, |
| + struct snd_ctl_elem_value *ucontrol) |
| +{ |
| + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); |
| + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); |
| + |
| + ucontrol->value.enumerated.item[0] = i2s->stereo_to_mono[I2S_TX_PATH]; |
| + |
| + return 0; |
| +} |
| + |
| +static int tegra210_i2s_cput_stereo_to_mono(struct snd_kcontrol *kcontrol, |
| + struct snd_ctl_elem_value *ucontrol) |
| +{ |
| + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); |
| + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); |
| + unsigned int value = ucontrol->value.enumerated.item[0]; |
| + |
| + if (value == i2s->stereo_to_mono[I2S_TX_PATH]) |
| + return 0; |
| + |
| + i2s->stereo_to_mono[I2S_TX_PATH] = value; |
| + |
| + return 1; |
| +} |
| + |
| +static int tegra210_i2s_cget_mono_to_stereo(struct snd_kcontrol *kcontrol, |
| + struct snd_ctl_elem_value *ucontrol) |
| +{ |
| + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); |
| + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); |
| + |
| + ucontrol->value.enumerated.item[0] = i2s->mono_to_stereo[I2S_TX_PATH]; |
| + |
| + return 0; |
| +} |
| + |
| +static int tegra210_i2s_cput_mono_to_stereo(struct snd_kcontrol *kcontrol, |
| + struct snd_ctl_elem_value *ucontrol) |
| +{ |
| + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); |
| + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); |
| + unsigned int value = ucontrol->value.enumerated.item[0]; |
| + |
| + if (value == i2s->mono_to_stereo[I2S_TX_PATH]) |
| + return 0; |
| + |
| + i2s->mono_to_stereo[I2S_TX_PATH] = value; |
| + |
| + return 1; |
| +} |
| + |
| +static int tegra210_i2s_pget_stereo_to_mono(struct snd_kcontrol *kcontrol, |
| + struct snd_ctl_elem_value *ucontrol) |
| +{ |
| + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); |
| + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); |
| + |
| + ucontrol->value.enumerated.item[0] = i2s->stereo_to_mono[I2S_RX_PATH]; |
| + |
| + return 0; |
| +} |
| + |
| +static int tegra210_i2s_pput_stereo_to_mono(struct snd_kcontrol *kcontrol, |
| + struct snd_ctl_elem_value *ucontrol) |
| +{ |
| + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); |
| + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); |
| + unsigned int value = ucontrol->value.enumerated.item[0]; |
| + |
| + if (value == i2s->stereo_to_mono[I2S_RX_PATH]) |
| + return 0; |
| + |
| + i2s->stereo_to_mono[I2S_RX_PATH] = value; |
| + |
| + return 1; |
| +} |
| + |
| +static int tegra210_i2s_pget_mono_to_stereo(struct snd_kcontrol *kcontrol, |
| + struct snd_ctl_elem_value *ucontrol) |
| +{ |
| + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); |
| + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); |
| + |
| + ucontrol->value.enumerated.item[0] = i2s->mono_to_stereo[I2S_RX_PATH]; |
| + |
| + return 0; |
| +} |
| + |
| +static int tegra210_i2s_pput_mono_to_stereo(struct snd_kcontrol *kcontrol, |
| + struct snd_ctl_elem_value *ucontrol) |
| +{ |
| + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); |
| + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); |
| + unsigned int value = ucontrol->value.enumerated.item[0]; |
| + |
| + if (value == i2s->mono_to_stereo[I2S_RX_PATH]) |
| + return 0; |
| + |
| + i2s->mono_to_stereo[I2S_RX_PATH] = value; |
| + |
| + return 1; |
| +} |
| + |
| +static int tegra210_i2s_pget_fifo_th(struct snd_kcontrol *kcontrol, |
| + struct snd_ctl_elem_value *ucontrol) |
| +{ |
| + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); |
| + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); |
| + |
| + ucontrol->value.integer.value[0] = i2s->rx_fifo_th; |
| + |
| + return 0; |
| +} |
| + |
| +static int tegra210_i2s_pput_fifo_th(struct snd_kcontrol *kcontrol, |
| + struct snd_ctl_elem_value *ucontrol) |
| +{ |
| + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); |
| + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); |
| + int value = ucontrol->value.integer.value[0]; |
| + |
| + if (value == i2s->rx_fifo_th) |
| + return 0; |
| + |
| + i2s->rx_fifo_th = value; |
| + |
| + return 1; |
| +} |
| + |
| +static int tegra210_i2s_get_bclk_ratio(struct snd_kcontrol *kcontrol, |
| + struct snd_ctl_elem_value *ucontrol) |
| +{ |
| + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); |
| + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); |
| + |
| + ucontrol->value.integer.value[0] = i2s->bclk_ratio; |
| + |
| + return 0; |
| +} |
| + |
| +static int tegra210_i2s_put_bclk_ratio(struct snd_kcontrol *kcontrol, |
| + struct snd_ctl_elem_value *ucontrol) |
| +{ |
| + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); |
| + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); |
| + int value = ucontrol->value.integer.value[0]; |
| + |
| + if (value == i2s->bclk_ratio) |
| + return 0; |
| + |
| + i2s->bclk_ratio = value; |
| + |
| + return 1; |
| +} |
| + |
| +static int tegra210_i2s_set_dai_bclk_ratio(struct snd_soc_dai *dai, |
| + unsigned int ratio) |
| +{ |
| + struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
| + |
| + i2s->bclk_ratio = ratio; |
| |
| return 0; |
| } |
| @@ -604,22 +748,28 @@ static const struct soc_enum tegra210_i2 |
| tegra210_i2s_stereo_conv_text); |
| |
| static const struct snd_kcontrol_new tegra210_i2s_controls[] = { |
| - SOC_SINGLE_EXT("Loopback", 0, 0, 1, 0, tegra210_i2s_get_control, |
| - tegra210_i2s_put_control), |
| - SOC_SINGLE_EXT("FSYNC Width", 0, 0, 255, 0, tegra210_i2s_get_control, |
| - tegra210_i2s_put_control), |
| + SOC_SINGLE_EXT("Loopback", 0, 0, 1, 0, tegra210_i2s_get_loopback, |
| + tegra210_i2s_put_loopback), |
| + SOC_SINGLE_EXT("FSYNC Width", 0, 0, 255, 0, |
| + tegra210_i2s_get_fsync_width, |
| + tegra210_i2s_put_fsync_width), |
| SOC_ENUM_EXT("Capture Stereo To Mono", tegra210_i2s_stereo_conv_enum, |
| - tegra210_i2s_get_control, tegra210_i2s_put_control), |
| + tegra210_i2s_cget_stereo_to_mono, |
| + tegra210_i2s_cput_stereo_to_mono), |
| SOC_ENUM_EXT("Capture Mono To Stereo", tegra210_i2s_mono_conv_enum, |
| - tegra210_i2s_get_control, tegra210_i2s_put_control), |
| + tegra210_i2s_cget_mono_to_stereo, |
| + tegra210_i2s_cput_mono_to_stereo), |
| SOC_ENUM_EXT("Playback Stereo To Mono", tegra210_i2s_stereo_conv_enum, |
| - tegra210_i2s_get_control, tegra210_i2s_put_control), |
| + tegra210_i2s_pget_mono_to_stereo, |
| + tegra210_i2s_pput_mono_to_stereo), |
| SOC_ENUM_EXT("Playback Mono To Stereo", tegra210_i2s_mono_conv_enum, |
| - tegra210_i2s_get_control, tegra210_i2s_put_control), |
| + tegra210_i2s_pget_stereo_to_mono, |
| + tegra210_i2s_pput_stereo_to_mono), |
| SOC_SINGLE_EXT("Playback FIFO Threshold", 0, 0, I2S_RX_FIFO_DEPTH - 1, |
| - 0, tegra210_i2s_get_control, tegra210_i2s_put_control), |
| - SOC_SINGLE_EXT("BCLK Ratio", 0, 0, INT_MAX, 0, tegra210_i2s_get_control, |
| - tegra210_i2s_put_control), |
| + 0, tegra210_i2s_pget_fifo_th, tegra210_i2s_pput_fifo_th), |
| + SOC_SINGLE_EXT("BCLK Ratio", 0, 0, INT_MAX, 0, |
| + tegra210_i2s_get_bclk_ratio, |
| + tegra210_i2s_put_bclk_ratio), |
| }; |
| |
| static const struct snd_soc_dapm_widget tegra210_i2s_widgets[] = { |