summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhani Kumar Uppalapati <phaniu@codeaurora.org>2016-08-19 00:13:15 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2016-08-26 11:59:14 -0700
commit68eef60c8f9764efe34ca888bdbf3dd203441181 (patch)
treea6c0d9b42ed8ee85e00edcbf3a6dda95b88b2604
parent10b823cd45fd688d25f5c82765cc5a90ea8208a7 (diff)
ASoC: wcd934x: Add DSD volume support
Add support for adjusting volume when DSD (Direct Stream Digital) audio playback is in progress. Change-Id: Ica51d40911d16059e8af21c60794b35c68bb695d Signed-off-by: Phani Kumar Uppalapati <phaniu@codeaurora.org>
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-dsd.c126
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-dsd.h2
2 files changed, 126 insertions, 2 deletions
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.c b/sound/soc/codecs/wcd934x/wcd934x-dsd.c
index ef20c53eeb96..246b3bfab876 100644
--- a/sound/soc/codecs/wcd934x/wcd934x-dsd.c
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.c
@@ -13,9 +13,24 @@
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/mfd/wcd934x/registers.h>
+#include <sound/tlv.h>
#include <sound/control.h>
#include "wcd934x-dsd.h"
+#define DSD_VOLUME_MAX_0dB 0
+#define DSD_VOLUME_MIN_M110dB -110
+
+#define DSD_VOLUME_RANGE_CHECK(x) ((x >= DSD_VOLUME_MIN_M110dB) &&\
+ (x <= DSD_VOLUME_MAX_0dB))
+#define DSD_VOLUME_STEPS 3
+#define DSD_VOLUME_UPDATE_DELAY_MS 30
+#define DSD_VOLUME_USLEEP_MARGIN_US 100
+#define DSD_VOLUME_STEP_DELAY_US ((1000 * DSD_VOLUME_UPDATE_DELAY_MS) / \
+ (2 * DSD_VOLUME_STEPS))
+
+static const DECLARE_TLV_DB_MINMAX(tavil_dsd_db_scale, DSD_VOLUME_MIN_M110dB,
+ DSD_VOLUME_MAX_0dB);
+
static const char *const dsd_if_text[] = {
"ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7",
"DSD_DATA_PAD"
@@ -358,6 +373,7 @@ static int tavil_enable_dsd(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec);
int rc, clk_users;
int interp_idx;
u8 pcm_rate_val;
@@ -404,7 +420,7 @@ static int tavil_enable_dsd(struct snd_soc_dapm_widget *w,
0x01, 0x01);
/* Apply Gain */
snd_soc_write(codec, WCD934X_CDC_DSD0_CFG1,
- snd_soc_read(codec, WCD934X_CDC_DSD0_CFG1));
+ dsd_conf->volume[DSD0]);
if (clk_users > 1)
snd_soc_update_bits(codec,
@@ -419,7 +435,7 @@ static int tavil_enable_dsd(struct snd_soc_dapm_widget *w,
0x01, 0x01);
/* Apply Gain */
snd_soc_write(codec, WCD934X_CDC_DSD1_CFG1,
- snd_soc_read(codec, WCD934X_CDC_DSD1_CFG1));
+ dsd_conf->volume[DSD1]);
if (clk_users > 1)
snd_soc_update_bits(codec,
@@ -457,6 +473,103 @@ static int tavil_enable_dsd(struct snd_soc_dapm_widget *w,
return 0;
}
+static int tavil_dsd_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = DSD_VOLUME_MIN_M110dB;
+ uinfo->value.integer.max = DSD_VOLUME_MAX_0dB;
+
+ return 0;
+}
+
+static int tavil_dsd_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec);
+ int nv[DSD_MAX], cv[DSD_MAX];
+ int step_size, nv1;
+ int i, dsd_idx;
+
+ if (!dsd_conf)
+ return 0;
+
+ mutex_lock(&dsd_conf->vol_mutex);
+
+ for (dsd_idx = DSD0; dsd_idx < DSD_MAX; dsd_idx++) {
+ cv[dsd_idx] = dsd_conf->volume[dsd_idx];
+ nv[dsd_idx] = ucontrol->value.integer.value[dsd_idx];
+ }
+
+ if ((!DSD_VOLUME_RANGE_CHECK(nv[DSD0])) ||
+ (!DSD_VOLUME_RANGE_CHECK(nv[DSD1])))
+ goto done;
+
+ for (dsd_idx = DSD0; dsd_idx < DSD_MAX; dsd_idx++) {
+ if (cv[dsd_idx] == nv[dsd_idx])
+ continue;
+
+ dev_dbg(codec->dev, "%s: DSD%d cur.vol: %d, new vol: %d\n",
+ __func__, dsd_idx, cv[dsd_idx], nv[dsd_idx]);
+
+ step_size = (nv[dsd_idx] - cv[dsd_idx]) /
+ DSD_VOLUME_STEPS;
+
+ nv1 = cv[dsd_idx];
+
+ for (i = 0; i < DSD_VOLUME_STEPS; i++) {
+ nv1 += step_size;
+ snd_soc_write(codec,
+ WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx,
+ nv1);
+ /* sleep required after each volume step */
+ usleep_range(DSD_VOLUME_STEP_DELAY_US,
+ (DSD_VOLUME_STEP_DELAY_US +
+ DSD_VOLUME_USLEEP_MARGIN_US));
+ }
+ if (nv1 != nv[dsd_idx])
+ snd_soc_write(codec,
+ WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx,
+ nv[dsd_idx]);
+
+ dsd_conf->volume[dsd_idx] = nv[dsd_idx];
+ }
+
+done:
+ mutex_unlock(&dsd_conf->vol_mutex);
+
+ return 0;
+}
+
+static int tavil_dsd_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec);
+
+ if (dsd_conf) {
+ ucontrol->value.integer.value[0] = dsd_conf->volume[DSD0];
+ ucontrol->value.integer.value[1] = dsd_conf->volume[DSD1];
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new tavil_dsd_vol_controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "DSD Volume",
+ .info = tavil_dsd_vol_info,
+ .get = tavil_dsd_vol_get,
+ .put = tavil_dsd_vol_put,
+ .tlv = { .p = tavil_dsd_db_scale },
+ },
+};
+
static const struct snd_soc_dapm_widget tavil_dsd_widgets[] = {
SND_SOC_DAPM_MUX("DSD_L IF MUX", SND_SOC_NOPM, 0, 0, &dsd_l_if_mux),
SND_SOC_DAPM_MUX_E("DSD_FILTER_0", SND_SOC_NOPM, 0, 0, &dsd_filt0_mux,
@@ -528,6 +641,13 @@ struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(dapm, tavil_dsd_audio_map,
ARRAY_SIZE(tavil_dsd_audio_map));
+ mutex_init(&dsd_conf->vol_mutex);
+ dsd_conf->volume[DSD0] = DSD_VOLUME_MAX_0dB;
+ dsd_conf->volume[DSD1] = DSD_VOLUME_MAX_0dB;
+
+ snd_soc_add_codec_controls(codec, tavil_dsd_vol_controls,
+ ARRAY_SIZE(tavil_dsd_vol_controls));
+
/* Enable DSD Interrupts */
snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x00);
@@ -549,6 +669,8 @@ void tavil_dsd_deinit(struct tavil_dsd_config *dsd_conf)
codec = dsd_conf->codec;
+ mutex_destroy(&dsd_conf->vol_mutex);
+
/* Disable DSD Interrupts */
snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x08);
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.h b/sound/soc/codecs/wcd934x/wcd934x-dsd.h
index 884e41d797d9..c033795beb9b 100644
--- a/sound/soc/codecs/wcd934x/wcd934x-dsd.h
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.h
@@ -38,6 +38,8 @@ struct tavil_dsd_config {
struct snd_soc_codec *codec;
unsigned int dsd_interp_mixer[INTERP_MAX];
u32 base_sample_rate[DSD_MAX];
+ int volume[DSD_MAX];
+ struct mutex vol_mutex;
};
#ifdef CONFIG_SND_SOC_WCD934X_DSD