[PATCH 00/15] Report jack and button detection + Capture Support
Hi All,
Here is a patch series for reporting to user space jack and button events and add the support for Capture. With some cleanups and fixes along the way.
Regards,
Lucas Tanure
Lucas Tanure (12): ASoC: cs42l42: Don't enable/disable regulator at Bias Level ASoC: cs42l42: Always wait at least 3ms after reset ASoC: cs42l42: Remove power if the driver is being removed ASoC: cs42l42: Disable regulators if probe fails ASoC: cs42l42: Fix Bitclock polarity inversion ASoC: cs42l42: Provide finer control on playback path ASoC: cs42l42: Set clock source for both ways of stream ASoC: cs42l42: Fix channel width support ASoC: cs42l42: Add Capture Support ASoC: cs42l42: Report jack and button detection ASoC: cs42l42: Use bclk from hw_params if set_sysclk was not called ASoC: cs42l42: Fix mixer volume control
Richard Fitzgerald (3): ASoC: cs42l42: Wait at least 150us after writing SCLK_PRESENT ASoC: cs42l42: Only start PLL if it is needed ASoC: cs42l42: Wait for PLL to lock before switching to it
sound/soc/codecs/cs42l42.c | 497 +++++++++++++++++++++---------------- sound/soc/codecs/cs42l42.h | 42 +++- 2 files changed, 317 insertions(+), 222 deletions(-)
-- 2.30.1
dev_pm_ops already enable/disable the codec if not in use
Signed-off-by: Lucas Tanure tanureal@opensource.cirrus.com --- sound/soc/codecs/cs42l42.c | 38 -------------------------------------- 1 file changed, 38 deletions(-)
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 210fcbedf2413..caae05b1729e9 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -511,43 +511,6 @@ static const struct snd_soc_dapm_route cs42l42_audio_map[] = { {"HP", NULL, "HPDRV"} };
-static int cs42l42_set_bias_level(struct snd_soc_component *component, - enum snd_soc_bias_level level) -{ - struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); - int ret; - - switch (level) { - case SND_SOC_BIAS_ON: - break; - case SND_SOC_BIAS_PREPARE: - break; - case SND_SOC_BIAS_STANDBY: - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { - regcache_cache_only(cs42l42->regmap, false); - regcache_sync(cs42l42->regmap); - ret = regulator_bulk_enable( - ARRAY_SIZE(cs42l42->supplies), - cs42l42->supplies); - if (ret != 0) { - dev_err(component->dev, - "Failed to enable regulators: %d\n", - ret); - return ret; - } - } - break; - case SND_SOC_BIAS_OFF: - - regcache_cache_only(cs42l42->regmap, true); - regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), - cs42l42->supplies); - break; - } - - return 0; -} - static int cs42l42_component_probe(struct snd_soc_component *component) { struct cs42l42_private *cs42l42 = @@ -560,7 +523,6 @@ static int cs42l42_component_probe(struct snd_soc_component *component)
static const struct snd_soc_component_driver soc_component_dev_cs42l42 = { .probe = cs42l42_component_probe, - .set_bias_level = cs42l42_set_bias_level, .dapm_widgets = cs42l42_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(cs42l42_dapm_widgets), .dapm_routes = cs42l42_audio_map,
This delay is part of the power-up sequence defined in the datasheet. A runtime_resume is a power-up so must also include the delay.
Signed-off-by: Lucas Tanure tanureal@opensource.cirrus.com --- sound/soc/codecs/cs42l42.c | 3 ++- sound/soc/codecs/cs42l42.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index caae05b1729e9..2a25a30f842b1 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -1763,7 +1763,7 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client, dev_dbg(&i2c_client->dev, "Found reset GPIO\n"); gpiod_set_value_cansleep(cs42l42->reset_gpio, 1); } - mdelay(3); + usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2);
/* Request IRQ */ ret = devm_request_threaded_irq(&i2c_client->dev, @@ -1888,6 +1888,7 @@ static int cs42l42_runtime_resume(struct device *dev) }
gpiod_set_value_cansleep(cs42l42->reset_gpio, 1); + usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2);
regcache_cache_only(cs42l42->regmap, false); regcache_sync(cs42l42->regmap); diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index 9e3cc528dcff0..6a500d0146a28 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -739,6 +739,7 @@ #define CS42L42_FRAC2_VAL(val) (((val) & 0xff0000) >> 16)
#define CS42L42_NUM_SUPPLIES 5 +#define CS42L42_BOOT_TIME_US 3000
static const char *const cs42l42_supply_names[CS42L42_NUM_SUPPLIES] = { "VA",
Ensure the power supplies are turned off when removing the driver
Signed-off-by: Lucas Tanure tanureal@opensource.cirrus.com --- sound/soc/codecs/cs42l42.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 2a25a30f842b1..00e303c711ddc 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -1852,6 +1852,8 @@ static int cs42l42_i2c_remove(struct i2c_client *i2c_client) /* Hold down reset */ gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
+ regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies); + return 0; }
On Tue, Mar 02, 2021 at 05:04:42PM +0000, Lucas Tanure wrote:
/* Hold down reset */ gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
- regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies);
Are you sure the device is always runtime resumed on remove?
In case of cs42l42_i2c_probe() fail, the regulators were left enabled.
Signed-off-by: Lucas Tanure tanureal@opensource.cirrus.com --- sound/soc/codecs/cs42l42.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 00e303c711ddc..a567d38af8d3f 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -1756,8 +1756,10 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client, /* Reset the Device */ cs42l42->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(cs42l42->reset_gpio)) - return PTR_ERR(cs42l42->reset_gpio); + if (IS_ERR(cs42l42->reset_gpio)) { + ret = PTR_ERR(cs42l42->reset_gpio); + goto err_disable; + }
if (cs42l42->reset_gpio) { dev_dbg(&i2c_client->dev, "Found reset GPIO\n"); @@ -1791,13 +1793,13 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client, dev_err(&i2c_client->dev, "CS42L42 Device ID (%X). Expected %X\n", devid, CS42L42_CHIP_ID); - return ret; + goto err_disable; }
ret = regmap_read(cs42l42->regmap, CS42L42_REVID, ®); if (ret < 0) { dev_err(&i2c_client->dev, "Get Revision ID failed\n"); - return ret; + goto err_disable; }
dev_info(&i2c_client->dev, @@ -1823,7 +1825,7 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client, if (i2c_client->dev.of_node) { ret = cs42l42_handle_device_data(i2c_client, cs42l42); if (ret != 0) - return ret; + goto err_disable; }
/* Setup headset detection */
The driver was setting bit clock polarity opposite to intended polarity. Also simplify the code by grouping ADC and DAC clock configurations into a single field.
Signed-off-by: Lucas Tanure tanureal@opensource.cirrus.com --- sound/soc/codecs/cs42l42.c | 20 ++++++++------------ sound/soc/codecs/cs42l42.h | 11 ++++++----- 2 files changed, 14 insertions(+), 17 deletions(-)
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index a567d38af8d3f..834b702fe4297 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -759,27 +759,23 @@ static int cs42l42_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) /* Bitclock/frame inversion */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: + asp_cfg_val |= CS42L42_ASP_SCPOL_NOR << CS42L42_ASP_SCPOL_SHIFT; break; case SND_SOC_DAIFMT_NB_IF: - asp_cfg_val |= CS42L42_ASP_POL_INV << - CS42L42_ASP_LCPOL_IN_SHIFT; + asp_cfg_val |= CS42L42_ASP_SCPOL_NOR << CS42L42_ASP_SCPOL_SHIFT; + asp_cfg_val |= CS42L42_ASP_LCPOL_INV << CS42L42_ASP_LCPOL_SHIFT; break; case SND_SOC_DAIFMT_IB_NF: - asp_cfg_val |= CS42L42_ASP_POL_INV << - CS42L42_ASP_SCPOL_IN_DAC_SHIFT; break; case SND_SOC_DAIFMT_IB_IF: - asp_cfg_val |= CS42L42_ASP_POL_INV << - CS42L42_ASP_LCPOL_IN_SHIFT; - asp_cfg_val |= CS42L42_ASP_POL_INV << - CS42L42_ASP_SCPOL_IN_DAC_SHIFT; + asp_cfg_val |= CS42L42_ASP_LCPOL_INV << CS42L42_ASP_LCPOL_SHIFT; break; }
- snd_soc_component_update_bits(component, CS42L42_ASP_CLK_CFG, - CS42L42_ASP_MODE_MASK | - CS42L42_ASP_SCPOL_IN_DAC_MASK | - CS42L42_ASP_LCPOL_IN_MASK, asp_cfg_val); + snd_soc_component_update_bits(component, CS42L42_ASP_CLK_CFG, CS42L42_ASP_MODE_MASK | + CS42L42_ASP_SCPOL_MASK | + CS42L42_ASP_LCPOL_MASK, + asp_cfg_val);
return 0; } diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index 6a500d0146a28..2bd370b184e1d 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -258,11 +258,12 @@ #define CS42L42_ASP_SLAVE_MODE 0x00 #define CS42L42_ASP_MODE_SHIFT 4 #define CS42L42_ASP_MODE_MASK (1 << CS42L42_ASP_MODE_SHIFT) -#define CS42L42_ASP_SCPOL_IN_DAC_SHIFT 2 -#define CS42L42_ASP_SCPOL_IN_DAC_MASK (1 << CS42L42_ASP_SCPOL_IN_DAC_SHIFT) -#define CS42L42_ASP_LCPOL_IN_SHIFT 0 -#define CS42L42_ASP_LCPOL_IN_MASK (1 << CS42L42_ASP_LCPOL_IN_SHIFT) -#define CS42L42_ASP_POL_INV 1 +#define CS42L42_ASP_SCPOL_SHIFT 2 +#define CS42L42_ASP_SCPOL_MASK (3 << CS42L42_ASP_SCPOL_SHIFT) +#define CS42L42_ASP_SCPOL_NOR 3 +#define CS42L42_ASP_LCPOL_SHIFT 0 +#define CS42L42_ASP_LCPOL_MASK (3 << CS42L42_ASP_LCPOL_SHIFT) +#define CS42L42_ASP_LCPOL_INV 3
#define CS42L42_ASP_FRM_CFG (CS42L42_PAGE_12 + 0x08) #define CS42L42_ASP_STP_SHIFT 4
On Tue, Mar 02, 2021 at 05:04:44PM +0000, Lucas Tanure wrote:
The driver was setting bit clock polarity opposite to intended polarity.
This is a bug fix which should be a separate patch and at the start of the series so it should be sent separately before any non-fix stuff.
Also simplify the code by grouping ADC and DAC clock configurations into a single field.
Removing cs42l42_hpdrv_evt that enables the entire chain and replace by a set of widgets that can better define the codec
Signed-off-by: Lucas Tanure tanureal@opensource.cirrus.com --- sound/soc/codecs/cs42l42.c | 65 +++++++++++++------------------------- sound/soc/codecs/cs42l42.h | 8 ++--- 2 files changed, 26 insertions(+), 47 deletions(-)
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 834b702fe4297..663a6a8104171 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -461,54 +461,33 @@ static const struct snd_kcontrol_new cs42l42_snd_controls[] = { 0x3e, 1, mixer_tlv) };
-static int cs42l42_hpdrv_evt(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - - if (event & SND_SOC_DAPM_POST_PMU) { - /* Enable the channels */ - snd_soc_component_update_bits(component, CS42L42_ASP_RX_DAI0_EN, - CS42L42_ASP_RX0_CH_EN_MASK, - (CS42L42_ASP_RX0_CH1_EN | - CS42L42_ASP_RX0_CH2_EN) << - CS42L42_ASP_RX0_CH_EN_SHIFT); - - /* Power up */ - snd_soc_component_update_bits(component, CS42L42_PWR_CTL1, - CS42L42_ASP_DAI_PDN_MASK | CS42L42_MIXER_PDN_MASK | - CS42L42_HP_PDN_MASK, 0); - } else if (event & SND_SOC_DAPM_PRE_PMD) { - /* Disable the channels */ - snd_soc_component_update_bits(component, CS42L42_ASP_RX_DAI0_EN, - CS42L42_ASP_RX0_CH_EN_MASK, 0); - - /* Power down */ - snd_soc_component_update_bits(component, CS42L42_PWR_CTL1, - CS42L42_ASP_DAI_PDN_MASK | CS42L42_MIXER_PDN_MASK | - CS42L42_HP_PDN_MASK, - CS42L42_ASP_DAI_PDN_MASK | CS42L42_MIXER_PDN_MASK | - CS42L42_HP_PDN_MASK); - } else { - dev_err(component->dev, "Invalid event 0x%x\n", event); - } - return 0; -} - static const struct snd_soc_dapm_widget cs42l42_dapm_widgets[] = { + /* Playback Path */ SND_SOC_DAPM_OUTPUT("HP"), - SND_SOC_DAPM_AIF_IN("SDIN", NULL, 0, CS42L42_ASP_CLK_CFG, - CS42L42_ASP_SCLK_EN_SHIFT, false), - SND_SOC_DAPM_OUT_DRV_E("HPDRV", SND_SOC_NOPM, 0, - 0, NULL, 0, cs42l42_hpdrv_evt, - SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD) + SND_SOC_DAPM_DAC("DAC", NULL, CS42L42_PWR_CTL1, CS42L42_HP_PDN_SHIFT, 1), + SND_SOC_DAPM_MIXER("MIXER", CS42L42_PWR_CTL1, CS42L42_MIXER_PDN_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_AIF_IN("SDIN1", NULL, 0, CS42L42_ASP_RX_DAI0_EN, CS42L42_ASP_RX0_CH1_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("SDIN2", NULL, 1, CS42L42_ASP_RX_DAI0_EN, CS42L42_ASP_RX0_CH2_SHIFT, 0), + + /* Playback Requirements */ + SND_SOC_DAPM_SUPPLY("ASP DAI0", CS42L42_PWR_CTL1, CS42L42_ASP_DAI_PDN_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("SCLK", CS42L42_ASP_CLK_CFG, CS42L42_ASP_SCLK_EN_SHIFT, 0, NULL, 0), };
static const struct snd_soc_dapm_route cs42l42_audio_map[] = { - {"SDIN", NULL, "Playback"}, - {"HPDRV", NULL, "SDIN"}, - {"HP", NULL, "HPDRV"} + /* Playback Path */ + {"HP", NULL, "DAC"}, + {"DAC", NULL, "MIXER"}, + {"MIXER", NULL, "SDIN1"}, + {"MIXER", NULL, "SDIN2"}, + {"SDIN1", NULL, "Playback"}, + {"SDIN2", NULL, "Playback"}, + + /* Playback Requirements */ + {"SDIN1", NULL, "ASP DAI0"}, + {"SDIN2", NULL, "ASP DAI0"}, + {"SDIN1", NULL, "SCLK"}, + {"SDIN2", NULL, "SCLK"}, };
static int cs42l42_component_probe(struct snd_soc_component *component) diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index 2bd370b184e1d..e17a0c0aeb4a0 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -695,10 +695,10 @@ #define CS42L42_ASP_RX_DAI0_EN (CS42L42_PAGE_2A + 0x01) #define CS42L42_ASP_RX0_CH_EN_SHIFT 2 #define CS42L42_ASP_RX0_CH_EN_MASK (0xf << CS42L42_ASP_RX0_CH_EN_SHIFT) -#define CS42L42_ASP_RX0_CH1_EN 1 -#define CS42L42_ASP_RX0_CH2_EN 2 -#define CS42L42_ASP_RX0_CH3_EN 4 -#define CS42L42_ASP_RX0_CH4_EN 8 +#define CS42L42_ASP_RX0_CH1_SHIFT 2 +#define CS42L42_ASP_RX0_CH2_SHIFT 3 +#define CS42L42_ASP_RX0_CH3_SHIFT 4 +#define CS42L42_ASP_RX0_CH4_SHIFT 5
#define CS42L42_ASP_RX_DAI0_CH1_AP_RES (CS42L42_PAGE_2A + 0x02) #define CS42L42_ASP_RX_DAI0_CH1_BIT_MSB (CS42L42_PAGE_2A + 0x03)
Move the enable/disable of clocks to cs42l42_mute_stream so the record path also get clocks.
Signed-off-by: Lucas Tanure tanureal@opensource.cirrus.com --- sound/soc/codecs/cs42l42.c | 85 +++++++++++++++++++++----------------- sound/soc/codecs/cs42l42.h | 1 + 2 files changed, 48 insertions(+), 38 deletions(-)
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 663a6a8104171..670f28f09ae01 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -786,52 +786,62 @@ static int cs42l42_set_sysclk(struct snd_soc_dai *dai, return 0; }
-static int cs42l42_mute(struct snd_soc_dai *dai, int mute, int direction) +static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) { struct snd_soc_component *component = dai->component; unsigned int regval; u8 fullScaleVol;
if (mute) { - /* Mark SCLK as not present to turn on the internal - * oscillator. - */ - snd_soc_component_update_bits(component, CS42L42_OSC_SWITCH, - CS42L42_SCLK_PRESENT_MASK, 0); - - snd_soc_component_update_bits(component, CS42L42_PLL_CTL1, - CS42L42_PLL_START_MASK, - 0 << CS42L42_PLL_START_SHIFT); - /* Mute the headphone */ - snd_soc_component_update_bits(component, CS42L42_HP_CTL, - CS42L42_HP_ANA_AMUTE_MASK | - CS42L42_HP_ANA_BMUTE_MASK, - CS42L42_HP_ANA_AMUTE_MASK | - CS42L42_HP_ANA_BMUTE_MASK); + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_component_update_bits(component, CS42L42_HP_CTL, + CS42L42_HP_ANA_AMUTE_MASK | + CS42L42_HP_ANA_BMUTE_MASK, + CS42L42_HP_ANA_AMUTE_MASK | + CS42L42_HP_ANA_BMUTE_MASK); + + cs42l42->stream_use &= ~(1 << stream); + if(!cs42l42->stream_use) { + /* + * Switch to the internal oscillator. + * SCLK must remain running until after this clock switch. + * Without a source of clock the I2C bus doesn't work. + */ + snd_soc_component_update_bits(component, CS42L42_OSC_SWITCH, + CS42L42_SCLK_PRESENT_MASK, 0); + snd_soc_component_update_bits(component, CS42L42_PLL_CTL1, + CS42L42_PLL_START_MASK, 0); + } } else { - snd_soc_component_update_bits(component, CS42L42_PLL_CTL1, - CS42L42_PLL_START_MASK, - 1 << CS42L42_PLL_START_SHIFT); - /* Read the headphone load */ - regval = snd_soc_component_read(component, CS42L42_LOAD_DET_RCSTAT); - if (((regval & CS42L42_RLA_STAT_MASK) >> - CS42L42_RLA_STAT_SHIFT) == CS42L42_RLA_STAT_15_OHM) { - fullScaleVol = CS42L42_HP_FULL_SCALE_VOL_MASK; - } else { - fullScaleVol = 0; + if (!cs42l42->stream_use) { + /* SCLK must be running before codec unmute */ + snd_soc_component_update_bits(component, CS42L42_PLL_CTL1, + CS42L42_PLL_START_MASK, 1); + + /* Mark SCLK as present, turn off internal oscillator */ + snd_soc_component_update_bits(component, CS42L42_OSC_SWITCH, + CS42L42_SCLK_PRESENT_MASK, + CS42L42_SCLK_PRESENT_MASK); } + cs42l42->stream_use |= 1 << stream; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* Read the headphone load */ + regval = snd_soc_component_read(component, CS42L42_LOAD_DET_RCSTAT); + if (((regval & CS42L42_RLA_STAT_MASK) >> CS42L42_RLA_STAT_SHIFT) == + CS42L42_RLA_STAT_15_OHM) { + fullScaleVol = CS42L42_HP_FULL_SCALE_VOL_MASK; + } else { + fullScaleVol = 0; + }
- /* Un-mute the headphone, set the full scale volume flag */ - snd_soc_component_update_bits(component, CS42L42_HP_CTL, - CS42L42_HP_ANA_AMUTE_MASK | - CS42L42_HP_ANA_BMUTE_MASK | - CS42L42_HP_FULL_SCALE_VOL_MASK, fullScaleVol); - - /* Mark SCLK as present, turn off internal oscillator */ - snd_soc_component_update_bits(component, CS42L42_OSC_SWITCH, - CS42L42_SCLK_PRESENT_MASK, - CS42L42_SCLK_PRESENT_MASK); + /* Un-mute the headphone, set the full scale volume flag */ + snd_soc_component_update_bits(component, CS42L42_HP_CTL, + CS42L42_HP_ANA_AMUTE_MASK | + CS42L42_HP_ANA_BMUTE_MASK | + CS42L42_HP_FULL_SCALE_VOL_MASK, fullScaleVol); + } }
return 0; @@ -846,8 +856,7 @@ static const struct snd_soc_dai_ops cs42l42_ops = { .hw_params = cs42l42_pcm_hw_params, .set_fmt = cs42l42_set_dai_fmt, .set_sysclk = cs42l42_set_sysclk, - .mute_stream = cs42l42_mute, - .no_capture_mute = 1, + .mute_stream = cs42l42_mute_stream, };
static struct snd_soc_dai_driver cs42l42_dai = { diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index e17a0c0aeb4a0..59e6eccb8d731 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -769,6 +769,7 @@ struct cs42l42_private { u8 bias_thresholds[CS42L42_NUM_BIASES]; u8 hs_bias_ramp_rate; u8 hs_bias_ramp_time; + u8 stream_use; };
#endif /* __CS42L42_H__ */
Hi Lucas,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on asoc/for-next] [also build test ERROR on v5.12-rc1 next-20210302] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Lucas-Tanure/Report-jack-and-button... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next config: arc-allyesconfig (attached as .config) compiler: arceb-elf-gcc (GCC) 9.3.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/0day-ci/linux/commit/d004d17fc0cf6b114d467e0b352fe619c2d6... git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Lucas-Tanure/Report-jack-and-button-detection-Capture-Support/20210303-012348 git checkout d004d17fc0cf6b114d467e0b352fe619c2d653a4 # save the attached .config to linux build tree COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=arc
If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot lkp@intel.com
Note: the linux-review/Lucas-Tanure/Report-jack-and-button-detection-Capture-Support/20210303-012348 HEAD 844324ce32306dc48f88e1a9fb44f51783b3942d builds fine. It only hurts bisectibility.
All errors (new ones prefixed by >>):
sound/soc/codecs/cs42l42.c: In function 'cs42l42_mute_stream':
sound/soc/codecs/cs42l42.c:804:3: error: 'cs42l42' undeclared (first use in this function)
804 | cs42l42->stream_use &= ~(1 << stream); | ^~~~~~~ sound/soc/codecs/cs42l42.c:804:3: note: each undeclared identifier is reported only once for each function it appears in
vim +/cs42l42 +804 sound/soc/codecs/cs42l42.c
788 789 static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) 790 { 791 struct snd_soc_component *component = dai->component; 792 unsigned int regval; 793 u8 fullScaleVol; 794 795 if (mute) { 796 /* Mute the headphone */ 797 if (stream == SNDRV_PCM_STREAM_PLAYBACK) 798 snd_soc_component_update_bits(component, CS42L42_HP_CTL, 799 CS42L42_HP_ANA_AMUTE_MASK | 800 CS42L42_HP_ANA_BMUTE_MASK, 801 CS42L42_HP_ANA_AMUTE_MASK | 802 CS42L42_HP_ANA_BMUTE_MASK); 803
804 cs42l42->stream_use &= ~(1 << stream);
805 if(!cs42l42->stream_use) { 806 /* 807 * Switch to the internal oscillator. 808 * SCLK must remain running until after this clock switch. 809 * Without a source of clock the I2C bus doesn't work. 810 */ 811 snd_soc_component_update_bits(component, CS42L42_OSC_SWITCH, 812 CS42L42_SCLK_PRESENT_MASK, 0); 813 snd_soc_component_update_bits(component, CS42L42_PLL_CTL1, 814 CS42L42_PLL_START_MASK, 0); 815 } 816 } else { 817 if (!cs42l42->stream_use) { 818 /* SCLK must be running before codec unmute */ 819 snd_soc_component_update_bits(component, CS42L42_PLL_CTL1, 820 CS42L42_PLL_START_MASK, 1); 821 822 /* Mark SCLK as present, turn off internal oscillator */ 823 snd_soc_component_update_bits(component, CS42L42_OSC_SWITCH, 824 CS42L42_SCLK_PRESENT_MASK, 825 CS42L42_SCLK_PRESENT_MASK); 826 } 827 cs42l42->stream_use |= 1 << stream; 828 829 if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 830 /* Read the headphone load */ 831 regval = snd_soc_component_read(component, CS42L42_LOAD_DET_RCSTAT); 832 if (((regval & CS42L42_RLA_STAT_MASK) >> CS42L42_RLA_STAT_SHIFT) == 833 CS42L42_RLA_STAT_15_OHM) { 834 fullScaleVol = CS42L42_HP_FULL_SCALE_VOL_MASK; 835 } else { 836 fullScaleVol = 0; 837 } 838 839 /* Un-mute the headphone, set the full scale volume flag */ 840 snd_soc_component_update_bits(component, CS42L42_HP_CTL, 841 CS42L42_HP_ANA_AMUTE_MASK | 842 CS42L42_HP_ANA_BMUTE_MASK | 843 CS42L42_HP_FULL_SCALE_VOL_MASK, fullScaleVol); 844 } 845 } 846 847 return 0; 848 } 849
--- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Remove the hard coded 32 bits width and replace with the correct width calculated by params_width.
Signed-off-by: Lucas Tanure tanureal@opensource.cirrus.com --- sound/soc/codecs/cs42l42.c | 47 ++++++++++++++++++-------------------- sound/soc/codecs/cs42l42.h | 1 - 2 files changed, 22 insertions(+), 26 deletions(-)
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 670f28f09ae01..6022b6f9c6d27 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -632,24 +632,6 @@ static int cs42l42_pll_config(struct snd_soc_component *component) CS42L42_CLK_OASRC_SEL_MASK, CS42L42_CLK_OASRC_SEL_12 << CS42L42_CLK_OASRC_SEL_SHIFT); - /* channel 1 on low LRCLK, 32 bit */ - snd_soc_component_update_bits(component, - CS42L42_ASP_RX_DAI0_CH1_AP_RES, - CS42L42_ASP_RX_CH_AP_MASK | - CS42L42_ASP_RX_CH_RES_MASK, - (CS42L42_ASP_RX_CH_AP_LOW << - CS42L42_ASP_RX_CH_AP_SHIFT) | - (CS42L42_ASP_RX_CH_RES_32 << - CS42L42_ASP_RX_CH_RES_SHIFT)); - /* Channel 2 on high LRCLK, 32 bit */ - snd_soc_component_update_bits(component, - CS42L42_ASP_RX_DAI0_CH2_AP_RES, - CS42L42_ASP_RX_CH_AP_MASK | - CS42L42_ASP_RX_CH_RES_MASK, - (CS42L42_ASP_RX_CH_AP_HI << - CS42L42_ASP_RX_CH_AP_SHIFT) | - (CS42L42_ASP_RX_CH_RES_32 << - CS42L42_ASP_RX_CH_RES_SHIFT)); if (pll_ratio_table[i].mclk_src_sel == 0) { /* Pass the clock straight through */ snd_soc_component_update_bits(component, @@ -765,14 +747,29 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_component *component = dai->component; struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); - int retval; + unsigned int width = (params_width(params) / 8) - 1; + unsigned int val = 0;
cs42l42->srate = params_rate(params); - cs42l42->swidth = params_width(params);
- retval = cs42l42_pll_config(component); + switch(substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + val |= width << CS42L42_ASP_RX_CH_RES_SHIFT; + /* channel 1 on low LRCLK */ + snd_soc_component_update_bits(component, CS42L42_ASP_RX_DAI0_CH1_AP_RES, + CS42L42_ASP_RX_CH_AP_MASK | + CS42L42_ASP_RX_CH_RES_MASK, val); + /* Channel 2 on high LRCLK */ + val |= CS42L42_ASP_RX_CH_AP_HI << CS42L42_ASP_RX_CH_AP_SHIFT; + snd_soc_component_update_bits(component, CS42L42_ASP_RX_DAI0_CH2_AP_RES, + CS42L42_ASP_RX_CH_AP_MASK | + CS42L42_ASP_RX_CH_RES_MASK, val); + break; + default: + break; + };
- return retval; + return cs42l42_pll_config(component); }
static int cs42l42_set_sysclk(struct snd_soc_dai *dai, @@ -847,9 +844,9 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) return 0; }
-#define CS42L42_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ - SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | \ - SNDRV_PCM_FMTBIT_S32_LE) +#define CS42L42_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE )
static const struct snd_soc_dai_ops cs42l42_ops = { diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index 59e6eccb8d731..3dcbfebc53b0f 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -758,7 +758,6 @@ struct cs42l42_private { struct completion pdn_done; u32 sclk; u32 srate; - u32 swidth; u8 plug_state; u8 hs_type; u8 ts_inv;
On Tue, Mar 02, 2021 at 05:04:47PM +0000, Lucas Tanure wrote:
Remove the hard coded 32 bits width and replace with the correct width calculated by params_width.
Again, fixes should go at the start of the series before any cleanups or anything so they can be merged independently.
Add support for capture path on headseat pins
Signed-off-by: Lucas Tanure tanureal@opensource.cirrus.com --- sound/soc/codecs/cs42l42.c | 39 ++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/cs42l42.h | 12 ++++++++++++ 2 files changed, 51 insertions(+)
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 6022b6f9c6d27..9a7217cf59f36 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -471,6 +471,18 @@ static const struct snd_soc_dapm_widget cs42l42_dapm_widgets[] = {
/* Playback Requirements */ SND_SOC_DAPM_SUPPLY("ASP DAI0", CS42L42_PWR_CTL1, CS42L42_ASP_DAI_PDN_SHIFT, 1, NULL, 0), + + /* Capture Path */ + SND_SOC_DAPM_INPUT("HS"), + SND_SOC_DAPM_ADC("ADC", NULL, CS42L42_PWR_CTL1, CS42L42_ADC_PDN_SHIFT, 1), + SND_SOC_DAPM_AIF_OUT("SDOUT1", NULL, 0, CS42L42_ASP_TX_CH_EN, CS42L42_ASP_TX0_CH1_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("SDOUT2", NULL, 1, CS42L42_ASP_TX_CH_EN, CS42L42_ASP_TX0_CH2_SHIFT, 0), + + /* Capture Requirements */ + SND_SOC_DAPM_SUPPLY("ASP DAO0", CS42L42_PWR_CTL1, CS42L42_ASP_DAO_PDN_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ASP TX EN", CS42L42_ASP_TX_SZ_EN, CS42L42_ASP_TX_EN_SHIFT, 0, NULL, 0), + + /* Playback/Capture Requirements */ SND_SOC_DAPM_SUPPLY("SCLK", CS42L42_ASP_CLK_CFG, CS42L42_ASP_SCLK_EN_SHIFT, 0, NULL, 0), };
@@ -488,6 +500,21 @@ static const struct snd_soc_dapm_route cs42l42_audio_map[] = { {"SDIN2", NULL, "ASP DAI0"}, {"SDIN1", NULL, "SCLK"}, {"SDIN2", NULL, "SCLK"}, + + /* Capture Path */ + {"ADC", NULL, "HS"}, + { "SDOUT1", NULL, "ADC" }, + { "SDOUT2", NULL, "ADC" }, + { "Capture", NULL, "SDOUT1" }, + { "Capture", NULL, "SDOUT2" }, + + /* Capture Requirements */ + { "SDOUT1", NULL, "ASP DAO0" }, + { "SDOUT2", NULL, "ASP DAO0" }, + { "SDOUT1", NULL, "SCLK" }, + { "SDOUT2", NULL, "SCLK" }, + { "SDOUT1", NULL, "ASP TX EN" }, + { "SDOUT2", NULL, "ASP TX EN" }, };
static int cs42l42_component_probe(struct snd_soc_component *component) @@ -747,12 +774,24 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_component *component = dai->component; struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); + unsigned int channels = params_channels(params); unsigned int width = (params_width(params) / 8) - 1; unsigned int val = 0;
cs42l42->srate = params_rate(params);
switch(substream->stream) { + case SNDRV_PCM_STREAM_CAPTURE: + if (channels == 2) { + val |= CS42L42_ASP_TX_CH2_AP_MASK; + val |= width << CS42L42_ASP_TX_CH2_RES_SHIFT; + } + val |= width << CS42L42_ASP_TX_CH1_RES_SHIFT; + + snd_soc_component_update_bits(component, CS42L42_ASP_TX_CH_AP_RES, + CS42L42_ASP_TX_CH1_AP_MASK | CS42L42_ASP_TX_CH2_AP_MASK | + CS42L42_ASP_TX_CH2_RES_MASK | CS42L42_ASP_TX_CH1_RES_MASK, val); + break; case SNDRV_PCM_STREAM_PLAYBACK: val |= width << CS42L42_ASP_RX_CH_RES_SHIFT; /* channel 1 on low LRCLK */ diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index 3dcbfebc53b0f..c373259ed46f7 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -683,8 +683,20 @@
/* Page 0x29 Serial Port TX Registers */ #define CS42L42_ASP_TX_SZ_EN (CS42L42_PAGE_29 + 0x01) +#define CS42L42_ASP_TX_EN_SHIFT 0 #define CS42L42_ASP_TX_CH_EN (CS42L42_PAGE_29 + 0x02) +#define CS42L42_ASP_TX0_CH2_SHIFT 1 +#define CS42L42_ASP_TX0_CH1_SHIFT 0 + #define CS42L42_ASP_TX_CH_AP_RES (CS42L42_PAGE_29 + 0x03) +#define CS42L42_ASP_TX_CH1_AP_SHIFT 7 +#define CS42L42_ASP_TX_CH1_AP_MASK (1 << CS42L42_ASP_TX_CH1_AP_SHIFT) +#define CS42L42_ASP_TX_CH2_AP_SHIFT 6 +#define CS42L42_ASP_TX_CH2_AP_MASK (1 << CS42L42_ASP_TX_CH2_AP_SHIFT) +#define CS42L42_ASP_TX_CH2_RES_SHIFT 2 +#define CS42L42_ASP_TX_CH2_RES_MASK (3 << CS42L42_ASP_TX_CH2_RES_SHIFT) +#define CS42L42_ASP_TX_CH1_RES_SHIFT 0 +#define CS42L42_ASP_TX_CH1_RES_MASK (3 << CS42L42_ASP_TX_CH1_RES_SHIFT) #define CS42L42_ASP_TX_CH1_BIT_MSB (CS42L42_PAGE_29 + 0x04) #define CS42L42_ASP_TX_CH1_BIT_LSB (CS42L42_PAGE_29 + 0x05) #define CS42L42_ASP_TX_HIZ_DLY_CFG (CS42L42_PAGE_29 + 0x06)
Report the Jack events to the user space through ALSA. Also moves request_threaded_irq() to component_probe so it don't get interrupts before the initialization the struct snd_soc_jack.
Signed-off-by: Lucas Tanure tanureal@opensource.cirrus.com --- sound/soc/codecs/cs42l42.c | 142 +++++++++++++++++++++++++------------ sound/soc/codecs/cs42l42.h | 4 ++ 2 files changed, 99 insertions(+), 47 deletions(-)
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 9a7217cf59f36..8345f52ec9d58 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -517,29 +517,6 @@ static const struct snd_soc_dapm_route cs42l42_audio_map[] = { { "SDOUT2", NULL, "ASP TX EN" }, };
-static int cs42l42_component_probe(struct snd_soc_component *component) -{ - struct cs42l42_private *cs42l42 = - (struct cs42l42_private *)snd_soc_component_get_drvdata(component); - - cs42l42->component = component; - - return 0; -} - -static const struct snd_soc_component_driver soc_component_dev_cs42l42 = { - .probe = cs42l42_component_probe, - .dapm_widgets = cs42l42_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(cs42l42_dapm_widgets), - .dapm_routes = cs42l42_audio_map, - .num_dapm_routes = ARRAY_SIZE(cs42l42_audio_map), - .controls = cs42l42_snd_controls, - .num_controls = ARRAY_SIZE(cs42l42_snd_controls), - .idle_bias_on = 1, - .endianness = 1, - .non_legacy_dai_naming = 1, -}; - struct cs42l42_pll_params { u32 sclk; u8 mclk_div; @@ -1196,7 +1173,7 @@ static void cs42l42_cancel_hs_type_detect(struct cs42l42_private *cs42l42) (3 << CS42L42_HSDET_AUTO_TIME_SHIFT)); }
-static void cs42l42_handle_button_press(struct cs42l42_private *cs42l42) +static int cs42l42_handle_button_press(struct cs42l42_private *cs42l42) { int bias_level; unsigned int detect_status; @@ -1239,17 +1216,24 @@ static void cs42l42_handle_button_press(struct cs42l42_private *cs42l42)
switch (bias_level) { case 1: /* Function C button press */ + bias_level = SND_JACK_BTN_2; dev_dbg(cs42l42->component->dev, "Function C button press\n"); break; case 2: /* Function B button press */ + bias_level = SND_JACK_BTN_1; dev_dbg(cs42l42->component->dev, "Function B button press\n"); break; case 3: /* Function D button press */ + bias_level = SND_JACK_BTN_3; dev_dbg(cs42l42->component->dev, "Function D button press\n"); break; case 4: /* Function A button press */ + bias_level = SND_JACK_BTN_0; dev_dbg(cs42l42->component->dev, "Function A button press\n"); break; + default: + bias_level = 0; + break; }
/* Set button detect level sensitivity back to default */ @@ -1279,6 +1263,8 @@ static void cs42l42_handle_button_press(struct cs42l42_private *cs42l42) (0 << CS42L42_M_HSBIAS_HIZ_SHIFT) | (1 << CS42L42_M_SHORT_RLS_SHIFT) | (1 << CS42L42_M_SHORT_DET_SHIFT)); + + return bias_level; }
struct cs42l42_irq_params { @@ -1323,6 +1309,8 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) unsigned int current_plug_status; unsigned int current_button_status; unsigned int i; + int report = 0; +
/* Read sticky registers to clear interurpt */ for (i = 0; i < ARRAY_SIZE(stickies); i++) { @@ -1349,9 +1337,20 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) if ((~masks[5]) & irq_params_table[5].mask) { if (stickies[5] & CS42L42_HSDET_AUTO_DONE_MASK) { cs42l42_process_hs_type_detect(cs42l42); - dev_dbg(component->dev, - "Auto detect done (%d)\n", - cs42l42->hs_type); + switch(cs42l42->hs_type){ + case CS42L42_PLUG_CTIA: + case CS42L42_PLUG_OMTP: + snd_soc_jack_report(&cs42l42->jack, SND_JACK_HEADSET, + SND_JACK_HEADSET); + break; + case CS42L42_PLUG_HEADPHONE: + snd_soc_jack_report(&cs42l42->jack, SND_JACK_HEADPHONE, + SND_JACK_HEADPHONE); + break; + default: + break; + } + dev_dbg(component->dev, "Auto detect done (%d)\n", cs42l42->hs_type); } }
@@ -1369,8 +1368,19 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) if (cs42l42->plug_state != CS42L42_TS_UNPLUG) { cs42l42->plug_state = CS42L42_TS_UNPLUG; cs42l42_cancel_hs_type_detect(cs42l42); - dev_dbg(component->dev, - "Unplug event\n"); + + switch(cs42l42->hs_type){ + case CS42L42_PLUG_CTIA: + case CS42L42_PLUG_OMTP: + snd_soc_jack_report(&cs42l42->jack, 0, SND_JACK_HEADSET); + break; + case CS42L42_PLUG_HEADPHONE: + snd_soc_jack_report(&cs42l42->jack, 0, SND_JACK_HEADPHONE); + break; + default: + break; + } + dev_dbg(component->dev, "Unplug event\n"); } break;
@@ -1385,14 +1395,15 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) if (!(current_button_status & CS42L42_M_HSBIAS_HIZ_MASK)) {
- if (current_button_status & - CS42L42_M_DETECT_TF_MASK) { - dev_dbg(component->dev, - "Button released\n"); - } else if (current_button_status & - CS42L42_M_DETECT_FT_MASK) { - cs42l42_handle_button_press(cs42l42); + if (current_button_status & CS42L42_M_DETECT_TF_MASK) { + dev_dbg(component->dev, "Button released\n"); + report = 0; + } else if (current_button_status & CS42L42_M_DETECT_FT_MASK) { + report = cs42l42_handle_button_press(cs42l42); + } + snd_soc_jack_report(&cs42l42->jack, report, SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); } }
@@ -1731,6 +1742,53 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client, return 0; }
+static int cs42l42_component_probe(struct snd_soc_component *comp) +{ + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(comp); + struct snd_soc_card *crd = comp->card; + int ret = 0; + + cs42l42->component = comp; + + ret = snd_soc_card_jack_new(crd, "CS42L42 Headset", SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, + &cs42l42->jack, NULL, 0); + if (ret < 0) { + dev_err(comp->dev, "Cannot create CS42L42 Headset: %d\n", ret); + return ret; + } + + /* Request IRQ */ + ret = request_threaded_irq(cs42l42->irq, NULL, cs42l42_irq_thread, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, "cs42l42", cs42l42); + + if (ret) + dev_err(comp->dev, "Failed to request IRQ: %d\n", ret); + + return ret; +} + +static void cs42l42_component_remove(struct snd_soc_component *comp) +{ + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(comp); + + free_irq(cs42l42->irq, cs42l42); +} + +static const struct snd_soc_component_driver soc_component_dev_cs42l42 = { + .probe = cs42l42_component_probe, + .remove = cs42l42_component_remove, + .dapm_widgets = cs42l42_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42l42_dapm_widgets), + .dapm_routes = cs42l42_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs42l42_audio_map), + .controls = cs42l42_snd_controls, + .num_controls = ARRAY_SIZE(cs42l42_snd_controls), + .idle_bias_on = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + static int cs42l42_i2c_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id) { @@ -1745,6 +1803,7 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client, return -ENOMEM;
i2c_set_clientdata(i2c_client, cs42l42); + cs42l42->irq = i2c_client->irq;
cs42l42->regmap = devm_regmap_init_i2c(i2c_client, &cs42l42_regmap); if (IS_ERR(cs42l42->regmap)) { @@ -1787,17 +1846,6 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client, } usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2);
- /* Request IRQ */ - ret = devm_request_threaded_irq(&i2c_client->dev, - i2c_client->irq, - NULL, cs42l42_irq_thread, - IRQF_ONESHOT | IRQF_TRIGGER_LOW, - "cs42l42", cs42l42); - - if (ret != 0) - dev_err(&i2c_client->dev, - "Failed to request IRQ: %d\n", ret); - /* initialize codec */ ret = regmap_read(cs42l42->regmap, CS42L42_DEVID_AB, ®); devid = (reg & 0xFF) << 12; diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index c373259ed46f7..267c2b616e57d 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -12,6 +12,8 @@ #ifndef __CS42L42_H__ #define __CS42L42_H__
+#include <sound/jack.h> + #define CS42L42_PAGE_REGISTER 0x00 /* Page Select Register */ #define CS42L42_WIN_START 0x00 #define CS42L42_WIN_LEN 0x100 @@ -768,6 +770,8 @@ struct cs42l42_private { struct regulator_bulk_data supplies[CS42L42_NUM_SUPPLIES]; struct gpio_desc *reset_gpio; struct completion pdn_done; + struct snd_soc_jack jack; + int irq; u32 sclk; u32 srate; u8 plug_state;
On Tue, Mar 02, 2021 at 05:04:49PM +0000, Lucas Tanure wrote:
Report the Jack events to the user space through ALSA. Also moves request_threaded_irq() to component_probe so it don't get interrupts before the initialization the struct snd_soc_jack.
This looks like it should be split into two changes too, one for the code motion and one for the jack API. However I'm not convinced it's a good idea to move the interrupt requesting to the component registration, in general we should be doing resource acquisition in the main probe function so things like deferred probe work well - it gets messy if we try to handle stuff like that in component registration.
I'd have expected the interrupt handling to just be able to check if there's a jack set, and note that snd_soc_jack_report() supports being called with a NULL jack anyway.
Add support for reading the source clock from snd_soc_params_to_bclk so the machine driver is not required to call cs42l42_set_sysclk
Signed-off-by: Lucas Tanure tanureal@opensource.cirrus.com --- sound/soc/codecs/cs42l42.c | 17 +++++++++++++---- sound/soc/codecs/cs42l42.h | 1 + 2 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 8345f52ec9d58..7898a2d52a3b7 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -556,10 +556,16 @@ static int cs42l42_pll_config(struct snd_soc_component *component) { struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); int i; + u32 clk; u32 fsync;
+ if (!cs42l42->sclk) + clk = cs42l42->bclk; + else + clk = cs42l42->sclk; + for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) { - if (pll_ratio_table[i].sclk == cs42l42->sclk) { + if (pll_ratio_table[i].sclk == clk) { /* Configure the internal sample rate */ snd_soc_component_update_bits(component, CS42L42_MCLK_CTL, CS42L42_INTERNAL_FS_MASK, @@ -579,12 +585,12 @@ static int cs42l42_pll_config(struct snd_soc_component *component) (pll_ratio_table[i].mclk_div << CS42L42_MCLKDIV_SHIFT)); /* Set up the LRCLK */ - fsync = cs42l42->sclk / cs42l42->srate; - if (((fsync * cs42l42->srate) != cs42l42->sclk) + fsync = clk / cs42l42->srate; + if (((fsync * cs42l42->srate) != clk) || ((fsync % 2) != 0)) { dev_err(component->dev, "Unsupported sclk %d/sample rate %d\n", - cs42l42->sclk, + clk, cs42l42->srate); return -EINVAL; } @@ -756,6 +762,7 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream, unsigned int val = 0;
cs42l42->srate = params_rate(params); + cs42l42->bclk = snd_soc_params_to_bclk(params);
switch(substream->stream) { case SNDRV_PCM_STREAM_CAPTURE: @@ -888,6 +895,8 @@ static struct snd_soc_dai_driver cs42l42_dai = { .rates = SNDRV_PCM_RATE_8000_192000, .formats = CS42L42_FORMATS, }, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, .ops = &cs42l42_ops, };
diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index 267c2b616e57d..1df9577e367c1 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -772,6 +772,7 @@ struct cs42l42_private { struct completion pdn_done; struct snd_soc_jack jack; int irq; + int bclk; u32 sclk; u32 srate; u8 plug_state;
From: Richard Fitzgerald rf@opensource.cirrus.com
There must be a delay of at least 150us after writing SCLK_PRESENT before issuing another I2C write.
This is done using struct reg_sequence because it can specify a delay after the write and the whole sequence is written atomically.
Signed-off-by: Richard Fitzgerald rf@opensource.cirrus.com --- sound/soc/codecs/cs42l42.c | 28 +++++++++++++++++++++++----- sound/soc/codecs/cs42l42.h | 1 + 2 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 7898a2d52a3b7..5755f8baddbcf 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -517,6 +517,24 @@ static const struct snd_soc_dapm_route cs42l42_audio_map[] = { { "SDOUT2", NULL, "ASP TX EN" }, };
+/* Switch to SCLK. Atomic delay after the write to allow the switch to complete. */ +static const struct reg_sequence cs42l42_to_sclk_seq[] = { + { + .reg = CS42L42_OSC_SWITCH, + .def = CS42L42_SCLK_PRESENT_MASK, + .delay_us = CS42L42_CLOCK_SWITCH_DELAY_US, + }, +}; + +/* Switch to OSC. Atomic delay after the write to allow the switch to complete. */ +static const struct reg_sequence cs42l42_to_osc_seq[] = { + { + .reg = CS42L42_OSC_SWITCH, + .def = 0, + .delay_us = CS42L42_CLOCK_SWITCH_DELAY_US, + }, +}; + struct cs42l42_pll_params { u32 sclk; u8 mclk_div; @@ -809,6 +827,7 @@ static int cs42l42_set_sysclk(struct snd_soc_dai *dai, static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) { struct snd_soc_component *component = dai->component; + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); unsigned int regval; u8 fullScaleVol;
@@ -828,8 +847,8 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) * SCLK must remain running until after this clock switch. * Without a source of clock the I2C bus doesn't work. */ - snd_soc_component_update_bits(component, CS42L42_OSC_SWITCH, - CS42L42_SCLK_PRESENT_MASK, 0); + regmap_multi_reg_write(cs42l42->regmap, cs42l42_to_osc_seq, + ARRAY_SIZE(cs42l42_to_osc_seq)); snd_soc_component_update_bits(component, CS42L42_PLL_CTL1, CS42L42_PLL_START_MASK, 0); } @@ -840,9 +859,8 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) CS42L42_PLL_START_MASK, 1);
/* Mark SCLK as present, turn off internal oscillator */ - snd_soc_component_update_bits(component, CS42L42_OSC_SWITCH, - CS42L42_SCLK_PRESENT_MASK, - CS42L42_SCLK_PRESENT_MASK); + regmap_multi_reg_write(cs42l42->regmap, cs42l42_to_sclk_seq, + ARRAY_SIZE(cs42l42_to_sclk_seq)); } cs42l42->stream_use |= 1 << stream;
diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index 1df9577e367c1..a0c87f2326e69 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -755,6 +755,7 @@
#define CS42L42_NUM_SUPPLIES 5 #define CS42L42_BOOT_TIME_US 3000 +#define CS42L42_CLOCK_SWITCH_DELAY_US 150
static const char *const cs42l42_supply_names[CS42L42_NUM_SUPPLIES] = { "VA",
On Tue, Mar 02, 2021 at 05:04:51PM +0000, Lucas Tanure wrote:
Signed-off-by: Richard Fitzgerald rf@opensource.cirrus.com
You've not provided a Signed-off-by for this so I can't do anything with it, please see Documentation/process/submitting-patches.rst for details on what this is and why it's important.
Hi Lucas,
I love your patch! Perhaps something to improve:
[auto build test WARNING on asoc/for-next] [also build test WARNING on v5.12-rc1 next-20210302] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Lucas-Tanure/Report-jack-and-button... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next config: microblaze-randconfig-c003-20210303 (attached as .config) compiler: microblaze-linux-gcc (GCC) 9.3.0
If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot lkp@intel.com
"coccinelle warnings: (new ones prefixed by >>)"
sound/soc/codecs/cs42l42.c:811:2-3: Unneeded semicolon
Please review and possibly fold the followup patch.
--- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
From: kernel test robot lkp@intel.com
sound/soc/codecs/cs42l42.c:811:2-3: Unneeded semicolon
Remove unneeded semicolon.
Generated by: scripts/coccinelle/misc/semicolon.cocci
CC: Richard Fitzgerald rf@opensource.cirrus.com Reported-by: kernel test robot lkp@intel.com Signed-off-by: kernel test robot lkp@intel.com ---
url: https://github.com/0day-ci/linux/commits/Lucas-Tanure/Report-jack-and-button... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
cs42l42.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
--- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -808,7 +808,7 @@ static int cs42l42_pcm_hw_params(struct break; default: break; - }; + }
return cs42l42_pll_config(component); }
From: Richard Fitzgerald rf@opensource.cirrus.com
The PLL is only needed for sclk < 11289600 Hz and cs42l42_pll_config() will not configure it for higher rates. So it must only be enabled when it is needed.
Signed-off-by: Richard Fitzgerald rf@opensource.cirrus.com --- sound/soc/codecs/cs42l42.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 5755f8baddbcf..68b5a450623d3 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -855,8 +855,9 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) } else { if (!cs42l42->stream_use) { /* SCLK must be running before codec unmute */ - snd_soc_component_update_bits(component, CS42L42_PLL_CTL1, - CS42L42_PLL_START_MASK, 1); + if ((cs42l42->bclk < 11289600) && (cs42l42->sclk < 11289600)) + snd_soc_component_update_bits(component, CS42L42_PLL_CTL1, + CS42L42_PLL_START_MASK, 1);
/* Mark SCLK as present, turn off internal oscillator */ regmap_multi_reg_write(cs42l42->regmap, cs42l42_to_sclk_seq,
From: Richard Fitzgerald rf@opensource.cirrus.com
The PLL should have locked before using it to supply MCLK.
Signed-off-by: Richard Fitzgerald rf@opensource.cirrus.com --- sound/soc/codecs/cs42l42.c | 12 +++++++++++- sound/soc/codecs/cs42l42.h | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 68b5a450623d3..96f4e98ceaa0b 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -830,6 +830,7 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); unsigned int regval; u8 fullScaleVol; + int ret;
if (mute) { /* Mute the headphone */ @@ -855,9 +856,18 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) } else { if (!cs42l42->stream_use) { /* SCLK must be running before codec unmute */ - if ((cs42l42->bclk < 11289600) && (cs42l42->sclk < 11289600)) + if ((cs42l42->bclk < 11289600) && (cs42l42->sclk < 11289600)) { snd_soc_component_update_bits(component, CS42L42_PLL_CTL1, CS42L42_PLL_START_MASK, 1); + ret = regmap_read_poll_timeout(cs42l42->regmap, + CS42L42_PLL_LOCK_STATUS, + regval, + (regval & 1), + CS42L42_PLL_LOCK_POLL_US, + CS42L42_PLL_LOCK_TIMEOUT_US); + if (ret < 0) + dev_warn(component->dev, "PLL failed to lock: %d\n", ret); + }
/* Mark SCLK as present, turn off internal oscillator */ regmap_multi_reg_write(cs42l42->regmap, cs42l42_to_sclk_seq, diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index a0c87f2326e69..7a5048dfade20 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -756,6 +756,8 @@ #define CS42L42_NUM_SUPPLIES 5 #define CS42L42_BOOT_TIME_US 3000 #define CS42L42_CLOCK_SWITCH_DELAY_US 150 +#define CS42L42_PLL_LOCK_POLL_US 250 +#define CS42L42_PLL_LOCK_TIMEOUT_US 1250
static const char *const cs42l42_supply_names[CS42L42_NUM_SUPPLIES] = { "VA",
The minimum value is 0x3f (-63dB), which also is mute
Signed-off-by: Lucas Tanure tanureal@opensource.cirrus.com --- sound/soc/codecs/cs42l42.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 96f4e98ceaa0b..39f742a83cd2c 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -401,7 +401,7 @@ static const struct regmap_config cs42l42_regmap = { };
static DECLARE_TLV_DB_SCALE(adc_tlv, -9600, 100, false); -static DECLARE_TLV_DB_SCALE(mixer_tlv, -6200, 100, false); +static DECLARE_TLV_DB_SCALE(mixer_tlv, -6300, 100, true);
static const char * const cs42l42_hpf_freq_text[] = { "1.86Hz", "120Hz", "235Hz", "466Hz" @@ -458,7 +458,7 @@ static const struct snd_kcontrol_new cs42l42_snd_controls[] = { CS42L42_DAC_HPF_EN_SHIFT, true, false), SOC_DOUBLE_R_TLV("Mixer Volume", CS42L42_MIXER_CHA_VOL, CS42L42_MIXER_CHB_VOL, CS42L42_MIXER_CH_VOL_SHIFT, - 0x3e, 1, mixer_tlv) + 0x3f, 1, mixer_tlv) };
static const struct snd_soc_dapm_widget cs42l42_dapm_widgets[] = {
participants (3)
-
kernel test robot
-
Lucas Tanure
-
Mark Brown