ASoC: wcd934x: Enable VI path on WCD9340
Speaker protection algorithm needs V(voltage) and I(current) readings from speaker. Enable VI path on the codec to provide readings to the speaker protection algorithm. Change-Id: I741e7f3076a7db595297ad7a1ee551c46d5c8213 Signed-off-by: Sudheer Papothi <spapothi@codeaurora.org>
This commit is contained in:
parent
f303bb651b
commit
c2108e1e67
2 changed files with 264 additions and 0 deletions
|
@ -114,6 +114,11 @@ const struct snd_soc_dapm_route tavil_slim_audio_map[] = {
|
|||
|
||||
const struct snd_soc_dapm_route tavil_audio_map[] = {
|
||||
|
||||
/* VI Feedback */
|
||||
{"AIF4_VI Mixer", "SPKR_VI_1", "VIINPUT"},
|
||||
{"AIF4_VI Mixer", "SPKR_VI_2", "VIINPUT"},
|
||||
{"AIF4 VI", NULL, "AIF4_VI Mixer"},
|
||||
|
||||
/* CDC Tx interface with SLIMBUS */
|
||||
{"SLIM TX0", NULL, "CDC_IF TX0 MUX"},
|
||||
{"SLIM TX1", NULL, "CDC_IF TX1 MUX"},
|
||||
|
|
|
@ -122,6 +122,8 @@ static const struct snd_kcontrol_new name##_mux = \
|
|||
#define CF_MIN_3DB_150HZ 0x2
|
||||
|
||||
enum {
|
||||
VI_SENSE_1,
|
||||
VI_SENSE_2,
|
||||
AUDIO_NOMINAL,
|
||||
HPH_PA_DELAY,
|
||||
};
|
||||
|
@ -148,6 +150,7 @@ enum {
|
|||
AIF3_PB,
|
||||
AIF3_CAP,
|
||||
AIF4_PB,
|
||||
AIF4_VIFEED,
|
||||
NUM_CODEC_DAIS,
|
||||
};
|
||||
|
||||
|
@ -474,6 +477,8 @@ struct tavil_priv {
|
|||
struct work_struct tavil_add_child_devices_work;
|
||||
struct hpf_work tx_hpf_work[WCD934X_NUM_DECIMATORS];
|
||||
struct tx_mute_work tx_mute_dwork[WCD934X_NUM_DECIMATORS];
|
||||
|
||||
unsigned int vi_feed_value;
|
||||
};
|
||||
|
||||
static const struct tavil_reg_mask_val tavil_spkr_default[] = {
|
||||
|
@ -700,6 +705,72 @@ void *tavil_get_afe_config(struct snd_soc_codec *codec,
|
|||
}
|
||||
EXPORT_SYMBOL(tavil_get_afe_config);
|
||||
|
||||
static int tavil_vi_feed_mixer_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dapm_widget_list *wlist =
|
||||
dapm_kcontrol_get_wlist(kcontrol);
|
||||
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
|
||||
struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
ucontrol->value.integer.value[0] = tavil_p->vi_feed_value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tavil_vi_feed_mixer_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dapm_widget_list *wlist =
|
||||
dapm_kcontrol_get_wlist(kcontrol);
|
||||
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
|
||||
struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
|
||||
struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
|
||||
struct soc_multi_mixer_control *mixer =
|
||||
((struct soc_multi_mixer_control *)kcontrol->private_value);
|
||||
u32 dai_id = widget->shift;
|
||||
u32 port_id = mixer->shift;
|
||||
u32 enable = ucontrol->value.integer.value[0];
|
||||
|
||||
dev_dbg(codec->dev, "%s: enable: %d, port_id:%d, dai_id: %d\n",
|
||||
__func__, enable, port_id, dai_id);
|
||||
|
||||
tavil_p->vi_feed_value = ucontrol->value.integer.value[0];
|
||||
|
||||
mutex_lock(&tavil_p->codec_mutex);
|
||||
if (enable) {
|
||||
if (port_id == WCD934X_TX14 && !test_bit(VI_SENSE_1,
|
||||
&tavil_p->status_mask)) {
|
||||
list_add_tail(&core->tx_chs[WCD934X_TX14].list,
|
||||
&tavil_p->dai[dai_id].wcd9xxx_ch_list);
|
||||
set_bit(VI_SENSE_1, &tavil_p->status_mask);
|
||||
}
|
||||
if (port_id == WCD934X_TX15 && !test_bit(VI_SENSE_2,
|
||||
&tavil_p->status_mask)) {
|
||||
list_add_tail(&core->tx_chs[WCD934X_TX15].list,
|
||||
&tavil_p->dai[dai_id].wcd9xxx_ch_list);
|
||||
set_bit(VI_SENSE_2, &tavil_p->status_mask);
|
||||
}
|
||||
} else {
|
||||
if (port_id == WCD934X_TX14 && test_bit(VI_SENSE_1,
|
||||
&tavil_p->status_mask)) {
|
||||
list_del_init(&core->tx_chs[WCD934X_TX14].list);
|
||||
clear_bit(VI_SENSE_1, &tavil_p->status_mask);
|
||||
}
|
||||
if (port_id == WCD934X_TX15 && test_bit(VI_SENSE_2,
|
||||
&tavil_p->status_mask)) {
|
||||
list_del_init(&core->tx_chs[WCD934X_TX15].list);
|
||||
clear_bit(VI_SENSE_2, &tavil_p->status_mask);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&tavil_p->codec_mutex);
|
||||
snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
|
@ -1060,6 +1131,143 @@ static int tavil_codec_enable_slimtx(struct snd_soc_dapm_widget *w,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int tavil_codec_enable_slimvi_feedback(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol,
|
||||
int event)
|
||||
{
|
||||
struct wcd9xxx *core = NULL;
|
||||
struct snd_soc_codec *codec = NULL;
|
||||
struct tavil_priv *tavil_p = NULL;
|
||||
int ret = 0;
|
||||
struct wcd9xxx_codec_dai_data *dai = NULL;
|
||||
|
||||
codec = snd_soc_dapm_to_codec(w->dapm);
|
||||
tavil_p = snd_soc_codec_get_drvdata(codec);
|
||||
core = dev_get_drvdata(codec->dev->parent);
|
||||
|
||||
dev_dbg(codec->dev,
|
||||
"%s: num_dai %d stream name %s w->name %s event %d shift %d\n",
|
||||
__func__, codec->component.num_dai, w->sname,
|
||||
w->name, event, w->shift);
|
||||
|
||||
if (w->shift != AIF4_VIFEED) {
|
||||
pr_err("%s Error in enabling the tx path\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
dai = &tavil_p->dai[w->shift];
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
if (test_bit(VI_SENSE_1, &tavil_p->status_mask)) {
|
||||
dev_dbg(codec->dev, "%s: spkr1 enabled\n", __func__);
|
||||
/* Enable V&I sensing */
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20,
|
||||
0x20);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x0F, 0x00);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x0F,
|
||||
0x00);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x10);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10,
|
||||
0x10);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x00);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20,
|
||||
0x00);
|
||||
}
|
||||
if (test_bit(VI_SENSE_2, &tavil_p->status_mask)) {
|
||||
pr_debug("%s: spkr2 enabled\n", __func__);
|
||||
/* Enable V&I sensing */
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20,
|
||||
0x20);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20,
|
||||
0x20);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x0F,
|
||||
0x00);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x0F,
|
||||
0x00);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10,
|
||||
0x10);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10,
|
||||
0x10);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20,
|
||||
0x00);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20,
|
||||
0x00);
|
||||
}
|
||||
dai->bus_down_in_recovery = false;
|
||||
tavil_codec_enable_slim_port_intr(dai, codec);
|
||||
(void) tavil_codec_enable_slim_chmask(dai, true);
|
||||
ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
|
||||
dai->rate, dai->bit_width,
|
||||
&dai->grph);
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
|
||||
dai->grph);
|
||||
if (ret)
|
||||
dev_err(codec->dev, "%s error in close_slim_sch_tx %d\n",
|
||||
__func__, ret);
|
||||
if (!dai->bus_down_in_recovery)
|
||||
ret = tavil_codec_enable_slim_chmask(dai, false);
|
||||
if (ret < 0) {
|
||||
ret = wcd9xxx_disconnect_port(core,
|
||||
&dai->wcd9xxx_ch_list,
|
||||
dai->grph);
|
||||
dev_dbg(codec->dev, "%s: Disconnect TX port, ret = %d\n",
|
||||
__func__, ret);
|
||||
}
|
||||
if (test_bit(VI_SENSE_1, &tavil_p->status_mask)) {
|
||||
/* Disable V&I sensing */
|
||||
dev_dbg(codec->dev, "%s: spkr1 disabled\n", __func__);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20,
|
||||
0x20);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x00);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10,
|
||||
0x00);
|
||||
}
|
||||
if (test_bit(VI_SENSE_2, &tavil_p->status_mask)) {
|
||||
/* Disable V&I sensing */
|
||||
dev_dbg(codec->dev, "%s: spkr2 disabled\n", __func__);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20,
|
||||
0x20);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20,
|
||||
0x20);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10,
|
||||
0x00);
|
||||
snd_soc_update_bits(codec,
|
||||
WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10,
|
||||
0x00);
|
||||
}
|
||||
break;
|
||||
}
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tavil_codec_enable_rx_bias(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
|
@ -3406,6 +3614,13 @@ static const char *const cdc_if_rx7_mux_text[] = {
|
|||
"SLIM RX7", "I2S_0 RX7"
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new aif4_vi_mixer[] = {
|
||||
SOC_SINGLE_EXT("SPKR_VI_1", SND_SOC_NOPM, WCD934X_TX14, 1, 0,
|
||||
tavil_vi_feed_mixer_get, tavil_vi_feed_mixer_put),
|
||||
SOC_SINGLE_EXT("SPKR_VI_2", SND_SOC_NOPM, WCD934X_TX15, 1, 0,
|
||||
tavil_vi_feed_mixer_get, tavil_vi_feed_mixer_put),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new aif1_cap_mixer[] = {
|
||||
SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0,
|
||||
slim_tx_mixer_get, slim_tx_mixer_put),
|
||||
|
@ -4094,6 +4309,13 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = {
|
|||
SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0,
|
||||
aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)),
|
||||
|
||||
SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM,
|
||||
AIF4_VIFEED, 0, tavil_codec_enable_slimvi_feedback,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_MIXER("AIF4_VI Mixer", SND_SOC_NOPM, AIF4_VIFEED, 0,
|
||||
aif4_vi_mixer, ARRAY_SIZE(aif4_vi_mixer)),
|
||||
SND_SOC_DAPM_INPUT("VIINPUT"),
|
||||
|
||||
SND_SOC_DAPM_MIXER("SLIM TX0", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_MIXER("SLIM TX1", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_MIXER("SLIM TX2", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
|
@ -4297,6 +4519,7 @@ static int tavil_get_channel_map(struct snd_soc_dai *dai,
|
|||
case AIF1_CAP:
|
||||
case AIF2_CAP:
|
||||
case AIF3_CAP:
|
||||
case AIF4_VIFEED:
|
||||
if (!tx_slot || !tx_num) {
|
||||
dev_err(tavil->dev, "%s: Invalid tx_slot 0x%pK or tx_num 0x%pK\n",
|
||||
__func__, tx_slot, tx_num);
|
||||
|
@ -4653,6 +4876,22 @@ static int tavil_prepare(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int tavil_vi_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec);
|
||||
|
||||
dev_dbg(tavil->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n",
|
||||
__func__, dai->name, dai->id, params_rate(params),
|
||||
params_channels(params));
|
||||
|
||||
tavil->dai[dai->id].rate = params_rate(params);
|
||||
tavil->dai[dai->id].bit_width = 32;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tavil_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
|
@ -4726,6 +4965,12 @@ static struct snd_soc_dai_ops tavil_dai_ops = {
|
|||
.get_channel_map = tavil_get_channel_map,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_ops tavil_vi_dai_ops = {
|
||||
.hw_params = tavil_vi_hw_params,
|
||||
.set_channel_map = tavil_set_channel_map,
|
||||
.get_channel_map = tavil_get_channel_map,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver tavil_dai[] = {
|
||||
{
|
||||
.name = "tavil_rx1",
|
||||
|
@ -4825,6 +5070,20 @@ static struct snd_soc_dai_driver tavil_dai[] = {
|
|||
},
|
||||
.ops = &tavil_dai_ops,
|
||||
},
|
||||
{
|
||||
.name = "tavil_vifeedback",
|
||||
.id = AIF4_VIFEED,
|
||||
.capture = {
|
||||
.stream_name = "VIfeed",
|
||||
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
|
||||
.formats = WCD934X_FORMATS_S16_S24_S32_LE,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 4,
|
||||
},
|
||||
.ops = &tavil_vi_dai_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int tavil_cdc_req_mclk_enable(struct tavil_priv *tavil,
|
||||
|
|
Loading…
Add table
Reference in a new issue