diff options
author | Nicolas Dechesne <nicolas.dechesne@linaro.org> | 2016-02-05 09:26:11 +0100 |
---|---|---|
committer | Nicolas Dechesne <nicolas.dechesne@linaro.org> | 2016-02-05 09:26:11 +0100 |
commit | ae24ced769f64ac7ee2ca959f7b76fe10adaace5 (patch) | |
tree | db35e31b719c450019db49a2b088c6b57922ddd3 | |
parent | 768bd4d3e0ee264d95e7342a83182127b3f5fd2c (diff) | |
parent | d76fd34be892fead095cfc3515a0d8f20c598ef7 (diff) |
Merge remote-tracking branch 'srini/v4.4-audio-capture' into release/qcomlt-4.4
-rw-r--r-- | Documentation/devicetree/bindings/sound/qcom,apq8016-sbc.txt | 29 | ||||
-rw-r--r-- | arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi | 41 | ||||
-rw-r--r-- | arch/arm64/boot/dts/qcom/msm8916.dtsi | 2 | ||||
-rw-r--r-- | arch/arm64/boot/dts/qcom/pm8916.dtsi | 3 | ||||
-rw-r--r-- | sound/soc/codecs/msm8x16-wcd.c | 952 | ||||
-rw-r--r-- | sound/soc/qcom/apq8016_sbc.c | 32 | ||||
-rw-r--r-- | sound/soc/qcom/lpass-apq8016.c | 31 | ||||
-rw-r--r-- | sound/soc/qcom/lpass-cpu.c | 146 | ||||
-rw-r--r-- | sound/soc/qcom/lpass-ipq806x.c | 11 | ||||
-rw-r--r-- | sound/soc/qcom/lpass-lpaif-reg.h | 118 | ||||
-rw-r--r-- | sound/soc/qcom/lpass-platform.c | 243 | ||||
-rw-r--r-- | sound/soc/qcom/lpass.h | 10 |
12 files changed, 1268 insertions, 350 deletions
diff --git a/Documentation/devicetree/bindings/sound/qcom,apq8016-sbc.txt b/Documentation/devicetree/bindings/sound/qcom,apq8016-sbc.txt index 48129368d4d97..593a12a67ba15 100644 --- a/Documentation/devicetree/bindings/sound/qcom,apq8016-sbc.txt +++ b/Documentation/devicetree/bindings/sound/qcom,apq8016-sbc.txt @@ -16,6 +16,23 @@ Required properties: * "spkr-iomux" - qcom,model : Name of the sound card. +- qcom,audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the + connection's sink, the second being the connection's + source. Valid names could be power supplies, MicBias + of msm8x16_wcd codec and the jacks on the board: + + Power supplies: + * MIC BIAS External + * MIC BIAS Internal1 + * MIC BIAS Internal2 + + Board connectors: + * Headset Mic + * Secondary Mic", + * DMIC + * Ext Spk + Dai-link subnode properties and subnodes: Required dai-link subnodes: @@ -37,6 +54,18 @@ sound: sound { reg-names = "mic-iomux", "spkr-iomux"; qcom,model = "DB410c"; + qcom,audio-routing = + "MIC BIAS External", "Handset Mic", + "MIC BIAS Internal2", "Headset Mic", + "MIC BIAS External", "Secondary Mic", + "AMIC1", "MIC BIAS External", + "AMIC2", "MIC BIAS Internal2", + "AMIC3", "MIC BIAS External", + "DMIC1", "MIC BIAS Internal1", + "MIC BIAS Internal1", "Digital Mic1", + "DMIC2", "MIC BIAS Internal1", + "MIC BIAS Internal1", "Digital Mic2"; + /* I2S - Internal codec */ internal-dai-link@0 { cpu { /* PRIMARY */ diff --git a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi index c27202e296d59..1f43b15f1ade7 100644 --- a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi +++ b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi @@ -373,7 +373,7 @@ pinctrl-names = "default","sleep"; pinctrl-0 = <&adv7533_int_active &adv7533_switch_active>; pinctrl-1 = <&adv7533_int_suspend &adv7533_switch_suspend>; - #sound-dai-cells = <0>; + #sound-dai-cells = <1>; port { adv_in: endpoint { @@ -420,11 +420,33 @@ */ &sound { status = "okay"; - pinctrl-0 = <&cdc_pdm_lines_act &ext_sec_tlmm_lines_act &ext_mclk_tlmm_lines_act>; - pinctrl-1 = <&cdc_pdm_lines_sus &ext_sec_tlmm_lines_sus &ext_mclk_tlmm_lines_sus>; + pinctrl-0 = <&cdc_pdm_lines_act &ext_sec_tlmm_lines_act &ext_mclk_tlmm_lines_act &cdc_dmic_lines_act>; + pinctrl-1 = <&cdc_pdm_lines_sus &ext_sec_tlmm_lines_sus &ext_mclk_tlmm_lines_sus &cdc_dmic_lines_sus>; pinctrl-names = "default", "sleep"; qcom,model = "DB410c"; + qcom,audio-routing = + "MIC BIAS External", "Handset Mic", + "MIC BIAS Internal2", "Headset Mic", + "MIC BIAS External", "Secondary Mic", + "AMIC1", "MIC BIAS External", + "AMIC2", "MIC BIAS Internal2", + "AMIC3", "MIC BIAS External", + "DMIC1", "MIC BIAS Internal1", + "MIC BIAS Internal1", "Digital Mic1", + "DMIC2", "MIC BIAS Internal1", + "MIC BIAS Internal1", "Digital Mic2"; + /* External Primary or External Secondary -ADV7533 HDMI */ + external-dai-link@0 { + link-name = "ADV7533"; + + cpu { /* QUAT */ + sound-dai = <&lpass MI2S_QUATERNARY>; + }; + codec { + sound-dai = <&adv_bridge 0>; + }; + }; internal-codec-playback-dai-link@0 { /* I2S - Internal codec */ link-name = "WCD"; @@ -436,17 +458,16 @@ }; }; - /* External Primary or External Secondary -ADV7533 HDMI */ - external-dai-link@0 { - link-name = "ADV7533"; - - cpu { /* QUAT */ - sound-dai = <&lpass MI2S_QUATERNARY>; + internal-codec-capture-dai-link@0 { /* I2S - Internal codec */ + link-name = "WCD-Capture"; + cpu { /* PRIMARY */ + sound-dai = <&lpass MI2S_TERTIARY>; }; codec { - sound-dai = <&adv_bridge 0>; + sound-dai = <&wcd_codec 1>; }; }; + }; /* default regulators required for mezzanine boards */ diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi index a7024deaf3a1b..36c094da050f4 100644 --- a/arch/arm64/boot/dts/qcom/msm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi @@ -690,7 +690,7 @@ <&gcc GCC_ULTAUDIO_PCNOC_SWAY_CLK>, <&gcc GCC_ULTAUDIO_LPAIF_PRI_I2S_CLK>, <&gcc GCC_ULTAUDIO_LPAIF_SEC_I2S_CLK>, - <&gcc GCC_ULTAUDIO_LPAIF_AUX_I2S_CLK>, + <&gcc GCC_ULTAUDIO_LPAIF_SEC_I2S_CLK>, <&gcc GCC_ULTAUDIO_LPAIF_AUX_I2S_CLK>; clock-names = "ahbix-clk", diff --git a/arch/arm64/boot/dts/qcom/pm8916.dtsi b/arch/arm64/boot/dts/qcom/pm8916.dtsi index fcc9303da1005..113ccb3fa8868 100644 --- a/arch/arm64/boot/dts/qcom/pm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/pm8916.dtsi @@ -221,9 +221,10 @@ wcd_codec: codec@f000 { compatible = "qcom,apq8016-wcd-codec"; reg = <0xf000 0x200>; - #sound-dai-cells = <0>; + #sound-dai-cells = <1>; vddio-supply = <&pm8916_l5>; vdd-pa-supply = <&pm8916_s4>; + vdd-mic-bias-supply = <&pm8916_l13>; }; }; }; diff --git a/sound/soc/codecs/msm8x16-wcd.c b/sound/soc/codecs/msm8x16-wcd.c index d0df8c3e1155f..0554f21d8f991 100644 --- a/sound/soc/codecs/msm8x16-wcd.c +++ b/sound/soc/codecs/msm8x16-wcd.c @@ -26,7 +26,6 @@ #define MSM8X16_WCD_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S24_LE) - #define TOMBAK_VERSION_1_0 0 #define TOMBAK_IS_1_0(ver) \ ((ver == TOMBAK_VERSION_1_0) ? 1 : 0) @@ -36,6 +35,18 @@ #define EAR_PA_DISABLE (0x01 << 3) #define SPKR_PA_DISABLE (0x01 << 4) +#define MICBIAS_DEFAULT_VAL 1800000 +#define MICBIAS_MIN_VAL 1600000 +#define MICBIAS_STEP_SIZE 50000 + +#define DEFAULT_BOOST_VOLTAGE 5000 +#define MIN_BOOST_VOLTAGE 4000 +#define MAX_BOOST_VOLTAGE 5550 +#define BOOST_VOLTAGE_STEP 50 + +#define VOLTAGE_CONVERTER(value, min_value, step_size)\ + ((value - min_value)/step_size); + enum { AIF1_PB = 0, AIF1_CAP, @@ -48,6 +59,11 @@ static unsigned long rx_digital_gain_reg[] = { MSM8X16_WCD_A_CDC_RX3_VOL_CTL_B2_CTL, }; +static unsigned long tx_digital_gain_reg[] = { + MSM8X16_WCD_A_CDC_TX1_VOL_CTL_GAIN, + MSM8X16_WCD_A_CDC_TX2_VOL_CTL_GAIN, +}; + struct wcd_chip { struct regmap *analog_map; struct regmap *digital_map; @@ -65,9 +81,9 @@ struct wcd_chip { struct regulator *vdd_pa; struct regulator *vdd_px; struct regulator *vdd_cp; + struct regulator *vdd_mic_bias; }; - static int msm8x16_wcd_volatile(struct snd_soc_codec *codec, unsigned int reg) { return msm8x16_wcd_reg_readonly[reg]; @@ -140,7 +156,6 @@ static int __msm8x16_wcd_reg_read(struct snd_soc_codec *codec, return temp; } - static unsigned int msm8x16_wcd_read(struct snd_soc_codec *codec, unsigned int reg) { @@ -245,7 +260,6 @@ static int msm8x16_wcd_device_up(struct snd_soc_codec *codec) msm8x16_wcd_codec_init_reg(codec); msm8x16_wcd_update_reg_defaults(codec); - return 0; } @@ -285,6 +299,45 @@ static int msm8x16_wcd_codec_enable_clock_block(struct snd_soc_codec *codec, return 0; } +#define MICBIAS_EXT_BYP_CAP 0x00 +#define MICBIAS_NO_EXT_BYP_CAP 0x01 + +static void msm8x16_wcd_configure_cap(struct snd_soc_codec *codec, + bool micbias1, bool micbias2) +{ + +// struct msm8916_asoc_mach_data *pdata = NULL; +//FIXME should come from DT + int micbias1_cap_mode = MICBIAS_EXT_BYP_CAP, micbias2_cap_mode = MICBIAS_NO_EXT_BYP_CAP; + + //pdata = snd_soc_card_get_drvdata(codec->card); + + pr_debug("\n %s: micbias1 %x micbias2 = %d\n", __func__, micbias1, + micbias2); + if (micbias1 && micbias2) { + if ((micbias1_cap_mode + == MICBIAS_EXT_BYP_CAP) || + (micbias2_cap_mode + == MICBIAS_EXT_BYP_CAP)) + snd_soc_update_bits(codec, + MSM8X16_WCD_A_ANALOG_MICB_1_EN, + 0x40, (MICBIAS_EXT_BYP_CAP << 6)); + else + snd_soc_update_bits(codec, + MSM8X16_WCD_A_ANALOG_MICB_1_EN, + 0x40, (MICBIAS_NO_EXT_BYP_CAP << 6)); + } else if (micbias2) { + snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_1_EN, + 0x40, (micbias2_cap_mode << 6)); + } else if (micbias1) { + snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_1_EN, + 0x40, (micbias1_cap_mode << 6)); + } else { + snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_1_EN, + 0x40, 0x00); + } +} + static int msm8x16_wcd_codec_probe(struct snd_soc_codec *codec) { struct wcd_chip *chip = dev_get_drvdata(codec->dev); @@ -302,18 +355,28 @@ static int msm8x16_wcd_codec_probe(struct snd_soc_codec *codec) msm8x16_wcd_device_up(codec); + /* Set initial cap mode */ + msm8x16_wcd_configure_cap(codec, false, false); + + regulator_set_voltage(chip->vddio, 1800000, 1800000); err = regulator_enable(chip->vddio); if (err < 0) { dev_err(codec->dev, "failed to enable VDD regulator\n"); return err; } - + regulator_set_voltage(chip->vdd_pa, 1800000, 2200000); err = regulator_enable(chip->vdd_pa); if (err < 0) { dev_err(codec->dev, "failed to enable VDD regulator\n"); return err; } + regulator_set_voltage(chip->vdd_mic_bias, 3075000, 3075000); + err = regulator_enable(chip->vdd_mic_bias); + if (err < 0) { + dev_err(codec->dev, "failed to enable micbias regulator\n"); + return err; + } msm8x16_wcd_codec_enable_clock_block(codec, 1); return 0; @@ -345,6 +408,7 @@ static int msm8x16_wcd_set_interpolator_rate(struct snd_soc_dai *dai, static int msm8x16_wcd_set_decimator_rate(struct snd_soc_dai *dai, u8 tx_fs_rate_reg_val, u32 sample_rate) { + return 0; } @@ -355,7 +419,7 @@ static int msm8x16_wcd_hw_params(struct snd_pcm_substream *substream, u8 tx_fs_rate, rx_fs_rate; int ret; - dev_dbg(dai->codec->dev, + dev_err(dai->codec->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d format %d\n", __func__, dai->name, dai->id, params_rate(params), params_channels(params), params_format(params)); @@ -394,6 +458,8 @@ static int msm8x16_wcd_hw_params(struct snd_pcm_substream *substream, switch (substream->stream) { case SNDRV_PCM_STREAM_CAPTURE: + snd_soc_update_bits(dai->codec, + MSM8X16_WCD_A_CDC_CLK_TX_I2S_CTL, 0x07, tx_fs_rate); ret = msm8x16_wcd_set_decimator_rate(dai, tx_fs_rate, params_rate(params)); if (ret < 0) { @@ -423,10 +489,14 @@ static int msm8x16_wcd_hw_params(struct snd_pcm_substream *substream, switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: snd_soc_update_bits(dai->codec, + MSM8X16_WCD_A_CDC_CLK_TX_I2S_CTL, 0x20, 0x20); + snd_soc_update_bits(dai->codec, MSM8X16_WCD_A_CDC_CLK_RX_I2S_CTL, 0x20, 0x20); break; case SNDRV_PCM_FORMAT_S24_LE: snd_soc_update_bits(dai->codec, + MSM8X16_WCD_A_CDC_CLK_TX_I2S_CTL, 0x20, 0x00); + snd_soc_update_bits(dai->codec, MSM8X16_WCD_A_CDC_CLK_RX_I2S_CTL, 0x20, 0x00); break; default: @@ -471,7 +541,7 @@ static struct snd_soc_dai_ops msm8x16_wcd_dai_ops = { }; static struct snd_soc_dai_driver msm8x16_wcd_codec_dai[] = { - { + [0] = { .name = "msm8x16_wcd_i2s_rx1", .id = AIF1_PB, .playback = { @@ -485,7 +555,7 @@ static struct snd_soc_dai_driver msm8x16_wcd_codec_dai[] = { }, .ops = &msm8x16_wcd_dai_ops, }, - { + [1] = { .name = "msm8x16_wcd_i2s_tx1", .id = AIF1_CAP, .capture = { @@ -507,7 +577,6 @@ static int msm8x16_wcd_codec_remove(struct snd_soc_codec *codec) return 0; }; - static int msm8x16_wcd_spk_boost_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -535,7 +604,6 @@ static int msm8x16_wcd_spk_boost_set(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wcd_chip *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); - switch (ucontrol->value.integer.value[0]) { case 0: msm8x16_wcd->spk_boost_set = false; @@ -558,7 +626,6 @@ static const char * const hph_text[] = { static const struct soc_enum hph_enum = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(hph_text), hph_text); - static const struct snd_kcontrol_new hphl_mux[] = { SOC_DAPM_ENUM("HPHL", hph_enum) }; @@ -572,147 +639,36 @@ static const struct snd_kcontrol_new spkr_switch[] = { MSM8X16_WCD_A_ANALOG_SPKR_DAC_CTL, 7, 1, 0) }; -static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); -static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); - -static const struct snd_soc_dapm_route audio_map[] = { - {"RX_I2S_CLK", NULL, "CDC_CONN"}, - {"I2S RX1", NULL, "RX_I2S_CLK"}, - {"I2S RX2", NULL, "RX_I2S_CLK"}, - {"I2S RX3", NULL, "RX_I2S_CLK"}, - - {"I2S TX1", NULL, "TX_I2S_CLK"}, - {"I2S TX2", NULL, "TX_I2S_CLK"}, - - {"I2S TX1", NULL, "DEC1 MUX"}, - {"I2S TX2", NULL, "DEC2 MUX"}, - - /* RDAC Connections */ - {"HPHR DAC", NULL, "RDAC2 MUX"}, - {"RDAC2 MUX", "RX1", "RX1 CHAIN"}, - {"RDAC2 MUX", "RX2", "RX2 CHAIN"}, - - /* Earpiece (RX MIX1) */ - {"EAR", NULL, "EAR_S"}, - {"EAR_S", "Switch", "EAR PA"}, - {"EAR PA", NULL, "RX_BIAS"}, - {"EAR PA", NULL, "HPHL DAC"}, - {"EAR PA", NULL, "HPHR DAC"}, - {"EAR PA", NULL, "EAR CP"}, - - /* Headset (RX MIX1 and RX MIX2) */ - {"HEADPHONE", NULL, "HPHL PA"}, - {"HEADPHONE", NULL, "HPHR PA"}, - - - {"HPHL PA", NULL, "HPHL"}, - {"HPHR PA", NULL, "HPHR"}, - {"HPHL", "Switch", "HPHL DAC"}, - {"HPHR", "Switch", "HPHR DAC"}, - {"HPHL PA", NULL, "CP"}, - {"HPHL PA", NULL, "RX_BIAS"}, - {"HPHR PA", NULL, "CP"}, - {"HPHR PA", NULL, "RX_BIAS"}, - {"HPHL DAC", NULL, "RX1 CHAIN"}, - - {"SPK_OUT", NULL, "SPK PA"}, - {"SPK PA", NULL, "SPK_RX_BIAS"}, - {"SPK PA", NULL, "SPK DAC"}, - {"SPK DAC", "Switch", "RX3 CHAIN"}, - - {"RX1 CHAIN", NULL, "RX1 CLK"}, - {"RX2 CHAIN", NULL, "RX2 CLK"}, - {"RX3 CHAIN", NULL, "RX3 CLK"}, - {"RX1 CHAIN", NULL, "RX1 MIX2"}, - {"RX2 CHAIN", NULL, "RX2 MIX2"}, - {"RX1 CHAIN", NULL, "RX1 MIX1"}, - {"RX2 CHAIN", NULL, "RX2 MIX1"}, - {"RX3 CHAIN", NULL, "RX3 MIX1"}, - - {"RX1 MIX1", NULL, "RX1 MIX1 INP1"}, - {"RX1 MIX1", NULL, "RX1 MIX1 INP2"}, - {"RX1 MIX1", NULL, "RX1 MIX1 INP3"}, - - {"RX2 MIX1", NULL, "RX2 MIX1 INP1"}, - {"RX2 MIX1", NULL, "RX2 MIX1 INP2"}, - - {"RX3 MIX1", NULL, "RX3 MIX1 INP1"}, - {"RX3 MIX1", NULL, "RX3 MIX1 INP2"}, - - {"RX1 MIX2", NULL, "RX1 MIX2 INP1"}, - {"RX2 MIX2", NULL, "RX2 MIX2 INP1"}, - - {"RX1 MIX1 INP1", "RX1", "I2S RX1"}, - {"RX1 MIX1 INP1", "RX2", "I2S RX2"}, - {"RX1 MIX1 INP1", "RX3", "I2S RX3"}, - {"RX1 MIX1 INP1", "IIR1", "IIR1"}, - {"RX1 MIX1 INP2", "RX1", "I2S RX1"}, - {"RX1 MIX1 INP2", "RX2", "I2S RX2"}, - {"RX1 MIX1 INP2", "RX3", "I2S RX3"}, - {"RX1 MIX1 INP2", "IIR1", "IIR1"}, - {"RX1 MIX1 INP3", "RX1", "I2S RX1"}, - {"RX1 MIX1 INP3", "RX2", "I2S RX2"}, - {"RX1 MIX1 INP3", "RX3", "I2S RX3"}, - - {"RX2 MIX1 INP1", "RX1", "I2S RX1"}, - {"RX2 MIX1 INP1", "RX2", "I2S RX2"}, - {"RX2 MIX1 INP1", "RX3", "I2S RX3"}, - {"RX2 MIX1 INP1", "IIR1", "IIR1"}, - {"RX2 MIX1 INP2", "RX1", "I2S RX1"}, - {"RX2 MIX1 INP2", "RX2", "I2S RX2"}, - {"RX2 MIX1 INP2", "RX3", "I2S RX3"}, - {"RX2 MIX1 INP2", "IIR1", "IIR1"}, - - {"RX3 MIX1 INP1", "RX1", "I2S RX1"}, - {"RX3 MIX1 INP1", "RX2", "I2S RX2"}, - {"RX3 MIX1 INP1", "RX3", "I2S RX3"}, - {"RX3 MIX1 INP1", "IIR1", "IIR1"}, - {"RX3 MIX1 INP2", "RX1", "I2S RX1"}, - {"RX3 MIX1 INP2", "RX2", "I2S RX2"}, - {"RX3 MIX1 INP2", "RX3", "I2S RX3"}, - {"RX3 MIX1 INP2", "IIR1", "IIR1"}, - - {"RX1 MIX2 INP1", "IIR1", "IIR1"}, - {"RX2 MIX2 INP1", "IIR1", "IIR1"}, - - /* Decimator Inputs */ - {"DEC1 MUX", "DMIC1", "DMIC1"}, - {"DEC1 MUX", "DMIC2", "DMIC2"}, - {"DEC1 MUX", "ADC1", "ADC1"}, - {"DEC1 MUX", "ADC2", "ADC2"}, - {"DEC1 MUX", "ADC3", "ADC3"}, - {"DEC1 MUX", NULL, "CDC_CONN"}, - - {"DEC2 MUX", "DMIC1", "DMIC1"}, - {"DEC2 MUX", "DMIC2", "DMIC2"}, - {"DEC2 MUX", "ADC1", "ADC1"}, - {"DEC2 MUX", "ADC2", "ADC2"}, - {"DEC2 MUX", "ADC3", "ADC3"}, - {"DEC2 MUX", NULL, "CDC_CONN"}, +static void msm8x16_wcd_codec_enable_adc_block(struct snd_soc_codec *codec, + int enable) +{ + //struct msm8x16_wcd_priv *wcd8x16 = snd_soc_codec_get_drvdata(codec); - /* ADC Connections */ - {"ADC2", NULL, "ADC2 MUX"}, - {"ADC3", NULL, "ADC2 MUX"}, - {"ADC2 MUX", "INP2", "ADC2_INP2"}, - {"ADC2 MUX", "INP3", "ADC2_INP3"}, + dev_dbg(codec->dev, "%s %d\n", __func__, enable); - {"ADC1", NULL, "AMIC1"}, - {"ADC2_INP2", NULL, "AMIC2"}, - {"ADC2_INP3", NULL, "AMIC3"}, + if (enable) { + //wcd8x16->adc_count++; + snd_soc_update_bits(codec, + MSM8X16_WCD_A_DIGITAL_CDC_ANA_CLK_CTL, + 0x20, 0x20); + snd_soc_update_bits(codec, + MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, + 0x10, 0x10); + } else { + //wcd8x16->adc_count--; + //if (!wcd8x16->adc_count) { + snd_soc_update_bits(codec, + MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, + 0x10, 0x00); + snd_soc_update_bits(codec, + MSM8X16_WCD_A_DIGITAL_CDC_ANA_CLK_CTL, + 0x20, 0x0); + //} + } +} - /* TODO: Fix this */ - {"IIR1", NULL, "IIR1 INP1 MUX"}, - {"IIR1 INP1 MUX", "DEC1", "DEC1 MUX"}, - {"IIR1 INP1 MUX", "DEC2", "DEC2 MUX"}, - {"MIC BIAS Internal1", NULL, "INT_LDO_H"}, - {"MIC BIAS Internal2", NULL, "INT_LDO_H"}, - {"MIC BIAS External", NULL, "INT_LDO_H"}, - {"MIC BIAS External2", NULL, "INT_LDO_H"}, - {"MIC BIAS Internal1", NULL, "MICBIAS_REGULATOR"}, - {"MIC BIAS Internal2", NULL, "MICBIAS_REGULATOR"}, - {"MIC BIAS External", NULL, "MICBIAS_REGULATOR"}, - {"MIC BIAS External2", NULL, "MICBIAS_REGULATOR"}, -}; +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); static const char * const rx_mix1_text[] = { "ZERO", "IIR1", "IIR2", "RX1", "RX2", "RX3" @@ -734,10 +690,17 @@ static const char * const rdac2_mux_text[] = { "ZERO", "RX2", "RX1" }; +static const char * const iir_inp1_text[] = { + "ZERO", "DEC1", "DEC2", "RX1", "RX2", "RX3" +}; + static const char * const iir1_inp1_text[] = { "ZERO", "DEC1", "DEC2", "RX1", "RX2", "RX3" }; +static const struct soc_enum adc2_enum = + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(adc2_mux_text), adc2_mux_text); + /* RX1 MIX1 */ static const struct soc_enum rx_mix1_inp1_chain_enum = SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_RX1_B1_CTL, @@ -782,19 +745,47 @@ static const struct soc_enum rx3_mix1_inp2_chain_enum = SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_RX3_B1_CTL, 3, 6, rx_mix1_text); +/* DEC */ +static const struct soc_enum dec1_mux_enum = + SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_TX_B1_CTL, + 0, 6, dec_mux_text); + +static const struct soc_enum dec2_mux_enum = + SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_TX_B1_CTL, + 3, 6, dec_mux_text); + static const struct soc_enum rdac2_mux_enum = SOC_ENUM_SINGLE(MSM8X16_WCD_A_DIGITAL_CDC_CONN_HPHR_DAC_CTL, 0, 3, rdac2_mux_text); +static const struct soc_enum iir1_inp1_mux_enum = + SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_EQ1_B1_CTL, + 0, 6, iir_inp1_text); + +static const struct soc_enum iir2_inp1_mux_enum = + SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_EQ2_B1_CTL, + 0, 6, iir_inp1_text); +static const struct snd_kcontrol_new iir2_inp1_mux = + SOC_DAPM_ENUM("IIR2 INP1 Mux", iir2_inp1_mux_enum); + static const struct soc_enum rx3_mix1_inp3_chain_enum = SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_RX3_B1_CTL, 0, 6, rx_mix1_text); static const struct snd_kcontrol_new rx_mix1_inp1_mux = SOC_DAPM_ENUM("RX1 MIX1 INP1 Mux", rx_mix1_inp1_chain_enum); +static const struct snd_kcontrol_new dec1_mux = + SOC_DAPM_ENUM("DEC1 MUX Mux", dec1_mux_enum); + +static const struct snd_kcontrol_new dec2_mux = + SOC_DAPM_ENUM("DEC2 MUX Mux", dec2_mux_enum); + static const struct snd_kcontrol_new rdac2_mux = SOC_DAPM_ENUM("RDAC2 MUX Mux", rdac2_mux_enum); +static const struct snd_kcontrol_new iir1_inp1_mux = + SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum); + static const struct snd_kcontrol_new rx_mix1_inp2_mux = SOC_DAPM_ENUM("RX1 MIX1 INP2 Mux", rx_mix1_inp2_chain_enum); @@ -825,13 +816,43 @@ static const struct snd_kcontrol_new rx1_mix2_inp1_mux = static const struct snd_kcontrol_new rx2_mix2_inp1_mux = SOC_DAPM_ENUM("RX2 MIX2 INP1 Mux", rx2_mix2_inp1_chain_enum); +static const struct snd_kcontrol_new tx_adc2_mux = + SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum); + static const char * const msm8x16_wcd_loopback_mode_ctrl_text[] = { "DISABLE", "ENABLE"}; - static const struct soc_enum msm8x16_wcd_loopback_mode_ctl_enum[] = { SOC_ENUM_SINGLE_EXT(2, msm8x16_wcd_loopback_mode_ctrl_text), }; +static int msm8x16_wcd_codec_enable_on_demand_supply( + struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wcd_chip *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = regulator_enable(msm8x16_wcd->vdd_mic_bias); + if (ret) + dev_err(codec->dev, "%s: Failed to enable vdd micbias\n", + __func__); + break; + case SND_SOC_DAPM_POST_PMD: + ret = regulator_disable(msm8x16_wcd->vdd_mic_bias); + if (ret) + dev_err(codec->dev, "%s: Failed to disable vdd-micbias\n", + __func__); + break; + default: + break; + } + + return ret; +} + static const char * const msm8x16_wcd_ear_pa_boost_ctrl_text[] = { "DISABLE", "ENABLE"}; static const struct soc_enum msm8x16_wcd_ear_pa_boost_ctl_enum[] = { @@ -904,7 +925,6 @@ static const struct snd_kcontrol_new msm8x16_wcd_snd_controls[] = { SOC_ENUM("RX3 HPF cut off", cf_rxmix3_enum), }; - static const struct snd_kcontrol_new ear_pa_switch[] = { SOC_DAPM_SINGLE("Switch", MSM8X16_WCD_A_ANALOG_RX_EAR_CTL, 5, 1, 0) @@ -958,6 +978,82 @@ static int msm8x16_wcd_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, return 0; } +static int msm8x16_wcd_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + //struct snd_soc_codec *codec = w->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 adc_reg; + u8 init_bit_shift; + + dev_dbg(codec->dev, "%s %d\n", __func__, event); + + adc_reg = MSM8X16_WCD_A_ANALOG_TX_1_2_TEST_CTL_2; + + if (w->reg == MSM8X16_WCD_A_ANALOG_TX_1_EN) + init_bit_shift = 5; + else if ((w->reg == MSM8X16_WCD_A_ANALOG_TX_2_EN) || + (w->reg == MSM8X16_WCD_A_ANALOG_TX_3_EN)) + init_bit_shift = 4; + else { + dev_err(codec->dev, "%s: Error, invalid adc register\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + msm8x16_wcd_codec_enable_adc_block(codec, 1); + if (w->reg == MSM8X16_WCD_A_ANALOG_TX_2_EN) + snd_soc_update_bits(codec, + MSM8X16_WCD_A_ANALOG_MICB_1_CTL, 0x02, 0x02); + /* + * Add delay of 10 ms to give sufficient time for the voltage + * to shoot up and settle so that the txfe init does not + * happen when the input voltage is changing too much. + */ + usleep_range(10000, 10010); + snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, + 1 << init_bit_shift); + if (w->reg == MSM8X16_WCD_A_ANALOG_TX_1_EN) + snd_soc_update_bits(codec, + MSM8X16_WCD_A_DIGITAL_CDC_CONN_TX1_CTL, + 0x03, 0x00); + else if ((w->reg == MSM8X16_WCD_A_ANALOG_TX_2_EN) || + (w->reg == MSM8X16_WCD_A_ANALOG_TX_3_EN)) + snd_soc_update_bits(codec, + MSM8X16_WCD_A_DIGITAL_CDC_CONN_TX2_CTL, + 0x03, 0x00); + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * Add delay of 12 ms before deasserting the init + * to reduce the tx pop + */ + usleep_range(12000, 12010); + snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, 0x00); + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + break; + case SND_SOC_DAPM_POST_PMD: + msm8x16_wcd_codec_enable_adc_block(codec, 0); + if (w->reg == MSM8X16_WCD_A_ANALOG_TX_2_EN) + snd_soc_update_bits(codec, + MSM8X16_WCD_A_ANALOG_MICB_1_CTL, 0x02, 0x00); + if (w->reg == MSM8X16_WCD_A_ANALOG_TX_1_EN) + snd_soc_update_bits(codec, + MSM8X16_WCD_A_DIGITAL_CDC_CONN_TX1_CTL, + 0x03, 0x02); + else if ((w->reg == MSM8X16_WCD_A_ANALOG_TX_2_EN) || + (w->reg == MSM8X16_WCD_A_ANALOG_TX_3_EN)) + snd_soc_update_bits(codec, + MSM8X16_WCD_A_DIGITAL_CDC_CONN_TX2_CTL, + 0x03, 0x02); + + break; + } + return 0; +} static int msm8x16_wcd_codec_enable_spk_pa(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -1022,6 +1118,286 @@ static int msm8x16_wcd_codec_enable_spk_pa(struct snd_soc_dapm_widget *w, return 0; } +static void msm8x16_wcd_micbias_2_enable(struct snd_soc_codec *codec, bool on) +{ + if (on) { + snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_1_CTL, + 0x60, 0x60); + snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MICB_1_VAL, + 0xC0); + /* + * Special headset needs MICBIAS as 2.7V so wait for + * 50 msec for the MICBIAS to reach 2.7 volts. + */ + msleep(50); + snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_1_CTL, + 0x60, 0x00); + } +} + +static s32 g_dmic_clk_cnt; +static int msm8x16_wcd_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u8 dmic_clk_en; + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + unsigned int dmic; + int ret; + char *dec_num = strpbrk(w->name, "12"); + + if (dec_num == NULL) { + dev_err(codec->dev, "%s: Invalid DMIC\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(dec_num, 10, &dmic); + if (ret < 0) { + dev_err(codec->dev, + "%s: Invalid DMIC line on the codec\n", __func__); + return -EINVAL; + } + + switch (dmic) { + case 1: + case 2: + dmic_clk_en = 0x01; + dmic_clk_cnt = &g_dmic_clk_cnt; + dmic_clk_reg = MSM8X16_WCD_A_CDC_CLK_DMIC_B1_CTL; + dev_dbg(codec->dev, + "%s() event %d DMIC%d dmic_1_2_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + break; + default: + dev_err(codec->dev, "%s: Invalid DMIC Selection\n", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_update_bits(codec, dmic_clk_reg, + 0x0E, 0x02); + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + if (dmic == 1) + snd_soc_update_bits(codec, + MSM8X16_WCD_A_CDC_TX1_DMIC_CTL, 0x07, 0x01); + if (dmic == 2) + snd_soc_update_bits(codec, + MSM8X16_WCD_A_CDC_TX2_DMIC_CTL, 0x07, 0x01); + break; + case SND_SOC_DAPM_POST_PMD: + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, 0); + break; + } + return 0; +} + +static int msm8x16_wcd_codec_enable_micbias(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 wcd_chip *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + u16 micb_int_reg; + char *internal1_text = "Internal1"; + char *internal2_text = "Internal2"; + char *internal3_text = "Internal3"; + char *external2_text = "External2"; + char *external_text = "External"; + bool micbias2; + + switch (w->reg) { + case MSM8X16_WCD_A_ANALOG_MICB_1_EN: + case MSM8X16_WCD_A_ANALOG_MICB_2_EN: + micb_int_reg = MSM8X16_WCD_A_ANALOG_MICB_1_INT_RBIAS; + break; + default: + dev_err(codec->dev, + "%s: Error, invalid micbias register 0x%x\n", + __func__, w->reg); + return -EINVAL; + } + + micbias2 = (snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN) & 0x80); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (strnstr(w->name, internal1_text, 30)) { + snd_soc_update_bits(codec, micb_int_reg, 0x80, 0x80); + } else if (strnstr(w->name, internal2_text, 30)) { + snd_soc_update_bits(codec, micb_int_reg, 0x10, 0x10); + snd_soc_update_bits(codec, w->reg, 0x60, 0x00); + } else if (strnstr(w->name, internal3_text, 30)) { + snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x2); + } + if (!strnstr(w->name, external_text, 30)) + snd_soc_update_bits(codec, + MSM8X16_WCD_A_ANALOG_MICB_1_EN, 0x05, 0x04); + if (w->reg == MSM8X16_WCD_A_ANALOG_MICB_1_EN) + msm8x16_wcd_configure_cap(codec, true, micbias2); + + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(20000, 20100); + if (strnstr(w->name, internal1_text, 30)) { + snd_soc_update_bits(codec, micb_int_reg, 0x40, 0x40); + } else if (strnstr(w->name, internal2_text, 30)) { + snd_soc_update_bits(codec, micb_int_reg, 0x08, 0x08); + msm8x16_wcd_micbias_2_enable(codec, true); + + msm8x16_wcd_configure_cap(codec, false, true); + regmap_write(msm8x16_wcd->analog_map, 0xf144, 0x95); + } else if (strnstr(w->name, internal3_text, 30)) { + snd_soc_update_bits(codec, micb_int_reg, 0x01, 0x01); + } else if (strnstr(w->name, external2_text, 30)) { + msm8x16_wcd_micbias_2_enable(codec, true); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (strnstr(w->name, internal1_text, 30)) { + snd_soc_update_bits(codec, micb_int_reg, 0xC0, 0x40); + } else if (strnstr(w->name, internal2_text, 30)) { + msm8x16_wcd_micbias_2_enable(codec, false); + } else if (strnstr(w->name, internal3_text, 30)) { + snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x0); + } else if (strnstr(w->name, external2_text, 30)) { + /* + * send micbias turn off event to mbhc driver and then + * break, as no need to set MICB_1_EN register. + */ + msm8x16_wcd_micbias_2_enable(codec, false); + break; + } + if (w->reg == MSM8X16_WCD_A_ANALOG_MICB_1_EN) + msm8x16_wcd_configure_cap(codec, false, micbias2); + break; + } + + return 0; +} + +#define TX_MUX_CTL_CUT_OFF_FREQ_MASK 0x30 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +static int msm8x16_wcd_codec_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + unsigned int decimator; + char *dec_name = NULL; + char *widget_name = NULL; + char *temp; + int ret = 0; + u16 dec_reset_reg, tx_vol_ctl_reg, tx_mux_ctl_reg; + u8 dec_hpf_cut_of_freq; + int offset; + char *dec_num; + dev_dbg(codec->dev, "%s %d\n", __func__, event); + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + temp = widget_name; + + dec_name = strsep(&widget_name, " "); + widget_name = temp; + if (!dec_name) { + dev_err(codec->dev, + "%s: Invalid decimator = %s\n", __func__, w->name); + ret = -EINVAL; + goto out; + } + + dec_num = strpbrk(dec_name, "12"); + if (dec_num == NULL) { + dev_err(codec->dev, "%s: Invalid Decimator\n", __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec_num, 10, &decimator); + if (ret < 0) { + dev_err(codec->dev, + "%s: Invalid decimator = %s\n", __func__, dec_name); + ret = -EINVAL; + goto out; + } + + dev_err(codec->dev, + "%s(): widget = %s dec_name = %s decimator = %u\n", __func__, + w->name, dec_name, decimator); + + if (w->reg == MSM8X16_WCD_A_CDC_CLK_TX_CLK_EN_B1_CTL) { + dec_reset_reg = MSM8X16_WCD_A_CDC_CLK_TX_RESET_B1_CTL; + offset = 0; + } else { + dev_err(codec->dev, "%s: Error, incorrect dec\n", __func__); + ret = -EINVAL; + goto out; + } + + tx_vol_ctl_reg = MSM8X16_WCD_A_CDC_TX1_VOL_CTL_CFG + + 32 * (decimator - 1); + tx_mux_ctl_reg = MSM8X16_WCD_A_CDC_TX1_MUX_CTL + + 32 * (decimator - 1); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enableable TX digital mute */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01); + dec_hpf_cut_of_freq = snd_soc_read(codec, tx_mux_ctl_reg); + dec_hpf_cut_of_freq = (dec_hpf_cut_of_freq & 0x30) >> 4; + if ((dec_hpf_cut_of_freq != CF_MIN_3DB_150HZ)) { + + /* set cut of freq to CF_MIN_3DB_150HZ (0x1); */ + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30, + CF_MIN_3DB_150HZ << 4); + } + snd_soc_update_bits(codec, + MSM8X16_WCD_A_ANALOG_TX_1_2_TXFE_CLKDIV, + 0xFF, 0x42); + + break; + case SND_SOC_DAPM_POST_PMU: + /* enable HPF */ + snd_soc_update_bits(codec, tx_mux_ctl_reg , 0x08, 0x00); + /* apply the digital gain after the decimator is enabled*/ + if ((w->shift) < ARRAY_SIZE(tx_digital_gain_reg)) + snd_soc_write(codec, + tx_digital_gain_reg[w->shift + offset], + snd_soc_read(codec, + tx_digital_gain_reg[w->shift + offset]) + ); + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x00); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01); + msleep(20); + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x08); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, + 1 << w->shift); + snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, 0x0); + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x08); + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x00); + break; + } + +out: + kfree(widget_name); + return ret; +} + static int msm8x16_wcd_codec_enable_interpolator(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -1195,7 +1571,6 @@ static int msm8x16_wcd_codec_enable_rx_bias(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct wcd_chip *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); - switch (event) { case SND_SOC_DAPM_PRE_PMU: msm8x16_wcd->rx_bias_count++; @@ -1353,12 +1728,158 @@ static int msm8x16_wcd_hph_pa_event(struct snd_soc_dapm_widget *w, return 0; } +static const struct snd_soc_dapm_route audio_map[] = { + {"RX_I2S_CLK", NULL, "CDC_CONN"}, + {"I2S RX1", NULL, "RX_I2S_CLK"}, + {"I2S RX2", NULL, "RX_I2S_CLK"}, + {"I2S RX3", NULL, "RX_I2S_CLK"}, + + {"I2S TX1", NULL, "TX_I2S_CLK"}, + {"I2S TX2", NULL, "TX_I2S_CLK"}, + + {"I2S TX1", NULL, "DEC1 MUX"}, + {"I2S TX2", NULL, "DEC2 MUX"}, + + /* RDAC Connections */ + {"HPHR DAC", NULL, "RDAC2 MUX"}, + {"RDAC2 MUX", "RX1", "RX1 CHAIN"}, + {"RDAC2 MUX", "RX2", "RX2 CHAIN"}, + + /* Earpiece (RX MIX1) */ + {"EAR", NULL, "EAR_S"}, + {"EAR_S", "Switch", "EAR PA"}, + {"EAR PA", NULL, "RX_BIAS"}, + {"EAR PA", NULL, "HPHL DAC"}, + {"EAR PA", NULL, "HPHR DAC"}, + {"EAR PA", NULL, "EAR CP"}, + + /* Headset (RX MIX1 and RX MIX2) */ + {"HEADPHONE", NULL, "HPHL PA"}, + {"HEADPHONE", NULL, "HPHR PA"}, + + {"HPHL PA", NULL, "HPHL"}, + {"HPHR PA", NULL, "HPHR"}, + {"HPHL", "Switch", "HPHL DAC"}, + {"HPHR", "Switch", "HPHR DAC"}, + {"HPHL PA", NULL, "CP"}, + {"HPHL PA", NULL, "RX_BIAS"}, + {"HPHR PA", NULL, "CP"}, + {"HPHR PA", NULL, "RX_BIAS"}, + {"HPHL DAC", NULL, "RX1 CHAIN"}, + + {"SPK_OUT", NULL, "SPK PA"}, + {"SPK PA", NULL, "SPK_RX_BIAS"}, + {"SPK PA", NULL, "SPK DAC"}, + {"SPK DAC", "Switch", "RX3 CHAIN"}, + {"SPK DAC", NULL, "VDD_SPKDRV"}, + + {"RX1 CHAIN", NULL, "RX1 CLK"}, + {"RX2 CHAIN", NULL, "RX2 CLK"}, + {"RX3 CHAIN", NULL, "RX3 CLK"}, + {"RX1 CHAIN", NULL, "RX1 MIX2"}, + {"RX2 CHAIN", NULL, "RX2 MIX2"}, + {"RX3 CHAIN", NULL, "RX3 MIX1"}, + + {"RX1 MIX1", NULL, "RX1 MIX1 INP1"}, + {"RX1 MIX1", NULL, "RX1 MIX1 INP2"}, + {"RX1 MIX1", NULL, "RX1 MIX1 INP3"}, + {"RX2 MIX1", NULL, "RX2 MIX1 INP1"}, + {"RX2 MIX1", NULL, "RX2 MIX1 INP2"}, + {"RX3 MIX1", NULL, "RX3 MIX1 INP1"}, + {"RX3 MIX1", NULL, "RX3 MIX1 INP2"}, + {"RX1 MIX2", NULL, "RX1 MIX1"}, + {"RX1 MIX2", NULL, "RX1 MIX2 INP1"}, + {"RX2 MIX2", NULL, "RX2 MIX1"}, + {"RX2 MIX2", NULL, "RX2 MIX2 INP1"}, + + {"RX1 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX1 MIX1 INP1", "IIR2", "IIR2"}, + {"RX1 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX1 MIX1 INP2", "IIR1", "IIR1"}, + {"RX1 MIX1 INP2", "IIR2", "IIR2"}, + {"RX1 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP3", "RX3", "I2S RX3"}, + + {"RX2 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX2 MIX1 INP1", "IIR1", "IIR1"}, + {"RX2 MIX1 INP1", "IIR2", "IIR2"}, + {"RX2 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX2 MIX1 INP2", "IIR1", "IIR1"}, + {"RX2 MIX1 INP2", "IIR2", "IIR2"}, + + {"RX3 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX3 MIX1 INP1", "IIR1", "IIR1"}, + {"RX3 MIX1 INP1", "IIR2", "IIR2"}, + {"RX3 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX3 MIX1 INP2", "IIR1", "IIR1"}, + {"RX3 MIX1 INP2", "IIR2", "IIR2"}, + + {"RX1 MIX2 INP1", "IIR1", "IIR1"}, + {"RX2 MIX2 INP1", "IIR1", "IIR1"}, + {"RX1 MIX2 INP1", "IIR2", "IIR2"}, + {"RX2 MIX2 INP1", "IIR2", "IIR2"}, + + /* Decimator Inputs */ + {"DEC1 MUX", "DMIC1", "DMIC1"}, + {"DEC1 MUX", "DMIC2", "DMIC2"}, + {"DEC1 MUX", "ADC1", "ADC1"}, + {"DEC1 MUX", "ADC2", "ADC2"}, + {"DEC1 MUX", "ADC3", "ADC3"}, + {"DEC1 MUX", NULL, "CDC_CONN"}, + + {"DEC2 MUX", "DMIC1", "DMIC1"}, + {"DEC2 MUX", "DMIC2", "DMIC2"}, + {"DEC2 MUX", "ADC1", "ADC1"}, + {"DEC2 MUX", "ADC2", "ADC2"}, + {"DEC2 MUX", "ADC3", "ADC3"}, + {"DEC2 MUX", NULL, "CDC_CONN"}, + + /* ADC Connections */ + {"ADC2", NULL, "ADC2 MUX"}, + {"ADC3", NULL, "ADC2 MUX"}, + {"ADC2 MUX", "INP2", "ADC2_INP2"}, + {"ADC2 MUX", "INP3", "ADC2_INP3"}, + + {"ADC1", NULL, "AMIC1"}, + {"ADC2_INP2", NULL, "AMIC2"}, + {"ADC2_INP3", NULL, "AMIC3"}, + + /* TODO: Fix this */ + {"IIR1", NULL, "IIR1 INP1 MUX"}, + {"IIR1 INP1 MUX", "DEC1", "DEC1 MUX"}, + {"IIR1 INP1 MUX", "DEC2", "DEC2 MUX"}, + {"IIR2", NULL, "IIR2 INP1 MUX"}, + {"IIR2 INP1 MUX", "DEC1", "DEC1 MUX"}, + {"IIR2 INP1 MUX", "DEC2", "DEC2 MUX"}, + {"MIC BIAS Internal1", NULL, "INT_LDO_H"}, + {"MIC BIAS Internal2", NULL, "INT_LDO_H"}, + {"MIC BIAS External", NULL, "INT_LDO_H"}, + {"MIC BIAS External2", NULL, "INT_LDO_H"}, + {"MIC BIAS Internal1", NULL, "MICBIAS_REGULATOR"}, + {"MIC BIAS Internal2", NULL, "MICBIAS_REGULATOR"}, + {"MIC BIAS External", NULL, "MICBIAS_REGULATOR"}, + {"MIC BIAS External2", NULL, "MICBIAS_REGULATOR"}, +}; + static int msm8x16_wcd_hphr_dac_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); - switch (event) { case SND_SOC_DAPM_PRE_PMU: snd_soc_update_bits(codec, @@ -1423,20 +1944,20 @@ static const struct snd_soc_dapm_widget msm8x16_wcd_dapm_widgets[] = { SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MUX("HPHR", SND_SOC_NOPM, 0, 0, hphr_mux), - SND_SOC_DAPM_MIXER("SPK DAC", SND_SOC_NOPM, 0, 0, - spkr_switch, ARRAY_SIZE(spkr_switch)), - SND_SOC_DAPM_MIXER_E("HPHR DAC", MSM8X16_WCD_A_ANALOG_RX_HPH_R_PA_DAC_CTL, 3, 0, NULL, 0, msm8x16_wcd_hphr_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("SPK DAC", SND_SOC_NOPM, 0, 0, + spkr_switch, ARRAY_SIZE(spkr_switch)), + /* Speaker */ SND_SOC_DAPM_OUTPUT("SPK_OUT"), SND_SOC_DAPM_PGA_E("SPK PA", MSM8X16_WCD_A_ANALOG_SPKR_DRV_CTL, 6, 0, NULL, 0, msm8x16_wcd_codec_enable_spk_pa, - SND_SOC_DAPM_PRE_REG| SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), @@ -1513,6 +2034,13 @@ static const struct snd_soc_dapm_widget msm8x16_wcd_dapm_widgets[] = { &rx1_mix2_inp1_mux), SND_SOC_DAPM_MUX("RX2 MIX2 INP1", SND_SOC_NOPM, 0, 0, &rx2_mix2_inp1_mux), + + SND_SOC_DAPM_SUPPLY("MICBIAS_REGULATOR", SND_SOC_NOPM, + ON_DEMAND_MICBIAS, 0, + msm8x16_wcd_codec_enable_on_demand_supply, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("CP", MSM8X16_WCD_A_ANALOG_NCP_EN, 0, 0, msm8x16_wcd_codec_enable_charge_pump, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), @@ -1525,16 +2053,108 @@ static const struct snd_soc_dapm_widget msm8x16_wcd_dapm_widgets[] = { 0, 0, msm8x16_wcd_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_SUPPLY("SPK_RX_BIAS", - SND_SOC_NOPM, 0, 0, + SND_SOC_DAPM_SUPPLY("SPK_RX_BIAS", SND_SOC_NOPM, 0, 0, msm8x16_wcd_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* TX */ + + SND_SOC_DAPM_SUPPLY_S("CDC_CONN", -2, MSM8X16_WCD_A_CDC_CLK_OTHR_CTL, + 2, 0, NULL, 0), + + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_SUPPLY("MIC BIAS Internal1", + MSM8X16_WCD_A_ANALOG_MICB_1_EN, 7, 0, + msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS Internal2", + MSM8X16_WCD_A_ANALOG_MICB_2_EN, 7, 0, + msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS Internal3", + MSM8X16_WCD_A_ANALOG_MICB_1_EN, 7, 0, + msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC1", NULL, MSM8X16_WCD_A_ANALOG_TX_1_EN, 7, 0, + msm8x16_wcd_codec_enable_adc, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2_INP2", + NULL, MSM8X16_WCD_A_ANALOG_TX_2_EN, 7, 0, + msm8x16_wcd_codec_enable_adc, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2_INP3", + NULL, MSM8X16_WCD_A_ANALOG_TX_3_EN, 7, 0, + msm8x16_wcd_codec_enable_adc, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, + &tx_adc2_mux), + + SND_SOC_DAPM_SUPPLY("MIC BIAS External", + MSM8X16_WCD_A_ANALOG_MICB_1_EN, 7, 0, + msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("MIC BIAS External2", + MSM8X16_WCD_A_ANALOG_MICB_2_EN, 7, 0, + msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + + SND_SOC_DAPM_INPUT("AMIC3"), + + SND_SOC_DAPM_MUX_E("DEC1 MUX", + MSM8X16_WCD_A_CDC_CLK_TX_CLK_EN_B1_CTL, 0, 0, + &dec1_mux, msm8x16_wcd_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("DEC2 MUX", + MSM8X16_WCD_A_CDC_CLK_TX_CLK_EN_B1_CTL, 1, 0, + &dec2_mux, msm8x16_wcd_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("RDAC2 MUX", SND_SOC_NOPM, 0, 0, &rdac2_mux), + + SND_SOC_DAPM_INPUT("AMIC2"), + + SND_SOC_DAPM_AIF_OUT("I2S TX1", "AIF1 Capture", 0, SND_SOC_NOPM, + 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX2", "AIF1 Capture", 0, SND_SOC_NOPM, + 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX3", "AIF1 Capture", 0, SND_SOC_NOPM, + 0, 0), + + + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + msm8x16_wcd_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0, + msm8x16_wcd_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + /* Sidetone */ + SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux), + SND_SOC_DAPM_PGA("IIR1", + MSM8X16_WCD_A_CDC_CLK_SD_CTL, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("IIR2 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp1_mux), + SND_SOC_DAPM_PGA("IIR2", + MSM8X16_WCD_A_CDC_CLK_SD_CTL, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", MSM8X16_WCD_A_CDC_CLK_RX_I2S_CTL, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TX_I2S_CLK", + MSM8X16_WCD_A_CDC_CLK_TX_I2S_CTL, 4, 0, + NULL, 0), }; - static struct snd_soc_codec_driver msm8x16_wcd_codec = { .probe = msm8x16_wcd_codec_probe, .remove = msm8x16_wcd_codec_remove, @@ -1581,6 +2201,12 @@ static int msm8x16_wcd_codec_parse_dt(struct platform_device *pdev, return PTR_ERR(chip->vdd_pa); } + chip->vdd_mic_bias = devm_regulator_get(dev, "vdd-mic-bias"); + if (IS_ERR(chip->vdd_mic_bias)) { + dev_err(dev, "Failed to get vdd micbias supply\n"); + return PTR_ERR(chip->vdd_mic_bias); + } + chip->mclk = devm_clk_get(dev, "mclk"); return 0; @@ -1600,6 +2226,7 @@ static int wcd_probe(struct platform_device *pdev) return -ENXIO; msm8x16_wcd_codec_parse_dt(pdev, chip); + clk_set_rate(chip->mclk, 9600000); clk_prepare_enable(chip->mclk); @@ -1612,18 +2239,21 @@ static int wcd_probe(struct platform_device *pdev) static int wcd_remove(struct platform_device *pdev) { + snd_soc_unregister_codec(&pdev->dev); + return 0; } static const struct of_device_id wcd_match_table[] = { { .compatible = "qcom,apq8016-wcd-codec" }, + { .compatible = "qcom,msm8x16-wcd-codec" }, { } }; MODULE_DEVICE_TABLE(of, wcd_match_table); static struct platform_driver wcd_driver = { .driver = { - .name = "spmi-wcd-codec", + .name = "msm8x16-wcd-codec", .of_match_table = wcd_match_table, }, .probe = wcd_probe, diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c index 1efdf0088ecdf..a295f254ea112 100644 --- a/sound/soc/qcom/apq8016_sbc.c +++ b/sound/soc/qcom/apq8016_sbc.c @@ -30,6 +30,7 @@ struct apq8016_sbc_data { struct snd_soc_dai_link dai_link[]; /* dynamically allocated */ }; +#define MIC_CTRL_TER_WS_SLAVE_SEL BIT(21) #define MIC_CTRL_QUA_WS_SLAVE_SEL_10 BIT(17) #define MIC_CTRL_TLMM_SCLK_EN BIT(1) #define SPKR_CTL_PRI_WS_SLAVE_SEL_11 (BIT(17) | BIT(16)) @@ -53,6 +54,12 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) MIC_CTRL_TLMM_SCLK_EN, pdata->mic_iomux); break; + case MI2S_TERTIARY: + writel(readl(pdata->mic_iomux) | MIC_CTRL_TER_WS_SLAVE_SEL | + MIC_CTRL_TLMM_SCLK_EN, + pdata->mic_iomux); + + break; default: dev_err(card->dev, "unsupported cpu dai configuration\n"); @@ -78,6 +85,15 @@ static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card) return ERR_PTR(ret); } + /* DAPM routes */ + if (of_property_read_bool(node, "qcom,audio-routing")) { + ret = snd_soc_of_parse_audio_routing(card, + "qcom,audio-routing"); + if (ret) + return ERR_PTR(ret); + } + + /* Populate links */ num_links = of_get_child_count(node); @@ -126,9 +142,6 @@ static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card) } link->platform_of_node = link->cpu_of_node; - /* For now we only support playback */ - link->playback_only = true; - ret = of_property_read_string(np, "link-name", &link->name); if (ret) { dev_err(card->dev, "error getting codec dai_link name\n"); @@ -143,6 +156,17 @@ static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card) return data; } +static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = { + + //SND_SOC_DAPM_SUPPLY_S("MCLK", -1, SND_SOC_NOPM, 0, 0, + //msm8x16_mclk_event, SND_SOC_DAPM_POST_PMD), FIXME?? + SND_SOC_DAPM_MIC("Handset Mic", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Secondary Mic", NULL), + SND_SOC_DAPM_MIC("Digital Mic1", NULL), + SND_SOC_DAPM_MIC("Digital Mic2", NULL), +}; + static int apq8016_sbc_platform_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -155,6 +179,8 @@ static int apq8016_sbc_platform_probe(struct platform_device *pdev) return -ENOMEM; card->dev = dev; + card->dapm_widgets = apq8016_sbc_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(apq8016_sbc_dapm_widgets); data = apq8016_sbc_parse_of(card); if (IS_ERR(data)) { dev_err(&pdev->dev, "Error resolving dai links: %ld\n", diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c index 94efc01020c44..3eef0c37ba50d 100644 --- a/sound/soc/qcom/lpass-apq8016.c +++ b/sound/soc/qcom/lpass-apq8016.c @@ -133,23 +133,36 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = { }, }; -static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata) +static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata, + int direction) { struct lpass_variant *v = drvdata->variant; - int chan = find_first_zero_bit(&drvdata->rdma_ch_bit_map, + int chan = 0; + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + chan = find_first_zero_bit(&drvdata->dma_ch_bit_map, v->rdma_channels); - if (chan >= v->rdma_channels) - return -EBUSY; + if (chan >= v->rdma_channels) + return -EBUSY; + } else { + chan = find_next_zero_bit(&drvdata->dma_ch_bit_map, + v->wrdma_channel_start + + v->wrdma_channels, + v->wrdma_channel_start); + + if (chan >= v->wrdma_channel_start + v->wrdma_channels) + return -EBUSY; + } - set_bit(chan, &drvdata->rdma_ch_bit_map); + set_bit(chan, &drvdata->dma_ch_bit_map); return chan; } static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) { - clear_bit(chan, &drvdata->rdma_ch_bit_map); + clear_bit(chan, &drvdata->dma_ch_bit_map); return 0; } @@ -212,7 +225,11 @@ static struct lpass_variant apq8016_data = { .rdma_reg_base = 0x8400, .rdma_reg_stride = 0x1000, .rdma_channels = 2, - .rdmactl_audif_start = 1, + .dmactl_audif_start = 1, + .wrdma_reg_base = 0xB000, + .wrdma_reg_stride = 0x1000, + .wrdma_channel_start = 5, + .wrdma_channels = 2, .dai_driver = apq8016_lpass_cpu_dai_driver, .num_dai = ARRAY_SIZE(apq8016_lpass_cpu_dai_driver), .init = apq8016_lpass_init, diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index e5101e0d2d372..3cde9fb977fa7 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -120,31 +120,60 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - switch (channels) { - case 1: - regval |= LPAIF_I2SCTL_SPKMODE_SD0; - regval |= LPAIF_I2SCTL_SPKMONO_MONO; - break; - case 2: - regval |= LPAIF_I2SCTL_SPKMODE_SD0; - regval |= LPAIF_I2SCTL_SPKMONO_STEREO; - break; - case 4: - regval |= LPAIF_I2SCTL_SPKMODE_QUAD01; - regval |= LPAIF_I2SCTL_SPKMONO_STEREO; - break; - case 6: - regval |= LPAIF_I2SCTL_SPKMODE_6CH; - regval |= LPAIF_I2SCTL_SPKMONO_STEREO; - break; - case 8: - regval |= LPAIF_I2SCTL_SPKMODE_8CH; - regval |= LPAIF_I2SCTL_SPKMONO_STEREO; - break; - default: - dev_err(dai->dev, "%s() invalid channels given: %u\n", - __func__, channels); - return -EINVAL; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (channels) { + case 1: + regval |= LPAIF_I2SCTL_SPKMODE_SD0; + regval |= LPAIF_I2SCTL_SPKMONO_MONO; + break; + case 2: + regval |= LPAIF_I2SCTL_SPKMODE_SD0; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + case 4: + regval |= LPAIF_I2SCTL_SPKMODE_QUAD01; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + case 6: + regval |= LPAIF_I2SCTL_SPKMODE_6CH; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + case 8: + regval |= LPAIF_I2SCTL_SPKMODE_8CH; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + default: + dev_err(dai->dev, "%s() invalid channels given: %u\n", + __func__, channels); + return -EINVAL; + } + } else { + switch (channels) { + case 1: + regval |= LPAIF_I2SCTL_MICMODE_SD0; + regval |= LPAIF_I2SCTL_MICMONO_MONO; + break; + case 2: + regval |= LPAIF_I2SCTL_MICMODE_SD0; + regval |= LPAIF_I2SCTL_MICMONO_STEREO; + break; + case 4: + regval |= LPAIF_I2SCTL_MICMODE_QUAD01; + regval |= LPAIF_I2SCTL_MICMONO_STEREO; + break; + case 6: + regval |= LPAIF_I2SCTL_MICMODE_6CH; + regval |= LPAIF_I2SCTL_MICMONO_STEREO; + break; + case 8: + regval |= LPAIF_I2SCTL_MICMODE_8CH; + regval |= LPAIF_I2SCTL_MICMONO_STEREO; + break; + default: + dev_err(dai->dev, "%s() invalid channels given: %u\n", + __func__, channels); + return -EINVAL; + } } ret = regmap_write(drvdata->lpaif_map, @@ -188,10 +217,19 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, { struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); int ret; + unsigned int val, mask; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + val = LPAIF_I2SCTL_SPKEN_ENABLE; + mask = LPAIF_I2SCTL_SPKEN_MASK; + } else { + val = LPAIF_I2SCTL_MICEN_ENABLE; + mask = LPAIF_I2SCTL_MICEN_MASK; + } ret = regmap_update_bits(drvdata->lpaif_map, LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), - LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE); + mask, val); if (ret) dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", __func__, ret); @@ -204,16 +242,24 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, { struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); int ret = -EINVAL; + unsigned int val, mask; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + val = LPAIF_I2SCTL_SPKEN_ENABLE; + mask = LPAIF_I2SCTL_SPKEN_MASK; + } else { + val = LPAIF_I2SCTL_MICEN_ENABLE; + mask = LPAIF_I2SCTL_MICEN_MASK; + } + ret = regmap_update_bits(drvdata->lpaif_map, LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), - LPAIF_I2SCTL_SPKEN_MASK, - LPAIF_I2SCTL_SPKEN_ENABLE); + mask, val); if (ret) dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", __func__, ret); @@ -221,11 +267,18 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + val = LPAIF_I2SCTL_SPKEN_DISABLE; + mask = LPAIF_I2SCTL_SPKEN_MASK; + } else { + val = LPAIF_I2SCTL_MICEN_DISABLE; + mask = LPAIF_I2SCTL_MICEN_MASK; + } + ret = regmap_update_bits(drvdata->lpaif_map, LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), - LPAIF_I2SCTL_SPKEN_MASK, - LPAIF_I2SCTL_SPKEN_DISABLE); + mask, val); if (ret) dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", __func__, ret); @@ -294,6 +347,17 @@ static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg) return true; } + for (i = 0; i < v->wrdma_channels; ++i) { + if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start)) + return true; + } + return false; } @@ -327,6 +391,19 @@ static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg) return true; } + for (i = 0; i < v->wrdma_channels; ++i) { + if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start)) + return true; + } + return false; } @@ -344,6 +421,10 @@ static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg) if (reg == LPAIF_RDMACURR_REG(v, i)) return true; + for (i = 0; i < v->wrdma_channels; ++i) + if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start)) + return true; + return false; } @@ -398,8 +479,9 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) return PTR_ERR((void const __force *)drvdata->lpaif); } - lpass_cpu_regmap_config.max_register = LPAIF_RDMAPER_REG(variant, - variant->rdma_channels); + lpass_cpu_regmap_config.max_register = LPAIF_WRDMAPER_REG(variant, + variant->wrdma_channels + + variant->wrdma_channel_start); drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif, &lpass_cpu_regmap_config); diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c index 7a41679527113..49237403e9a8c 100644 --- a/sound/soc/qcom/lpass-ipq806x.c +++ b/sound/soc/qcom/lpass-ipq806x.c @@ -63,9 +63,12 @@ static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = { .ops = &asoc_qcom_lpass_cpu_dai_ops, }; -static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata) +static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir) { - return IPQ806X_LPAIF_RDMA_CHAN_MI2S; + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + return IPQ806X_LPAIF_RDMA_CHAN_MI2S; + else /* Capture not supported */ + return -EINVAL; } static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) @@ -83,6 +86,10 @@ static struct lpass_variant ipq806x_data = { .rdma_reg_base = 0x6000, .rdma_reg_stride = 0x1000, .rdma_channels = 4, + .wrdma_reg_base = 0xB000, + .wrdma_reg_stride = 0x1000, + .wrdma_channel_start = 5, + .wrdma_channels = 4, .dai_driver = &ipq806x_lpass_cpu_dai_driver, .num_dai = 1, .alloc_dma_channel = ipq806x_lpass_alloc_dma_channel, diff --git a/sound/soc/qcom/lpass-lpaif-reg.h b/sound/soc/qcom/lpass-lpaif-reg.h index 95e22f1310529..2114b3e8e955b 100644 --- a/sound/soc/qcom/lpass-lpaif-reg.h +++ b/sound/soc/qcom/lpass-lpaif-reg.h @@ -47,6 +47,28 @@ #define LPAIF_I2SCTL_SPKMONO_STEREO (0 << LPAIF_I2SCTL_SPKMONO_SHIFT) #define LPAIF_I2SCTL_SPKMONO_MONO (1 << LPAIF_I2SCTL_SPKMONO_SHIFT) +#define LPAIF_I2SCTL_MICEN_MASK GENMASK(8, 8) +#define LPAIF_I2SCTL_MICEN_SHIFT 8 +#define LPAIF_I2SCTL_MICEN_DISABLE (0 << LPAIF_I2SCTL_MICEN_SHIFT) +#define LPAIF_I2SCTL_MICEN_ENABLE (1 << LPAIF_I2SCTL_MICEN_SHIFT) + +#define LPAIF_I2SCTL_MICMODE_MASK GENMASK(7, 4) +#define LPAIF_I2SCTL_MICMODE_SHIFT 4 +#define LPAIF_I2SCTL_MICMODE_NONE (0 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_SD0 (1 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_SD1 (2 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_SD2 (3 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_SD3 (4 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_QUAD01 (5 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_QUAD02 (6 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_6CH (7 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_8CH (8 << LPAIF_I2SCTL_MICMODE_SHIFT) + +#define LPAIF_I2SCTL_MIMONO_MASK GENMASK(3, 3) +#define LPAIF_I2SCTL_MICMONO_SHIFT 3 +#define LPAIF_I2SCTL_MICMONO_STEREO (0 << LPAIF_I2SCTL_MICMONO_SHIFT) +#define LPAIF_I2SCTL_MICMONO_MONO (1 << LPAIF_I2SCTL_MICMONO_SHIFT) + #define LPAIF_I2SCTL_WSSRC_MASK 0x0004 #define LPAIF_I2SCTL_WSSRC_SHIFT 2 #define LPAIF_I2SCTL_WSSRC_INTERNAL (0 << LPAIF_I2SCTL_WSSRC_SHIFT) @@ -90,37 +112,67 @@ #define LPAIF_RDMAPER_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x10, (chan)) #define LPAIF_RDMAPERCNT_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x14, (chan)) -#define LPAIF_RDMACTL_BURSTEN_MASK 0x800 -#define LPAIF_RDMACTL_BURSTEN_SHIFT 11 -#define LPAIF_RDMACTL_BURSTEN_SINGLE (0 << LPAIF_RDMACTL_BURSTEN_SHIFT) -#define LPAIF_RDMACTL_BURSTEN_INCR4 (1 << LPAIF_RDMACTL_BURSTEN_SHIFT) - -#define LPAIF_RDMACTL_WPSCNT_MASK 0x700 -#define LPAIF_RDMACTL_WPSCNT_SHIFT 8 -#define LPAIF_RDMACTL_WPSCNT_ONE (0 << LPAIF_RDMACTL_WPSCNT_SHIFT) -#define LPAIF_RDMACTL_WPSCNT_TWO (1 << LPAIF_RDMACTL_WPSCNT_SHIFT) -#define LPAIF_RDMACTL_WPSCNT_THREE (2 << LPAIF_RDMACTL_WPSCNT_SHIFT) -#define LPAIF_RDMACTL_WPSCNT_FOUR (3 << LPAIF_RDMACTL_WPSCNT_SHIFT) -#define LPAIF_RDMACTL_WPSCNT_SIX (5 << LPAIF_RDMACTL_WPSCNT_SHIFT) -#define LPAIF_RDMACTL_WPSCNT_EIGHT (7 << LPAIF_RDMACTL_WPSCNT_SHIFT) - -#define LPAIF_RDMACTL_AUDINTF_MASK 0x0F0 -#define LPAIF_RDMACTL_AUDINTF_SHIFT 4 - -#define LPAIF_RDMACTL_FIFOWM_MASK 0x00E -#define LPAIF_RDMACTL_FIFOWM_SHIFT 1 -#define LPAIF_RDMACTL_FIFOWM_1 (0 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_2 (1 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_3 (2 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_4 (3 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_5 (4 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_6 (5 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_7 (6 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_8 (7 << LPAIF_RDMACTL_FIFOWM_SHIFT) - -#define LPAIF_RDMACTL_ENABLE_MASK 0x1 -#define LPAIF_RDMACTL_ENABLE_SHIFT 0 -#define LPAIF_RDMACTL_ENABLE_OFF (0 << LPAIF_RDMACTL_ENABLE_SHIFT) -#define LPAIF_RDMACTL_ENABLE_ON (1 << LPAIF_RDMACTL_ENABLE_SHIFT) - +#define LPAIF_WRDMA_REG_ADDR(v, addr, chan) \ + (v->wrdma_reg_base + (addr) + \ + v->wrdma_reg_stride * (chan - v->wrdma_channel_start)) + +#define LPAIF_WRDMACTL_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x00, (chan)) +#define LPAIF_WRDMABASE_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x04, (chan)) +#define LPAIF_WRDMABUFF_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x08, (chan)) +#define LPAIF_WRDMACURR_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x0C, (chan)) +#define LPAIF_WRDMAPER_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x10, (chan)) +#define LPAIF_WRDMAPERCNT_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x14, (chan)) + +#define __LPAIF_DMA_REG(v, chan, dir, reg) \ + (dir == SNDRV_PCM_STREAM_PLAYBACK) ? \ + LPAIF_RDMA##reg##_REG(v, chan) : \ + LPAIF_WRDMA##reg##_REG(v, chan) + +#define LPAIF_DMACTL_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CTL) +#define LPAIF_DMABASE_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BASE) +#define LPAIF_DMABUFF_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BUFF) +#define LPAIF_DMACURR_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CURR) +#define LPAIF_DMAPER_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PER) +#define LPAIF_DMAPERCNT_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PERCNT) + + +#define LPAIF_DMACTL_AUDINTF(id) (id << LPAIF_DMACTL_AUDINTF_SHIFT) + +#define LPAIF_DMACTL_BURSTEN_MASK 0x800 +#define LPAIF_DMACTL_BURSTEN_SHIFT 11 +#define LPAIF_DMACTL_BURSTEN_SINGLE (0 << LPAIF_DMACTL_BURSTEN_SHIFT) +#define LPAIF_DMACTL_BURSTEN_INCR4 (1 << LPAIF_DMACTL_BURSTEN_SHIFT) + +#define LPAIF_DMACTL_WPSCNT_MASK 0x700 +#define LPAIF_DMACTL_WPSCNT_SHIFT 8 +#define LPAIF_DMACTL_WPSCNT_ONE (0 << LPAIF_DMACTL_WPSCNT_SHIFT) +#define LPAIF_DMACTL_WPSCNT_TWO (1 << LPAIF_DMACTL_WPSCNT_SHIFT) +#define LPAIF_DMACTL_WPSCNT_THREE (2 << LPAIF_DMACTL_WPSCNT_SHIFT) +#define LPAIF_DMACTL_WPSCNT_FOUR (3 << LPAIF_DMACTL_WPSCNT_SHIFT) +#define LPAIF_DMACTL_WPSCNT_SIX (5 << LPAIF_DMACTL_WPSCNT_SHIFT) +#define LPAIF_DMACTL_WPSCNT_EIGHT (7 << LPAIF_DMACTL_WPSCNT_SHIFT) + +#define LPAIF_DMACTL_AUDINTF_MASK 0x0F0 +#define LPAIF_DMACTL_AUDINTF_SHIFT 4 + +#define LPAIF_DMACTL_FIFOWM_MASK 0x00E +#define LPAIF_DMACTL_FIFOWM_SHIFT 1 +#define LPAIF_DMACTL_FIFOWM_1 (0 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_2 (1 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_3 (2 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_4 (3 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_5 (4 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_6 (5 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_7 (6 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_8 (7 << LPAIF_DMACTL_FIFOWM_SHIFT) + +#define LPAIF_DMACTL_ENABLE_MASK 0x1 +#define LPAIF_DMACTL_ENABLE_SHIFT 0 +#define LPAIF_DMACTL_ENABLE_OFF (0 << LPAIF_DMACTL_ENABLE_SHIFT) +#define LPAIF_DMACTL_ENABLE_ON (1 << LPAIF_DMACTL_ENABLE_SHIFT) + +#define LPAIF_DMACTL_DYNCLK_MASK BIT(12) +#define LPAIF_DMACTL_DYNCLK_SHIFT 12 +#define LPAIF_DMACTL_DYNCLK_OFF (0 << LPAIF_DMACTL_DYNCLK_SHIFT) +#define LPAIF_DMACTL_DYNCLK_ON (1 << LPAIF_DMACTL_DYNCLK_SHIFT) #endif /* __LPASS_LPAIF_REG_H__ */ diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 4aeb8e1a7160b..574aa33f3fca3 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -26,6 +26,7 @@ struct lpass_pcm_data { int rdma_ch; + int wrdma_ch; int i2s_port; }; @@ -90,8 +91,15 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, snd_pcm_format_t format = params_format(params); unsigned int channels = params_channels(params); unsigned int regval; + int dir = substream->stream; int bitwidth; - int ret, rdma_port = pcm_data->i2s_port + v->rdmactl_audif_start; + int ch, ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ch = pcm_data->rdma_ch; + else + ch = pcm_data->wrdma_ch; + bitwidth = snd_pcm_format_width(format); if (bitwidth < 0) { @@ -100,25 +108,25 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, return bitwidth; } - regval = LPAIF_RDMACTL_BURSTEN_INCR4 | - LPAIF_RDMACTL_AUDINTF(rdma_port) | - LPAIF_RDMACTL_FIFOWM_8; + regval = LPAIF_DMACTL_BURSTEN_INCR4 | + LPAIF_DMACTL_AUDINTF(dma_port) | + LPAIF_DMACTL_FIFOWM_8; switch (bitwidth) { case 16: switch (channels) { case 1: case 2: - regval |= LPAIF_RDMACTL_WPSCNT_ONE; + regval |= LPAIF_DMACTL_WPSCNT_ONE; break; case 4: - regval |= LPAIF_RDMACTL_WPSCNT_TWO; + regval |= LPAIF_DMACTL_WPSCNT_TWO; break; case 6: - regval |= LPAIF_RDMACTL_WPSCNT_THREE; + regval |= LPAIF_DMACTL_WPSCNT_THREE; break; case 8: - regval |= LPAIF_RDMACTL_WPSCNT_FOUR; + regval |= LPAIF_DMACTL_WPSCNT_FOUR; break; default: dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", @@ -130,19 +138,19 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, case 32: switch (channels) { case 1: - regval |= LPAIF_RDMACTL_WPSCNT_ONE; + regval |= LPAIF_DMACTL_WPSCNT_ONE; break; case 2: - regval |= LPAIF_RDMACTL_WPSCNT_TWO; + regval |= LPAIF_DMACTL_WPSCNT_TWO; break; case 4: - regval |= LPAIF_RDMACTL_WPSCNT_FOUR; + regval |= LPAIF_DMACTL_WPSCNT_FOUR; break; case 6: - regval |= LPAIF_RDMACTL_WPSCNT_SIX; + regval |= LPAIF_DMACTL_WPSCNT_SIX; break; case 8: - regval |= LPAIF_RDMACTL_WPSCNT_EIGHT; + regval |= LPAIF_DMACTL_WPSCNT_EIGHT; break; default: dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", @@ -157,7 +165,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, } ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), regval); + LPAIF_DMACTL_REG(v, ch, dir), regval); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -174,10 +182,15 @@ static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream) struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; + unsigned int reg; int ret; - ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), 0); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch); + else + reg = LPAIF_WRDMACTL_REG(v, pcm_data->wrdma_ch); + + ret = regmap_write(drvdata->lpaif_map, reg, 0); if (ret) dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -193,10 +206,15 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; - int ret, ch = pcm_data->rdma_ch; + int ret, ch, dir = substream->stream; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ch = pcm_data->rdma_ch; + else + ch = pcm_data->wrdma_ch; ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMABASE_REG(v, ch), + LPAIF_DMABASE_REG(v, ch, dir), runtime->dma_addr); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n", @@ -205,7 +223,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) } ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMABUFF_REG(v, ch), + LPAIF_DMABUFF_REG(v, ch, dir), (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n", @@ -214,7 +232,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) } ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMAPER_REG(v, ch), + LPAIF_DMAPER_REG(v, ch, dir), (snd_pcm_lib_period_bytes(substream) >> 2) - 1); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n", @@ -223,8 +241,8 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) } ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, ch), - LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON); + LPAIF_DMACTL_REG(v, ch, dir), + LPAIF_DMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_ON); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -242,7 +260,12 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; - int ret, ch = pcm_data->rdma_ch; + int ret, ch, dir = substream->stream; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ch = pcm_data->rdma_ch; + else + ch = pcm_data->wrdma_ch; switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -269,9 +292,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, } ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, ch), - LPAIF_RDMACTL_ENABLE_MASK, - LPAIF_RDMACTL_ENABLE_ON); + LPAIF_DMACTL_REG(v, ch, dir), + LPAIF_DMACTL_ENABLE_MASK, + LPAIF_DMACTL_ENABLE_ON); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -282,9 +305,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, ch), - LPAIF_RDMACTL_ENABLE_MASK, - LPAIF_RDMACTL_ENABLE_OFF); + LPAIF_DMACTL_REG(v, ch, dir), + LPAIF_DMACTL_ENABLE_MASK, + LPAIF_DMACTL_ENABLE_OFF); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -314,10 +337,15 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; unsigned int base_addr, curr_addr; - int ret, ch = pcm_data->rdma_ch; + int ret, ch, dir = substream->stream; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ch = pcm_data->rdma_ch; + else + ch = pcm_data->wrdma_ch; ret = regmap_read(drvdata->lpaif_map, - LPAIF_RDMABASE_REG(v, ch), &base_addr); + LPAIF_DMABASE_REG(v, ch, dir), &base_addr); if (ret) { dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n", __func__, ret); @@ -325,7 +353,7 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( } ret = regmap_read(drvdata->lpaif_map, - LPAIF_RDMACURR_REG(v, ch), &curr_addr); + LPAIF_DMACURR_REG(v, ch, dir), &curr_addr); if (ret) { dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n", __func__, ret); @@ -439,101 +467,122 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) return IRQ_HANDLED; } -static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream, - struct snd_soc_pcm_runtime *rt) -{ - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = lpass_platform_pcm_hardware.buffer_bytes_max; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = rt->platform->dev; - buf->private_data = NULL; - buf->area = dma_alloc_coherent(rt->platform->dev, size, &buf->addr, - GFP_KERNEL); - if (!buf->area) { - dev_err(rt->platform->dev, "%s: Could not allocate DMA buffer\n", - __func__); - return -ENOMEM; - } - buf->bytes = size; - - return 0; -} - -static void lpass_platform_free_buffer(struct snd_pcm_substream *substream, - struct snd_soc_pcm_runtime *rt) -{ - struct snd_dma_buffer *buf = &substream->dma_buffer; - - if (buf->area) { - dma_free_coherent(rt->dev, buf->bytes, buf->area, - buf->addr); - } - buf->area = NULL; -} - static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) { struct snd_pcm *pcm = soc_runtime->pcm; - struct snd_pcm_substream *substream = - pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct snd_pcm_substream *psubstream, *csubstream; struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; int ret; struct lpass_pcm_data *data; + size_t size = lpass_platform_pcm_hardware.buffer_bytes_max; data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - if (v->alloc_dma_channel) - data->rdma_ch = v->alloc_dma_channel(drvdata); + data->i2s_port = cpu_dai->driver->id; + snd_soc_pcm_set_drvdata(soc_runtime, data); - if (IS_ERR_VALUE(data->rdma_ch)) - return data->rdma_ch; + psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (psubstream) { + if (v->alloc_dma_channel) + data->rdma_ch = v->alloc_dma_channel(drvdata, + SNDRV_PCM_STREAM_PLAYBACK); - drvdata->substream[data->rdma_ch] = substream; - data->i2s_port = cpu_dai->driver->id; + if (IS_ERR_VALUE(data->rdma_ch)) + return data->rdma_ch; - snd_soc_pcm_set_drvdata(soc_runtime, data); + drvdata->substream[data->rdma_ch] = psubstream; - ret = lpass_platform_alloc_buffer(substream, soc_runtime); - if (ret) - return ret; + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev, + size, &psubstream->dma_buffer); + if (ret) + goto playback_alloc_err; - ret = regmap_write(drvdata->lpaif_map, + ret = regmap_write(drvdata->lpaif_map, LPAIF_RDMACTL_REG(v, data->rdma_ch), 0); - if (ret) { - dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", + if (ret) { + dev_err(soc_runtime->dev, + "%s() error writing to rdmactl reg: %d\n", + __func__, ret); + goto capture_alloc_err; + } + } + + csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + if (csubstream) { + if (v->alloc_dma_channel) + data->wrdma_ch = v->alloc_dma_channel(drvdata, + SNDRV_PCM_STREAM_CAPTURE); + + if (IS_ERR_VALUE(data->wrdma_ch)) + goto capture_alloc_err; + + drvdata->substream[data->wrdma_ch] = csubstream; + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev, + size, &csubstream->dma_buffer); + if (ret) + goto capture_alloc_err; + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_WRDMACTL_REG(v, data->wrdma_ch), 0); + if (ret) { + dev_err(soc_runtime->dev, + "%s() error writing to wrdmactl reg: %d\n", __func__, ret); - goto err_buf; + goto capture_reg_err; + } } return 0; -err_buf: - lpass_platform_free_buffer(substream, soc_runtime); +capture_reg_err: + if (csubstream) + snd_dma_free_pages(&csubstream->dma_buffer); + +capture_alloc_err: + if (psubstream) + snd_dma_free_pages(&psubstream->dma_buffer); + + playback_alloc_err: + dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n"); + return ret; } static void lpass_platform_pcm_free(struct snd_pcm *pcm) { - struct snd_pcm_substream *substream = - pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct lpass_data *drvdata = - snd_soc_platform_get_drvdata(soc_runtime->platform); - struct lpass_pcm_data *data = snd_soc_pcm_get_drvdata(soc_runtime); - struct lpass_variant *v = drvdata->variant; - - drvdata->substream[data->rdma_ch] = NULL; - - if (v->free_dma_channel) - v->free_dma_channel(drvdata, data->rdma_ch); - - lpass_platform_free_buffer(substream, soc_runtime); + struct snd_soc_pcm_runtime *rt; + struct lpass_data *drvdata; + struct lpass_pcm_data *data; + struct lpass_variant *v; + struct snd_pcm_substream *substream; + int ch, i; + + for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) { + substream = pcm->streams[i].substream; + if (substream) { + rt = substream->private_data; + data = snd_soc_pcm_get_drvdata(rt); + drvdata = snd_soc_platform_get_drvdata(rt->platform); + + ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ? data->rdma_ch + : data->wrdma_ch; + v = drvdata->variant; + drvdata->substream[ch] = NULL; + if (v->free_dma_channel) + v->free_dma_channel(drvdata, ch); + + snd_dma_free_pages(&substream->dma_buffer); + substream->dma_buffer.area = NULL; + substream->dma_buffer.addr = 0; + } + } } static struct snd_soc_platform_driver lpass_platform_driver = { diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index 0b63e2e5bcc92..30714ad1e138d 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -50,7 +50,7 @@ struct lpass_data { struct lpass_variant *variant; /* bit map to keep track of static channel allocations */ - unsigned long rdma_ch_bit_map; + unsigned long dma_ch_bit_map; /* used it for handling interrupt per dma channel */ struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS]; @@ -71,16 +71,20 @@ struct lpass_variant { u32 rdma_reg_base; u32 rdma_reg_stride; u32 rdma_channels; + u32 wrdma_reg_base; + u32 wrdma_reg_stride; + u32 wrdma_channels; /** * on SOCs like APQ8016 the channel control bits start * at different offset to ipq806x **/ - u32 rdmactl_audif_start; + u32 dmactl_audif_start; + u32 wrdma_channel_start; /* SOC specific intialization like clocks */ int (*init)(struct platform_device *pdev); int (*exit)(struct platform_device *pdev); - int (*alloc_dma_channel)(struct lpass_data *data); + int (*alloc_dma_channel)(struct lpass_data *data, int direction); int (*free_dma_channel)(struct lpass_data *data, int ch); /* SOC specific dais */ |