Loading asoc/codecs/wcd-mbhc-v2.c +47 −252 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include <linux/input.h> #include <linux/firmware.h> #include <linux/completion.h> #include <linux/soc/qcom/fsa4480-i2c.h> #include <sound/soc.h> #include <sound/jack.h> #include "msm-cdc-pinctrl.h" Loading Loading @@ -958,6 +959,9 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc) mbhc->extn_cable_hph_rem = false; wcd_mbhc_report_plug(mbhc, 0, jack_type); if (mbhc->mbhc_cfg->enable_usbc_analog) WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0); } else if (!detection_type) { /* Disable external voltage source to micbias if present */ if (mbhc->mbhc_cb->enable_mb_source) Loading Loading @@ -1291,8 +1295,8 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) * by an external source */ if (mbhc->mbhc_cfg->enable_usbc_analog) { mbhc->hphl_swh = 1; mbhc->gnd_swh = 1; mbhc->hphl_swh = 0; mbhc->gnd_swh = 0; if (mbhc->mbhc_cb->hph_pull_up_control_v2) mbhc->mbhc_cb->hph_pull_up_control_v2(codec, Loading @@ -1310,6 +1314,15 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) if (mbhc->mbhc_cfg->gnd_det_en && mbhc->mbhc_cb->mbhc_gnd_det_ctrl) mbhc->mbhc_cb->mbhc_gnd_det_ctrl(codec, true); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1); /* * Disable L_DET for USB-C analog audio to avoid spurious interrupts * when a non-audio accessory is inserted. L_DET_EN sets to 1 when FSA * I2C driver notifies that ANALOG_AUDIO_ADAPTER is inserted */ if (mbhc->mbhc_cfg->enable_usbc_analog) WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0); else WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1); if (mbhc->mbhc_cfg->enable_usbc_analog) { Loading Loading @@ -1481,207 +1494,26 @@ static int wcd_mbhc_set_keycode(struct wcd_mbhc *mbhc) return result; } static int wcd_mbhc_usb_c_analog_setup_gpios(struct wcd_mbhc *mbhc, bool active) { int rc = 0; struct usbc_ana_audio_config *config = &mbhc->mbhc_cfg->usbc_analog_cfg; union power_supply_propval pval; dev_dbg(mbhc->codec->dev, "%s: setting GPIOs active = %d\n", __func__, active); memset(&pval, 0, sizeof(pval)); if (active) { pval.intval = POWER_SUPPLY_TYPEC_PR_SOURCE; if (power_supply_set_property(mbhc->usb_psy, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &pval)) dev_info(mbhc->codec->dev, "%s: force PR_SOURCE mode unsuccessful\n", __func__); else mbhc->usbc_force_pr_mode = true; if (config->usbc_en1_gpio_p) rc = msm_cdc_pinctrl_select_active_state( config->usbc_en1_gpio_p); if (rc == 0 && config->usbc_force_gpio_p) rc = msm_cdc_pinctrl_select_active_state( config->usbc_force_gpio_p); mbhc->usbc_mode = POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER; } else { /* no delay is required when disabling GPIOs */ if (config->usbc_en1_gpio_p) msm_cdc_pinctrl_select_sleep_state( config->usbc_en1_gpio_p); if (config->usbc_force_gpio_p) msm_cdc_pinctrl_select_sleep_state( config->usbc_force_gpio_p); if (mbhc->usbc_force_pr_mode) { pval.intval = POWER_SUPPLY_TYPEC_PR_DUAL; if (power_supply_set_property(mbhc->usb_psy, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &pval)) dev_info(mbhc->codec->dev, "%s: force PR_DUAL mode unsuccessful\n", __func__); mbhc->usbc_force_pr_mode = false; } mbhc->usbc_mode = POWER_SUPPLY_TYPEC_NONE; if (mbhc->mbhc_cfg->swap_gnd_mic) mbhc->mbhc_cfg->swap_gnd_mic(mbhc->codec, false); } return rc; } /* workqueue */ static void wcd_mbhc_usbc_analog_work_fn(struct work_struct *work) static int wcd_mbhc_usbc_ana_event_handler(struct notifier_block *nb, unsigned long mode, void *ptr) { struct wcd_mbhc *mbhc = container_of(work, struct wcd_mbhc, usbc_analog_work); wcd_mbhc_usb_c_analog_setup_gpios(mbhc, mbhc->usbc_mode != POWER_SUPPLY_TYPEC_NONE); } /* this callback function is used to process PMI notification */ static int wcd_mbhc_usb_c_event_changed(struct notifier_block *nb, unsigned long evt, void *ptr) { int ret; union power_supply_propval mode; struct wcd_mbhc *mbhc = container_of(nb, struct wcd_mbhc, psy_nb); struct snd_soc_codec *codec = mbhc->codec; struct wcd_mbhc *mbhc = container_of(nb, struct wcd_mbhc, fsa_nb); if (ptr != mbhc->usb_psy || evt != PSY_EVENT_PROP_CHANGED) return 0; ret = power_supply_get_property(mbhc->usb_psy, POWER_SUPPLY_PROP_TYPEC_MODE, &mode); if (ret) { dev_err(codec->dev, "%s: Unable to read USB TYPEC_MODE: %d\n", __func__, ret); return ret; } dev_dbg(codec->dev, "%s: USB change event received\n", __func__); dev_dbg(codec->dev, "%s: supply mode %d, expected %d\n", __func__, mode.intval, POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER); switch (mode.intval) { case POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER: case POWER_SUPPLY_TYPEC_NONE: dev_dbg(codec->dev, "%s: usbc_mode: %d; mode.intval: %d\n", __func__, mbhc->usbc_mode, mode.intval); if (mbhc->usbc_mode == mode.intval) break; /* filter notifications received before */ mbhc->usbc_mode = mode.intval; dev_dbg(codec->dev, "%s: queueing usbc_analog_work\n", __func__); schedule_work(&mbhc->usbc_analog_work); break; default: break; } return ret; } /* PMI registration code */ static int wcd_mbhc_usb_c_analog_init(struct wcd_mbhc *mbhc) { int ret = 0; struct snd_soc_codec *codec = mbhc->codec; dev_dbg(mbhc->codec->dev, "%s: usb-c analog setup start\n", __func__); INIT_WORK(&mbhc->usbc_analog_work, wcd_mbhc_usbc_analog_work_fn); mbhc->usb_psy = power_supply_get_by_name("usb"); if (IS_ERR_OR_NULL(mbhc->usb_psy)) { dev_err(codec->dev, "%s: could not get USB psy info\n", __func__); ret = -EPROBE_DEFER; if (IS_ERR(mbhc->usb_psy)) ret = PTR_ERR(mbhc->usb_psy); mbhc->usb_psy = NULL; goto err; } ret = wcd_mbhc_usb_c_analog_setup_gpios(mbhc, false); if (ret) { dev_err(codec->dev, "%s: error while setting USBC ana gpios\n", __func__); goto err; } mbhc->psy_nb.notifier_call = wcd_mbhc_usb_c_event_changed; mbhc->psy_nb.priority = 0; ret = power_supply_reg_notifier(&mbhc->psy_nb); if (ret) { dev_err(codec->dev, "%s: power supply registration failed\n", __func__); goto err; } if (!mbhc) return -EINVAL; /* * as part of the init sequence check if there is a connected * USB C analog adapter */ dev_dbg(mbhc->codec->dev, "%s: verify if USB adapter is already inserted\n", __func__); ret = wcd_mbhc_usb_c_event_changed(&mbhc->psy_nb, PSY_EVENT_PROP_CHANGED, mbhc->usb_psy); dev_dbg(mbhc->codec->dev, "%s: mode = %lu\n", __func__, mode); err: return ret; if (mode == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) { /* insertion detected, enable L_DET_EN */ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1); } static int wcd_mbhc_usb_c_analog_deinit(struct wcd_mbhc *mbhc) { wcd_mbhc_usb_c_analog_setup_gpios(mbhc, false); /* deregister from PMI */ power_supply_unreg_notifier(&mbhc->psy_nb); return 0; } static int wcd_mbhc_init_gpio(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg, const char *gpio_dt_str, int *gpio, struct device_node **gpio_dn) { int rc = 0; struct snd_soc_codec *codec = mbhc->codec; struct snd_soc_card *card = codec->component.card; dev_dbg(mbhc->codec->dev, "%s: gpio %s\n", __func__, gpio_dt_str); *gpio_dn = of_parse_phandle(card->dev->of_node, gpio_dt_str, 0); if (!(*gpio_dn)) { *gpio = of_get_named_gpio(card->dev->of_node, gpio_dt_str, 0); if (!gpio_is_valid(*gpio)) { dev_err(card->dev, "%s, property %s not in node %s", __func__, gpio_dt_str, card->dev->of_node->full_name); rc = -EINVAL; } } return rc; } int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) { int rc = 0; struct usbc_ana_audio_config *config; struct snd_soc_codec *codec; struct snd_soc_card *card; const char *usb_c_dt = "qcom,msm-mbhc-usbc-audio-supported"; Loading @@ -1689,7 +1521,6 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) if (!mbhc || !mbhc_cfg) return -EINVAL; config = &mbhc_cfg->usbc_analog_cfg; codec = mbhc->codec; card = codec->component.card; Loading @@ -1705,42 +1536,24 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) &mbhc_cfg->enable_usbc_analog); } if (mbhc_cfg->enable_usbc_analog == 0 || rc != 0) { dev_info(card->dev, dev_dbg(card->dev, "%s: %s in dt node is missing or false\n", __func__, usb_c_dt); dev_info(card->dev, dev_dbg(card->dev, "%s: skipping USB c analog configuration\n", __func__); } /* initialize GPIOs */ /* Parse fsa switch handle */ if (mbhc_cfg->enable_usbc_analog) { dev_dbg(mbhc->codec->dev, "%s: usbc analog enabled\n", __func__); mbhc->swap_thr = GND_MIC_USBC_SWAP_THRESHOLD; rc = wcd_mbhc_init_gpio(mbhc, mbhc_cfg, "qcom,usbc-analog-en1-gpio", &config->usbc_en1_gpio, &config->usbc_en1_gpio_p); if (rc) goto err; if (of_find_property(card->dev->of_node, "qcom,usbc-analog-force_detect_gpio", NULL)) { rc = wcd_mbhc_init_gpio(mbhc, mbhc_cfg, "qcom,usbc-analog-force_detect_gpio", &config->usbc_force_gpio, &config->usbc_force_gpio_p); if (rc) goto err; } dev_dbg(mbhc->codec->dev, "%s: calling usb_c_analog_init\n", mbhc->fsa_np = of_parse_phandle(card->dev->of_node, "fsa4480-i2c-handle", 0); if (!mbhc->fsa_np) { dev_err(card->dev, "%s: fsa4480 i2c node not found\n", __func__); /* init PMI notifier */ rc = wcd_mbhc_usb_c_analog_init(mbhc); if (rc) { rc = EPROBE_DEFER; rc = -EINVAL; goto err; } } Loading @@ -1753,6 +1566,11 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw) || (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_cal)) { rc = wcd_mbhc_initialise(mbhc); if (rc) { dev_err(card->dev, "%s: wcd mbhc initialize failed\n", __func__); goto err; } } else { if (!mbhc->mbhc_fw || !mbhc->mbhc_cal) schedule_delayed_work(&mbhc->mbhc_firmware_dwork, Loading @@ -1762,24 +1580,14 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) __func__, mbhc->mbhc_fw, mbhc->mbhc_cal); } if (mbhc_cfg->enable_usbc_analog) { mbhc->fsa_nb.notifier_call = wcd_mbhc_usbc_ana_event_handler; mbhc->fsa_nb.priority = 0; rc = fsa4480_reg_notifier(&mbhc->fsa_nb, mbhc->fsa_np); } return rc; err: if (config->usbc_en1_gpio > 0) { dev_dbg(card->dev, "%s free usb en1 gpio %d\n", __func__, config->usbc_en1_gpio); gpio_free(config->usbc_en1_gpio); config->usbc_en1_gpio = 0; } if (config->usbc_force_gpio > 0) { dev_dbg(card->dev, "%s free usb_force gpio %d\n", __func__, config->usbc_force_gpio); gpio_free(config->usbc_force_gpio); config->usbc_force_gpio = 0; } if (config->usbc_en1_gpio_p) of_node_put(config->usbc_en1_gpio_p); if (config->usbc_force_gpio_p) of_node_put(config->usbc_force_gpio_p); dev_dbg(mbhc->codec->dev, "%s: leave %d\n", __func__, rc); return rc; } Loading @@ -1787,8 +1595,6 @@ EXPORT_SYMBOL(wcd_mbhc_start); void wcd_mbhc_stop(struct wcd_mbhc *mbhc) { struct usbc_ana_audio_config *config = &mbhc->mbhc_cfg->usbc_analog_cfg; pr_debug("%s: enter\n", __func__); if (mbhc->current_plug != MBHC_PLUG_TYPE_NONE) { Loading @@ -1813,19 +1619,8 @@ void wcd_mbhc_stop(struct wcd_mbhc *mbhc) mbhc->mbhc_cal = NULL; } if (mbhc->mbhc_cfg->enable_usbc_analog) { wcd_mbhc_usb_c_analog_deinit(mbhc); /* free GPIOs */ if (config->usbc_en1_gpio > 0) gpio_free(config->usbc_en1_gpio); if (config->usbc_force_gpio) gpio_free(config->usbc_force_gpio); if (config->usbc_en1_gpio_p) of_node_put(config->usbc_en1_gpio_p); if (config->usbc_force_gpio_p) of_node_put(config->usbc_force_gpio_p); } if (mbhc->mbhc_cfg->enable_usbc_analog) fsa4480_unreg_notifier(&mbhc->fsa_nb, mbhc->fsa_np); pr_debug("%s: leave\n", __func__); } Loading asoc/codecs/wcd-mbhc-v2.h +2 −16 Original line number Diff line number Diff line Loading @@ -422,15 +422,6 @@ enum mbhc_moisture_rref { R_184_KOHM, }; struct usbc_ana_audio_config { int usbc_en1_gpio; int usbc_en2_gpio; int usbc_force_gpio; struct device_node *usbc_en1_gpio_p; /* used by pinctrl API */ struct device_node *usbc_en2_gpio_p; /* used by pinctrl API */ struct device_node *usbc_force_gpio_p; /* used by pinctrl API */ }; struct wcd_mbhc_config { bool read_fw_bin; void *calibration; Loading @@ -446,7 +437,6 @@ struct wcd_mbhc_config { int anc_micbias; bool enable_anc_mic_detect; u32 enable_usbc_analog; struct usbc_ana_audio_config usbc_analog_cfg; }; struct wcd_mbhc_intr { Loading Loading @@ -599,14 +589,10 @@ struct wcd_mbhc { unsigned long intr_status; bool is_hph_ocp_pending; bool usbc_force_pr_mode; int usbc_mode; struct notifier_block psy_nb; struct power_supply *usb_psy; struct work_struct usbc_analog_work; struct wcd_mbhc_fn *mbhc_fn; bool force_linein; struct device_node *fsa_np; struct notifier_block fsa_nb; }; void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc, Loading Loading
asoc/codecs/wcd-mbhc-v2.c +47 −252 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include <linux/input.h> #include <linux/firmware.h> #include <linux/completion.h> #include <linux/soc/qcom/fsa4480-i2c.h> #include <sound/soc.h> #include <sound/jack.h> #include "msm-cdc-pinctrl.h" Loading Loading @@ -958,6 +959,9 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc) mbhc->extn_cable_hph_rem = false; wcd_mbhc_report_plug(mbhc, 0, jack_type); if (mbhc->mbhc_cfg->enable_usbc_analog) WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0); } else if (!detection_type) { /* Disable external voltage source to micbias if present */ if (mbhc->mbhc_cb->enable_mb_source) Loading Loading @@ -1291,8 +1295,8 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) * by an external source */ if (mbhc->mbhc_cfg->enable_usbc_analog) { mbhc->hphl_swh = 1; mbhc->gnd_swh = 1; mbhc->hphl_swh = 0; mbhc->gnd_swh = 0; if (mbhc->mbhc_cb->hph_pull_up_control_v2) mbhc->mbhc_cb->hph_pull_up_control_v2(codec, Loading @@ -1310,6 +1314,15 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) if (mbhc->mbhc_cfg->gnd_det_en && mbhc->mbhc_cb->mbhc_gnd_det_ctrl) mbhc->mbhc_cb->mbhc_gnd_det_ctrl(codec, true); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1); /* * Disable L_DET for USB-C analog audio to avoid spurious interrupts * when a non-audio accessory is inserted. L_DET_EN sets to 1 when FSA * I2C driver notifies that ANALOG_AUDIO_ADAPTER is inserted */ if (mbhc->mbhc_cfg->enable_usbc_analog) WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0); else WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1); if (mbhc->mbhc_cfg->enable_usbc_analog) { Loading Loading @@ -1481,207 +1494,26 @@ static int wcd_mbhc_set_keycode(struct wcd_mbhc *mbhc) return result; } static int wcd_mbhc_usb_c_analog_setup_gpios(struct wcd_mbhc *mbhc, bool active) { int rc = 0; struct usbc_ana_audio_config *config = &mbhc->mbhc_cfg->usbc_analog_cfg; union power_supply_propval pval; dev_dbg(mbhc->codec->dev, "%s: setting GPIOs active = %d\n", __func__, active); memset(&pval, 0, sizeof(pval)); if (active) { pval.intval = POWER_SUPPLY_TYPEC_PR_SOURCE; if (power_supply_set_property(mbhc->usb_psy, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &pval)) dev_info(mbhc->codec->dev, "%s: force PR_SOURCE mode unsuccessful\n", __func__); else mbhc->usbc_force_pr_mode = true; if (config->usbc_en1_gpio_p) rc = msm_cdc_pinctrl_select_active_state( config->usbc_en1_gpio_p); if (rc == 0 && config->usbc_force_gpio_p) rc = msm_cdc_pinctrl_select_active_state( config->usbc_force_gpio_p); mbhc->usbc_mode = POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER; } else { /* no delay is required when disabling GPIOs */ if (config->usbc_en1_gpio_p) msm_cdc_pinctrl_select_sleep_state( config->usbc_en1_gpio_p); if (config->usbc_force_gpio_p) msm_cdc_pinctrl_select_sleep_state( config->usbc_force_gpio_p); if (mbhc->usbc_force_pr_mode) { pval.intval = POWER_SUPPLY_TYPEC_PR_DUAL; if (power_supply_set_property(mbhc->usb_psy, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &pval)) dev_info(mbhc->codec->dev, "%s: force PR_DUAL mode unsuccessful\n", __func__); mbhc->usbc_force_pr_mode = false; } mbhc->usbc_mode = POWER_SUPPLY_TYPEC_NONE; if (mbhc->mbhc_cfg->swap_gnd_mic) mbhc->mbhc_cfg->swap_gnd_mic(mbhc->codec, false); } return rc; } /* workqueue */ static void wcd_mbhc_usbc_analog_work_fn(struct work_struct *work) static int wcd_mbhc_usbc_ana_event_handler(struct notifier_block *nb, unsigned long mode, void *ptr) { struct wcd_mbhc *mbhc = container_of(work, struct wcd_mbhc, usbc_analog_work); wcd_mbhc_usb_c_analog_setup_gpios(mbhc, mbhc->usbc_mode != POWER_SUPPLY_TYPEC_NONE); } /* this callback function is used to process PMI notification */ static int wcd_mbhc_usb_c_event_changed(struct notifier_block *nb, unsigned long evt, void *ptr) { int ret; union power_supply_propval mode; struct wcd_mbhc *mbhc = container_of(nb, struct wcd_mbhc, psy_nb); struct snd_soc_codec *codec = mbhc->codec; struct wcd_mbhc *mbhc = container_of(nb, struct wcd_mbhc, fsa_nb); if (ptr != mbhc->usb_psy || evt != PSY_EVENT_PROP_CHANGED) return 0; ret = power_supply_get_property(mbhc->usb_psy, POWER_SUPPLY_PROP_TYPEC_MODE, &mode); if (ret) { dev_err(codec->dev, "%s: Unable to read USB TYPEC_MODE: %d\n", __func__, ret); return ret; } dev_dbg(codec->dev, "%s: USB change event received\n", __func__); dev_dbg(codec->dev, "%s: supply mode %d, expected %d\n", __func__, mode.intval, POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER); switch (mode.intval) { case POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER: case POWER_SUPPLY_TYPEC_NONE: dev_dbg(codec->dev, "%s: usbc_mode: %d; mode.intval: %d\n", __func__, mbhc->usbc_mode, mode.intval); if (mbhc->usbc_mode == mode.intval) break; /* filter notifications received before */ mbhc->usbc_mode = mode.intval; dev_dbg(codec->dev, "%s: queueing usbc_analog_work\n", __func__); schedule_work(&mbhc->usbc_analog_work); break; default: break; } return ret; } /* PMI registration code */ static int wcd_mbhc_usb_c_analog_init(struct wcd_mbhc *mbhc) { int ret = 0; struct snd_soc_codec *codec = mbhc->codec; dev_dbg(mbhc->codec->dev, "%s: usb-c analog setup start\n", __func__); INIT_WORK(&mbhc->usbc_analog_work, wcd_mbhc_usbc_analog_work_fn); mbhc->usb_psy = power_supply_get_by_name("usb"); if (IS_ERR_OR_NULL(mbhc->usb_psy)) { dev_err(codec->dev, "%s: could not get USB psy info\n", __func__); ret = -EPROBE_DEFER; if (IS_ERR(mbhc->usb_psy)) ret = PTR_ERR(mbhc->usb_psy); mbhc->usb_psy = NULL; goto err; } ret = wcd_mbhc_usb_c_analog_setup_gpios(mbhc, false); if (ret) { dev_err(codec->dev, "%s: error while setting USBC ana gpios\n", __func__); goto err; } mbhc->psy_nb.notifier_call = wcd_mbhc_usb_c_event_changed; mbhc->psy_nb.priority = 0; ret = power_supply_reg_notifier(&mbhc->psy_nb); if (ret) { dev_err(codec->dev, "%s: power supply registration failed\n", __func__); goto err; } if (!mbhc) return -EINVAL; /* * as part of the init sequence check if there is a connected * USB C analog adapter */ dev_dbg(mbhc->codec->dev, "%s: verify if USB adapter is already inserted\n", __func__); ret = wcd_mbhc_usb_c_event_changed(&mbhc->psy_nb, PSY_EVENT_PROP_CHANGED, mbhc->usb_psy); dev_dbg(mbhc->codec->dev, "%s: mode = %lu\n", __func__, mode); err: return ret; if (mode == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) { /* insertion detected, enable L_DET_EN */ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1); } static int wcd_mbhc_usb_c_analog_deinit(struct wcd_mbhc *mbhc) { wcd_mbhc_usb_c_analog_setup_gpios(mbhc, false); /* deregister from PMI */ power_supply_unreg_notifier(&mbhc->psy_nb); return 0; } static int wcd_mbhc_init_gpio(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg, const char *gpio_dt_str, int *gpio, struct device_node **gpio_dn) { int rc = 0; struct snd_soc_codec *codec = mbhc->codec; struct snd_soc_card *card = codec->component.card; dev_dbg(mbhc->codec->dev, "%s: gpio %s\n", __func__, gpio_dt_str); *gpio_dn = of_parse_phandle(card->dev->of_node, gpio_dt_str, 0); if (!(*gpio_dn)) { *gpio = of_get_named_gpio(card->dev->of_node, gpio_dt_str, 0); if (!gpio_is_valid(*gpio)) { dev_err(card->dev, "%s, property %s not in node %s", __func__, gpio_dt_str, card->dev->of_node->full_name); rc = -EINVAL; } } return rc; } int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) { int rc = 0; struct usbc_ana_audio_config *config; struct snd_soc_codec *codec; struct snd_soc_card *card; const char *usb_c_dt = "qcom,msm-mbhc-usbc-audio-supported"; Loading @@ -1689,7 +1521,6 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) if (!mbhc || !mbhc_cfg) return -EINVAL; config = &mbhc_cfg->usbc_analog_cfg; codec = mbhc->codec; card = codec->component.card; Loading @@ -1705,42 +1536,24 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) &mbhc_cfg->enable_usbc_analog); } if (mbhc_cfg->enable_usbc_analog == 0 || rc != 0) { dev_info(card->dev, dev_dbg(card->dev, "%s: %s in dt node is missing or false\n", __func__, usb_c_dt); dev_info(card->dev, dev_dbg(card->dev, "%s: skipping USB c analog configuration\n", __func__); } /* initialize GPIOs */ /* Parse fsa switch handle */ if (mbhc_cfg->enable_usbc_analog) { dev_dbg(mbhc->codec->dev, "%s: usbc analog enabled\n", __func__); mbhc->swap_thr = GND_MIC_USBC_SWAP_THRESHOLD; rc = wcd_mbhc_init_gpio(mbhc, mbhc_cfg, "qcom,usbc-analog-en1-gpio", &config->usbc_en1_gpio, &config->usbc_en1_gpio_p); if (rc) goto err; if (of_find_property(card->dev->of_node, "qcom,usbc-analog-force_detect_gpio", NULL)) { rc = wcd_mbhc_init_gpio(mbhc, mbhc_cfg, "qcom,usbc-analog-force_detect_gpio", &config->usbc_force_gpio, &config->usbc_force_gpio_p); if (rc) goto err; } dev_dbg(mbhc->codec->dev, "%s: calling usb_c_analog_init\n", mbhc->fsa_np = of_parse_phandle(card->dev->of_node, "fsa4480-i2c-handle", 0); if (!mbhc->fsa_np) { dev_err(card->dev, "%s: fsa4480 i2c node not found\n", __func__); /* init PMI notifier */ rc = wcd_mbhc_usb_c_analog_init(mbhc); if (rc) { rc = EPROBE_DEFER; rc = -EINVAL; goto err; } } Loading @@ -1753,6 +1566,11 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw) || (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_cal)) { rc = wcd_mbhc_initialise(mbhc); if (rc) { dev_err(card->dev, "%s: wcd mbhc initialize failed\n", __func__); goto err; } } else { if (!mbhc->mbhc_fw || !mbhc->mbhc_cal) schedule_delayed_work(&mbhc->mbhc_firmware_dwork, Loading @@ -1762,24 +1580,14 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) __func__, mbhc->mbhc_fw, mbhc->mbhc_cal); } if (mbhc_cfg->enable_usbc_analog) { mbhc->fsa_nb.notifier_call = wcd_mbhc_usbc_ana_event_handler; mbhc->fsa_nb.priority = 0; rc = fsa4480_reg_notifier(&mbhc->fsa_nb, mbhc->fsa_np); } return rc; err: if (config->usbc_en1_gpio > 0) { dev_dbg(card->dev, "%s free usb en1 gpio %d\n", __func__, config->usbc_en1_gpio); gpio_free(config->usbc_en1_gpio); config->usbc_en1_gpio = 0; } if (config->usbc_force_gpio > 0) { dev_dbg(card->dev, "%s free usb_force gpio %d\n", __func__, config->usbc_force_gpio); gpio_free(config->usbc_force_gpio); config->usbc_force_gpio = 0; } if (config->usbc_en1_gpio_p) of_node_put(config->usbc_en1_gpio_p); if (config->usbc_force_gpio_p) of_node_put(config->usbc_force_gpio_p); dev_dbg(mbhc->codec->dev, "%s: leave %d\n", __func__, rc); return rc; } Loading @@ -1787,8 +1595,6 @@ EXPORT_SYMBOL(wcd_mbhc_start); void wcd_mbhc_stop(struct wcd_mbhc *mbhc) { struct usbc_ana_audio_config *config = &mbhc->mbhc_cfg->usbc_analog_cfg; pr_debug("%s: enter\n", __func__); if (mbhc->current_plug != MBHC_PLUG_TYPE_NONE) { Loading @@ -1813,19 +1619,8 @@ void wcd_mbhc_stop(struct wcd_mbhc *mbhc) mbhc->mbhc_cal = NULL; } if (mbhc->mbhc_cfg->enable_usbc_analog) { wcd_mbhc_usb_c_analog_deinit(mbhc); /* free GPIOs */ if (config->usbc_en1_gpio > 0) gpio_free(config->usbc_en1_gpio); if (config->usbc_force_gpio) gpio_free(config->usbc_force_gpio); if (config->usbc_en1_gpio_p) of_node_put(config->usbc_en1_gpio_p); if (config->usbc_force_gpio_p) of_node_put(config->usbc_force_gpio_p); } if (mbhc->mbhc_cfg->enable_usbc_analog) fsa4480_unreg_notifier(&mbhc->fsa_nb, mbhc->fsa_np); pr_debug("%s: leave\n", __func__); } Loading
asoc/codecs/wcd-mbhc-v2.h +2 −16 Original line number Diff line number Diff line Loading @@ -422,15 +422,6 @@ enum mbhc_moisture_rref { R_184_KOHM, }; struct usbc_ana_audio_config { int usbc_en1_gpio; int usbc_en2_gpio; int usbc_force_gpio; struct device_node *usbc_en1_gpio_p; /* used by pinctrl API */ struct device_node *usbc_en2_gpio_p; /* used by pinctrl API */ struct device_node *usbc_force_gpio_p; /* used by pinctrl API */ }; struct wcd_mbhc_config { bool read_fw_bin; void *calibration; Loading @@ -446,7 +437,6 @@ struct wcd_mbhc_config { int anc_micbias; bool enable_anc_mic_detect; u32 enable_usbc_analog; struct usbc_ana_audio_config usbc_analog_cfg; }; struct wcd_mbhc_intr { Loading Loading @@ -599,14 +589,10 @@ struct wcd_mbhc { unsigned long intr_status; bool is_hph_ocp_pending; bool usbc_force_pr_mode; int usbc_mode; struct notifier_block psy_nb; struct power_supply *usb_psy; struct work_struct usbc_analog_work; struct wcd_mbhc_fn *mbhc_fn; bool force_linein; struct device_node *fsa_np; struct notifier_block fsa_nb; }; void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc, Loading