From 98d3d590bbf8345827b513c94d7b750d1199db1b Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Tue, 12 Sep 2017 12:23:32 -0700 Subject: [PATCH] asoc: codecs: enable i2s mode for tavil Add support for tavil i2c access. Add audio routings dapm widgets for data and control. Change-Id: I96ebf7a5700b10f294f4fadfeeb21dab490a9313 Signed-off-by: Karthikeyan Mani --- asoc/codecs/wcd934x/wcd934x-routing.h | 143 +++- asoc/codecs/wcd934x/wcd934x.c | 930 ++++++++++++++++++++++++-- 2 files changed, 966 insertions(+), 107 deletions(-) diff --git a/asoc/codecs/wcd934x/wcd934x-routing.h b/asoc/codecs/wcd934x/wcd934x-routing.h index 93a1ad3bab..8adeef79a7 100644 --- a/asoc/codecs/wcd934x/wcd934x-routing.h +++ b/asoc/codecs/wcd934x/wcd934x-routing.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -17,13 +17,9 @@ const struct snd_soc_dapm_route tavil_slim_audio_map[] = { - /* Virtual input widgets */ - {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, - {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, - {"AIF3 CAP", NULL, "AIF3_CAP Mixer"}, {"AIF4 MAD", NULL, "AIF4_MAD Mixer"}, - /* Virtual input widget Mixer */ + /* Virtual input widget Mixer SLIMBUS*/ {"AIF1_CAP Mixer", "SLIM TX0", "SLIM TX0"}, {"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1"}, {"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2"}, @@ -68,6 +64,21 @@ const struct snd_soc_dapm_route tavil_slim_audio_map[] = { {"AIF4_MAD Mixer", "SLIM TX13", "SLIM TX13"}, + /* CDC Tx interface with SLIMBUS */ + {"SLIM TX0", NULL, "CDC_IF TX0 MUX"}, + {"SLIM TX1", NULL, "CDC_IF TX1 MUX"}, + {"SLIM TX2", NULL, "CDC_IF TX2 MUX"}, + {"SLIM TX3", NULL, "CDC_IF TX3 MUX"}, + {"SLIM TX4", NULL, "CDC_IF TX4 MUX"}, + {"SLIM TX5", NULL, "CDC_IF TX5 MUX"}, + {"SLIM TX6", NULL, "CDC_IF TX6 MUX"}, + {"SLIM TX7", NULL, "CDC_IF TX7 MUX"}, + {"SLIM TX8", NULL, "CDC_IF TX8 MUX"}, + {"SLIM TX9", NULL, "CDC_IF TX9 MUX"}, + {"SLIM TX10", NULL, "CDC_IF TX10 MUX"}, + {"SLIM TX11", NULL, "CDC_IF TX11 MUX"}, + {"SLIM TX13", NULL, "CDC_IF TX13 MUX"}, + {"SLIM RX0 MUX", "AIF1_PB", "AIF1 PB"}, {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"}, {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"}, @@ -113,10 +124,100 @@ const struct snd_soc_dapm_route tavil_slim_audio_map[] = { {"SLIM RX6", NULL, "SLIM RX6 MUX"}, {"SLIM RX7", NULL, "SLIM RX7 MUX"}, + /* CDC Rx interface with SLIMBUS */ + {"CDC_IF RX0 MUX", "SLIM RX0", "SLIM RX0"}, + {"CDC_IF RX1 MUX", "SLIM RX1", "SLIM RX1"}, + {"CDC_IF RX2 MUX", "SLIM RX2", "SLIM RX2"}, + {"CDC_IF RX3 MUX", "SLIM RX3", "SLIM RX3"}, + {"CDC_IF RX4 MUX", "SLIM RX4", "SLIM RX4"}, + {"CDC_IF RX5 MUX", "SLIM RX5", "SLIM RX5"}, + {"CDC_IF RX6 MUX", "SLIM RX6", "SLIM RX6"}, + {"CDC_IF RX7 MUX", "SLIM RX7", "SLIM RX7"}, + + /* VI Feedback */ + {"AIF4_VI Mixer", "SPKR_VI_1", "VIINPUT"}, + {"AIF4_VI Mixer", "SPKR_VI_2", "VIINPUT"}, + {"AIF4 VI", NULL, "AIF4_VI Mixer"}, + +}; + +const struct snd_soc_dapm_route tavil_i2s_audio_map[] = { + + /* Virtual input widget Mixer I2S*/ + {"AIF1_CAP Mixer", "I2S TX1", "I2S TX1"}, + {"AIF1_CAP Mixer", "I2S TX2", "I2S TX2"}, + {"AIF1_CAP Mixer", "I2S TX3", "I2S TX3"}, + {"AIF1_CAP Mixer", "I2S TX4", "I2S TX4"}, + {"AIF1_CAP Mixer", "I2S TX5", "I2S TX5"}, + {"AIF1_CAP Mixer", "I2S TX6", "I2S TX6"}, + {"AIF1_CAP Mixer", "I2S TX7", "I2S TX7"}, + + {"AIF2_CAP Mixer", "I2S TX8", "I2S TX8"}, + {"AIF2_CAP Mixer", "I2S TX11", "I2S TX11"}, + + {"AIF3_CAP Mixer", "I2S TX0", "I2S TX0"}, + {"AIF3_CAP Mixer", "I2S TX1", "I2S TX1"}, + + /* CDC Tx interface with I2S */ + {"I2S TX0", NULL, "CDC_IF TX0 MUX"}, + {"I2S TX1", NULL, "CDC_IF TX1 MUX"}, + {"I2S TX2", NULL, "CDC_IF TX2 MUX"}, + {"I2S TX3", NULL, "CDC_IF TX3 MUX"}, + {"I2S TX4", NULL, "CDC_IF TX4 MUX"}, + {"I2S TX5", NULL, "CDC_IF TX5 MUX"}, + {"I2S TX6", NULL, "CDC_IF TX6 MUX"}, + {"I2S TX7", NULL, "CDC_IF TX7 MUX"}, + {"I2S TX8", NULL, "CDC_IF TX8 MUX"}, + {"I2S TX11", NULL, "CDC_IF TX11 MUX"}, + + {"I2S RX0 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX1 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX2 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX3 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX4 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX5 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX6 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX7 MUX", "AIF1_PB", "AIF1 PB"}, + + {"I2S RX2 MUX", "AIF2_PB", "AIF2 PB"}, + {"I2S RX3 MUX", "AIF2_PB", "AIF2 PB"}, + + {"I2S RX4 MUX", "AIF3_PB", "AIF3 PB"}, + {"I2S RX5 MUX", "AIF3_PB", "AIF3 PB"}, + + {"I2S RX0", NULL, "I2S RX0 MUX"}, + {"I2S RX1", NULL, "I2S RX1 MUX"}, + {"I2S RX2", NULL, "I2S RX2 MUX"}, + {"I2S RX3", NULL, "I2S RX3 MUX"}, + {"I2S RX4", NULL, "I2S RX4 MUX"}, + {"I2S RX5", NULL, "I2S RX5 MUX"}, + {"I2S RX6", NULL, "I2S RX6 MUX"}, + {"I2S RX7", NULL, "I2S RX7 MUX"}, + + /* CDC Rx interface with I2S */ + {"CDC_IF RX0 MUX", "I2S RX0", "I2S RX0"}, + {"CDC_IF RX1 MUX", "I2S RX1", "I2S RX1"}, + {"CDC_IF RX2 MUX", "I2S RX2", "I2S RX2"}, + {"CDC_IF RX3 MUX", "I2S RX3", "I2S RX3"}, + {"CDC_IF RX4 MUX", "I2S RX4", "I2S RX4"}, + {"CDC_IF RX5 MUX", "I2S RX5", "I2S RX5"}, + {"CDC_IF RX6 MUX", "I2S RX6", "I2S RX6"}, + {"CDC_IF RX7 MUX", "I2S RX7", "I2S RX7"}, + }; const struct snd_soc_dapm_route tavil_audio_map[] = { + /* + * AIF CAP to Mixer routes are common + * for both SLIM as well as I2S + */ + + /* Virtual input widgets */ + {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, + {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, + {"AIF3 CAP", NULL, "AIF3_CAP Mixer"}, + /* WDMA3 */ {"WDMA3 PORT0 MUX", "DEC0", "ADC MUX0"}, {"WDMA3 PORT0 MUX", "RX_MIX_TX0", "RX MIX TX0 MUX"}, @@ -195,26 +296,6 @@ const struct snd_soc_dapm_route tavil_audio_map[] = { {"MAD_CPE_OUT1", NULL, "MAD_CPE1"}, {"MAD_CPE_OUT2", NULL, "MAD_CPE2"}, - /* 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"}, - {"SLIM TX2", NULL, "CDC_IF TX2 MUX"}, - {"SLIM TX3", NULL, "CDC_IF TX3 MUX"}, - {"SLIM TX4", NULL, "CDC_IF TX4 MUX"}, - {"SLIM TX5", NULL, "CDC_IF TX5 MUX"}, - {"SLIM TX6", NULL, "CDC_IF TX6 MUX"}, - {"SLIM TX7", NULL, "CDC_IF TX7 MUX"}, - {"SLIM TX8", NULL, "CDC_IF TX8 MUX"}, - {"SLIM TX9", NULL, "CDC_IF TX9 MUX"}, - {"SLIM TX10", NULL, "CDC_IF TX10 MUX"}, - {"SLIM TX11", NULL, "CDC_IF TX11 MUX"}, - {"SLIM TX13", NULL, "CDC_IF TX13 MUX"}, - {"CDC_IF TX0 MUX", "DEC0", "ADC MUX0"}, {"CDC_IF TX0 MUX", "RX_MIX_TX0", "RX MIX TX0 MUX"}, {"CDC_IF TX0 MUX", "DEC0_192", "ADC US MUX0"}, @@ -568,16 +649,6 @@ const struct snd_soc_dapm_route tavil_audio_map[] = { {"ADC3", NULL, "AMIC3"}, {"ADC4", NULL, "AMIC4_5 SEL"}, - /* CDC Rx interface with SLIMBUS */ - {"CDC_IF RX0 MUX", "SLIM RX0", "SLIM RX0"}, - {"CDC_IF RX1 MUX", "SLIM RX1", "SLIM RX1"}, - {"CDC_IF RX2 MUX", "SLIM RX2", "SLIM RX2"}, - {"CDC_IF RX3 MUX", "SLIM RX3", "SLIM RX3"}, - {"CDC_IF RX4 MUX", "SLIM RX4", "SLIM RX4"}, - {"CDC_IF RX5 MUX", "SLIM RX5", "SLIM RX5"}, - {"CDC_IF RX6 MUX", "SLIM RX6", "SLIM RX6"}, - {"CDC_IF RX7 MUX", "SLIM RX7", "SLIM RX7"}, - {"RX INT0_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, {"RX INT0_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, {"RX INT0_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index e7cec2b4d3..1be2f66ec7 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1273,6 +1273,93 @@ static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol, return 0; } +static int i2s_tx_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + 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->tx_port_value; + return 0; +} + +static int i2s_tx_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_update *update = NULL; + 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]; + u32 vtable; + + dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n", + __func__, + widget->name, ucontrol->id.name, tavil_p->tx_port_value, + widget->shift, ucontrol->value.integer.value[0]); + + mutex_lock(&tavil_p->codec_mutex); + if (dai_id >= ARRAY_SIZE(vport_slim_check_table)) { + dev_err(codec->dev, "%s: dai_id: %d, out of bounds\n", + __func__, dai_id); + mutex_unlock(&tavil_p->codec_mutex); + return -EINVAL; + } + vtable = vport_slim_check_table[dai_id]; + + switch (dai_id) { + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + /* only add to the list if value not set */ + if (enable && !(tavil_p->tx_port_value & 1 << port_id)) { + if (wcd9xxx_tx_vport_validation(vtable, port_id, + tavil_p->dai, NUM_CODEC_DAIS)) { + dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n", + __func__, port_id); + mutex_unlock(&tavil_p->codec_mutex); + return 0; + } + tavil_p->tx_port_value |= 1 << port_id; + } else if (!enable && (tavil_p->tx_port_value & + 1 << port_id)) { + tavil_p->tx_port_value &= ~(1 << port_id); + } else { + if (enable) + dev_dbg(codec->dev, "%s: TX%u port is used by\n" + "this virtual port\n", + __func__, port_id); + else + dev_dbg(codec->dev, "%s: TX%u port is not used by\n" + "this virtual port\n", + __func__, port_id); + /* avoid update power function */ + mutex_unlock(&tavil_p->codec_mutex); + return 0; + } + break; + default: + dev_err(codec->dev, "Unknown AIF %d\n", dai_id); + mutex_unlock(&tavil_p->codec_mutex); + return -EINVAL; + } + dev_dbg(codec->dev, "%s: name %s sname %s updated value %u shift %d\n", + __func__, widget->name, widget->sname, tavil_p->tx_port_value, + widget->shift); + + mutex_unlock(&tavil_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update); + + return 0; +} + static int slim_rx_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1477,9 +1564,192 @@ static void tavil_codec_mute_dsd(struct snd_soc_codec *codec, } } -static int tavil_codec_enable_slimrx(struct snd_soc_dapm_widget *w, +static int tavil_codec_set_i2s_rx_ch(struct snd_soc_dapm_widget *w, + u32 i2s_reg, bool up) +{ + int rx_fs_rate = -EINVAL; + int i2s_bit_mode; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + + dai = &tavil_p->dai[w->shift]; + dev_dbg(tavil_p->dev, "%s: %d up/down, %d width, %d rate\n", + __func__, up, dai->bit_width, dai->rate); + if (up) { + if (dai->bit_width == 16) + i2s_bit_mode = 0x01; + else + i2s_bit_mode = 0x00; + + switch (dai->rate) { + case 8000: + rx_fs_rate = 0; + break; + case 16000: + rx_fs_rate = 1; + break; + case 32000: + rx_fs_rate = 2; + break; + case 48000: + rx_fs_rate = 3; + break; + case 96000: + rx_fs_rate = 4; + break; + case 192000: + rx_fs_rate = 5; + break; + case 384000: + rx_fs_rate = 6; + break; + default: + dev_err(tavil_p->dev, "%s: Invalid RX sample rate: %d\n", + __func__, dai->rate); + return -EINVAL; + }; + snd_soc_update_bits(codec, i2s_reg, + 0x40, i2s_bit_mode << 6); + snd_soc_update_bits(codec, i2s_reg, + 0x3c, (rx_fs_rate << 2)); + } else { + snd_soc_update_bits(codec, i2s_reg, + 0x40, 0x00); + snd_soc_update_bits(codec, i2s_reg, + 0x3c, 0x00); + } + return 0; +} + +static int tavil_codec_set_i2s_tx_ch(struct snd_soc_dapm_widget *w, + u32 i2s_reg, bool up) +{ + int tx_fs_rate = -EINVAL; + int i2s_bit_mode; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + + dai = &tavil_p->dai[w->shift]; + if (up) { + if (dai->bit_width == 16) + i2s_bit_mode = 0x01; + else + i2s_bit_mode = 0x00; + + snd_soc_update_bits(codec, i2s_reg, 0x40, i2s_bit_mode << 6); + + switch (dai->rate) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 2; + break; + case 48000: + tx_fs_rate = 3; + break; + case 96000: + tx_fs_rate = 4; + break; + case 192000: + tx_fs_rate = 5; + break; + case 384000: + tx_fs_rate = 6; + break; + default: + dev_err(tavil_p->dev, "%s: Invalid sample rate: %d\n", + __func__, dai->rate); + return -EINVAL; + }; + + snd_soc_update_bits(codec, i2s_reg, 0x3c, tx_fs_rate << 2); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX0_CFG, + 0x03, 0x01); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX0_CFG, + 0x0C, 0x01); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX1_0_CFG, + 0x03, 0x01); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX1_1_CFG, + 0x05, 0x05); + } else { + snd_soc_update_bits(codec, i2s_reg, 0x40, 0x00); + snd_soc_update_bits(codec, i2s_reg, 0x3c, 0x00); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX0_CFG, + 0x03, 0x00); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX0_CFG, + 0x0C, 0x00); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX1_0_CFG, + 0x03, 0x00); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX1_1_CFG, + 0x05, 0x00); + } + return 0; +} + +static int tavil_codec_enable_rx_i2c(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_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + int ret = -EINVAL; + u32 i2s_reg; + + switch (tavil_p->rx_port_value[w->shift]) { + case AIF1_PB: + case AIF1_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_0_CTL; + break; + case AIF2_PB: + case AIF2_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_1_CTL; + break; + case AIF3_PB: + case AIF3_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_2_CTL; + break; + default: + dev_err(codec->dev, "%s Invalid i2s Id received", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + ret = tavil_codec_set_i2s_rx_ch(w, i2s_reg, true); + break; + case SND_SOC_DAPM_POST_PMD: + ret = tavil_codec_set_i2s_rx_ch(w, i2s_reg, false); + break; + } + + return ret; +} + +static int tavil_codec_enable_rx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) { struct wcd9xxx *core; struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); @@ -1499,6 +1769,11 @@ static int tavil_codec_enable_slimrx(struct snd_soc_dapm_widget *w, dev_dbg(codec->dev, "%s: w->name %s w->shift %d event %d\n", __func__, w->name, w->shift, event); + if (tavil_p->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + ret = tavil_codec_enable_rx_i2c(w, kcontrol, event); + return ret; + } + switch (event) { case SND_SOC_DAPM_POST_PMU: dai->bus_down_in_recovery = false; @@ -1530,9 +1805,48 @@ static int tavil_codec_enable_slimrx(struct snd_soc_dapm_widget *w, return ret; } -static int tavil_codec_enable_slimtx(struct snd_soc_dapm_widget *w, +static int tavil_codec_enable_tx_i2c(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_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + int ret = -EINVAL; + u32 i2s_reg; + + switch (tavil_p->rx_port_value[w->shift]) { + case AIF1_PB: + case AIF1_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_0_CTL; + break; + case AIF2_PB: + case AIF2_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_1_CTL; + break; + case AIF3_PB: + case AIF3_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_2_CTL; + break; + default: + dev_err(codec->dev, "%s Invalid i2s Id received", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + ret = tavil_codec_set_i2s_tx_ch(w, i2s_reg, true); + break; + case SND_SOC_DAPM_POST_PMD: + ret = tavil_codec_set_i2s_tx_ch(w, i2s_reg, false); + break; + } + + return ret; +} + +static int tavil_codec_enable_tx(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_priv *tavil_p = snd_soc_codec_get_drvdata(codec); @@ -1548,6 +1862,11 @@ static int tavil_codec_enable_slimtx(struct snd_soc_dapm_widget *w, dai = &tavil_p->dai[w->shift]; core = dev_get_drvdata(codec->dev->parent); + if (tavil_p->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + ret = tavil_codec_enable_tx_i2c(w, kcontrol, event); + return ret; + } + switch (event) { case SND_SOC_DAPM_POST_PMU: dai->bus_down_in_recovery = false; @@ -2234,6 +2553,83 @@ static int tavil_codec_enable_lineout_pa(struct snd_soc_dapm_widget *w, return 0; } +static int i2s_rx_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + 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.enumerated.item[0] = + tavil_p->rx_port_value[widget->shift]; + return 0; +} + +static int i2s_rx_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + unsigned int rx_port_value; + u32 port_id = widget->shift; + + tavil_p->rx_port_value[port_id] = ucontrol->value.enumerated.item[0]; + rx_port_value = tavil_p->rx_port_value[port_id]; + + dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n", + __func__, widget->name, ucontrol->id.name, + rx_port_value, widget->shift, + ucontrol->value.integer.value[0]); + + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, + rx_port_value, e, update); + return 0; +} + +static int tavil_codec_enable_i2s_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + u32 i2s_reg; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + switch (tavil_p->rx_port_value[w->shift]) { + case AIF1_PB: + case AIF1_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_0_CTL; + break; + case AIF2_PB: + case AIF2_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_1_CTL; + break; + case AIF3_PB: + case AIF3_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_2_CTL; + break; + default: + dev_err(codec->dev, "%s Invalid i2s Id received", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = snd_soc_update_bits(codec, i2s_reg, 0x01, 0x01); + break; + case SND_SOC_DAPM_POST_PMD: + ret = snd_soc_update_bits(codec, i2s_reg, 0x01, 0x00); + break; + } + + return ret; +} + static int tavil_codec_ear_dac_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -6285,29 +6681,45 @@ static const char *const slim_rx_mux_text[] = { "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB" }; +static const char *const i2s_rx01_mux_text[] = { + "ZERO", "AIF1_PB" +}; + +static const char *const i2s_rx23_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB" +}; + +static const char *const i2s_rx45_mux_text[] = { + "ZERO", "AIF1_PB", "AIF3_PB" +}; + +static const char *const i2s_rx67_mux_text[] = { + "ZERO", "AIF1_PB" +}; + static const char *const cdc_if_rx0_mux_text[] = { - "SLIM RX0", "I2S_0 RX0" + "SLIM RX0", "I2S RX0" }; static const char *const cdc_if_rx1_mux_text[] = { - "SLIM RX1", "I2S_0 RX1" + "SLIM RX1", "I2S RX1" }; static const char *const cdc_if_rx2_mux_text[] = { - "SLIM RX2", "I2S_0 RX2" + "SLIM RX2", "I2S RX2" }; static const char *const cdc_if_rx3_mux_text[] = { - "SLIM RX3", "I2S_0 RX3" + "SLIM RX3", "I2S RX3" }; static const char *const cdc_if_rx4_mux_text[] = { - "SLIM RX4", "I2S_0 RX4" + "SLIM RX4", "I2S RX4" }; static const char *const cdc_if_rx5_mux_text[] = { - "SLIM RX5", "I2S_0 RX5" + "SLIM RX5", "I2S RX5" }; static const char *const cdc_if_rx6_mux_text[] = { - "SLIM RX6", "I2S_0 RX6" + "SLIM RX6", "I2S RX6" }; static const char *const cdc_if_rx7_mux_text[] = { - "SLIM RX7", "I2S_0 RX7" + "SLIM RX7", "I2S RX7" }; static const char * const asrc0_mux_text[] = { @@ -6370,7 +6782,7 @@ static const struct snd_kcontrol_new aif4_vi_mixer[] = { tavil_vi_feed_mixer_get, tavil_vi_feed_mixer_put), }; -static const struct snd_kcontrol_new aif1_cap_mixer[] = { +static const struct snd_kcontrol_new aif1_slim_cap_mixer[] = { SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, slim_tx_mixer_get, slim_tx_mixer_put), SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, @@ -6399,7 +6811,24 @@ static const struct snd_kcontrol_new aif1_cap_mixer[] = { slim_tx_mixer_get, slim_tx_mixer_put), }; -static const struct snd_kcontrol_new aif2_cap_mixer[] = { +static const struct snd_kcontrol_new aif1_i2s_cap_mixer[] = { + SOC_SINGLE_EXT("I2S TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif2_slim_cap_mixer[] = { SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, slim_tx_mixer_get, slim_tx_mixer_put), SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, @@ -6428,7 +6857,14 @@ static const struct snd_kcontrol_new aif2_cap_mixer[] = { slim_tx_mixer_get, slim_tx_mixer_put), }; -static const struct snd_kcontrol_new aif3_cap_mixer[] = { +static const struct snd_kcontrol_new aif2_i2s_cap_mixer[] = { + SOC_SINGLE_EXT("I2S TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif3_slim_cap_mixer[] = { SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, slim_tx_mixer_get, slim_tx_mixer_put), SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, @@ -6457,9 +6893,16 @@ static const struct snd_kcontrol_new aif3_cap_mixer[] = { slim_tx_mixer_get, slim_tx_mixer_put), }; -static const struct snd_kcontrol_new aif4_mad_mixer[] = { +static const struct snd_kcontrol_new aif3_i2s_cap_mixer[] = { + SOC_SINGLE_EXT("I2S TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif4_slim_mad_mixer[] = { SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, - slim_tx_mixer_get, slim_tx_mixer_put), + slim_tx_mixer_get, slim_tx_mixer_put), }; WCD_DAPM_ENUM_EXT(slim_rx0, SND_SOC_NOPM, 0, slim_rx_mux_text, @@ -6768,6 +7211,22 @@ WCD_DAPM_ENUM(int8_2_native, SND_SOC_NOPM, 0, native_mux_text); WCD_DAPM_ENUM(anc0_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 0, anc0_fb_mux_text); WCD_DAPM_ENUM(anc1_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 3, anc1_fb_mux_text); +WCD_DAPM_ENUM_EXT(i2s_rx0, SND_SOC_NOPM, 0, i2s_rx01_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx1, SND_SOC_NOPM, 0, i2s_rx01_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx2, SND_SOC_NOPM, 0, i2s_rx23_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx3, SND_SOC_NOPM, 0, i2s_rx23_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx4, SND_SOC_NOPM, 0, i2s_rx45_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx5, SND_SOC_NOPM, 0, i2s_rx45_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx6, SND_SOC_NOPM, 0, i2s_rx67_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx7, SND_SOC_NOPM, 0, i2s_rx67_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); WCD_DAPM_ENUM(wdma3_port0, WCD934X_DMA_WDMA3_PRT_CFG, 0, wdma3_port0_text); WCD_DAPM_ENUM(wdma3_port1, WCD934X_DMA_WDMA3_PRT_CFG, 1, wdma3_port1_text); @@ -6852,6 +7311,89 @@ static const struct snd_kcontrol_new rx_int4_asrc_switch[] = { static const struct snd_kcontrol_new wdma3_onoff_switch = SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); +static const struct snd_soc_dapm_widget tavil_dapm_i2s_widgets[] = { + + SND_SOC_DAPM_MUX_E("I2S RX0 MUX", SND_SOC_NOPM, WCD934X_RX0, 0, + &i2s_rx0_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX1 MUX", SND_SOC_NOPM, WCD934X_RX1, 0, + &i2s_rx1_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX2 MUX", SND_SOC_NOPM, WCD934X_RX2, 0, + &i2s_rx2_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX3 MUX", SND_SOC_NOPM, WCD934X_RX3, 0, + &i2s_rx3_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX4 MUX", SND_SOC_NOPM, WCD934X_RX4, 0, + &i2s_rx4_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX5 MUX", SND_SOC_NOPM, WCD934X_RX5, 0, + &i2s_rx5_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX6 MUX", SND_SOC_NOPM, WCD934X_RX6, 0, + &i2s_rx6_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX7 MUX", SND_SOC_NOPM, WCD934X_RX7, 0, + &i2s_rx7_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("I2S RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX7", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER_E("I2S TX0", SND_SOC_NOPM, WCD934X_TX0, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX1", SND_SOC_NOPM, WCD934X_TX1, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX2", SND_SOC_NOPM, WCD934X_TX2, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX3", SND_SOC_NOPM, WCD934X_TX3, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX4", SND_SOC_NOPM, WCD934X_TX4, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX5", SND_SOC_NOPM, WCD934X_TX5, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX6", SND_SOC_NOPM, WCD934X_TX6, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX7", SND_SOC_NOPM, WCD934X_TX7, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX8", SND_SOC_NOPM, WCD934X_TX8, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX11", SND_SOC_NOPM, WCD934X_TX11, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, + aif1_i2s_cap_mixer, ARRAY_SIZE(aif1_i2s_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0, + aif2_i2s_cap_mixer, ARRAY_SIZE(aif2_i2s_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, + aif3_i2s_cap_mixer, ARRAY_SIZE(aif3_i2s_cap_mixer)), +}; + static int tavil_dsd_mixer_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -6917,19 +7459,22 @@ static const struct snd_kcontrol_new lo2_mixer[] = { tavil_dsd_mixer_get, tavil_dsd_mixer_put), }; -static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { - SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM, - AIF1_PB, 0, tavil_codec_enable_slimrx, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM, - AIF2_PB, 0, tavil_codec_enable_slimrx, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM, - AIF3_PB, 0, tavil_codec_enable_slimrx, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +static const struct snd_soc_dapm_widget tavil_dapm_slim_widgets[] = { SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM, - AIF4_PB, 0, tavil_codec_enable_slimrx, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + AIF4_PB, 0, tavil_codec_enable_rx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + 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_AIF_OUT("AIF4 MAD", "AIF4 MAD TX", 0, + SND_SOC_NOPM, 0, 0), + + 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"), WCD_DAPM_MUX("SLIM RX0 MUX", WCD934X_RX0, slim_rx0), WCD_DAPM_MUX("SLIM RX1 MUX", WCD934X_RX1, slim_rx1), @@ -6949,6 +7494,31 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, + aif1_slim_cap_mixer, + ARRAY_SIZE(aif1_slim_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0, + aif2_slim_cap_mixer, + ARRAY_SIZE(aif2_slim_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, + aif3_slim_cap_mixer, + ARRAY_SIZE(aif3_slim_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF4_MAD Mixer", SND_SOC_NOPM, AIF4_MAD_TX, 0, + aif4_slim_mad_mixer, + ARRAY_SIZE(aif4_slim_mad_mixer)), +}; + +static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM, + AIF1_PB, 0, tavil_codec_enable_rx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM, + AIF2_PB, 0, tavil_codec_enable_rx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM, + AIF3_PB, 0, tavil_codec_enable_rx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + WCD_DAPM_MUX("CDC_IF RX0 MUX", WCD934X_RX0, cdc_if_rx0), WCD_DAPM_MUX("CDC_IF RX1 MUX", WCD934X_RX1, cdc_if_rx1), WCD_DAPM_MUX("CDC_IF RX2 MUX", WCD934X_RX2, cdc_if_rx2), @@ -7237,33 +7807,14 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM, - AIF1_CAP, 0, tavil_codec_enable_slimtx, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + AIF1_CAP, 0, tavil_codec_enable_tx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM, - AIF2_CAP, 0, tavil_codec_enable_slimtx, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + AIF2_CAP, 0, tavil_codec_enable_tx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM, - AIF3_CAP, 0, tavil_codec_enable_slimtx, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, - aif1_cap_mixer, ARRAY_SIZE(aif1_cap_mixer)), - SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0, - aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)), - SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, - aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)), - SND_SOC_DAPM_MIXER("AIF4_MAD Mixer", SND_SOC_NOPM, AIF4_MAD_TX, 0, - aif4_mad_mixer, ARRAY_SIZE(aif4_mad_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_AIF_OUT("AIF4 MAD", "AIF4 MAD TX", 0, - SND_SOC_NOPM, 0, 0), - - 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"), + AIF3_CAP, 0, tavil_codec_enable_tx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), 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), @@ -8047,6 +8598,46 @@ static int tavil_hw_params(struct snd_pcm_substream *substream, return 0; } +static int tavil_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec); + u32 i2s_reg; + + switch (dai->id) { + case AIF1_PB: + case AIF1_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_0_CTL; + break; + case AIF2_PB: + case AIF2_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_1_CTL; + break; + case AIF3_PB: + case AIF3_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_2_CTL; + break; + default: + dev_err(dai->codec->dev, "%s Invalid i2s Id", __func__); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* CPU is master */ + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) + snd_soc_update_bits(dai->codec, i2s_reg, 0x2, 0x0); + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* CPU is slave */ + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) + snd_soc_update_bits(dai->codec, i2s_reg, 0x2, 0x2); + break; + default: + return -EINVAL; + } + return 0; +} + static struct snd_soc_dai_ops tavil_dai_ops = { .startup = tavil_startup, .shutdown = tavil_shutdown, @@ -8056,13 +8647,21 @@ static struct snd_soc_dai_ops tavil_dai_ops = { .get_channel_map = tavil_get_channel_map, }; +static struct snd_soc_dai_ops tavil_i2s_dai_ops = { + .startup = tavil_startup, + .shutdown = tavil_shutdown, + .hw_params = tavil_hw_params, + .prepare = tavil_prepare, + .set_fmt = tavil_set_dai_fmt, +}; + 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[] = { +static struct snd_soc_dai_driver tavil_slim_dai[] = { { .name = "tavil_rx1", .id = AIF1_PB, @@ -8191,6 +8790,93 @@ static struct snd_soc_dai_driver tavil_dai[] = { }, }; +static struct snd_soc_dai_driver tavil_i2s_dai[] = { + { + .name = "tavil_i2s_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_i2s_dai_ops, + }, + { + .name = "tavil_i2s_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_i2s_dai_ops, + }, + { + .name = "tavil_i2s_rx2", + .id = AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_i2s_dai_ops, + }, + { + .name = "tavil_i2s_tx2", + .id = AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_i2s_dai_ops, + }, + { + .name = "tavil_i2s_rx3", + .id = AIF3_PB, + .playback = { + .stream_name = "AIF3 Playback", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_i2s_dai_ops, + }, + { + .name = "tavil_i2s_tx3", + .id = AIF3_CAP, + .capture = { + .stream_name = "AIF3 Capture", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_i2s_dai_ops, + }, +}; + static void tavil_codec_power_gate_digital_core(struct tavil_priv *tavil) { mutex_lock(&tavil->power_lock); @@ -8747,6 +9433,22 @@ static void tavil_codec_init_reg(struct tavil_priv *priv) } } +static const struct tavil_reg_mask_val tavil_codec_reg_i2c_defaults[] = { + {WCD934X_CLK_SYS_MCLK_PRG, 0x40, 0x00}, + {WCD934X_CODEC_RPM_CLK_GATE, 0x03, 0x01}, + {WCD934X_CODEC_RPM_CLK_MCLK_CFG, 0x03, 0x00}, + {WCD934X_CODEC_RPM_CLK_MCLK_CFG, 0x05, 0x05}, + {WCD934X_DATA_HUB_RX0_CFG, 0x71, 0x31}, + {WCD934X_DATA_HUB_RX1_CFG, 0x71, 0x31}, + {WCD934X_DATA_HUB_RX2_CFG, 0x03, 0x01}, + {WCD934X_DATA_HUB_RX3_CFG, 0x03, 0x01}, + {WCD934X_DATA_HUB_I2S_TX0_CFG, 0x01, 0x01}, + {WCD934X_DATA_HUB_I2S_TX0_CFG, 0x04, 0x01}, + {WCD934X_DATA_HUB_I2S_TX1_0_CFG, 0x01, 0x01}, + {WCD934X_DATA_HUB_I2S_TX1_1_CFG, 0x05, 0x05}, + {WCD934X_CHIP_TIER_CTRL_ALT_FUNC_EN, 0x1, 0x1}, +}; + static void tavil_update_reg_defaults(struct tavil_priv *tavil) { u32 i; @@ -8758,6 +9460,15 @@ static void tavil_update_reg_defaults(struct tavil_priv *tavil) tavil_codec_reg_defaults[i].reg, tavil_codec_reg_defaults[i].mask, tavil_codec_reg_defaults[i].val); + + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_i2c_defaults); i++) { + regmap_update_bits(wcd9xxx->regmap, + tavil_codec_reg_i2c_defaults[i].reg, + tavil_codec_reg_i2c_defaults[i].mask, + tavil_codec_reg_i2c_defaults[i].val); + } + } } static void tavil_update_cpr_defaults(struct tavil_priv *tavil) @@ -9402,19 +10113,33 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec) goto err_hwdep; } - snd_soc_dapm_add_routes(dapm, tavil_slim_audio_map, - ARRAY_SIZE(tavil_slim_audio_map)); + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) + snd_soc_dapm_new_controls(dapm, tavil_dapm_i2s_widgets, + ARRAY_SIZE(tavil_dapm_i2s_widgets)); + else + snd_soc_dapm_new_controls(dapm, tavil_dapm_slim_widgets, + ARRAY_SIZE(tavil_dapm_slim_widgets)); + + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + snd_soc_dapm_add_routes(dapm, tavil_slim_audio_map, + ARRAY_SIZE(tavil_slim_audio_map)); + else + snd_soc_dapm_add_routes(dapm, tavil_i2s_audio_map, + ARRAY_SIZE(tavil_i2s_audio_map)); + for (i = 0; i < NUM_CODEC_DAIS; i++) { INIT_LIST_HEAD(&tavil->dai[i].wcd9xxx_ch_list); init_waitqueue_head(&tavil->dai[i].dai_wait); } - tavil_slimbus_slave_port_cfg.slave_dev_intfdev_la = - control->slim_slave->laddr; - tavil_slimbus_slave_port_cfg.slave_dev_pgd_la = - control->slim->laddr; - tavil_slimbus_slave_port_cfg.slave_port_mapping[0] = - WCD934X_TX13; - tavil_init_slim_slave_cfg(codec); + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + tavil_slimbus_slave_port_cfg.slave_dev_intfdev_la = + control->slim_slave->laddr; + tavil_slimbus_slave_port_cfg.slave_dev_pgd_la = + control->slim->laddr; + tavil_slimbus_slave_port_cfg.slave_port_mapping[0] = + WCD934X_TX13; + tavil_init_slim_slave_cfg(codec); + } control->num_rx_port = WCD934X_RX_MAX; control->rx_chs = ptr; @@ -9469,9 +10194,11 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec) snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture"); snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback"); snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture"); - snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback"); - snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX"); - snd_soc_dapm_ignore_suspend(dapm, "VIfeed"); + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX"); + snd_soc_dapm_ignore_suspend(dapm, "VIfeed"); + } snd_soc_dapm_sync(dapm); @@ -9577,6 +10304,39 @@ static const struct dev_pm_ops tavil_pm_ops = { }; #endif +static int wcd9xxx_swrm_i2c_bulk_write(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_reg_val *bulk_reg, + size_t len) +{ + int i, ret = 0; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + + swr_wr_addr_base = WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD934X_SWR_AHB_BRIDGE_WR_DATA_0; + + for (i = 0; i < (len * 2); i += 2) { + /* First Write the Data to register */ + ret = regmap_bulk_write(wcd9xxx->regmap, + swr_wr_data_base, bulk_reg[i].buf, 4); + if (ret < 0) { + dev_err(wcd9xxx->dev, "%s: WR Data Failure\n", + __func__); + break; + } + /* Next Write Address */ + ret = regmap_bulk_write(wcd9xxx->regmap, + swr_wr_addr_base, + bulk_reg[i+1].buf, 4); + if (ret < 0) { + dev_err(wcd9xxx->dev, "%s: WR Addr Failure\n", + __func__); + break; + } + } + return ret; +} + static int tavil_swrm_read(void *handle, int reg) { struct tavil_priv *tavil; @@ -9655,8 +10415,11 @@ static int tavil_swrm_bulk_write(void *handle, u32 *reg, u32 *val, size_t len) } mutex_lock(&tavil->swr.write_mutex); - ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, - (len * 2), false); + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, + (len * 2), false); + else + ret = wcd9xxx_swrm_i2c_bulk_write(wcd9xxx, bulk_reg, len); if (ret) { dev_err(tavil->dev, "%s: swrm bulk write failed, ret: %d\n", __func__, ret); @@ -9695,7 +10458,10 @@ static int tavil_swrm_write(void *handle, int reg, int val) bulk_reg[1].bytes = 4; mutex_lock(&tavil->swr.write_mutex); - ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2, false); + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2, false); + else + ret = wcd9xxx_swrm_i2c_bulk_write(wcd9xxx, bulk_reg, 1); if (ret < 0) dev_err(tavil->dev, "%s: WR Data Failure\n", __func__); mutex_unlock(&tavil->swr.write_mutex); @@ -10081,6 +10847,21 @@ static int tavil_probe(struct platform_device *pdev) if (!tavil) return -ENOMEM; + tavil->intf_type = wcd9xxx_get_intf_type(); + if (tavil->intf_type != WCD9XXX_INTERFACE_TYPE_I2C && + tavil->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + devm_kfree(&pdev->dev, tavil); + return -EPROBE_DEFER; + } + + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + if (apr_get_subsys_state() == APR_SUBSYS_DOWN) { + dev_err(&pdev->dev, "%s: dsp down\n", __func__); + devm_kfree(&pdev->dev, tavil); + return -EPROBE_DEFER; + } + } + platform_set_drvdata(pdev, tavil); tavil->wcd9xxx = dev_get_drvdata(pdev->dev.parent); @@ -10161,8 +10942,15 @@ static int tavil_probe(struct platform_device *pdev) tavil_update_cpr_defaults(tavil); /* Register with soc framework */ - ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tavil, - tavil_dai, ARRAY_SIZE(tavil_dai)); + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tavil, + tavil_i2s_dai, + ARRAY_SIZE(tavil_i2s_dai)); + else + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tavil, + tavil_slim_dai, + ARRAY_SIZE(tavil_slim_dai)); + if (ret) { dev_err(&pdev->dev, "%s: Codec registration failed\n", __func__);