Commit 0c105997 authored by Neil Armstrong's avatar Neil Armstrong Committed by Mark Brown

ASoC: codec: wcd-mbhc-v2: add support when connected behind an USB-C audio mux

When the WCD codec is connected behind an USB-C audio mux,
plug/unplug events, clock control, pull-up and threshold are
different.
Add a typec_analog_mux config enabling those changes and add
two callbacks to trigger plug/unplug events from USB-C events.
Signed-off-by: default avatarNeil Armstrong <neil.armstrong@linaro.org>
Link: https://msgid.link/r/20231219-topic-sm8650-upstream-wcd939x-codec-v4-3-1c3bbff2d7ab@linaro.orgSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent edf647d1
......@@ -16,6 +16,7 @@
#define HS_DETECT_PLUG_TIME_MS (3 * 1000)
#define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250
#define GND_MIC_SWAP_THRESHOLD 4
#define GND_MIC_USBC_SWAP_THRESHOLD 2
#define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100
#define HPHL_CROSS_CONN_THRESHOLD 100
#define HS_VREF_MIN_VAL 1400
......@@ -52,12 +53,15 @@ struct wcd_mbhc {
struct wcd_mbhc_field *fields;
/* Delayed work to report long button press */
struct delayed_work mbhc_btn_dwork;
/* Work to handle plug report */
struct work_struct mbhc_plug_detect_work;
/* Work to correct accessory type */
struct work_struct correct_plug_swch;
struct mutex lock;
int buttons_pressed;
u32 hph_status; /* track headhpone status */
u8 current_plug;
unsigned int swap_thr;
bool is_btn_press;
bool in_swch_irq_handler;
bool hs_detect_work_stop;
......@@ -506,14 +510,13 @@ static void wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc *mbhc)
}
}
static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
static void mbhc_plug_detect_fn(struct work_struct *work)
{
struct snd_soc_component *component;
struct wcd_mbhc *mbhc = container_of(work, struct wcd_mbhc, mbhc_plug_detect_work);
struct snd_soc_component *component = mbhc->component;
enum snd_jack_types jack_type;
struct wcd_mbhc *mbhc = data;
bool detection_type;
component = mbhc->component;
mutex_lock(&mbhc->lock);
mbhc->in_swch_irq_handler = true;
......@@ -576,9 +579,51 @@ static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
exit:
mbhc->in_swch_irq_handler = false;
mutex_unlock(&mbhc->lock);
}
static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
{
struct wcd_mbhc *mbhc = data;
if (!mbhc->cfg->typec_analog_mux)
schedule_work(&mbhc->mbhc_plug_detect_work);
return IRQ_HANDLED;
}
int wcd_mbhc_typec_report_unplug(struct wcd_mbhc *mbhc)
{
if (!mbhc || !mbhc->cfg->typec_analog_mux)
return -EINVAL;
if (mbhc->mbhc_cb->clk_setup)
mbhc->mbhc_cb->clk_setup(mbhc->component, false);
wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 0);
wcd_mbhc_write_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE, 0);
schedule_work(&mbhc->mbhc_plug_detect_work);
return 0;
}
EXPORT_SYMBOL_GPL(wcd_mbhc_typec_report_unplug);
int wcd_mbhc_typec_report_plug(struct wcd_mbhc *mbhc)
{
if (!mbhc || !mbhc->cfg->typec_analog_mux)
return -EINVAL;
if (mbhc->mbhc_cb->clk_setup)
mbhc->mbhc_cb->clk_setup(mbhc->component, true);
wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1);
schedule_work(&mbhc->mbhc_plug_detect_work);
return 0;
}
EXPORT_SYMBOL_GPL(wcd_mbhc_typec_report_plug);
static int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc)
{
int mask = 0;
......@@ -725,14 +770,23 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
mutex_lock(&mbhc->lock);
/* enable HS detection */
if (mbhc->cfg->typec_analog_mux)
mbhc->swap_thr = GND_MIC_USBC_SWAP_THRESHOLD;
else
mbhc->swap_thr = GND_MIC_SWAP_THRESHOLD;
/* setup HS detection */
if (mbhc->mbhc_cb->hph_pull_up_control_v2)
mbhc->mbhc_cb->hph_pull_up_control_v2(component,
HS_PULLUP_I_DEFAULT);
mbhc->cfg->typec_analog_mux ?
HS_PULLUP_I_OFF : HS_PULLUP_I_DEFAULT);
else if (mbhc->mbhc_cb->hph_pull_up_control)
mbhc->mbhc_cb->hph_pull_up_control(component, I_DEFAULT);
mbhc->mbhc_cb->hph_pull_up_control(component,
mbhc->cfg->typec_analog_mux ?
I_OFF : I_DEFAULT);
else
wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_CTRL, 3);
wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_CTRL,
mbhc->cfg->typec_analog_mux ? 0 : 3);
wcd_mbhc_write_field(mbhc, WCD_MBHC_HPHL_PLUG_TYPE, mbhc->cfg->hphl_swh);
wcd_mbhc_write_field(mbhc, WCD_MBHC_GND_PLUG_TYPE, mbhc->cfg->gnd_swh);
......@@ -741,10 +795,18 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
mbhc->mbhc_cb->mbhc_gnd_det_ctrl(component, true);
wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1);
wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1);
/* Plug detect is triggered manually if analog goes through USBCC */
if (mbhc->cfg->typec_analog_mux)
wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 0);
else
wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1);
/* Insertion debounce set to 96ms */
wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 6);
if (mbhc->cfg->typec_analog_mux)
/* Insertion debounce set to 48ms */
wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 4);
else
/* Insertion debounce set to 96ms */
wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 6);
/* Button Debounce set to 16ms */
wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_DBNC, 2);
......@@ -753,7 +815,8 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
mbhc->mbhc_cb->mbhc_bias(component, true);
/* enable MBHC clock */
if (mbhc->mbhc_cb->clk_setup)
mbhc->mbhc_cb->clk_setup(component, true);
mbhc->mbhc_cb->clk_setup(component,
mbhc->cfg->typec_analog_mux ? false : true);
/* program HS_VREF value */
wcd_program_hs_vref(mbhc);
......@@ -1115,7 +1178,7 @@ static void wcd_correct_swch_plug(struct work_struct *work)
do {
cross_conn = wcd_check_cross_conn(mbhc);
try++;
} while (try < GND_MIC_SWAP_THRESHOLD);
} while (try < mbhc->swap_thr);
if (cross_conn > 0) {
plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
......@@ -1183,7 +1246,7 @@ static void wcd_correct_swch_plug(struct work_struct *work)
cross_conn = wcd_check_cross_conn(mbhc);
if (cross_conn > 0) { /* cross-connection */
pt_gnd_mic_swap_cnt++;
if (pt_gnd_mic_swap_cnt < GND_MIC_SWAP_THRESHOLD)
if (pt_gnd_mic_swap_cnt < mbhc->swap_thr)
continue;
else
plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
......@@ -1194,7 +1257,7 @@ static void wcd_correct_swch_plug(struct work_struct *work)
} else /* Error if (cross_conn < 0) */
continue;
if (pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) {
if (pt_gnd_mic_swap_cnt == mbhc->swap_thr) {
/* US_EU gpio present, flip switch */
if (mbhc->cfg->swap_gnd_mic) {
if (mbhc->cfg->swap_gnd_mic(component, true))
......@@ -1473,6 +1536,7 @@ struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
mutex_init(&mbhc->lock);
INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
INIT_WORK(&mbhc->mbhc_plug_detect_work, mbhc_plug_detect_fn);
ret = request_threaded_irq(mbhc->intr_ids->mbhc_sw_intr, NULL,
wcd_mbhc_mech_plug_detect_irq,
......@@ -1562,6 +1626,7 @@ void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
mutex_lock(&mbhc->lock);
wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
cancel_work_sync(&mbhc->mbhc_plug_detect_work);
mutex_unlock(&mbhc->lock);
kfree(mbhc);
......
......@@ -193,6 +193,7 @@ struct wcd_mbhc_config {
int v_hs_max;
int num_btn;
bool mono_stero_detection;
bool typec_analog_mux;
bool (*swap_gnd_mic)(struct snd_soc_component *component, bool active);
bool hs_ext_micbias;
bool gnd_det_en;
......@@ -273,6 +274,8 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg,
void wcd_mbhc_stop(struct wcd_mbhc *mbhc);
void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type);
int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc);
int wcd_mbhc_typec_report_plug(struct wcd_mbhc *mbhc);
int wcd_mbhc_typec_report_unplug(struct wcd_mbhc *mbhc);
struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
const struct wcd_mbhc_cb *mbhc_cb,
const struct wcd_mbhc_intr *mbhc_cdc_intr_ids,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment