Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c33373ce authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "asoc: wcd-mbhc: add support for usbc analog audio on msmnile"

parents cc484448 8fe02478
Loading
Loading
Loading
Loading
+47 −252
Original line number Diff line number Diff line
@@ -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"
@@ -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)
@@ -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,
@@ -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) {
@@ -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";
@@ -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;

@@ -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;
		}
	}
@@ -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,
@@ -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;
}
@@ -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) {
@@ -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__);
}
+2 −16
Original line number Diff line number Diff line
@@ -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;
@@ -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 {
@@ -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,