ASoC: codec: wcd-mbhc-v2: add support when connected behind an USB-C audio mux
authorNeil Armstrong <neil.armstrong@linaro.org>
Tue, 19 Dec 2023 12:45:36 +0000 (13:45 +0100)
committerMark Brown <broonie@kernel.org>
Mon, 22 Jan 2024 00:06:17 +0000 (00:06 +0000)
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: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://msgid.link/r/20231219-topic-sm8650-upstream-wcd939x-codec-v4-3-1c3bbff2d7ab@linaro.org
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/wcd-mbhc-v2.c
sound/soc/codecs/wcd-mbhc-v2.h

index 5da1934527f347019bdc71bff8f00ea7e93307ca..0e6218ed0e5e013882a3d77b26c8f120ee3bad0f 100644 (file)
@@ -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 @@ correct_plug_type:
                        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 @@ correct_plug_type:
                        } 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);
index 006118f3e81f2a4c44c6f7375c5f64587401251a..df68e99c81a33766591ccfda5a31303715db2ab4 100644 (file)
@@ -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,