Merge series "Report jack and button detection + Capture Support" from Lucas Tanure <tanureal@opensource.cirrus.com>:

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: Fix Bitclock polarity inversion
  ASoC: cs42l42: Fix channel width support
  ASoC: cs42l42: Fix mixer volume control
  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: Provide finer control on playback path
  ASoC: cs42l42: Set clock source for both ways of stream
  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

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 | 435 +++++++++++++++++++++----------------
 sound/soc/codecs/cs42l42.h |  41 +++-
 2 files changed, 282 insertions(+), 194 deletions(-)

--
2.30.1
This commit is contained in:
Mark Brown 2021-03-09 19:04:34 +00:00
commit 64682e1b77
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
2 changed files with 241 additions and 110 deletions

View File

@ -25,6 +25,7 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
@ -461,64 +462,78 @@ static const struct snd_kcontrol_new cs42l42_snd_controls[] = {
0x3f, 1, mixer_tlv) 0x3f, 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[] = { static const struct snd_soc_dapm_widget cs42l42_dapm_widgets[] = {
/* Playback Path */
SND_SOC_DAPM_OUTPUT("HP"), SND_SOC_DAPM_OUTPUT("HP"),
SND_SOC_DAPM_AIF_IN("SDIN", NULL, 0, CS42L42_ASP_CLK_CFG, SND_SOC_DAPM_DAC("DAC", NULL, CS42L42_PWR_CTL1, CS42L42_HP_PDN_SHIFT, 1),
CS42L42_ASP_SCLK_EN_SHIFT, false), SND_SOC_DAPM_MIXER("MIXER", CS42L42_PWR_CTL1, CS42L42_MIXER_PDN_SHIFT, 1, NULL, 0),
SND_SOC_DAPM_OUT_DRV_E("HPDRV", SND_SOC_NOPM, 0, SND_SOC_DAPM_AIF_IN("SDIN1", NULL, 0, CS42L42_ASP_RX_DAI0_EN, CS42L42_ASP_RX0_CH1_SHIFT, 0),
0, NULL, 0, cs42l42_hpdrv_evt, SND_SOC_DAPM_AIF_IN("SDIN2", NULL, 1, CS42L42_ASP_RX_DAI0_EN, CS42L42_ASP_RX0_CH2_SHIFT, 0),
SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_PRE_PMD) /* 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),
}; };
static const struct snd_soc_dapm_route cs42l42_audio_map[] = { static const struct snd_soc_dapm_route cs42l42_audio_map[] = {
{"SDIN", NULL, "Playback"}, /* Playback Path */
{"HPDRV", NULL, "SDIN"}, {"HP", NULL, "DAC"},
{"HP", NULL, "HPDRV"} {"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"},
/* 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) static int cs42l42_component_probe(struct snd_soc_component *component)
{ {
struct cs42l42_private *cs42l42 = struct cs42l42_private *cs42l42 =
(struct cs42l42_private *)snd_soc_component_get_drvdata(component); (struct cs42l42_private *)snd_soc_component_get_drvdata(component);
struct snd_soc_card *crd = component->card;
int ret = 0;
cs42l42->component = component; cs42l42->component = component;
return 0; 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(component->dev, "Cannot create CS42L42 Headset: %d\n", ret);
return ret;
} }
static const struct snd_soc_component_driver soc_component_dev_cs42l42 = { static const struct snd_soc_component_driver soc_component_dev_cs42l42 = {
@ -534,6 +549,24 @@ static const struct snd_soc_component_driver soc_component_dev_cs42l42 = {
.non_legacy_dai_naming = 1, .non_legacy_dai_naming = 1,
}; };
/* 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 { struct cs42l42_pll_params {
u32 sclk; u32 sclk;
u8 mclk_div; u8 mclk_div;
@ -573,10 +606,16 @@ static int cs42l42_pll_config(struct snd_soc_component *component)
{ {
struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
int i; int i;
u32 clk;
u32 fsync; u32 fsync;
if (!cs42l42->sclk)
clk = cs42l42->bclk;
else
clk = cs42l42->sclk;
for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) { 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 */ /* Configure the internal sample rate */
snd_soc_component_update_bits(component, CS42L42_MCLK_CTL, snd_soc_component_update_bits(component, CS42L42_MCLK_CTL,
CS42L42_INTERNAL_FS_MASK, CS42L42_INTERNAL_FS_MASK,
@ -596,12 +635,12 @@ static int cs42l42_pll_config(struct snd_soc_component *component)
(pll_ratio_table[i].mclk_div << (pll_ratio_table[i].mclk_div <<
CS42L42_MCLKDIV_SHIFT)); CS42L42_MCLKDIV_SHIFT));
/* Set up the LRCLK */ /* Set up the LRCLK */
fsync = cs42l42->sclk / cs42l42->srate; fsync = clk / cs42l42->srate;
if (((fsync * cs42l42->srate) != cs42l42->sclk) if (((fsync * cs42l42->srate) != clk)
|| ((fsync % 2) != 0)) { || ((fsync % 2) != 0)) {
dev_err(component->dev, dev_err(component->dev,
"Unsupported sclk %d/sample rate %d\n", "Unsupported sclk %d/sample rate %d\n",
cs42l42->sclk, clk,
cs42l42->srate); cs42l42->srate);
return -EINVAL; return -EINVAL;
} }
@ -768,12 +807,25 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream,
{ {
struct snd_soc_component *component = dai->component; struct snd_soc_component *component = dai->component;
struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(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 width = (params_width(params) / 8) - 1;
unsigned int val = 0; unsigned int val = 0;
cs42l42->srate = params_rate(params); cs42l42->srate = params_rate(params);
cs42l42->bclk = snd_soc_params_to_bclk(params);
switch(substream->stream) { 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: case SNDRV_PCM_STREAM_PLAYBACK:
val |= width << CS42L42_ASP_RX_CH_RES_SHIFT; val |= width << CS42L42_ASP_RX_CH_RES_SHIFT;
/* channel 1 on low LRCLK */ /* channel 1 on low LRCLK */
@ -804,52 +856,73 @@ static int cs42l42_set_sysclk(struct snd_soc_dai *dai,
return 0; 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; struct snd_soc_component *component = dai->component;
struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
unsigned int regval; unsigned int regval;
u8 fullScaleVol; u8 fullScaleVol;
int ret;
if (mute) { 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 */ /* Mute the headphone */
snd_soc_component_update_bits(component, CS42L42_HP_CTL, if (stream == SNDRV_PCM_STREAM_PLAYBACK)
CS42L42_HP_ANA_AMUTE_MASK | snd_soc_component_update_bits(component, CS42L42_HP_CTL,
CS42L42_HP_ANA_BMUTE_MASK, CS42L42_HP_ANA_AMUTE_MASK |
CS42L42_HP_ANA_AMUTE_MASK | CS42L42_HP_ANA_BMUTE_MASK,
CS42L42_HP_ANA_BMUTE_MASK); CS42L42_HP_ANA_AMUTE_MASK |
} else { CS42L42_HP_ANA_BMUTE_MASK);
snd_soc_component_update_bits(component, CS42L42_PLL_CTL1,
CS42L42_PLL_START_MASK, cs42l42->stream_use &= ~(1 << stream);
1 << CS42L42_PLL_START_SHIFT); if(!cs42l42->stream_use) {
/* Read the headphone load */ /*
regval = snd_soc_component_read(component, CS42L42_LOAD_DET_RCSTAT); * Switch to the internal oscillator.
if (((regval & CS42L42_RLA_STAT_MASK) >> * SCLK must remain running until after this clock switch.
CS42L42_RLA_STAT_SHIFT) == CS42L42_RLA_STAT_15_OHM) { * Without a source of clock the I2C bus doesn't work.
fullScaleVol = CS42L42_HP_FULL_SCALE_VOL_MASK; */
} else { regmap_multi_reg_write(cs42l42->regmap, cs42l42_to_osc_seq,
fullScaleVol = 0; ARRAY_SIZE(cs42l42_to_osc_seq));
snd_soc_component_update_bits(component, CS42L42_PLL_CTL1,
CS42L42_PLL_START_MASK, 0);
} }
} else {
if (!cs42l42->stream_use) {
/* SCLK must be running before codec unmute */
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);
}
/* Un-mute the headphone, set the full scale volume flag */ /* Mark SCLK as present, turn off internal oscillator */
snd_soc_component_update_bits(component, CS42L42_HP_CTL, regmap_multi_reg_write(cs42l42->regmap, cs42l42_to_sclk_seq,
CS42L42_HP_ANA_AMUTE_MASK | ARRAY_SIZE(cs42l42_to_sclk_seq));
CS42L42_HP_ANA_BMUTE_MASK | }
CS42L42_HP_FULL_SCALE_VOL_MASK, fullScaleVol); cs42l42->stream_use |= 1 << stream;
/* Mark SCLK as present, turn off internal oscillator */ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
snd_soc_component_update_bits(component, CS42L42_OSC_SWITCH, /* Read the headphone load */
CS42L42_SCLK_PRESENT_MASK, regval = snd_soc_component_read(component, CS42L42_LOAD_DET_RCSTAT);
CS42L42_SCLK_PRESENT_MASK); 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);
}
} }
return 0; return 0;
@ -864,8 +937,7 @@ static const struct snd_soc_dai_ops cs42l42_ops = {
.hw_params = cs42l42_pcm_hw_params, .hw_params = cs42l42_pcm_hw_params,
.set_fmt = cs42l42_set_dai_fmt, .set_fmt = cs42l42_set_dai_fmt,
.set_sysclk = cs42l42_set_sysclk, .set_sysclk = cs42l42_set_sysclk,
.mute_stream = cs42l42_mute, .mute_stream = cs42l42_mute_stream,
.no_capture_mute = 1,
}; };
static struct snd_soc_dai_driver cs42l42_dai = { static struct snd_soc_dai_driver cs42l42_dai = {
@ -884,6 +956,8 @@ static struct snd_soc_dai_driver cs42l42_dai = {
.rates = SNDRV_PCM_RATE_8000_192000, .rates = SNDRV_PCM_RATE_8000_192000,
.formats = CS42L42_FORMATS, .formats = CS42L42_FORMATS,
}, },
.symmetric_rate = 1,
.symmetric_sample_bits = 1,
.ops = &cs42l42_ops, .ops = &cs42l42_ops,
}; };
@ -1169,7 +1243,7 @@ static void cs42l42_cancel_hs_type_detect(struct cs42l42_private *cs42l42)
(3 << CS42L42_HSDET_AUTO_TIME_SHIFT)); (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; int bias_level;
unsigned int detect_status; unsigned int detect_status;
@ -1212,17 +1286,24 @@ static void cs42l42_handle_button_press(struct cs42l42_private *cs42l42)
switch (bias_level) { switch (bias_level) {
case 1: /* Function C button press */ case 1: /* Function C button press */
bias_level = SND_JACK_BTN_2;
dev_dbg(cs42l42->component->dev, "Function C button press\n"); dev_dbg(cs42l42->component->dev, "Function C button press\n");
break; break;
case 2: /* Function B button press */ case 2: /* Function B button press */
bias_level = SND_JACK_BTN_1;
dev_dbg(cs42l42->component->dev, "Function B button press\n"); dev_dbg(cs42l42->component->dev, "Function B button press\n");
break; break;
case 3: /* Function D button press */ case 3: /* Function D button press */
bias_level = SND_JACK_BTN_3;
dev_dbg(cs42l42->component->dev, "Function D button press\n"); dev_dbg(cs42l42->component->dev, "Function D button press\n");
break; break;
case 4: /* Function A button press */ case 4: /* Function A button press */
bias_level = SND_JACK_BTN_0;
dev_dbg(cs42l42->component->dev, "Function A button press\n"); dev_dbg(cs42l42->component->dev, "Function A button press\n");
break; break;
default:
bias_level = 0;
break;
} }
/* Set button detect level sensitivity back to default */ /* Set button detect level sensitivity back to default */
@ -1252,6 +1333,8 @@ static void cs42l42_handle_button_press(struct cs42l42_private *cs42l42)
(0 << CS42L42_M_HSBIAS_HIZ_SHIFT) | (0 << CS42L42_M_HSBIAS_HIZ_SHIFT) |
(1 << CS42L42_M_SHORT_RLS_SHIFT) | (1 << CS42L42_M_SHORT_RLS_SHIFT) |
(1 << CS42L42_M_SHORT_DET_SHIFT)); (1 << CS42L42_M_SHORT_DET_SHIFT));
return bias_level;
} }
struct cs42l42_irq_params { struct cs42l42_irq_params {
@ -1296,6 +1379,8 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
unsigned int current_plug_status; unsigned int current_plug_status;
unsigned int current_button_status; unsigned int current_button_status;
unsigned int i; unsigned int i;
int report = 0;
/* Read sticky registers to clear interurpt */ /* Read sticky registers to clear interurpt */
for (i = 0; i < ARRAY_SIZE(stickies); i++) { for (i = 0; i < ARRAY_SIZE(stickies); i++) {
@ -1322,9 +1407,20 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
if ((~masks[5]) & irq_params_table[5].mask) { if ((~masks[5]) & irq_params_table[5].mask) {
if (stickies[5] & CS42L42_HSDET_AUTO_DONE_MASK) { if (stickies[5] & CS42L42_HSDET_AUTO_DONE_MASK) {
cs42l42_process_hs_type_detect(cs42l42); cs42l42_process_hs_type_detect(cs42l42);
dev_dbg(component->dev, switch(cs42l42->hs_type){
"Auto detect done (%d)\n", case CS42L42_PLUG_CTIA:
cs42l42->hs_type); 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);
} }
} }
@ -1342,8 +1438,19 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
if (cs42l42->plug_state != CS42L42_TS_UNPLUG) { if (cs42l42->plug_state != CS42L42_TS_UNPLUG) {
cs42l42->plug_state = CS42L42_TS_UNPLUG; cs42l42->plug_state = CS42L42_TS_UNPLUG;
cs42l42_cancel_hs_type_detect(cs42l42); 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; break;
@ -1358,14 +1465,15 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
if (!(current_button_status & if (!(current_button_status &
CS42L42_M_HSBIAS_HIZ_MASK)) { CS42L42_M_HSBIAS_HIZ_MASK)) {
if (current_button_status & if (current_button_status & CS42L42_M_DETECT_TF_MASK) {
CS42L42_M_DETECT_TF_MASK) { dev_dbg(component->dev, "Button released\n");
dev_dbg(component->dev, report = 0;
"Button released\n"); } else if (current_button_status & CS42L42_M_DETECT_FT_MASK) {
} else if (current_button_status & report = cs42l42_handle_button_press(cs42l42);
CS42L42_M_DETECT_FT_MASK) {
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);
} }
} }
@ -1749,8 +1857,10 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
/* Reset the Device */ /* Reset the Device */
cs42l42->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, cs42l42->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
"reset", GPIOD_OUT_LOW); "reset", GPIOD_OUT_LOW);
if (IS_ERR(cs42l42->reset_gpio)) if (IS_ERR(cs42l42->reset_gpio)) {
return PTR_ERR(cs42l42->reset_gpio); ret = PTR_ERR(cs42l42->reset_gpio);
goto err_disable;
}
if (cs42l42->reset_gpio) { if (cs42l42->reset_gpio) {
dev_dbg(&i2c_client->dev, "Found reset GPIO\n"); dev_dbg(&i2c_client->dev, "Found reset GPIO\n");
@ -1784,13 +1894,13 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
dev_err(&i2c_client->dev, dev_err(&i2c_client->dev,
"CS42L42 Device ID (%X). Expected %X\n", "CS42L42 Device ID (%X). Expected %X\n",
devid, CS42L42_CHIP_ID); devid, CS42L42_CHIP_ID);
return ret; goto err_disable;
} }
ret = regmap_read(cs42l42->regmap, CS42L42_REVID, &reg); ret = regmap_read(cs42l42->regmap, CS42L42_REVID, &reg);
if (ret < 0) { if (ret < 0) {
dev_err(&i2c_client->dev, "Get Revision ID failed\n"); dev_err(&i2c_client->dev, "Get Revision ID failed\n");
return ret; goto err_disable;
} }
dev_info(&i2c_client->dev, dev_info(&i2c_client->dev,
@ -1816,7 +1926,7 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
if (i2c_client->dev.of_node) { if (i2c_client->dev.of_node) {
ret = cs42l42_handle_device_data(i2c_client, cs42l42); ret = cs42l42_handle_device_data(i2c_client, cs42l42);
if (ret != 0) if (ret != 0)
return ret; goto err_disable;
} }
/* Setup headset detection */ /* Setup headset detection */
@ -1842,8 +1952,9 @@ static int cs42l42_i2c_remove(struct i2c_client *i2c_client)
{ {
struct cs42l42_private *cs42l42 = i2c_get_clientdata(i2c_client); struct cs42l42_private *cs42l42 = i2c_get_clientdata(i2c_client);
/* Hold down reset */ devm_free_irq(&i2c_client->dev, i2c_client->irq, cs42l42);
gpiod_set_value_cansleep(cs42l42->reset_gpio, 0); pm_runtime_suspend(&i2c_client->dev);
pm_runtime_disable(&i2c_client->dev);
return 0; return 0;
} }

View File

@ -12,6 +12,8 @@
#ifndef __CS42L42_H__ #ifndef __CS42L42_H__
#define __CS42L42_H__ #define __CS42L42_H__
#include <sound/jack.h>
#define CS42L42_PAGE_REGISTER 0x00 /* Page Select Register */ #define CS42L42_PAGE_REGISTER 0x00 /* Page Select Register */
#define CS42L42_WIN_START 0x00 #define CS42L42_WIN_START 0x00
#define CS42L42_WIN_LEN 0x100 #define CS42L42_WIN_LEN 0x100
@ -683,8 +685,20 @@
/* Page 0x29 Serial Port TX Registers */ /* Page 0x29 Serial Port TX Registers */
#define CS42L42_ASP_TX_SZ_EN (CS42L42_PAGE_29 + 0x01) #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_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_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_MSB (CS42L42_PAGE_29 + 0x04)
#define CS42L42_ASP_TX_CH1_BIT_LSB (CS42L42_PAGE_29 + 0x05) #define CS42L42_ASP_TX_CH1_BIT_LSB (CS42L42_PAGE_29 + 0x05)
#define CS42L42_ASP_TX_HIZ_DLY_CFG (CS42L42_PAGE_29 + 0x06) #define CS42L42_ASP_TX_HIZ_DLY_CFG (CS42L42_PAGE_29 + 0x06)
@ -695,10 +709,10 @@
#define CS42L42_ASP_RX_DAI0_EN (CS42L42_PAGE_2A + 0x01) #define CS42L42_ASP_RX_DAI0_EN (CS42L42_PAGE_2A + 0x01)
#define CS42L42_ASP_RX0_CH_EN_SHIFT 2 #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_CH_EN_MASK (0xf << CS42L42_ASP_RX0_CH_EN_SHIFT)
#define CS42L42_ASP_RX0_CH1_EN 1 #define CS42L42_ASP_RX0_CH1_SHIFT 2
#define CS42L42_ASP_RX0_CH2_EN 2 #define CS42L42_ASP_RX0_CH2_SHIFT 3
#define CS42L42_ASP_RX0_CH3_EN 4 #define CS42L42_ASP_RX0_CH3_SHIFT 4
#define CS42L42_ASP_RX0_CH4_EN 8 #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_AP_RES (CS42L42_PAGE_2A + 0x02)
#define CS42L42_ASP_RX_DAI0_CH1_BIT_MSB (CS42L42_PAGE_2A + 0x03) #define CS42L42_ASP_RX_DAI0_CH1_BIT_MSB (CS42L42_PAGE_2A + 0x03)
@ -741,6 +755,9 @@
#define CS42L42_NUM_SUPPLIES 5 #define CS42L42_NUM_SUPPLIES 5
#define CS42L42_BOOT_TIME_US 3000 #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] = { static const char *const cs42l42_supply_names[CS42L42_NUM_SUPPLIES] = {
"VA", "VA",
@ -756,6 +773,8 @@ struct cs42l42_private {
struct regulator_bulk_data supplies[CS42L42_NUM_SUPPLIES]; struct regulator_bulk_data supplies[CS42L42_NUM_SUPPLIES];
struct gpio_desc *reset_gpio; struct gpio_desc *reset_gpio;
struct completion pdn_done; struct completion pdn_done;
struct snd_soc_jack jack;
int bclk;
u32 sclk; u32 sclk;
u32 srate; u32 srate;
u8 plug_state; u8 plug_state;
@ -768,6 +787,7 @@ struct cs42l42_private {
u8 bias_thresholds[CS42L42_NUM_BIASES]; u8 bias_thresholds[CS42L42_NUM_BIASES];
u8 hs_bias_ramp_rate; u8 hs_bias_ramp_rate;
u8 hs_bias_ramp_time; u8 hs_bias_ramp_time;
u8 stream_use;
}; };
#endif /* __CS42L42_H__ */ #endif /* __CS42L42_H__ */