ASoC: wcd9335: add mbhc support

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
This commit is contained in:
Srinivas Kandagatla 2018-08-31 15:46:15 +01:00 committed by Jami Kettunen
parent 8be99e829f
commit 2e4e748dfb

View file

@ -11,6 +11,7 @@
#include <linux/regulator/consumer.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <sound/jack.h>
#include <linux/kernel.h>
#include <linux/slimbus.h>
#include <sound/soc.h>
@ -86,12 +87,18 @@
#define WCD9335_DEC_PWR_LVL_HP 0x04
#define WCD9335_DEC_PWR_LVL_DF 0x00
#define WCD9335_MBHC_MAX_BUTTONS (8)
#define WCD9335_SLIM_RX_CH(p) \
{.port = p + WCD9335_RX_START, .shift = p,}
#define WCD9335_SLIM_TX_CH(p) \
{.port = p, .shift = p,}
static int btn_mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4;
static int hs_jack_mask = SND_JACK_HEADPHONE | SND_JACK_HEADSET;
/* vout step value */
#define WCD9335_CALCULATE_VOUT_D(req_mv) (((req_mv - 650) * 10) / 25)
@ -319,6 +326,16 @@ struct wcd9335_codec {
u32 num_rx_port;
u32 num_tx_port;
struct snd_soc_jack *jack;
bool hphl_jack_type_normally_open;
bool gnd_jack_type_normally_open;
bool mbhc_btn_enabled;
int mbhc_btn0_released;
bool detect_accessory_type;
int accessory_type;
/* Voltage threshold for button detection */
u32 vref_btn[WCD9335_MBHC_MAX_BUTTONS];
int sido_input_src;
enum wcd9335_sido_voltage sido_voltage;
@ -2814,7 +2831,6 @@ static int wcd9335_codec_enable_dec(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_POST_PMU:
snd_soc_component_update_bits(comp, hpf_gate_reg, 0x01, 0x00);
if (decimator == 0) {
snd_soc_component_write(comp,
WCD9335_MBHC_ZDET_RAMP_CTL, 0x83);
@ -2825,7 +2841,6 @@ static int wcd9335_codec_enable_dec(struct snd_soc_dapm_widget *w,
snd_soc_component_write(comp,
WCD9335_MBHC_ZDET_RAMP_CTL, 0x03);
}
snd_soc_component_update_bits(comp, hpf_gate_reg,
0x01, 0x01);
snd_soc_component_update_bits(comp, tx_vol_ctl_reg,
@ -3963,6 +3978,131 @@ static int wcd9335_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
return 0;
}
static irqreturn_t wcd9335_mbhc_sw_irq(int irq, void *data)
{
struct wcd9335_codec *wcd = data;
struct snd_soc_component *component = wcd->component;
bool ins = false;
if (snd_soc_component_read32(component, WCD9335_ANA_MBHC_MECH) &
WCD9335_MBHC_MECH_DETECT_TYPE_MASK)
ins = true;
/* Set the detection type appropriately */
snd_soc_component_update_bits(component, WCD9335_ANA_MBHC_MECH,
WCD9335_MBHC_MECH_DETECT_TYPE_MASK,
(!ins << WCD9335_MBHC_MECH_DETECT_TYPE_SHIFT));
if (ins) { /* hs insertion */
u32 btndet_curr_src;
/*
* If no micbias is enabled, then enable 100uA internal
* current source for Button detection
*/
if (snd_soc_component_read32(component, WCD9335_ANA_MICB2) &
WCD9335_ANA_MICB2_ENABLE)
btndet_curr_src = WCD9335_ANA_MBHC_BD_ISRC_OFF;
else
btndet_curr_src = WCD9335_ANA_MBHC_BD_ISRC_100UA;
snd_soc_component_update_bits(component,
WCD9335_ANA_MBHC_ELECT,
WCD9335_ANA_MBHC_BD_ISRC_CTL_MASK,
btndet_curr_src);
/*
* if only a btn0 press event is receive just before
* insert event then its a 3 pole headphone else if
* both press and release event received then its
* a headset.
*/
if (wcd->mbhc_btn0_released) {
snd_soc_jack_report(wcd->jack,
SND_JACK_HEADSET, hs_jack_mask);
wcd->accessory_type = SND_JACK_HEADSET;
} else {
snd_soc_jack_report(wcd->jack,
SND_JACK_HEADPHONE, hs_jack_mask);
wcd->accessory_type = SND_JACK_HEADPHONE;
}
wcd->detect_accessory_type = false;
} else { /* removal */
snd_soc_jack_report(wcd->jack, 0, hs_jack_mask);
wcd->detect_accessory_type = true;
wcd->mbhc_btn0_released = false;
}
return IRQ_HANDLED;
}
static irqreturn_t wcd9335_mbhc_btn_press_irq(int irq, void *data)
{
struct wcd9335_codec *wcd = data;
struct snd_soc_component *comp = wcd->component;
u32 btn_result, result;
/* do not handle any button events for headset without buttons */
if (wcd->accessory_type == SND_JACK_HEADPHONE)
return IRQ_HANDLED;
result = snd_soc_component_read32(comp, WCD9335_ANA_MBHC_RESULT_3);
btn_result = result & WCD9335_MBHC_BTN_RESULT_MASK;
switch (btn_result) {
case 0xf:
snd_soc_jack_report(wcd->jack, SND_JACK_BTN_4, btn_mask);
break;
case 0x4:
snd_soc_jack_report(wcd->jack, SND_JACK_BTN_4, btn_mask);
break;
case 0x3:
snd_soc_jack_report(wcd->jack, SND_JACK_BTN_3, btn_mask);
break;
case 0x2:
snd_soc_jack_report(wcd->jack, SND_JACK_BTN_2, btn_mask);
break;
case 0x1:
snd_soc_jack_report(wcd->jack, SND_JACK_BTN_1, btn_mask);
break;
case 0x0:
/* handle BTN_0 specially for type detection */
if (!wcd->detect_accessory_type)
snd_soc_jack_report(wcd->jack,
SND_JACK_BTN_0, btn_mask);
break;
default:
dev_err(comp->dev,
"Unexpected button press result (%x)", btn_result);
break;
}
return IRQ_HANDLED;
}
static irqreturn_t wcd9335_mbhc_bt_rel_irq(int irq, void *data)
{
struct wcd9335_codec *wcd = data;
if (wcd->detect_accessory_type) {
u32 result = snd_soc_component_read32(wcd->component,
WCD9335_ANA_MBHC_RESULT_3);
/* check if its BTN0 thats released */
if (!(result & WCD9335_MBHC_BTN_RESULT_MASK))
wcd->mbhc_btn0_released = true;
} else {
if (wcd->accessory_type != SND_JACK_HEADPHONE)
snd_soc_jack_report(wcd->jack, 0, btn_mask);
}
return IRQ_HANDLED;
}
static irqreturn_t wcd9335_slimbus_irq(int irq, void *data)
{
struct wcd9335_codec *wcd = data;
@ -4037,11 +4177,95 @@ static irqreturn_t wcd9335_slimbus_irq(int irq, void *data)
return ret;
}
static void wcd9335_program_btn_threshold(struct wcd9335_codec *wcd)
{
int i, vth;
for (i = 0; i < WCD9335_MBHC_MAX_BUTTONS; i++) {
vth = ((wcd->vref_btn[i] * 2) / 25) & 0x3F;
snd_soc_component_update_bits(wcd->component,
WCD9335_ANA_MBHC_BTN0 + i,
0xFC, vth << 2);
}
}
static void wcd9335_mbhc_initialise(struct wcd9335_codec *wcd)
{
struct snd_soc_component *comp = wcd->component;
u32 plug_type = 0;
snd_soc_component_update_bits(comp, WCD9335_MBHC_PLUG_DETECT_CTL,
WCD9335_MBHC_HSDET_PULLUP_CTL_MASK,
WCD9335_MBHC_HSDET_PULLUP_CTL_1_2P0_UA);
if (wcd->hphl_jack_type_normally_open)
plug_type |= WCD9335_MBHC_HPHL_PLUG_TYPE_NO;
if (wcd->gnd_jack_type_normally_open)
plug_type |= WCD9335_MBHC_GND_PLUG_TYPE_NO;
snd_soc_component_write(wcd->component, WCD9335_ANA_MBHC_MECH,
plug_type |
WCD9335_MBHC_L_DET_EN |
WCD9335_MBHC_HSL_PULLUP_COMP_EN |
WCD9335_MBHC_HPHL_100K_TO_GND_EN);
/* Insertion debounce set to 96ms */
snd_soc_component_write(wcd->component,
WCD9335_MBHC_PLUG_DETECT_CTL,
WCD9335_MBHC_DBNC_TIMER_INSREM_DBNC_T_96_MS|
WCD9335_MBHC_HSDET_PULLUP_CTL_1_2P0_UA);
/* Button Debounce set to 16ms */
snd_soc_component_update_bits(wcd->component, WCD9335_MBHC_CTL_1,
WCD9335_MBHC_BTN_DBNC_MASK,
WCD9335_MBHC_BTN_DBNC_T_16_MS);
/* enable bias distribution control */
snd_soc_component_update_bits(comp, WCD9335_ANA_MBHC_ELECT,
WCD9335_ANA_MBHC_BIAS_EN_MASK,
WCD9335_ANA_MBHC_BIAS_EN);
snd_soc_component_update_bits(wcd->component,
WCD9335_ANA_MBHC_ELECT,
WCD9335_ANA_MBHC_BD_ISRC_CTL_MASK,
WCD9335_ANA_MBHC_BD_ISRC_100UA);
/* enable MBHC clock */
snd_soc_component_update_bits(wcd->component, WCD9335_MBHC_CTL_1,
WCD9335_MBHC_CTL_RCO_EN_MASK,
WCD9335_MBHC_CTL_RCO_EN);
snd_soc_component_update_bits(wcd->component, WCD9335_MBHC_CTL_2,
WCD9335_MBHC_HS_VREF_CTL_MASK,
WCD9335_MBHC_HS_VREF_1P5_V);
/* program HS_VREF value */
wcd9335_program_btn_threshold(wcd);
/* Start FSM */
snd_soc_component_update_bits(wcd->component, WCD9335_ANA_MBHC_ELECT,
BIT(7), BIT(7));
wcd->mbhc_btn0_released = false;
wcd->detect_accessory_type = true;
}
static struct wcd9335_irq wcd9335_irqs[] = {
{
.irq = WCD9335_IRQ_SLIMBUS,
.handler = wcd9335_slimbus_irq,
.name = "SLIM Slave",
}, {
.irq = WCD9335_IRQ_MBHC_SW_DET,
.handler = wcd9335_mbhc_sw_irq,
.name = "Headset Mech Insert Removal",
}, {
.irq = WCD9335_IRQ_MBHC_BUTTON_PRESS_DET,
.handler = wcd9335_mbhc_btn_press_irq,
.name = "Headset Button Press",
}, {
.irq = WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET,
.handler = wcd9335_mbhc_bt_rel_irq,
.name = "Headset Button Release",
},
};
@ -4850,6 +5074,7 @@ static void wcd9335_codec_init(struct snd_soc_component *component)
wcd9335_codec_reg_init[i].val);
wcd9335_enable_efuse_sensing(component);
wcd9335_mbhc_initialise(wcd);
}
static int wcd9335_codec_probe(struct snd_soc_component *component)
@ -4914,10 +5139,21 @@ static int wcd9335_codec_set_sysclk(struct snd_soc_component *comp,
return clk_set_rate(wcd->mclk, freq);
}
static int wcd9335_codec_set_jack(struct snd_soc_component *comp,
struct snd_soc_jack *jack, void *data)
{
struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev);
wcd->jack = jack;
return 0;
}
static const struct snd_soc_component_driver wcd9335_component_drv = {
.probe = wcd9335_codec_probe,
.remove = wcd9335_codec_remove,
.set_sysclk = wcd9335_codec_set_sysclk,
.set_jack = wcd9335_codec_set_jack,
.controls = wcd9335_snd_controls,
.num_controls = ARRAY_SIZE(wcd9335_snd_controls),
.dapm_widgets = wcd9335_dapm_widgets,
@ -4926,10 +5162,39 @@ static const struct snd_soc_component_driver wcd9335_component_drv = {
.num_dapm_routes = ARRAY_SIZE(wcd9335_audio_map),
};
static void of_parse_mbhc_data(struct device *dev, struct wcd9335_codec *wcd)
{
int rval;
if (of_property_read_bool(dev->of_node,
"qcom,hphl-jack-type-normally-open"))
wcd->hphl_jack_type_normally_open = true;
else
wcd->hphl_jack_type_normally_open = false;
if (of_property_read_bool(dev->of_node,
"qcom,gnd-jack-type-normally-open"))
wcd->gnd_jack_type_normally_open = true;
else
wcd->gnd_jack_type_normally_open = false;
wcd->mbhc_btn_enabled = true;
rval = of_property_read_u32_array(dev->of_node,
"qcom,mbhc-vthreshold",
&wcd->vref_btn[0],
WCD9335_MBHC_MAX_BUTTONS);
if (rval < 0) {
wcd->mbhc_btn_enabled = false;
dev_err(dev, "MBHC btn detection disabled\n");
}
}
static int wcd9335_probe(struct wcd9335_codec *wcd)
{
struct device *dev = wcd->dev;
of_parse_mbhc_data(dev, wcd);
memcpy(wcd->rx_chs, wcd9335_rx_chs, sizeof(wcd9335_rx_chs));
memcpy(wcd->tx_chs, wcd9335_tx_chs, sizeof(wcd9335_tx_chs));
@ -5017,6 +5282,33 @@ static const struct regmap_irq wcd9335_codec_irqs[] = {
.type_reg_mask = BIT(0),
},
},
[WCD9335_IRQ_MBHC_SW_DET] = {
.reg_offset = 1,
.mask = BIT(0),
.type = {
.type_reg_offset = 1,
.types_supported = IRQ_TYPE_EDGE_BOTH,
.type_reg_mask = BIT(0),
},
},
[WCD9335_IRQ_MBHC_BUTTON_PRESS_DET] = {
.reg_offset = 1,
.mask = BIT(2),
.type = {
.type_reg_offset = 1,
.types_supported = IRQ_TYPE_EDGE_BOTH,
.type_reg_mask = BIT(2),
},
},
[WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET] = {
.reg_offset = 1,
.mask = BIT(3),
.type = {
.type_reg_offset = 1,
.types_supported = IRQ_TYPE_EDGE_BOTH,
.type_reg_mask = BIT(3),
},
}
};
static const struct regmap_irq_chip wcd9335_regmap_irq1_chip = {