diff options
Diffstat (limited to 'sound/soc/codecs')
26 files changed, 1456 insertions, 232 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index db16071205ba..82ee233a269d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -187,6 +187,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT715_SDCA_SDW imply SND_SOC_RT1308_SDW imply SND_SOC_RT1316_SDW + imply SND_SOC_SDW_MOCKUP imply SND_SOC_SGTL5000 imply SND_SOC_SI476X imply SND_SOC_SIMPLE_AMPLIFIER @@ -847,7 +848,7 @@ config SND_SOC_HDAC_HDA select SND_HDA config SND_SOC_ICS43432 - tristate + tristate "ICS43423 and compatible i2s microphones" config SND_SOC_INNO_RK3036 tristate "Inno codec driver for RK3036 SoC" @@ -1287,6 +1288,23 @@ config SND_SOC_RT715_SDCA_SDW select REGMAP_SOUNDWIRE select REGMAP_SOUNDWIRE_MBQ +config SND_SOC_SDW_MOCKUP + tristate "SoundWire mockup codec" + depends on EXPERT + depends on SOUNDWIRE + help + This option enables a SoundWire mockup codec that does not drive the + bus, take part in the command/command protocol or generate data on a + Source port. + This option is only intended to be used for tests on a device + with a connector, in combination with a bus analyzer, or to test new + topologies that differ from the actual hardware layout. + This mockup device could be totally virtual but could also be a + real physical one with one key restriction: it is not allowed by the + SoundWire specification to be configured via a sideband mechanism and + generate audio data for capture. However, nothing prevents such a + peripheral device from snooping the bus. + #Freescale sgtl5000 codec config SND_SOC_SGTL5000 tristate "Freescale SGTL5000 CODEC" @@ -1564,6 +1582,7 @@ config SND_SOC_WCD938X config SND_SOC_WCD938X_SDW tristate "WCD9380/WCD9385 Codec - SDW" select SND_SOC_WCD938X + select SND_SOC_WCD_MBHC depends on SOUNDWIRE select REGMAP_SOUNDWIRE help diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 7bb38c370842..8dcea2c4604a 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -203,6 +203,7 @@ snd-soc-rt711-objs := rt711.o rt711-sdw.o snd-soc-rt711-sdca-objs := rt711-sdca.o rt711-sdca-sdw.o snd-soc-rt715-objs := rt715.o rt715-sdw.o snd-soc-rt715-sdca-objs := rt715-sdca.o rt715-sdca-sdw.o +snd-soc-sdw-mockup-objs := sdw-mockup.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o snd-soc-alc5632-objs := alc5632.o @@ -530,6 +531,7 @@ obj-$(CONFIG_SND_SOC_RT711) += snd-soc-rt711.o obj-$(CONFIG_SND_SOC_RT711_SDCA_SDW) += snd-soc-rt711-sdca.o obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o +obj-$(CONFIG_SND_SOC_SDW_MOCKUP) += snd-soc-sdw-mockup.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index f37ab7eda615..278a55af158b 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -316,6 +316,13 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream, int word_len = 0, master_rate = 0; struct snd_soc_component *component = dai->component; struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); + bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + u8 dacc0; + + dev_dbg(dai->dev, "%s() rate=%u format=%#x width=%u channels=%u\n", + __func__, params_rate(params), params_format(params), + params_width(params), params_channels(params)); + /* bit size */ switch (params_width(params)) { @@ -346,6 +353,25 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream, break; } + if (is_playback) { + switch (params_rate(params)) { + case 48000: + dacc0 = AD193X_DAC_SR_48; + break; + case 96000: + dacc0 = AD193X_DAC_SR_96; + break; + case 192000: + dacc0 = AD193X_DAC_SR_192; + break; + default: + dev_err(dai->dev, "invalid sampling rate: %d\n", params_rate(params)); + return -EINVAL; + } + + regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL0, AD193X_DAC_SR_MASK, dacc0); + } + regmap_update_bits(ad193x->regmap, AD193X_PLL_CLK_CTRL0, AD193X_PLL_INPUT_MASK, master_rate); @@ -385,7 +411,7 @@ static struct snd_soc_dai_driver ad193x_dai = { .stream_name = "Playback", .channels_min = 2, .channels_max = 8, - .rates = SNDRV_PCM_RATE_48000, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000, .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, }, @@ -407,7 +433,7 @@ static struct snd_soc_dai_driver ad193x_no_adc_dai = { .stream_name = "Playback", .channels_min = 2, .channels_max = 8, - .rates = SNDRV_PCM_RATE_48000, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000, .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, }, diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h index 377854712c20..61f4648861d5 100644 --- a/sound/soc/codecs/ad193x.h +++ b/sound/soc/codecs/ad193x.h @@ -37,6 +37,10 @@ int ad193x_probe(struct device *dev, struct regmap *regmap, #define AD193X_PLL_CLK_SRC_MCLK (1 << 1) #define AD193X_DAC_CTRL0 0x02 #define AD193X_DAC_POWERDOWN 0x01 +#define AD193X_DAC_SR_MASK 0x06 +#define AD193X_DAC_SR_48 (0 << 1) +#define AD193X_DAC_SR_96 (1 << 1) +#define AD193X_DAC_SR_192 (2 << 1) #define AD193X_DAC_SERFMT_MASK 0xC0 #define AD193X_DAC_SERFMT_STEREO (0 << 6) #define AD193X_DAC_SERFMT_TDM (1 << 6) diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 99c022be94a6..fb1e4c33e27d 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -586,6 +586,7 @@ struct cs42l42_pll_params { * Table 4-5 from the Datasheet */ static const struct cs42l42_pll_params pll_ratio_table[] = { + { 1411200, 0, 1, 0x00, 0x80, 0x000000, 0x03, 0x10, 11289600, 128, 2}, { 1536000, 0, 1, 0x00, 0x7D, 0x000000, 0x03, 0x10, 12000000, 125, 2}, { 2304000, 0, 1, 0x00, 0x55, 0xC00000, 0x02, 0x10, 12288000, 85, 2}, { 2400000, 0, 1, 0x00, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2}, @@ -843,6 +844,13 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream, if (channels == 1) cs42l42->bclk *= 2; + /* + * Assume 24-bit samples are in 32-bit slots, to prevent SCLK being + * more than assumed (which would result in overclocking). + */ + if (params_width(params) == 24) + cs42l42->bclk = (cs42l42->bclk / 3) * 4; + switch(substream->stream) { case SNDRV_PCM_STREAM_CAPTURE: if (channels == 2) { @@ -890,10 +898,23 @@ static int cs42l42_set_sysclk(struct snd_soc_dai *dai, { struct snd_soc_component *component = dai->component; struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); + int i; - cs42l42->sclk = freq; + if (freq == 0) { + cs42l42->sclk = 0; + return 0; + } - return 0; + for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) { + if (pll_ratio_table[i].sclk == freq) { + cs42l42->sclk = freq; + return 0; + } + } + + dev_err(component->dev, "SCLK %u not supported\n", freq); + + return -EINVAL; } static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) @@ -2106,4 +2127,7 @@ MODULE_DESCRIPTION("ASoC CS42L42 driver"); MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>"); MODULE_AUTHOR("Michael White, Cirrus Logic Inc, <michael.white@cirrus.com>"); +MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>"); +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); +MODULE_AUTHOR("Vitaly Rodionov <vitalyr@opensource.cirrus.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index ec8d6e74b467..13258f3ca9aa 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -206,7 +206,7 @@ static int cx20442_write(struct snd_soc_component *component, unsigned int reg, */ /* Modem init: echo off, digital speaker off, quiet off, voice mode */ -static const char *v253_init = "ate0m0q0+fclass=8\r"; +static const char v253_init[] = "ate0m0q0+fclass=8\r"; /* Line discipline .open() */ static int v253_open(struct tty_struct *tty) @@ -279,11 +279,6 @@ static void v253_receive(struct tty_struct *tty, const unsigned char *cp, } } -/* Line discipline .write_wakeup() */ -static void v253_wakeup(struct tty_struct *tty) -{ -} - struct tty_ldisc_ops v253_ops = { .name = "cx20442", .owner = THIS_MODULE, @@ -291,7 +286,6 @@ struct tty_ldisc_ops v253_ops = { .close = v253_close, .hangup = v253_hangup, .receive_buf = v253_receive, - .write_wakeup = v253_wakeup, }; EXPORT_SYMBOL_GPL(v253_ops); diff --git a/sound/soc/codecs/ics43432.c b/sound/soc/codecs/ics43432.c index 47e749f03940..de4c8460ab3d 100644 --- a/sound/soc/codecs/ics43432.c +++ b/sound/soc/codecs/ics43432.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * I2S MEMS microphone driver for InvenSense ICS-43432 + * I2S MEMS microphone driver for InvenSense ICS-43432 and similar + * MEMS-based microphones. * * - Non configurable. * - I2S interface, 64 BCLs per frame, 32 bits per channel, 24 bit data @@ -53,6 +54,7 @@ static int ics43432_probe(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id ics43432_ids[] = { { .compatible = "invensense,ics43432", }, + { .compatible = "cui,cmm-4030d-261", }, { } }; MODULE_DEVICE_TABLE(of, ics43432_ids); diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 3622961f7c2c..196b06898eeb 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -1722,42 +1722,43 @@ static int rx_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream) case RX_MACRO_AIF2_PB: case RX_MACRO_AIF3_PB: case RX_MACRO_AIF4_PB: - for (j = 0; j < INTERP_MAX; j++) { - reg = CDC_RX_RXn_RX_PATH_CTL(j); - mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(j); - dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(j); - - if (mute) { - snd_soc_component_update_bits(component, reg, - CDC_RX_PATH_PGA_MUTE_MASK, - CDC_RX_PATH_PGA_MUTE_ENABLE); - snd_soc_component_update_bits(component, mix_reg, - CDC_RX_PATH_PGA_MUTE_MASK, - CDC_RX_PATH_PGA_MUTE_ENABLE); - } else { - snd_soc_component_update_bits(component, reg, - CDC_RX_PATH_PGA_MUTE_MASK, 0x0); - snd_soc_component_update_bits(component, mix_reg, - CDC_RX_PATH_PGA_MUTE_MASK, 0x0); - } - - if (j == INTERP_AUX) - dsm_reg = CDC_RX_RX2_RX_PATH_DSM_CTL; + for (j = 0; j < INTERP_MAX; j++) { + reg = CDC_RX_RXn_RX_PATH_CTL(j); + mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(j); + dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(j); + + if (mute) { + snd_soc_component_update_bits(component, reg, + CDC_RX_PATH_PGA_MUTE_MASK, + CDC_RX_PATH_PGA_MUTE_ENABLE); + snd_soc_component_update_bits(component, mix_reg, + CDC_RX_PATH_PGA_MUTE_MASK, + CDC_RX_PATH_PGA_MUTE_ENABLE); + } else { + snd_soc_component_update_bits(component, reg, + CDC_RX_PATH_PGA_MUTE_MASK, 0x0); + snd_soc_component_update_bits(component, mix_reg, + CDC_RX_PATH_PGA_MUTE_MASK, 0x0); + } - int_mux_cfg0 = CDC_RX_INP_MUX_RX_INT0_CFG0 + j * 8; - int_mux_cfg1 = int_mux_cfg0 + 4; - int_mux_cfg0_val = snd_soc_component_read(component, int_mux_cfg0); - int_mux_cfg1_val = snd_soc_component_read(component, int_mux_cfg1); + if (j == INTERP_AUX) + dsm_reg = CDC_RX_RX2_RX_PATH_DSM_CTL; - if (snd_soc_component_read(component, dsm_reg) & 0x01) { - if (int_mux_cfg0_val || (int_mux_cfg1_val & 0xF0)) - snd_soc_component_update_bits(component, reg, 0x20, 0x20); - if (int_mux_cfg1_val & 0x0F) { - snd_soc_component_update_bits(component, reg, 0x20, 0x20); - snd_soc_component_update_bits(component, mix_reg, 0x20, 0x20); + int_mux_cfg0 = CDC_RX_INP_MUX_RX_INT0_CFG0 + j * 8; + int_mux_cfg1 = int_mux_cfg0 + 4; + int_mux_cfg0_val = snd_soc_component_read(component, int_mux_cfg0); + int_mux_cfg1_val = snd_soc_component_read(component, int_mux_cfg1); + + if (snd_soc_component_read(component, dsm_reg) & 0x01) { + if (int_mux_cfg0_val || (int_mux_cfg1_val & 0xF0)) + snd_soc_component_update_bits(component, reg, 0x20, 0x20); + if (int_mux_cfg1_val & 0x0F) { + snd_soc_component_update_bits(component, reg, 0x20, 0x20); + snd_soc_component_update_bits(component, mix_reg, 0x20, + 0x20); + } } } - } break; default: break; diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index bc30a1dc7530..b45ec35cd63c 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2158,13 +2158,11 @@ static void max98090_jack_work(struct work_struct *work) msleep(50); - reg = snd_soc_component_read(component, M98090_REG_JACK_STATUS); + snd_soc_component_read(component, M98090_REG_JACK_STATUS); /* Weak pull up allows only insertion detection */ snd_soc_component_update_bits(component, M98090_REG_JACK_DETECT, M98090_JDWK_MASK, M98090_JDWK_MASK); - } else { - reg = snd_soc_component_read(component, M98090_REG_JACK_STATUS); } reg = snd_soc_component_read(component, M98090_REG_JACK_STATUS); diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index 94773ccee9d5..b392567c2b3e 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -765,17 +765,26 @@ static int max98390_dsm_init(struct snd_soc_component *component) vendor = dmi_get_system_info(DMI_SYS_VENDOR); product = dmi_get_system_info(DMI_PRODUCT_NAME); - if (vendor && product) { - snprintf(filename, sizeof(filename), "dsm_param_%s_%s.bin", - vendor, product); + if (!strcmp(max98390->dsm_param_name, "default")) { + if (vendor && product) { + snprintf(filename, sizeof(filename), + "dsm_param_%s_%s.bin", vendor, product); + } else { + sprintf(filename, "dsm_param.bin"); + } } else { - sprintf(filename, "dsm_param.bin"); + snprintf(filename, sizeof(filename), "%s", + max98390->dsm_param_name); } ret = request_firmware(&fw, filename, component->dev); if (ret) { ret = request_firmware(&fw, "dsm_param.bin", component->dev); - if (ret) - goto err; + if (ret) { + ret = request_firmware(&fw, "dsmparam.bin", + component->dev); + if (ret) + goto err; + } } dev_dbg(component->dev, @@ -1047,6 +1056,11 @@ static int max98390_i2c_probe(struct i2c_client *i2c, __func__, max98390->ref_rdc_value, max98390->ambient_temp_value); + ret = device_property_read_string(&i2c->dev, "maxim,dsm_param_name", + &max98390->dsm_param_name); + if (ret) + max98390->dsm_param_name = "default"; + /* voltage/current slot configuration */ max98390_slot_config(i2c, max98390); diff --git a/sound/soc/codecs/max98390.h b/sound/soc/codecs/max98390.h index e31516717d3b..c250740f73a2 100644 --- a/sound/soc/codecs/max98390.h +++ b/sound/soc/codecs/max98390.h @@ -662,5 +662,6 @@ struct max98390_priv { unsigned int i_l_slot; unsigned int ref_rdc_value; unsigned int ambient_temp_value; + const char *dsm_param_name; }; #endif diff --git a/sound/soc/codecs/mt6359-accdet.c b/sound/soc/codecs/mt6359-accdet.c index 78314187d37e..6d3d170144a0 100644 --- a/sound/soc/codecs/mt6359-accdet.c +++ b/sound/soc/codecs/mt6359-accdet.c @@ -234,7 +234,7 @@ static void recover_eint_setting(struct mt6359_accdet *priv) static void mt6359_accdet_recover_jd_setting(struct mt6359_accdet *priv) { - int ret = 0; + int ret; unsigned int value = 0; regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, @@ -461,7 +461,7 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) { struct mt6359_accdet *priv = data; unsigned int irq_val = 0, val = 0, value = 0; - int ret = 0; + int ret; mutex_lock(&priv->res_lock); regmap_read(priv->regmap, ACCDET_IRQ_ADDR, &irq_val); @@ -551,7 +551,7 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) static int mt6359_accdet_parse_dt(struct mt6359_accdet *priv) { - int ret = 0; + int ret; struct device *dev = priv->dev; struct device_node *node = NULL; int pwm_deb[15] = {0}; @@ -926,7 +926,7 @@ static int mt6359_accdet_probe(struct platform_device *pdev) { struct mt6359_accdet *priv; struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent); - int ret = 0; + int ret; dev_dbg(&pdev->dev, "%s(), dev name %s\n", __func__, dev_name(&pdev->dev)); @@ -1057,22 +1057,7 @@ static struct platform_driver mt6359_accdet_driver = { .probe = mt6359_accdet_probe, }; -static int __init mt6359_accdet_driver_init(void) -{ - int ret = 0; - - ret = platform_driver_register(&mt6359_accdet_driver); - if (ret) - return -ENODEV; - return 0; -} - -static void __exit mt6359_accdet_driver_exit(void) -{ - platform_driver_unregister(&mt6359_accdet_driver); -} -module_init(mt6359_accdet_driver_init); -module_exit(mt6359_accdet_driver_exit); +module_platform_driver(mt6359_accdet_driver) /* Module information */ MODULE_DESCRIPTION("MT6359 ALSA SoC codec jack driver"); diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c index 9238f12999aa..c0c5952cdff7 100644 --- a/sound/soc/codecs/rt1015.c +++ b/sound/soc/codecs/rt1015.c @@ -547,31 +547,6 @@ static int rt1015_bypass_boost_put(struct snd_kcontrol *kcontrol, return 0; } -static void rt1015_flush_work(struct work_struct *work) -{ - struct rt1015_priv *rt1015 = container_of(work, struct rt1015_priv, - flush_work.work); - struct snd_soc_component *component = rt1015->component; - unsigned int val, i; - - for (i = 0; i < 200; ++i) { - usleep_range(1000, 1500); - dev_dbg(component->dev, "Flush DAC (retry:%u)\n", i); - regmap_read(rt1015->regmap, RT1015_CLK_DET, &val); - if (val & 0x800) - break; - } - - regmap_write(rt1015->regmap, RT1015_SYS_RST1, 0x0597); - regmap_write(rt1015->regmap, RT1015_SYS_RST1, 0x05f7); - regmap_write(rt1015->regmap, RT1015_MAN_I2C, 0x0028); - - if (val & 0x800) - dev_dbg(component->dev, "Flush DAC completed.\n"); - else - dev_warn(component->dev, "Fail to flush DAC data.\n"); -} - static const struct snd_kcontrol_new rt1015_snd_controls[] = { SOC_SINGLE_TLV("DAC Playback Volume", RT1015_DAC1, RT1015_DAC_VOL_SFT, 127, 0, dac_vol_tlv), @@ -630,10 +605,6 @@ static int r1015_dac_event(struct snd_soc_dapm_widget *w, } break; - case SND_SOC_DAPM_POST_PMU: - regmap_write(rt1015->regmap, RT1015_MAN_I2C, 0x00a8); - break; - case SND_SOC_DAPM_POST_PMD: if (rt1015->bypass_boost == RT1015_Enable_Boost) { snd_soc_component_write(component, @@ -653,8 +624,6 @@ static int r1015_dac_event(struct snd_soc_dapm_widget *w, RT1015_SYS_RST2, 0x0b9a); } rt1015->dac_is_used = 0; - - cancel_delayed_work_sync(&rt1015->flush_work); break; default: @@ -687,8 +656,6 @@ static int rt1015_amp_drv_event(struct snd_soc_dapm_widget *w, } break; case SND_SOC_DAPM_POST_PMU: - if (rt1015->hw_config == RT1015_HW_28) - schedule_delayed_work(&rt1015->flush_work, msecs_to_jiffies(10)); msleep(rt1015->pdata.power_up_delay_ms); break; default: @@ -702,7 +669,7 @@ static const struct snd_soc_dapm_widget rt1015_dapm_widgets[] = { NULL, 0), SND_SOC_DAPM_AIF_IN("AIFRX", "AIF Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, - r1015_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + r1015_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, rt1015_amp_drv_event, SND_SOC_DAPM_PRE_PMU | @@ -722,7 +689,7 @@ static int rt1015_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_component *component = dai->component; struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); - int pre_div, bclk_ms, frame_size, lrck; + int pre_div, frame_size, lrck; unsigned int val_len = 0; lrck = params_rate(params); @@ -739,10 +706,7 @@ static int rt1015_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - bclk_ms = frame_size > 32; - - dev_dbg(component->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", - bclk_ms, pre_div, dai->id); + dev_dbg(component->dev, "pre_div is %d for iis %d\n", pre_div, dai->id); dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n", lrck, pre_div, dai->id); @@ -1028,7 +992,6 @@ static int rt1015_probe(struct snd_soc_component *component) snd_soc_component_get_drvdata(component); rt1015->component = component; - INIT_DELAYED_WORK(&rt1015->flush_work, rt1015_flush_work); return 0; } @@ -1037,7 +1000,6 @@ static void rt1015_remove(struct snd_soc_component *component) { struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); - cancel_delayed_work_sync(&rt1015->flush_work); regmap_write(rt1015->regmap, RT1015_RESET, 0); } @@ -1180,8 +1142,6 @@ static int rt1015_i2c_probe(struct i2c_client *i2c, return ret; } - rt1015->hw_config = (i2c->addr == 0x29) ? RT1015_HW_29 : RT1015_HW_28; - ret = regmap_read(rt1015->regmap, RT1015_DEVICE_ID, &val); if (ret) { dev_err(&i2c->dev, diff --git a/sound/soc/codecs/rt1015.h b/sound/soc/codecs/rt1015.h index 14344532048e..c9f636af7fd1 100644 --- a/sound/soc/codecs/rt1015.h +++ b/sound/soc/codecs/rt1015.h @@ -444,8 +444,6 @@ struct rt1015_priv { int bypass_boost; int dac_is_used; int cali_done; - int hw_config; - struct delayed_work flush_work; }; #endif /* __RT1015_H__ */ diff --git a/sound/soc/codecs/rt1015p.c b/sound/soc/codecs/rt1015p.c index 40f2063aefbe..415cfb3b2f0d 100644 --- a/sound/soc/codecs/rt1015p.c +++ b/sound/soc/codecs/rt1015p.c @@ -127,6 +127,7 @@ static int rt1015p_platform_probe(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id rt1015p_device_id[] = { { .compatible = "realtek,rt1015p" }, + { .compatible = "realtek,rt1019p" }, {} }; MODULE_DEVICE_TABLE(of, rt1015p_device_id); @@ -135,6 +136,7 @@ MODULE_DEVICE_TABLE(of, rt1015p_device_id); #ifdef CONFIG_ACPI static const struct acpi_device_id rt1015p_acpi_match[] = { { "RTL1015", 0}, + { "RTL1019", 0}, { }, }; MODULE_DEVICE_TABLE(acpi, rt1015p_acpi_match); diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index 7081142a355e..4b1ad5054e8d 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -494,7 +494,7 @@ static const struct snd_kcontrol_new rt5514_sto2_dmic_mux = */ static int rt5514_calc_dmic_clk(struct snd_soc_component *component, int rate) { - int div[] = {2, 3, 4, 8, 12, 16, 24, 32}; + static const int div[] = {2, 3, 4, 8, 12, 16, 24, 32}; int i; if (rate < 1000000 * div[0]) { diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 9523f4b5c800..cd1db5caabad 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2093,7 +2093,7 @@ int rt5640_sel_asrc_clk_src(struct snd_soc_component *component, } EXPORT_SYMBOL_GPL(rt5640_sel_asrc_clk_src); -static void rt5640_enable_micbias1_for_ovcd(struct snd_soc_component *component) +void rt5640_enable_micbias1_for_ovcd(struct snd_soc_component *component) { struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); @@ -2105,8 +2105,9 @@ static void rt5640_enable_micbias1_for_ovcd(struct snd_soc_component *component) snd_soc_dapm_sync_unlocked(dapm); snd_soc_dapm_mutex_unlock(dapm); } +EXPORT_SYMBOL_GPL(rt5640_enable_micbias1_for_ovcd); -static void rt5640_disable_micbias1_for_ovcd(struct snd_soc_component *component) +void rt5640_disable_micbias1_for_ovcd(struct snd_soc_component *component) { struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); @@ -2117,6 +2118,7 @@ static void rt5640_disable_micbias1_for_ovcd(struct snd_soc_component *component snd_soc_dapm_sync_unlocked(dapm); snd_soc_dapm_mutex_unlock(dapm); } +EXPORT_SYMBOL_GPL(rt5640_disable_micbias1_for_ovcd); static void rt5640_enable_micbias1_ovcd_irq(struct snd_soc_component *component) { @@ -2241,7 +2243,7 @@ static void rt5640_button_press_work(struct work_struct *work) schedule_delayed_work(&rt5640->bp_work, msecs_to_jiffies(BP_POLL_TIME)); } -static int rt5640_detect_headset(struct snd_soc_component *component) +int rt5640_detect_headset(struct snd_soc_component *component, struct gpio_desc *hp_det_gpio) { int i, headset_count = 0, headphone_count = 0; @@ -2259,8 +2261,13 @@ static int rt5640_detect_headset(struct snd_soc_component *component) msleep(JACK_SETTLE_TIME); /* Check the jack is still connected before checking ovcd */ - if (!rt5640_jack_inserted(component)) - return 0; + if (hp_det_gpio) { + if (gpiod_get_value_cansleep(hp_det_gpio)) + return 0; + } else { + if (!rt5640_jack_inserted(component)) + return 0; + } if (rt5640_micbias1_ovcd(component)) { /* @@ -2285,6 +2292,7 @@ static int rt5640_detect_headset(struct snd_soc_component *component) dev_err(component->dev, "Error detecting headset vs headphones, bad contact?, assuming headphones\n"); return SND_JACK_HEADPHONE; } +EXPORT_SYMBOL_GPL(rt5640_detect_headset); static void rt5640_jack_work(struct work_struct *work) { @@ -2309,7 +2317,7 @@ static void rt5640_jack_work(struct work_struct *work) /* Jack inserted */ WARN_ON(rt5640->ovcd_irq_enabled); rt5640_enable_micbias1_for_ovcd(component); - status = rt5640_detect_headset(component); + status = rt5640_detect_headset(component, NULL); if (status == SND_JACK_HEADSET) { /* Enable ovcd IRQ for button press detect. */ rt5640_enable_micbias1_ovcd_irq(component); @@ -2362,10 +2370,59 @@ static void rt5640_cancel_work(void *data) cancel_delayed_work_sync(&rt5640->bp_work); } +void rt5640_set_ovcd_params(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + snd_soc_component_write(component, RT5640_PR_BASE + RT5640_BIAS_CUR4, + 0xa800 | rt5640->ovcd_sf); + + snd_soc_component_update_bits(component, RT5640_MICBIAS, + RT5640_MIC1_OVTH_MASK | RT5640_MIC1_OVCD_MASK, + rt5640->ovcd_th | RT5640_MIC1_OVCD_EN); + + /* + * The over-current-detect is only reliable in detecting the absence + * of over-current, when the mic-contact in the jack is short-circuited, + * the hardware periodically retries if it can apply the bias-current + * leading to the ovcd status flip-flopping 1-0-1 with it being 0 about + * 10% of the time, as we poll the ovcd status bit we might hit that + * 10%, so we enable sticky mode and when checking OVCD we clear the + * status, msleep() a bit and then check to get a reliable reading. + */ + snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2, + RT5640_MB1_OC_STKY_MASK, RT5640_MB1_OC_STKY_EN); +} +EXPORT_SYMBOL_GPL(rt5640_set_ovcd_params); + +static void rt5640_disable_jack_detect(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + /* + * soc_remove_component() force-disables jack and thus rt5640->jack + * could be NULL at the time of driver's module unloading. + */ + if (!rt5640->jack) + return; + + free_irq(rt5640->irq, rt5640); + rt5640_cancel_work(rt5640); + + if (rt5640->jack->status & SND_JACK_MICROPHONE) { + rt5640_disable_micbias1_ovcd_irq(component); + rt5640_disable_micbias1_for_ovcd(component); + snd_soc_jack_report(rt5640->jack, 0, SND_JACK_BTN_0); + } + + rt5640->jack = NULL; +} + static void rt5640_enable_jack_detect(struct snd_soc_component *component, struct snd_soc_jack *jack) { struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + int ret; /* Select JD-source */ snd_soc_component_update_bits(component, RT5640_JD_CTRL, @@ -2385,24 +2442,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, /* Enabling jd2 in general control 2 */ snd_soc_component_write(component, RT5640_DUMMY2, 0x4001); - snd_soc_component_write(component, RT5640_PR_BASE + RT5640_BIAS_CUR4, - 0xa800 | rt5640->ovcd_sf); - - snd_soc_component_update_bits(component, RT5640_MICBIAS, - RT5640_MIC1_OVTH_MASK | RT5640_MIC1_OVCD_MASK, - rt5640->ovcd_th | RT5640_MIC1_OVCD_EN); - - /* - * The over-current-detect is only reliable in detecting the absence - * of over-current, when the mic-contact in the jack is short-circuited, - * the hardware periodically retries if it can apply the bias-current - * leading to the ovcd status flip-flopping 1-0-1 with it being 0 about - * 10% of the time, as we poll the ovcd status bit we might hit that - * 10%, so we enable sticky mode and when checking OVCD we clear the - * status, msleep() a bit and then check to get a reliable reading. - */ - snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2, - RT5640_MB1_OC_STKY_MASK, RT5640_MB1_OC_STKY_EN); + rt5640_set_ovcd_params(component); /* * All IRQs get or-ed together, so we need the jack IRQ to report 0 @@ -2423,32 +2463,19 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, rt5640_enable_micbias1_ovcd_irq(component); } - enable_irq(rt5640->irq); - /* sync initial jack state */ - queue_work(system_long_wq, &rt5640->jack_work); -} - -static void rt5640_disable_jack_detect(struct snd_soc_component *component) -{ - struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); - - /* - * soc_remove_component() force-disables jack and thus rt5640->jack - * could be NULL at the time of driver's module unloading. - */ - if (!rt5640->jack) + ret = request_irq(rt5640->irq, rt5640_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "rt5640", rt5640); + if (ret) { + dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret); + rt5640->irq = -ENXIO; + /* Undo above settings */ + rt5640_disable_jack_detect(component); return; - - disable_irq(rt5640->irq); - rt5640_cancel_work(rt5640); - - if (rt5640->jack->status & SND_JACK_MICROPHONE) { - rt5640_disable_micbias1_ovcd_irq(component); - rt5640_disable_micbias1_for_ovcd(component); - snd_soc_jack_report(rt5640->jack, 0, SND_JACK_BTN_0); } - rt5640->jack = NULL; + /* sync initial jack state */ + queue_work(system_long_wq, &rt5640->jack_work); } static int rt5640_set_jack(struct snd_soc_component *component, @@ -2836,18 +2863,6 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, if (ret) return ret; - ret = devm_request_irq(&i2c->dev, rt5640->irq, rt5640_irq, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING - | IRQF_ONESHOT, "rt5640", rt5640); - if (ret == 0) { - /* Gets re-enabled by rt5640_set_jack() */ - disable_irq(rt5640->irq); - } else { - dev_warn(&i2c->dev, "Failed to reguest IRQ %d: %d\n", - rt5640->irq, ret); - rt5640->irq = -ENXIO; - } - return devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5640, rt5640_dai, ARRAY_SIZE(rt5640_dai)); diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index 4fd47f2b936b..2c28f83e338a 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -10,6 +10,7 @@ #define _RT5640_H #include <linux/clk.h> +#include <linux/gpio/consumer.h> #include <linux/workqueue.h> #include <dt-bindings/sound/rt5640.h> @@ -2157,4 +2158,9 @@ int rt5640_dmic_enable(struct snd_soc_component *component, int rt5640_sel_asrc_clk_src(struct snd_soc_component *component, unsigned int filter_mask, unsigned int clk_src); +void rt5640_set_ovcd_params(struct snd_soc_component *component); +void rt5640_enable_micbias1_for_ovcd(struct snd_soc_component *component); +void rt5640_disable_micbias1_for_ovcd(struct snd_soc_component *component); +int rt5640_detect_headset(struct snd_soc_component *component, struct gpio_desc *hp_det_gpio); + #endif diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c index 4a56a52adab5..b9d5d7a0975b 100644 --- a/sound/soc/codecs/rt5682-i2c.c +++ b/sound/soc/codecs/rt5682-i2c.c @@ -117,6 +117,13 @@ static struct snd_soc_dai_driver rt5682_dai[] = { }, }; +static void rt5682_i2c_disable_regulators(void *data) +{ + struct rt5682_priv *rt5682 = data; + + regulator_bulk_disable(ARRAY_SIZE(rt5682->supplies), rt5682->supplies); +} + static int rt5682_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -157,6 +164,11 @@ static int rt5682_i2c_probe(struct i2c_client *i2c, return ret; } + ret = devm_add_action_or_reset(&i2c->dev, rt5682_i2c_disable_regulators, + rt5682); + if (ret) + return ret; + ret = regulator_bulk_enable(ARRAY_SIZE(rt5682->supplies), rt5682->supplies); if (ret) { @@ -282,10 +294,7 @@ static void rt5682_i2c_shutdown(struct i2c_client *client) static int rt5682_i2c_remove(struct i2c_client *client) { - struct rt5682_priv *rt5682 = i2c_get_clientdata(client); - rt5682_i2c_shutdown(client); - regulator_bulk_disable(ARRAY_SIZE(rt5682->supplies), rt5682->supplies); return 0; } diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 51ecaa2abcd1..e822fa1b9d4b 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -43,8 +43,9 @@ static const struct reg_sequence patch_list[] = { {RT5682_DAC_ADC_DIG_VOL1, 0xa020}, {RT5682_I2C_CTRL, 0x000f}, {RT5682_PLL2_INTERNAL, 0x8266}, - {RT5682_SAR_IL_CMD_3, 0x8365}, - {RT5682_SAR_IL_CMD_6, 0x0180}, + {RT5682_SAR_IL_CMD_1, 0x22b7}, + {RT5682_SAR_IL_CMD_3, 0x0365}, + {RT5682_SAR_IL_CMD_6, 0x0110}, }; void rt5682_apply_patch_list(struct rt5682_priv *rt5682, struct device *dev) @@ -1727,8 +1728,6 @@ static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = { SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5682_STO1_ADC_DIG_VOL, RT5682_R_MUTE_SFT, 1, rt5682_sto1_adc_r_mix, ARRAY_SIZE(rt5682_sto1_adc_r_mix)), - SND_SOC_DAPM_SUPPLY("BTN Detection Mode", RT5682_SAR_IL_CMD_1, - 14, 1, NULL, 0), /* ADC PGA */ SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -1899,8 +1898,6 @@ static const struct snd_soc_dapm_route rt5682_dapm_routes[] = { {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"}, {"Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter"}, - {"ADC Stereo1 Filter", NULL, "BTN Detection Mode"}, - {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL"}, {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR"}, @@ -2916,10 +2913,47 @@ static void rt5682_remove(struct snd_soc_component *component) static int rt5682_suspend(struct snd_soc_component *component) { struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + unsigned int val; if (rt5682->is_sdw) return 0; + cancel_delayed_work_sync(&rt5682->jack_detect_work); + cancel_delayed_work_sync(&rt5682->jd_check_work); + if (rt5682->hs_jack && rt5682->jack_type == SND_JACK_HEADSET) { + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, + RT5682_MB1_PATH_MASK | RT5682_MB2_PATH_MASK, + RT5682_CTRL_MB1_REG | RT5682_CTRL_MB2_REG); + val = snd_soc_component_read(component, + RT5682_CBJ_CTRL_2) & RT5682_JACK_TYPE_MASK; + + switch (val) { + case 0x1: + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_SEL_MB1_MASK | RT5682_SAR_SEL_MB2_MASK, + RT5682_SAR_SEL_MB1_NOSEL | RT5682_SAR_SEL_MB2_SEL); + break; + case 0x2: + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_SEL_MB1_MASK | RT5682_SAR_SEL_MB2_MASK, + RT5682_SAR_SEL_MB1_SEL | RT5682_SAR_SEL_MB2_NOSEL); + break; + default: + break; + } + + snd_soc_component_update_bits(component, RT5682_PWR_ANLG_3, + RT5682_PWR_CBJ, 0); + + /* enter SAR ADC power saving mode */ + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_BUTT_DET_MASK | RT5682_SAR_BUTDET_MODE_MASK | + RT5682_SAR_BUTDET_RST_MASK | RT5682_SAR_SEL_MB1_MB2_MASK, 0); + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_BUTT_DET_MASK | RT5682_SAR_BUTDET_MODE_MASK | RT5682_SAR_BUTDET_RST_MASK, + RT5682_SAR_BUTT_DET_EN | RT5682_SAR_BUTDET_POW_SAV | RT5682_SAR_BUTDET_RST_NORMAL); + } + regcache_cache_only(rt5682->regmap, true); regcache_mark_dirty(rt5682->regmap); return 0; @@ -2935,6 +2969,17 @@ static int rt5682_resume(struct snd_soc_component *component) regcache_cache_only(rt5682->regmap, false); regcache_sync(rt5682->regmap); + if (rt5682->hs_jack && rt5682->jack_type == SND_JACK_HEADSET) { + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_BUTDET_MODE_MASK | RT5682_SAR_SEL_MB1_MB2_MASK, + RT5682_SAR_BUTDET_POW_NORM | RT5682_SAR_SEL_MB1_MB2_AUTO); + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, + RT5682_MB1_PATH_MASK | RT5682_MB2_PATH_MASK, + RT5682_CTRL_MB1_FSM | RT5682_CTRL_MB2_FSM); + snd_soc_component_update_bits(component, RT5682_PWR_ANLG_3, + RT5682_PWR_CBJ, RT5682_PWR_CBJ); + } + mod_delayed_work(system_power_efficient_wq, &rt5682->jack_detect_work, msecs_to_jiffies(250)); diff --git a/sound/soc/codecs/sdw-mockup.c b/sound/soc/codecs/sdw-mockup.c new file mode 100644 index 000000000000..a4f79eb2c69d --- /dev/null +++ b/sound/soc/codecs/sdw-mockup.c @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// sdw-mockup.c -- a mockup SoundWire codec for tests where only the host +// drives the bus. +// +// Copyright(c) 2021 Intel Corporation +// +// + +#include <linux/device.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <linux/soundwire/sdw_registers.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +struct sdw_mockup_priv { + struct sdw_slave *slave; +}; + +struct sdw_stream_data { + struct sdw_stream_runtime *sdw_stream; +}; + +static int sdw_mockup_component_probe(struct snd_soc_component *component) +{ + return 0; +} + +static void sdw_mockup_component_remove(struct snd_soc_component *component) +{ +} + +static const struct snd_soc_component_driver snd_soc_sdw_mockup_component = { + .probe = sdw_mockup_component_probe, + .remove = sdw_mockup_component_remove, +}; + +static int sdw_mockup_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + struct sdw_stream_data *stream; + + if (!sdw_stream) + return 0; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + + stream->sdw_stream = sdw_stream; + + /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dai->playback_dma_data = stream; + else + dai->capture_dma_data = stream; + + return 0; +} + +static void sdw_mockup_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_stream_data *stream; + + stream = snd_soc_dai_get_dma_data(dai, substream); + snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(stream); +} + +static int sdw_mockup_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct sdw_mockup_priv *sdw_mockup = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_data *stream; + int num_channels; + int port; + int ret; + + stream = snd_soc_dai_get_dma_data(dai, substream); + if (!stream) + return -EINVAL; + + if (!sdw_mockup->slave) + return -EINVAL; + + /* SoundWire specific configuration */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + port = 1; + } else { + direction = SDW_DATA_DIR_TX; + port = 8; + } + + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = params_channels(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + num_channels = params_channels(params); + port_config.ch_mask = (1 << num_channels) - 1; + port_config.num = port; + + ret = sdw_stream_add_slave(sdw_mockup->slave, &stream_config, + &port_config, 1, stream->sdw_stream); + if (ret) + dev_err(dai->dev, "Unable to configure port\n"); + + return ret; +} + +static int sdw_mockup_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct sdw_mockup_priv *sdw_mockup = snd_soc_component_get_drvdata(component); + struct sdw_stream_data *stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_mockup->slave) + return -EINVAL; + + sdw_stream_remove_slave(sdw_mockup->slave, stream->sdw_stream); + return 0; +} + +static const struct snd_soc_dai_ops sdw_mockup_ops = { + .hw_params = sdw_mockup_pcm_hw_params, + .hw_free = sdw_mockup_pcm_hw_free, + .set_sdw_stream = sdw_mockup_set_sdw_stream, + .shutdown = sdw_mockup_shutdown, +}; + +static struct snd_soc_dai_driver sdw_mockup_dai[] = { + { + .name = "sdw-mockup-aif1", + .id = 1, + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 1, + .channels_max = 2, + }, + .capture = { + .stream_name = "DP8 Capture", + .channels_min = 1, + .channels_max = 2, + }, + .ops = &sdw_mockup_ops, + }, +}; + +static int sdw_mockup_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + return 0; +} + +static int sdw_mockup_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval; + int i, j; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->paging_support = false; + + /* + * first we need to allocate memory for set bits in port lists + * the port allocation is completely arbitrary: + * DP0 is not supported + * DP1 is sink + * DP8 is source + */ + prop->source_ports = BIT(8); + prop->sink_ports = BIT(1); + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + j = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[j].num = bit; + dpn[j].type = SDW_DPN_FULL; + dpn[j].simple_ch_prep_sm = true; + j++; + } + + prop->simple_clk_stop_capable = true; + + /* wake-up event */ + prop->wake_capable = 0; + + return 0; +} + +static int sdw_mockup_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + return 0; +} + +static int sdw_mockup_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + return 0; +} + +static const struct sdw_slave_ops sdw_mockup_slave_ops = { + .read_prop = sdw_mockup_read_prop, + .interrupt_callback = sdw_mockup_interrupt_callback, + .update_status = sdw_mockup_update_status, + .bus_config = sdw_mockup_bus_config, +}; + +static int sdw_mockup_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct device *dev = &slave->dev; + struct sdw_mockup_priv *sdw_mockup; + int ret; + + sdw_mockup = devm_kzalloc(dev, sizeof(*sdw_mockup), GFP_KERNEL); + if (!sdw_mockup) + return -ENOMEM; + + dev_set_drvdata(dev, sdw_mockup); + sdw_mockup->slave = slave; + + ret = devm_snd_soc_register_component(dev, + &snd_soc_sdw_mockup_component, + sdw_mockup_dai, + ARRAY_SIZE(sdw_mockup_dai)); + + return ret; +} + +static int sdw_mockup_sdw_remove(struct sdw_slave *slave) +{ + return 0; +} + +/* + * Intel reserved parts ID with the following mapping expected: + * 0xAAAA: generic full-duplex codec + * 0xAA55: headset codec (mock-up of RT711/RT5682) - full-duplex + * 0x55AA: amplifier (mock-up of RT1308/Maxim 98373) - playback only with + * IV feedback + * 0x5555: mic codec (mock-up of RT715) - capture-only + */ +static const struct sdw_device_id sdw_mockup_id[] = { + SDW_SLAVE_ENTRY_EXT(0x0105, 0xAAAA, 0x0, 0, 0), + SDW_SLAVE_ENTRY_EXT(0x0105, 0xAA55, 0x0, 0, 0), + SDW_SLAVE_ENTRY_EXT(0x0105, 0x55AA, 0x0, 0, 0), + SDW_SLAVE_ENTRY_EXT(0x0105, 0x5555, 0x0, 0, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, sdw_mockup_id); + +static struct sdw_driver sdw_mockup_sdw_driver = { + .driver = { + .name = "sdw-mockup", + .owner = THIS_MODULE, + }, + .probe = sdw_mockup_sdw_probe, + .remove = sdw_mockup_sdw_remove, + .ops = &sdw_mockup_slave_ops, + .id_table = sdw_mockup_id, +}; +module_sdw_driver(sdw_mockup_sdw_driver); + +MODULE_DESCRIPTION("ASoC SDW mockup codec driver"); +MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 2e9175b37dc9..d39c7d52ecfd 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -714,7 +714,7 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, unsigned long adc_clock_rate, dac_clock_rate; int ret; - struct clk_bulk_data clocks[] = { + static struct clk_bulk_data clocks[] = { { .id = "pll" }, { .id = "nadc" }, { .id = "madc" }, @@ -878,7 +878,7 @@ static int aic32x4_set_bias_level(struct snd_soc_component *component, { int ret; - struct clk_bulk_data clocks[] = { + static struct clk_bulk_data clocks[] = { { .id = "madc" }, { .id = "mdac" }, { .id = "bdiv" }, @@ -994,7 +994,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) u32 tmp_reg; int ret; - struct clk_bulk_data clocks[] = { + static struct clk_bulk_data clocks[] = { { .id = "codec_clkin" }, { .id = "pll" }, { .id = "bdiv" }, @@ -1131,7 +1131,7 @@ static struct snd_soc_dai_driver aic32x4_tas2505_dai = { .playback = { .stream_name = "Playback", .channels_min = 1, - .channels_max = 1, + .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_96000, .formats = AIC32X4_FORMATS,}, .ops = &aic32x4_ops, @@ -1144,7 +1144,7 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) u32 tmp_reg; int ret; - struct clk_bulk_data clocks[] = { + static struct clk_bulk_data clocks[] = { { .id = "codec_clkin" }, { .id = "pll" }, { .id = "bdiv" }, diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 86c92e03ea5d..d885ced34f60 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -4076,6 +4076,16 @@ static int wcd9335_setup_irqs(struct wcd9335_codec *wcd) return ret; } +static void wcd9335_teardown_irqs(struct wcd9335_codec *wcd) +{ + int i; + + /* disable interrupts on all slave ports */ + for (i = 0; i < WCD9335_SLIM_NUM_PORT_REG; i++) + regmap_write(wcd->if_regmap, WCD9335_SLIM_PGD_PORT_INT_EN0 + i, + 0x00); +} + static void wcd9335_cdc_sido_ccl_enable(struct wcd9335_codec *wcd, bool ccl_flag) { @@ -4844,6 +4854,7 @@ static void wcd9335_codec_init(struct snd_soc_component *component) static int wcd9335_codec_probe(struct snd_soc_component *component) { struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + int ret; int i; snd_soc_component_init_regmap(component, wcd->regmap); @@ -4861,7 +4872,15 @@ static int wcd9335_codec_probe(struct snd_soc_component *component) for (i = 0; i < NUM_CODEC_DAIS; i++) INIT_LIST_HEAD(&wcd->dai[i].slim_ch_list); - return wcd9335_setup_irqs(wcd); + ret = wcd9335_setup_irqs(wcd); + if (ret) + goto free_clsh_ctrl; + + return 0; + +free_clsh_ctrl: + wcd_clsh_ctrl_free(wcd->clsh_ctrl); + return ret; } static void wcd9335_codec_remove(struct snd_soc_component *comp) @@ -4869,7 +4888,7 @@ static void wcd9335_codec_remove(struct snd_soc_component *comp) struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); wcd_clsh_ctrl_free(wcd->clsh_ctrl); - free_irq(regmap_irq_get_virq(wcd->irq_data, WCD9335_IRQ_SLIMBUS), wcd); + wcd9335_teardown_irqs(wcd); } static int wcd9335_codec_set_sysclk(struct snd_soc_component *comp, diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index 2fcc97370be2..f0daf8defcf1 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -21,6 +21,7 @@ #include <linux/regulator/consumer.h> #include "wcd-clsh-v2.h" +#include "wcd-mbhc-v2.h" #include "wcd938x.h" #define WCD938X_MAX_MICBIAS (4) @@ -173,6 +174,11 @@ struct wcd938x_priv { struct device *rxdev; struct device_node *rxnode, *txnode; struct regmap *regmap; + struct mutex micb_lock; + /* mbhc module */ + struct wcd_mbhc *wcd_mbhc; + struct wcd_mbhc_config mbhc_cfg; + struct wcd_mbhc_intr intr_ids; struct wcd_clsh_ctrl *clsh_info; struct irq_domain *virq; struct regmap_irq_chip *wcd_regmap_irq_chip; @@ -201,24 +207,70 @@ struct wcd938x_priv { bool bcs_dis; }; -enum { - MIC_BIAS_1 = 1, - MIC_BIAS_2, - MIC_BIAS_3, - MIC_BIAS_4 -}; - -enum { - MICB_PULLUP_ENABLE, - MICB_PULLUP_DISABLE, - MICB_ENABLE, - MICB_DISABLE, -}; - static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800); static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(line_gain, 600, -3000); static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(analog_gain, 0, 3000); +struct wcd938x_mbhc_zdet_param { + u16 ldo_ctl; + u16 noff; + u16 nshift; + u16 btn5; + u16 btn6; + u16 btn7; +}; + +static struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { + WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD938X_ANA_MBHC_MECH, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD938X_ANA_MBHC_MECH, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD938X_ANA_MBHC_MECH, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, WCD938X_MBHC_NEW_PLUG_DETECT_CTL, 0x30), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, WCD938X_ANA_MBHC_ELECT, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, WCD938X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x1F), + WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, WCD938X_ANA_MBHC_MECH, 0x04), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, WCD938X_ANA_MBHC_MECH, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, WCD938X_ANA_MBHC_MECH, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, WCD938X_ANA_MBHC_MECH, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, WCD938X_ANA_MBHC_ELECT, 0x06), + WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, WCD938X_ANA_MBHC_ELECT, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, WCD938X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F), + WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, WCD938X_MBHC_NEW_CTL_1, 0x03), + WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, WCD938X_MBHC_NEW_CTL_2, 0x03), + WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, WCD938X_ANA_MBHC_RESULT_3, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, WCD938X_ANA_MBHC_RESULT_3, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, WCD938X_ANA_MBHC_RESULT_3, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, WCD938X_ANA_MBHC_RESULT_3, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, WCD938X_ANA_MBHC_RESULT_3, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_OCP_FSM_EN, WCD938X_HPH_OCP_CTL, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, WCD938X_ANA_MBHC_RESULT_3, 0x07), + WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, WCD938X_ANA_MBHC_ELECT, 0x70), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, WCD938X_ANA_MBHC_RESULT_3, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, WCD938X_ANA_MICB2, 0xC0), + WCD_MBHC_FIELD(WCD_MBHC_HPH_CNP_WG_TIME, WCD938X_HPH_CNP_WG_TIME, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, WCD938X_ANA_HPH, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, WCD938X_ANA_HPH, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, WCD938X_ANA_HPH, 0xC0), + WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, WCD938X_ANA_MBHC_RESULT_3, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_ANC_DET_EN, WCD938X_MBHC_CTL_BCS, 0x02), + WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, WCD938X_MBHC_NEW_FSM_STATUS, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, WCD938X_MBHC_NEW_CTL_2, 0x70), + WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, WCD938X_MBHC_NEW_FSM_STATUS, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_GND, WCD938X_HPH_PA_CTL2, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_GND, WCD938X_HPH_PA_CTL2, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, WCD938X_HPH_L_TEST, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, WCD938X_HPH_R_TEST, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, WCD938X_DIGITAL_INTR_STATUS_0, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, WCD938X_DIGITAL_INTR_STATUS_0, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, WCD938X_MBHC_NEW_CTL_1, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, WCD938X_MBHC_NEW_FSM_STATUS, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, WCD938X_MBHC_NEW_FSM_STATUS, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, WCD938X_MBHC_NEW_ADC_RESULT, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, WCD938X_ANA_MICB2, 0x3F), + WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, WCD938X_MBHC_NEW_CTL_1, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, WCD938X_MBHC_NEW_CTL_1, 0x04), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, WCD938X_ANA_MBHC_ZDET, 0x02), +}; + static const struct reg_default wcd938x_defaults[] = { {WCD938X_ANA_PAGE_REGISTER, 0x00}, {WCD938X_ANA_BIAS, 0x00}, @@ -1360,7 +1412,6 @@ static int wcd938x_io_init(struct wcd938x_priv *wcd938x) static int wcd938x_sdw_connect_port(struct wcd938x_sdw_ch_info *ch_info, struct sdw_port_config *port_config, - u32 mstr_port_num, u8 enable) { u8 ch_mask, port_num; @@ -1380,14 +1431,12 @@ static int wcd938x_sdw_connect_port(struct wcd938x_sdw_ch_info *ch_info, static int wcd938x_connect_port(struct wcd938x_sdw_priv *wcd, u8 ch_id, u8 enable) { - u8 port_num, mstr_port_num; + u8 port_num; port_num = wcd->ch_info[ch_id].port_num; - mstr_port_num = wcd->port_map[port_num - 1]; return wcd938x_sdw_connect_port(&wcd->ch_info[ch_id], &wcd->port_config[port_num], - mstr_port_num, enable); } @@ -1623,7 +1672,6 @@ static int wcd938x_codec_aux_dac_event(struct snd_soc_dapm_widget *w, { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); - int ret = 0; switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -1651,7 +1699,7 @@ static int wcd938x_codec_aux_dac_event(struct snd_soc_dapm_widget *w, WCD938X_ANA_RX_DIV4_CLK_EN_MASK, 0); break; } - return ret; + return 0; } @@ -1728,6 +1776,8 @@ static int wcd938x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, usleep_range(7000, 7100); snd_soc_component_write_field(component, WCD938X_ANA_HPH, WCD938X_HPHR_EN_MASK, 0); + wcd_mbhc_event_notify(wcd938x->wcd_mbhc, + WCD_EVENT_PRE_HPHR_PA_OFF); set_bit(HPH_PA_DELAY, &wcd938x->status_mask); break; case SND_SOC_DAPM_POST_PMD: @@ -1743,6 +1793,8 @@ static int wcd938x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, usleep_range(7000, 7100); clear_bit(HPH_PA_DELAY, &wcd938x->status_mask); } + wcd_mbhc_event_notify(wcd938x->wcd_mbhc, + WCD_EVENT_POST_HPHR_PA_OFF); snd_soc_component_write_field(component, WCD938X_ANA_HPH, WCD938X_HPHR_REF_EN_MASK, 0); snd_soc_component_write_field(component, WCD938X_DIGITAL_PDM_WD_CTL1, @@ -1830,6 +1882,7 @@ static int wcd938x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, usleep_range(7000, 7100); snd_soc_component_write_field(component, WCD938X_ANA_HPH, WCD938X_HPHL_EN_MASK, 0); + wcd_mbhc_event_notify(wcd938x->wcd_mbhc, WCD_EVENT_PRE_HPHL_PA_OFF); set_bit(HPH_PA_DELAY, &wcd938x->status_mask); break; case SND_SOC_DAPM_POST_PMD: @@ -1845,6 +1898,8 @@ static int wcd938x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, usleep_range(7000, 7100); clear_bit(HPH_PA_DELAY, &wcd938x->status_mask); } + wcd_mbhc_event_notify(wcd938x->wcd_mbhc, + WCD_EVENT_POST_HPHL_PA_OFF); snd_soc_component_write_field(component, WCD938X_ANA_HPH, WCD938X_HPHL_REF_EN_MASK, 0); snd_soc_component_write_field(component, WCD938X_DIGITAL_PDM_WD_CTL0, @@ -1866,7 +1921,6 @@ static int wcd938x_codec_enable_aux_pa(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); int hph_mode = wcd938x->hph_mode; - int ret = 0; switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -1902,7 +1956,7 @@ static int wcd938x_codec_enable_aux_pa(struct snd_soc_dapm_widget *w, WCD938X_EN_CUR_DET_MASK, 1); break; } - return ret; + return 0; } static int wcd938x_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, @@ -2355,7 +2409,14 @@ static int wcd938x_micbias_control(struct snd_soc_component *component, snd_soc_component_write_field(component, micb_reg, WCD938X_MICB_EN_MASK, WCD938X_MICB_ENABLE); + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd938x->wcd_mbhc, + WCD_EVENT_POST_MICBIAS_2_ON); } + if (micb_num == MIC_BIAS_2 && is_dapm) + wcd_mbhc_event_notify(wcd938x->wcd_mbhc, + WCD_EVENT_POST_DAPM_MICBIAS_2_ON); + break; case MICB_DISABLE: @@ -2369,10 +2430,19 @@ static int wcd938x_micbias_control(struct snd_soc_component *component, WCD938X_MICB_PULL_UP); else if ((wcd938x->micb_ref[micb_index] == 0) && (wcd938x->pullup_ref[micb_index] == 0)) { + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd938x->wcd_mbhc, + WCD_EVENT_PRE_MICBIAS_2_OFF); snd_soc_component_write_field(component, micb_reg, WCD938X_MICB_EN_MASK, 0); + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd938x->wcd_mbhc, + WCD_EVENT_POST_MICBIAS_2_OFF); } + if (is_dapm && micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd938x->wcd_mbhc, + WCD_EVENT_POST_DAPM_MICBIAS_2_OFF); break; } @@ -2845,6 +2915,704 @@ static int wcd938x_set_swr_port(struct snd_kcontrol *kcontrol, } +/* MBHC related */ +static void wcd938x_mbhc_clk_setup(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_1, + WCD938X_MBHC_CTL_RCO_EN_MASK, enable); +} + +static void wcd938x_mbhc_mbhc_bias_control(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD938X_ANA_MBHC_ELECT, + WCD938X_ANA_MBHC_BIAS_EN, enable); +} + +static void wcd938x_mbhc_program_btn_thr(struct snd_soc_component *component, + int *btn_low, int *btn_high, + int num_btn, bool is_micbias) +{ + int i, vth; + + if (num_btn > WCD_MBHC_DEF_BUTTONS) { + dev_err(component->dev, "%s: invalid number of buttons: %d\n", + __func__, num_btn); + return; + } + + for (i = 0; i < num_btn; i++) { + vth = ((btn_high[i] * 2) / 25) & 0x3F; + snd_soc_component_write_field(component, WCD938X_ANA_MBHC_BTN0 + i, + WCD938X_MBHC_BTN_VTH_MASK, vth); + dev_dbg(component->dev, "%s: btn_high[%d]: %d, vth: %d\n", + __func__, i, btn_high[i], vth); + } +} + +static bool wcd938x_mbhc_micb_en_status(struct snd_soc_component *component, int micb_num) +{ + u8 val; + + if (micb_num == MIC_BIAS_2) { + val = snd_soc_component_read_field(component, + WCD938X_ANA_MICB2, + WCD938X_ANA_MICB2_ENABLE_MASK); + if (val == WCD938X_MICB_ENABLE) + return true; + } + return false; +} + +static void wcd938x_mbhc_hph_l_pull_up_control(struct snd_soc_component *component, + int pull_up_cur) +{ + /* Default pull up current to 2uA */ + if (pull_up_cur > HS_PULLUP_I_OFF || pull_up_cur < HS_PULLUP_I_3P0_UA) + pull_up_cur = HS_PULLUP_I_2P0_UA; + + snd_soc_component_write_field(component, + WCD938X_MBHC_NEW_INT_MECH_DET_CURRENT, + WCD938X_HSDET_PULLUP_C_MASK, pull_up_cur); +} + +static int wcd938x_mbhc_request_micbias(struct snd_soc_component *component, + int micb_num, int req) +{ + return wcd938x_micbias_control(component, micb_num, req, false); +} + +static void wcd938x_mbhc_micb_ramp_control(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_write_field(component, WCD938X_ANA_MICB2_RAMP, + WCD938X_RAMP_SHIFT_CTRL_MASK, 0x0C); + snd_soc_component_write_field(component, WCD938X_ANA_MICB2_RAMP, + WCD938X_RAMP_EN_MASK, 1); + } else { + snd_soc_component_write_field(component, WCD938X_ANA_MICB2_RAMP, + WCD938X_RAMP_EN_MASK, 0); + snd_soc_component_write_field(component, WCD938X_ANA_MICB2_RAMP, + WCD938X_RAMP_SHIFT_CTRL_MASK, 0); + } +} + +static int wcd938x_get_micb_vout_ctl_val(u32 micb_mv) +{ + /* min micbias voltage is 1V and maximum is 2.85V */ + if (micb_mv < 1000 || micb_mv > 2850) + return -EINVAL; + + return (micb_mv - 1000) / 50; +} + +static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + int req_volt, int micb_num) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD938X_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD938X_ANA_MICB2; + break; + case MIC_BIAS_3: + micb_reg = WCD938X_ANA_MICB3; + break; + case MIC_BIAS_4: + micb_reg = WCD938X_ANA_MICB4; + break; + default: + return -EINVAL; + } + mutex_lock(&wcd938x->micb_lock); + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as + * per requested value. If micbias is already enabled, then + * to avoid slow micbias ramp-up or down enable pull-up + * momentarily, change the micbias value and then re-enable + * micbias. + */ + micb_en = snd_soc_component_read_field(component, micb_reg, + WCD938X_MICB_EN_MASK); + cur_vout_ctl = snd_soc_component_read_field(component, micb_reg, + WCD938X_MICB_VOUT_MASK); + + req_vout_ctl = wcd938x_get_micb_vout_ctl_val(req_volt); + if (req_vout_ctl < 0) { + ret = -EINVAL; + goto exit; + } + + if (cur_vout_ctl == req_vout_ctl) { + ret = 0; + goto exit; + } + + if (micb_en == WCD938X_MICB_ENABLE) + snd_soc_component_write_field(component, micb_reg, + WCD938X_MICB_EN_MASK, + WCD938X_MICB_PULL_UP); + + snd_soc_component_write_field(component, micb_reg, + WCD938X_MICB_VOUT_MASK, + req_vout_ctl); + + if (micb_en == WCD938X_MICB_ENABLE) { + snd_soc_component_write_field(component, micb_reg, + WCD938X_MICB_EN_MASK, + WCD938X_MICB_ENABLE); + /* + * Add 2ms delay as per HW requirement after enabling + * micbias + */ + usleep_range(2000, 2100); + } +exit: + mutex_unlock(&wcd938x->micb_lock); + return ret; +} + +static int wcd938x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, + int micb_num, bool req_en) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + int rc, micb_mv; + + if (micb_num != MIC_BIAS_2) + return -EINVAL; + /* + * If device tree micbias level is already above the minimum + * voltage needed to detect threshold microphone, then do + * not change the micbias, just return. + */ + if (wcd938x->micb2_mv >= WCD_MBHC_THR_HS_MICB_MV) + return 0; + + micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd938x->micb2_mv; + + rc = wcd938x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2); + + return rc; +} + +static inline void wcd938x_mbhc_get_result_params(struct wcd938x_priv *wcd938x, + s16 *d1_a, u16 noff, + int32_t *zdet) +{ + int i; + int val, val1; + s16 c1; + s32 x1, d1; + int32_t denom; + int minCode_param[] = { + 3277, 1639, 820, 410, 205, 103, 52, 26 + }; + + regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MBHC_ZDET, 0x20, 0x20); + for (i = 0; i < WCD938X_ZDET_NUM_MEASUREMENTS; i++) { + regmap_read(wcd938x->regmap, WCD938X_ANA_MBHC_RESULT_2, &val); + if (val & 0x80) + break; + } + val = val << 0x8; + regmap_read(wcd938x->regmap, WCD938X_ANA_MBHC_RESULT_1, &val1); + val |= val1; + regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MBHC_ZDET, 0x20, 0x00); + x1 = WCD938X_MBHC_GET_X1(val); + c1 = WCD938X_MBHC_GET_C1(val); + /* If ramp is not complete, give additional 5ms */ + if ((c1 < 2) && x1) + usleep_range(5000, 5050); + + if (!c1 || !x1) { + pr_err("%s: Impedance detect ramp error, c1=%d, x1=0x%x\n", + __func__, c1, x1); + goto ramp_down; + } + d1 = d1_a[c1]; + denom = (x1 * d1) - (1 << (14 - noff)); + if (denom > 0) + *zdet = (WCD938X_MBHC_ZDET_CONST * 1000) / denom; + else if (x1 < minCode_param[noff]) + *zdet = WCD938X_ZDET_FLOATING_IMPEDANCE; + + pr_err("%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n", + __func__, d1, c1, x1, *zdet); +ramp_down: + i = 0; + while (x1) { + regmap_read(wcd938x->regmap, + WCD938X_ANA_MBHC_RESULT_1, &val); + regmap_read(wcd938x->regmap, + WCD938X_ANA_MBHC_RESULT_2, &val1); + val = val << 0x08; + val |= val1; + x1 = WCD938X_MBHC_GET_X1(val); + i++; + if (i == WCD938X_ZDET_NUM_MEASUREMENTS) + break; + } +} + +static void wcd938x_mbhc_zdet_ramp(struct snd_soc_component *component, + struct wcd938x_mbhc_zdet_param *zdet_param, + int32_t *zl, int32_t *zr, s16 *d1_a) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + int32_t zdet = 0; + + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_ZDET_ANA_CTL, + WCD938X_ZDET_MAXV_CTL_MASK, zdet_param->ldo_ctl); + snd_soc_component_update_bits(component, WCD938X_ANA_MBHC_BTN5, + WCD938X_VTH_MASK, zdet_param->btn5); + snd_soc_component_update_bits(component, WCD938X_ANA_MBHC_BTN6, + WCD938X_VTH_MASK, zdet_param->btn6); + snd_soc_component_update_bits(component, WCD938X_ANA_MBHC_BTN7, + WCD938X_VTH_MASK, zdet_param->btn7); + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_ZDET_ANA_CTL, + WCD938X_ZDET_RANGE_CTL_MASK, zdet_param->noff); + snd_soc_component_update_bits(component, WCD938X_MBHC_NEW_ZDET_RAMP_CTL, + 0x0F, zdet_param->nshift); + + if (!zl) + goto z_right; + /* Start impedance measurement for HPH_L */ + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_ZDET, 0x80, 0x80); + dev_dbg(component->dev, "%s: ramp for HPH_L, noff = %d\n", + __func__, zdet_param->noff); + wcd938x_mbhc_get_result_params(wcd938x, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_ZDET, 0x80, 0x00); + + *zl = zdet; + +z_right: + if (!zr) + return; + /* Start impedance measurement for HPH_R */ + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_ZDET, 0x40, 0x40); + dev_dbg(component->dev, "%s: ramp for HPH_R, noff = %d\n", + __func__, zdet_param->noff); + wcd938x_mbhc_get_result_params(wcd938x, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_ZDET, 0x40, 0x00); + + *zr = zdet; +} + +static inline void wcd938x_wcd_mbhc_qfuse_cal(struct snd_soc_component *component, + int32_t *z_val, int flag_l_r) +{ + s16 q1; + int q1_cal; + + if (*z_val < (WCD938X_ZDET_VAL_400/1000)) + q1 = snd_soc_component_read(component, + WCD938X_DIGITAL_EFUSE_REG_23 + (2 * flag_l_r)); + else + q1 = snd_soc_component_read(component, + WCD938X_DIGITAL_EFUSE_REG_24 + (2 * flag_l_r)); + if (q1 & 0x80) + q1_cal = (10000 - ((q1 & 0x7F) * 25)); + else + q1_cal = (10000 + (q1 * 25)); + if (q1_cal > 0) + *z_val = ((*z_val) * 10000) / q1_cal; +} + +static void wcd938x_wcd_mbhc_calc_impedance(struct snd_soc_component *component, + uint32_t *zl, uint32_t *zr) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + s16 reg0, reg1, reg2, reg3, reg4; + int32_t z1L, z1R, z1Ls; + int zMono, z_diff1, z_diff2; + bool is_fsm_disable = false; + struct wcd938x_mbhc_zdet_param zdet_param[] = { + {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */ + {2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */ + {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */ + {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */ + }; + struct wcd938x_mbhc_zdet_param *zdet_param_ptr = NULL; + s16 d1_a[][4] = { + {0, 30, 90, 30}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + }; + s16 *d1 = NULL; + + reg0 = snd_soc_component_read(component, WCD938X_ANA_MBHC_BTN5); + reg1 = snd_soc_component_read(component, WCD938X_ANA_MBHC_BTN6); + reg2 = snd_soc_component_read(component, WCD938X_ANA_MBHC_BTN7); + reg3 = snd_soc_component_read(component, WCD938X_MBHC_CTL_CLK); + reg4 = snd_soc_component_read(component, WCD938X_MBHC_NEW_ZDET_ANA_CTL); + + if (snd_soc_component_read(component, WCD938X_ANA_MBHC_ELECT) & 0x80) { + is_fsm_disable = true; + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_ELECT, 0x80, 0x00); + } + + /* For NO-jack, disable L_DET_EN before Z-det measurements */ + if (wcd938x->mbhc_cfg.hphl_swh) + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_MECH, 0x80, 0x00); + + /* Turn off 100k pull down on HPHL */ + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_MECH, 0x01, 0x00); + + /* Disable surge protection before impedance detection. + * This is done to give correct value for high impedance. + */ + regmap_update_bits(wcd938x->regmap, + WCD938X_HPH_SURGE_HPHLR_SURGE_EN, 0xC0, 0x00); + /* 1ms delay needed after disable surge protection */ + usleep_range(1000, 1010); + + /* First get impedance on Left */ + d1 = d1_a[1]; + zdet_param_ptr = &zdet_param[1]; + wcd938x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1L, NULL, d1); + + if (!WCD938X_MBHC_IS_SECOND_RAMP_REQUIRED(z1L)) + goto left_ch_impedance; + + /* Second ramp for left ch */ + if (z1L < WCD938X_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1L > WCD938X_ZDET_VAL_400) && + (z1L <= WCD938X_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1L > WCD938X_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + wcd938x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1L, NULL, d1); + +left_ch_impedance: + if ((z1L == WCD938X_ZDET_FLOATING_IMPEDANCE) || + (z1L > WCD938X_ZDET_VAL_100K)) { + *zl = WCD938X_ZDET_FLOATING_IMPEDANCE; + zdet_param_ptr = &zdet_param[1]; + d1 = d1_a[1]; + } else { + *zl = z1L/1000; + wcd938x_wcd_mbhc_qfuse_cal(component, zl, 0); + } + dev_dbg(component->dev, "%s: impedance on HPH_L = %d(ohms)\n", + __func__, *zl); + + /* Start of right impedance ramp and calculation */ + wcd938x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1R, d1); + if (WCD938X_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) { + if (((z1R > WCD938X_ZDET_VAL_1200) && + (zdet_param_ptr->noff == 0x6)) || + ((*zl) != WCD938X_ZDET_FLOATING_IMPEDANCE)) + goto right_ch_impedance; + /* Second ramp for right ch */ + if (z1R < WCD938X_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1R > WCD938X_ZDET_VAL_400) && + (z1R <= WCD938X_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1R > WCD938X_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + wcd938x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1R, d1); + } +right_ch_impedance: + if ((z1R == WCD938X_ZDET_FLOATING_IMPEDANCE) || + (z1R > WCD938X_ZDET_VAL_100K)) { + *zr = WCD938X_ZDET_FLOATING_IMPEDANCE; + } else { + *zr = z1R/1000; + wcd938x_wcd_mbhc_qfuse_cal(component, zr, 1); + } + dev_dbg(component->dev, "%s: impedance on HPH_R = %d(ohms)\n", + __func__, *zr); + + /* Mono/stereo detection */ + if ((*zl == WCD938X_ZDET_FLOATING_IMPEDANCE) && + (*zr == WCD938X_ZDET_FLOATING_IMPEDANCE)) { + dev_dbg(component->dev, + "%s: plug type is invalid or extension cable\n", + __func__); + goto zdet_complete; + } + if ((*zl == WCD938X_ZDET_FLOATING_IMPEDANCE) || + (*zr == WCD938X_ZDET_FLOATING_IMPEDANCE) || + ((*zl < WCD_MONO_HS_MIN_THR) && (*zr > WCD_MONO_HS_MIN_THR)) || + ((*zl > WCD_MONO_HS_MIN_THR) && (*zr < WCD_MONO_HS_MIN_THR))) { + dev_dbg(component->dev, + "%s: Mono plug type with one ch floating or shorted to GND\n", + __func__); + wcd_mbhc_set_hph_type(wcd938x->wcd_mbhc, WCD_MBHC_HPH_MONO); + goto zdet_complete; + } + snd_soc_component_write_field(component, WCD938X_HPH_R_ATEST, + WCD938X_HPHPA_GND_OVR_MASK, 1); + snd_soc_component_write_field(component, WCD938X_HPH_PA_CTL2, + WCD938X_HPHPA_GND_R_MASK, 1); + if (*zl < (WCD938X_ZDET_VAL_32/1000)) + wcd938x_mbhc_zdet_ramp(component, &zdet_param[0], &z1Ls, NULL, d1); + else + wcd938x_mbhc_zdet_ramp(component, &zdet_param[1], &z1Ls, NULL, d1); + snd_soc_component_write_field(component, WCD938X_HPH_PA_CTL2, + WCD938X_HPHPA_GND_R_MASK, 0); + snd_soc_component_write_field(component, WCD938X_HPH_R_ATEST, + WCD938X_HPHPA_GND_OVR_MASK, 0); + z1Ls /= 1000; + wcd938x_wcd_mbhc_qfuse_cal(component, &z1Ls, 0); + /* Parallel of left Z and 9 ohm pull down resistor */ + zMono = ((*zl) * 9) / ((*zl) + 9); + z_diff1 = (z1Ls > zMono) ? (z1Ls - zMono) : (zMono - z1Ls); + z_diff2 = ((*zl) > z1Ls) ? ((*zl) - z1Ls) : (z1Ls - (*zl)); + if ((z_diff1 * (*zl + z1Ls)) > (z_diff2 * (z1Ls + zMono))) { + dev_dbg(component->dev, "%s: stereo plug type detected\n", + __func__); + wcd_mbhc_set_hph_type(wcd938x->wcd_mbhc, WCD_MBHC_HPH_STEREO); + } else { + dev_dbg(component->dev, "%s: MONO plug type detected\n", + __func__); + wcd_mbhc_set_hph_type(wcd938x->wcd_mbhc, WCD_MBHC_HPH_MONO); + } + + /* Enable surge protection again after impedance detection */ + regmap_update_bits(wcd938x->regmap, + WCD938X_HPH_SURGE_HPHLR_SURGE_EN, 0xC0, 0xC0); +zdet_complete: + snd_soc_component_write(component, WCD938X_ANA_MBHC_BTN5, reg0); + snd_soc_component_write(component, WCD938X_ANA_MBHC_BTN6, reg1); + snd_soc_component_write(component, WCD938X_ANA_MBHC_BTN7, reg2); + /* Turn on 100k pull down on HPHL */ + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_MECH, 0x01, 0x01); + + /* For NO-jack, re-enable L_DET_EN after Z-det measurements */ + if (wcd938x->mbhc_cfg.hphl_swh) + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_MECH, 0x80, 0x80); + + snd_soc_component_write(component, WCD938X_MBHC_NEW_ZDET_ANA_CTL, reg4); + snd_soc_component_write(component, WCD938X_MBHC_CTL_CLK, reg3); + if (is_fsm_disable) + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_ELECT, 0x80, 0x80); +} + +static void wcd938x_mbhc_gnd_det_ctrl(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_write_field(component, WCD938X_ANA_MBHC_MECH, + WCD938X_MBHC_HSG_PULLUP_COMP_EN, 1); + snd_soc_component_write_field(component, WCD938X_ANA_MBHC_MECH, + WCD938X_MBHC_GND_DET_EN_MASK, 1); + } else { + snd_soc_component_write_field(component, WCD938X_ANA_MBHC_MECH, + WCD938X_MBHC_GND_DET_EN_MASK, 0); + snd_soc_component_write_field(component, WCD938X_ANA_MBHC_MECH, + WCD938X_MBHC_HSG_PULLUP_COMP_EN, 0); + } +} + +static void wcd938x_mbhc_hph_pull_down_ctrl(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD938X_HPH_PA_CTL2, + WCD938X_HPHPA_GND_R_MASK, enable); + snd_soc_component_write_field(component, WCD938X_HPH_PA_CTL2, + WCD938X_HPHPA_GND_L_MASK, enable); +} + +static void wcd938x_mbhc_moisture_config(struct snd_soc_component *component) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + if (wcd938x->mbhc_cfg.moist_rref == R_OFF) { + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2, + WCD938X_M_RTH_CTL_MASK, R_OFF); + return; + } + + /* Do not enable moisture detection if jack type is NC */ + if (!wcd938x->mbhc_cfg.hphl_swh) { + dev_dbg(component->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2, + WCD938X_M_RTH_CTL_MASK, R_OFF); + return; + } + + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2, + WCD938X_M_RTH_CTL_MASK, wcd938x->mbhc_cfg.moist_rref); +} + +static void wcd938x_mbhc_moisture_detect_en(struct snd_soc_component *component, bool enable) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + if (enable) + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2, + WCD938X_M_RTH_CTL_MASK, wcd938x->mbhc_cfg.moist_rref); + else + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2, + WCD938X_M_RTH_CTL_MASK, R_OFF); +} + +static bool wcd938x_mbhc_get_moisture_status(struct snd_soc_component *component) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + bool ret = false; + + if (wcd938x->mbhc_cfg.moist_rref == R_OFF) { + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2, + WCD938X_M_RTH_CTL_MASK, R_OFF); + goto done; + } + + /* Do not enable moisture detection if jack type is NC */ + if (!wcd938x->mbhc_cfg.hphl_swh) { + dev_dbg(component->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2, + WCD938X_M_RTH_CTL_MASK, R_OFF); + goto done; + } + + /* + * If moisture_en is already enabled, then skip to plug type + * detection. + */ + if (snd_soc_component_read_field(component, WCD938X_MBHC_NEW_CTL_2, WCD938X_M_RTH_CTL_MASK)) + goto done; + + wcd938x_mbhc_moisture_detect_en(component, true); + /* Read moisture comparator status */ + ret = ((snd_soc_component_read(component, WCD938X_MBHC_NEW_FSM_STATUS) + & 0x20) ? 0 : 1); + +done: + return ret; + +} + +static void wcd938x_mbhc_moisture_polling_ctrl(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, + WCD938X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, + WCD938X_MOISTURE_EN_POLLING_MASK, enable); +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .clk_setup = wcd938x_mbhc_clk_setup, + .mbhc_bias = wcd938x_mbhc_mbhc_bias_control, + .set_btn_thr = wcd938x_mbhc_program_btn_thr, + .micbias_enable_status = wcd938x_mbhc_micb_en_status, + .hph_pull_up_control_v2 = wcd938x_mbhc_hph_l_pull_up_control, + .mbhc_micbias_control = wcd938x_mbhc_request_micbias, + .mbhc_micb_ramp_control = wcd938x_mbhc_micb_ramp_control, + .mbhc_micb_ctrl_thr_mic = wcd938x_mbhc_micb_ctrl_threshold_mic, + .compute_impedance = wcd938x_wcd_mbhc_calc_impedance, + .mbhc_gnd_det_ctrl = wcd938x_mbhc_gnd_det_ctrl, + .hph_pull_down_ctrl = wcd938x_mbhc_hph_pull_down_ctrl, + .mbhc_moisture_config = wcd938x_mbhc_moisture_config, + .mbhc_get_moisture_status = wcd938x_mbhc_get_moisture_status, + .mbhc_moisture_polling_ctrl = wcd938x_mbhc_moisture_polling_ctrl, + .mbhc_moisture_detect_en = wcd938x_mbhc_moisture_detect_en, +}; + +static int wcd938x_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wcd_mbhc_get_hph_type(wcd938x->wcd_mbhc); + + return 0; +} + +static int wcd938x_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t zl, zr; + bool hphr; + struct soc_mixer_control *mc; + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + mc = (struct soc_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + wcd_mbhc_get_impedance(wcd938x->wcd_mbhc, &zl, &zr); + dev_dbg(component->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr); + ucontrol->value.integer.value[0] = hphr ? zr : zl; + + return 0; +} + +static const struct snd_kcontrol_new hph_type_detect_controls[] = { + SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0, + wcd938x_get_hph_type, NULL), +}; + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0, + wcd938x_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0, + wcd938x_hph_impedance_get, NULL), +}; + +static int wcd938x_mbhc_init(struct snd_soc_component *component) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + struct wcd_mbhc_intr *intr_ids = &wcd938x->intr_ids; + + intr_ids->mbhc_sw_intr = regmap_irq_get_virq(wcd938x->irq_chip, + WCD938X_IRQ_MBHC_SW_DET); + intr_ids->mbhc_btn_press_intr = regmap_irq_get_virq(wcd938x->irq_chip, + WCD938X_IRQ_MBHC_BUTTON_PRESS_DET); + intr_ids->mbhc_btn_release_intr = regmap_irq_get_virq(wcd938x->irq_chip, + WCD938X_IRQ_MBHC_BUTTON_RELEASE_DET); + intr_ids->mbhc_hs_ins_intr = regmap_irq_get_virq(wcd938x->irq_chip, + WCD938X_IRQ_MBHC_ELECT_INS_REM_LEG_DET); + intr_ids->mbhc_hs_rem_intr = regmap_irq_get_virq(wcd938x->irq_chip, + WCD938X_IRQ_MBHC_ELECT_INS_REM_DET); + intr_ids->hph_left_ocp = regmap_irq_get_virq(wcd938x->irq_chip, + WCD938X_IRQ_HPHL_OCP_INT); + intr_ids->hph_right_ocp = regmap_irq_get_virq(wcd938x->irq_chip, + WCD938X_IRQ_HPHR_OCP_INT); + + wcd938x->wcd_mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, wcd_mbhc_fields, true); + + snd_soc_add_component_controls(component, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_component_controls(component, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + return 0; +} +/* END MBHC */ + static const struct snd_kcontrol_new wcd938x_snd_controls[] = { SOC_SINGLE_EXT("HPHL_COMP Switch", WCD938X_COMP_L, 0, 1, 0, wcd938x_get_compander, wcd938x_set_compander), @@ -3225,15 +3993,6 @@ static const struct snd_soc_dapm_route wcd938x_audio_map[] = { {"EAR", NULL, "EAR PGA"}, }; -static int wcd938x_get_micb_vout_ctl_val(u32 micb_mv) -{ - /* min micbias voltage is 1V and maximum is 2.85V */ - if (micb_mv < 1000 || micb_mv > 2850) - return -EINVAL; - - return (micb_mv - 1000) / 50; -} - static int wcd938x_set_micbias_data(struct wcd938x_priv *wcd938x) { int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4; @@ -3372,10 +4131,27 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component) default: break; } + + ret = wcd938x_mbhc_init(component); + if (ret) + dev_err(component->dev, "mbhc initialization failed\n"); err: return ret; } +static int wcd938x_codec_set_jack(struct snd_soc_component *comp, + struct snd_soc_jack *jack, void *data) +{ + struct wcd938x_priv *wcd = dev_get_drvdata(comp->dev); + + if (!jack) + return wcd_mbhc_start(wcd->wcd_mbhc, &wcd->mbhc_cfg, jack); + + wcd_mbhc_stop(wcd->wcd_mbhc); + + return 0; +} + static const struct snd_soc_component_driver soc_codec_dev_wcd938x = { .name = "wcd938x_codec", .probe = wcd938x_soc_codec_probe, @@ -3385,6 +4161,7 @@ static const struct snd_soc_component_driver soc_codec_dev_wcd938x = { .num_dapm_widgets = ARRAY_SIZE(wcd938x_dapm_widgets), .dapm_routes = wcd938x_audio_map, .num_dapm_routes = ARRAY_SIZE(wcd938x_audio_map), + .set_jack = wcd938x_codec_set_jack, }; static void wcd938x_dt_parse_micbias_info(struct device *dev, struct wcd938x_priv *wcd) @@ -3420,6 +4197,7 @@ static void wcd938x_dt_parse_micbias_info(struct device *dev, struct wcd938x_pri static int wcd938x_populate_dt_data(struct wcd938x_priv *wcd938x, struct device *dev) { + struct wcd_mbhc_config *cfg = &wcd938x->mbhc_cfg; int ret; wcd938x->reset_gpio = of_get_named_gpio(dev->of_node, "reset-gpios", 0); @@ -3448,6 +4226,17 @@ static int wcd938x_populate_dt_data(struct wcd938x_priv *wcd938x, struct device wcd938x_dt_parse_micbias_info(dev, wcd938x); + cfg->mbhc_micbias = MIC_BIAS_2; + cfg->anc_micbias = MIC_BIAS_2; + cfg->v_hs_max = WCD_MBHC_HS_V_MAX; + cfg->num_btn = WCD938X_MBHC_MAX_BUTTONS; + cfg->micb_mv = wcd938x->micb2_mv; + cfg->linein_th = 5000; + cfg->hs_thr = 1700; + cfg->hph_thr = 50; + + wcd_dt_parse_mbhc_data(dev, cfg); + return 0; } @@ -3679,6 +4468,7 @@ static int wcd938x_probe(struct platform_device *pdev) return -ENOMEM; dev_set_drvdata(dev, wcd938x); + mutex_init(&wcd938x->micb_lock); ret = wcd938x_populate_dt_data(wcd938x, dev); if (ret) { @@ -3703,7 +4493,7 @@ static int wcd938x_probe(struct platform_device *pdev) pm_runtime_enable(dev); pm_runtime_idle(dev); - return ret; + return 0; } static int wcd938x_remove(struct platform_device *pdev) diff --git a/sound/soc/codecs/wcd938x.h b/sound/soc/codecs/wcd938x.h index 07b08de4cebf..ea82039e7843 100644 --- a/sound/soc/codecs/wcd938x.h +++ b/sound/soc/codecs/wcd938x.h @@ -658,7 +658,6 @@ struct wcd938x_sdw_priv { struct sdw_port_config port_config[WCD938X_MAX_SWR_PORTS]; struct wcd938x_sdw_ch_info *ch_info; bool port_enable[WCD938X_MAX_SWR_CH_IDS]; - int port_map[WCD938X_MAX_SWR_PORTS]; int active_ports; int num_ports; bool is_tx; diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index fe15cbc7bcaf..f7c800927cb2 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -747,6 +747,8 @@ static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) { wm_adsp_debugfs_clear(dsp); + debugfs_remove_recursive(dsp->debugfs_root); + dsp->debugfs_root = NULL; } #else static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp, @@ -2029,10 +2031,9 @@ static struct wm_coeff_ctl *wm_adsp_get_ctl(struct wm_adsp *dsp, if (!pos->subname) continue; if (strncmp(pos->subname, name, pos->subname_len) == 0 && - strncmp(pos->fw_name, fw_txt, - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 0 && - pos->alg_region.alg == alg && - pos->alg_region.type == type) { + pos->fw_name == fw_txt && + pos->alg_region.alg == alg && + pos->alg_region.type == type) { rslt = pos; break; } |