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

Commit c3c9a38b 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: add notifier between codec and mbhc"

parents 0ae44c35 3625f243
Loading
Loading
Loading
Loading
+29 −28
Original line number Diff line number Diff line
@@ -118,11 +118,6 @@ enum {
	BAND_MAX,
};

enum {
	ON_DEMAND_MICBIAS = 0,
	ON_DEMAND_SUPPLIES_MAX,
};

struct hpf_work {
	struct msm8x16_wcd_priv *msm8x16_wcd;
	u32 decimator;
@@ -132,33 +127,10 @@ struct hpf_work {

static struct hpf_work tx_hpf_work[NUM_DECIMATORS];

struct on_demand_supply {
	struct regulator *supply;
	atomic_t ref;
};

static char on_demand_supply_name[][MAX_ON_DEMAND_SUPPLY_NAME_LENGTH] = {
	"cdc-vdd-mic-bias",
};

struct msm8x16_wcd_priv {
	struct snd_soc_codec *codec;
	u16 pmic_rev;
	u32 adc_count;
	u32 rx_bias_count;
	s32 dmic_1_2_clk_cnt;
	u32 mute_mask;
	bool mclk_enabled;
	bool clock_active;
	bool config_mode_active;
	bool spk_boost_set;
	bool ear_pa_boost_set;
	bool dec_active[NUM_DECIMATORS];
	struct on_demand_supply on_demand_list[ON_DEMAND_SUPPLIES_MAX];
	/* mbhc module */
	struct wcd_mbhc mbhc;
};

static unsigned long rx_digital_gain_reg[] = {
	MSM8X16_WCD_A_CDC_RX1_VOL_CTL_B2_CTL,
	MSM8X16_WCD_A_CDC_RX2_VOL_CTL_B2_CTL,
@@ -208,6 +180,31 @@ static const struct wcd_mbhc_cb mbhc_cb = {
	.enable_mb_source = msm8x16_wcd_enable_ext_mb_source,
};

int msm8x16_unregister_notifier(struct snd_soc_codec *codec,
				     struct notifier_block *nblock)
{
	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);

	return blocking_notifier_chain_unregister(&msm8x16_wcd->notifier,
			nblock);
}

int msm8x16_register_notifier(struct snd_soc_codec *codec,
				     struct notifier_block *nblock)
{
	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);

	return blocking_notifier_chain_register(&msm8x16_wcd->notifier, nblock);
}

void msm8x16_notifier_call(struct snd_soc_codec *codec,
				  const enum wcd_notify_event event)
{
	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);

	pr_debug("%s: notifier call event %d\n", __func__, event);
	blocking_notifier_call_chain(&msm8x16_wcd->notifier, event, codec);
}

static int get_spmi_msm8x16_wcd_device_info(u16 *reg,
			struct msm8x16_wcd_spmi **msm8x16_wcd)
@@ -1838,6 +1835,7 @@ static int msm8x16_wcd_codec_enable_micbias(struct snd_soc_dapm_widget *w,
			snd_soc_update_bits(codec, micb_int_reg, 0x08, 0x08);
		else if (strnstr(w->name, internal3_text, 30))
			snd_soc_update_bits(codec, micb_int_reg, 0x01, 0x01);
		msm8x16_notifier_call(codec, WCD_EVENT_PRE_MICBIAS_2_ON);
		break;
	case SND_SOC_DAPM_POST_PMD:
		if (strnstr(w->name, internal1_text, 30)) {
@@ -1851,6 +1849,7 @@ static int msm8x16_wcd_codec_enable_micbias(struct snd_soc_dapm_widget *w,
		}
		snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_1_EN,
				0x45, 0x01);
		msm8x16_notifier_call(codec, WCD_EVENT_PRE_MICBIAS_2_OFF);
		break;
	}
	return 0;
@@ -3234,6 +3233,8 @@ static int msm8x16_wcd_codec_probe(struct snd_soc_codec *codec)
				on_demand_supply_name[ON_DEMAND_MICBIAS]);
	atomic_set(&msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].ref, 0);

	BLOCKING_INIT_NOTIFIER_HEAD(&msm8x16_wcd_priv->notifier);

	wcd_mbhc_init(&msm8x16_wcd_priv->mbhc, codec, &mbhc_cb, &intr_ids,
			false);

+53 −0
Original line number Diff line number Diff line
@@ -46,6 +46,8 @@
#define MCLK_SUS_RSC	2
#define MCLK_SUS_NO_ACT	3

#define NUM_DECIMATORS	2

extern const u8 msm8x16_wcd_reg_readable[MSM8X16_WCD_CACHE_SIZE];
extern const u8 msm8x16_wcd_reg_readonly[MSM8X16_WCD_CACHE_SIZE];
extern const u8 msm8x16_wcd_reset_reg_defaults[MSM8X16_WCD_CACHE_SIZE];
@@ -105,6 +107,26 @@ enum {
	MSM8X16_WCD_NUM_IRQS,
};

enum wcd_notify_event {
	WCD_EVENT_INVALID,
	/* events for micbias ON and OFF */
	WCD_EVENT_PRE_MICBIAS_2_OFF,
	WCD_EVENT_POST_MICBIAS_2_OFF,
	WCD_EVENT_PRE_MICBIAS_2_ON,
	WCD_EVENT_POST_MICBIAS_2_ON,
	/* events for PA ON and OFF */
	WCD_EVENT_PRE_HPHL_PA_ON,
	WCD_EVENT_POST_HPHL_PA_OFF,
	WCD_EVENT_PRE_HPHR_PA_ON,
	WCD_EVENT_POST_HPHR_PA_OFF,
	WCD_EVENT_LAST,
};

enum {
	ON_DEMAND_MICBIAS = 0,
	ON_DEMAND_SUPPLIES_MAX,
};

/*
 * The delay list is per codec HW specification.
 * Please add delay in the list in the future instead
@@ -172,6 +194,31 @@ struct msm8x16_wcd {
	char __iomem *dig_base;
};

struct on_demand_supply {
	struct regulator *supply;
	atomic_t ref;
};

struct msm8x16_wcd_priv {
	struct snd_soc_codec *codec;
	u16 pmic_rev;
	u32 adc_count;
	u32 rx_bias_count;
	s32 dmic_1_2_clk_cnt;
	u32 mute_mask;
	bool mclk_enabled;
	bool clock_active;
	bool config_mode_active;
	bool spk_boost_set;
	bool ear_pa_boost_set;
	bool dec_active[NUM_DECIMATORS];
	struct on_demand_supply on_demand_list[ON_DEMAND_SUPPLIES_MAX];
	/* mbhc module */
	struct wcd_mbhc mbhc;
	struct blocking_notifier_head notifier;

};

extern int msm8x16_wcd_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
			     bool dapm);

@@ -180,5 +227,11 @@ extern int msm8x16_wcd_hs_detect(struct snd_soc_codec *codec,

extern void msm8x16_wcd_hs_detect_exit(struct snd_soc_codec *codec);

extern int msm8x16_register_notifier(struct snd_soc_codec *codec,
				     struct notifier_block *nblock);

extern int msm8x16_unregister_notifier(struct snd_soc_codec *codec,
				     struct notifier_block *nblock);

#endif
+127 −73
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@
#include "wcd9xxx-mbhc.h"
#include "msm8x16_wcd_registers.h"
#include "msm8916-wcd-irq.h"
#include "msm8x16-wcd.h"

#define WCD_MBHC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
			   SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \
@@ -43,6 +44,7 @@
				  SND_JACK_BTN_4)
#define OCP_ATTEMPT 1
#define HS_DETECT_PLUG_TIME_MS (3 * 1000)
#define SPECIAL_HS_DETECT_TIME_MS (2 * 1000)
#define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250

#define WCD_MBHC_RSC_LOCK(mbhc)			\
@@ -64,6 +66,49 @@
		  "%s: BCL should have acquired\n", __func__); \
}

static int wcd_event_notify(struct notifier_block *self, unsigned long val,
				void *data)
{
	struct snd_soc_codec *codec = (struct snd_soc_codec *)data;
	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
	struct wcd_mbhc *mbhc = &msm8x16_wcd->mbhc;
	enum wcd_notify_event event = (enum wcd_notify_event)val;

	pr_debug("%s: event %d\n", __func__, event);
	switch (event) {
	/* MICBIAS usage change */
	case WCD_EVENT_PRE_MICBIAS_2_ON:
		if (mbhc->micbias_enable) {
			snd_soc_write(codec,
					MSM8X16_WCD_A_ANALOG_MICB_1_VAL,
					0xC0);
			snd_soc_update_bits(codec,
					MSM8X16_WCD_A_ANALOG_MICB_2_EN,
					0x18, 0x10);
		}
		/* Disable current source if micbias enabled */
		snd_soc_update_bits(codec,
				    MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
				    0xB0, 0x80);
		break;
	/* MICBIAS usage change */
	case WCD_EVENT_PRE_MICBIAS_2_OFF:
		snd_soc_update_bits(codec,
				MSM8X16_WCD_A_ANALOG_MICB_2_EN,
				0x18, 0x00);
		snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MICB_1_VAL,
				0x20);
		/* Enable current source again for polling */
		snd_soc_update_bits(codec,
				    MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
				    0xB0, 0xB0);
		break;
	default:
		break;
	}
	return 0;
}

static void wcd_program_btn_threshold(const struct wcd_mbhc *mbhc)
{
	struct wcd_mbhc_btn_detect_cfg *btn_det;
@@ -425,13 +470,8 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
				~WCD_MBHC_JACK_BUTTON_MASK;
		}

		/*
		 * Set micbias back to 1.8V if accessory was special
		 * headset and thus micbias was increased to 2.8V
		 */
		if (mbhc->micbias_enable)
			snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MICB_1_VAL,
				      0x20);
			mbhc->micbias_enable = false;

		mbhc->zl = mbhc->zr = 0;
		mbhc->is_hs_inserted = false;
@@ -461,11 +501,8 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
		    jack_type == SND_JACK_LINEOUT) &&
		    (mbhc->hph_status && mbhc->hph_status != jack_type)) {

			if (mbhc->micbias_enable &&
			    mbhc->hph_status == SND_JACK_HEADSET)
				snd_soc_write(codec,
					      MSM8X16_WCD_A_ANALOG_MICB_1_VAL,
					      0x20);
		if (mbhc->micbias_enable)
			mbhc->micbias_enable = false;

			mbhc->zl = mbhc->zr = 0;
			mbhc->is_hs_inserted = false;
@@ -560,6 +597,69 @@ static void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,
	pr_debug("%s: leave\n", __func__);
}

static bool wcd_is_special_headset(struct wcd_mbhc *mbhc)
{
	u16 result2;
	struct snd_soc_codec *codec = mbhc->codec;
	int delay = 0;
	bool ret = false;
	s16 reg;

	/*
	 * Enable micbias if not already enabled
	 * and disable current source if using micbias
	 */
	reg = snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL);
	snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
			0xB0, 0x80);
	/* Enable micbias if not already enabled */
	snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN,
			    0x80, 0x80);
	pr_debug("%s: special headset, start register writes\n", __func__);
	result2 = snd_soc_read(codec,
			MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT);
	while (result2 & 0x01)  {
		if (wcd_swch_level_remove(mbhc)) {
			pr_debug("%s: Switch level is low\n", __func__);
			break;
		}
		delay = delay + 50;
		snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_1_CTL,
				    0x60, 0x60);
		snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MICB_1_VAL,
			      0xC0);
		/* Wait for 50msec for MICBIAS to settle down */
		msleep(50);
		snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN,
					0x18, 0x10);
		/* Wait for 50msec for FSM to update result values */
		msleep(50);
		result2 = snd_soc_read(codec,
			MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT);
		if (!(result2 & 0x01))
			pr_debug("%s: Special headset detected in %d msecs\n",
					__func__, (delay * 2));
		if (delay == SPECIAL_HS_DETECT_TIME_MS) {
			pr_debug("%s: Spl headset didnt get detect in 4 sec\n",
					__func__);
			break;
		}
	}
	if (!(result2 & 0x01)) {
		pr_debug("%s: Headset with threshold found\n",  __func__);
		mbhc->micbias_enable = true;
		ret = true;
	}
	snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_1_CTL, 0x60, 0x00);
	snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN, 0x80, 0x00);
	snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL, reg);
	snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MICB_1_VAL, 0x20);
	snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN, 0x18, 0x00);

	pr_debug("%s: leave\n", __func__);
	return ret;
}

static void wcd_correct_swch_plug(struct work_struct *work)
{
	struct wcd_mbhc *mbhc;
@@ -568,7 +668,6 @@ static void wcd_correct_swch_plug(struct work_struct *work)
	unsigned long timeout;
	u16 result1, result2;
	bool wrk_complete = false;
	int delay = 0;

	pr_debug("%s: enter\n", __func__);

@@ -626,71 +725,15 @@ static void wcd_correct_swch_plug(struct work_struct *work)
		plug_type = MBHC_PLUG_TYPE_INVALID;

	if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) {
		/* Enable external voltage source to micbias if present */
		if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
			mbhc->mbhc_cb->enable_mb_source(codec, true);

		/* Enable micbias if not already enabled*/
		snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN,
				    0x80, 0x80);
		pr_debug("DEBUG special headset, start register writes");

		result2 = snd_soc_read(codec,
				MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT);
		while (result2 & 0x01)  {
			if (wcd_swch_level_remove(mbhc)) {
				pr_debug("%s: Switch level is low ", __func__);
				break;
			}
			delay = delay + 50;
			snd_soc_update_bits(codec,
					MSM8X16_WCD_A_ANALOG_MICB_1_CTL,
					0x60, 0x60);
			snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MICB_1_VAL,
					0xC0);
			/*
			 * Special headset needs micbias voltage above 2.4,
			 * it is taking time for micbias to rampup and
			 * for result2 to change.This delay is also dependent
			 * on type of headset.
			 */
			msleep(delay);
			snd_soc_update_bits(codec,
					MSM8X16_WCD_A_ANALOG_MICB_2_EN,
					0x18, 0x10);
			msleep(50);
			result2 = snd_soc_read(codec,
				MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT);
			if (!(result2 & 0x01))
				pr_debug("spl headset detected in %d msecs",
						delay);
			if (delay == 2000) {
				pr_debug("spl headset not detected in 2 sec");
				break;
			}
		}
		if (!result1 && !(result2 & 0x01)) {
			pr_debug("%s: Headset with threshold found\n",
				 __func__);
		if (wcd_is_special_headset(mbhc)) {
			pr_debug("%s: Special headset found %d\n",
					__func__, plug_type);
			plug_type = MBHC_PLUG_TYPE_HEADSET;
			mbhc->micbias_enable = true;
			goto report;
		}
		/* Disable autozero and put micbias back to 1.8V */
		snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN,
				    0x18, 0x00);
		snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_1_CTL,
			    0x60, 0x00);
		snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN,
				    0x80, 0x00);
		if (!mbhc->micbias_enable)
			snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MICB_1_VAL,
			0x20);

		/* Disable external voltage source to micbias if present */
		if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
			mbhc->mbhc_cb->enable_mb_source(codec, false);
	}

report:
	wcd_mbhc_find_plug_and_report(mbhc, plug_type);
exit:
	/* Disable external voltage source to micbias if present */
@@ -1280,6 +1323,14 @@ int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec,

	}

	/* Register event notifier */
	mbhc->nblock.notifier_call = wcd_event_notify;
	ret = msm8x16_register_notifier(codec, &mbhc->nblock);
	if (ret) {
		pr_err("%s: Failed to register notifier %d\n", __func__, ret);
		return ret;
	}

	init_waitqueue_head(&mbhc->wait_btn_press);
	mutex_init(&mbhc->codec_resource_lock);

@@ -1352,6 +1403,7 @@ err_btn_release_irq:
err_btn_press_irq:
	wcd9xxx_spmi_free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);
err_mbhc_sw_irq:
	msm8x16_unregister_notifier(codec, &mbhc->nblock);
	mutex_destroy(&mbhc->codec_resource_lock);
err:
	pr_debug("%s: leave ret %d\n", __func__, ret);
@@ -1361,6 +1413,7 @@ EXPORT_SYMBOL(wcd_mbhc_init);

void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
{
	struct snd_soc_codec *codec = mbhc->codec;

	wcd9xxx_spmi_free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);
	wcd9xxx_spmi_free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc);
@@ -1368,6 +1421,7 @@ void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
	wcd9xxx_spmi_free_irq(mbhc->intr_ids->mbhc_hs_ins_rem_intr, mbhc);
	wcd9xxx_spmi_free_irq(mbhc->intr_ids->hph_left_ocp, mbhc);
	wcd9xxx_spmi_free_irq(mbhc->intr_ids->hph_right_ocp, mbhc);
	msm8x16_unregister_notifier(codec, &mbhc->nblock);
	mutex_destroy(&mbhc->codec_resource_lock);
}
EXPORT_SYMBOL(wcd_mbhc_deinit);
+1 −0
Original line number Diff line number Diff line
@@ -104,6 +104,7 @@ struct wcd_mbhc {

	/* Work to correct accessory type */
	struct work_struct correct_plug_swch;
	struct notifier_block nblock;
};

#define WCD_MBHC_CAL_BTN_DET_PTR(cali) ( \