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

Commit 23163fa7 authored by Phani Kumar Uppalapati's avatar Phani Kumar Uppalapati
Browse files

ASoC: wcd9xxx: Add support to compute hph L & R impedance



Add support to compute impedance of the headphone left
and right on Tapan codec. This can be used to adjust gain
according to the value of impedance so that user can have
consistent volume level with different headphones.

Change-Id: Id187bbfaf20c428d367bffa77538182554917e09
Signed-off-by: default avatarPhani Kumar Uppalapati <phaniu@codeaurora.org>
parent 86155e46
Loading
Loading
Loading
Loading
+270 −2
Original line number Diff line number Diff line
@@ -45,6 +45,9 @@
#define TAPAN_VDD_CX_OPTIMAL_UA 10000
#define TAPAN_VDD_CX_SLEEP_UA 2000

/* RX_HPH_CNP_WG_TIME increases by 0.24ms */
#define TAPAN_WG_TIME_FACTOR_US  240

static struct regulator *tapan_codec_find_regulator(
	struct snd_soc_codec *codec,
	const char *name);
@@ -90,6 +93,12 @@ MODULE_PARM_DESC(spkr_drv_wrnd,

#define TAPAN_IRQ_MBHC_JACK_SWITCH 21

/*
 * Multiplication factor to compute impedance on Tapan
 * This is computed from (Vx / (m*Ical)) = (10mV/(180*30uA))
 */
#define TAPAN_ZDET_MUL_FACTOR 1852

enum {
	AIF1_PB = 0,
	AIF1_CAP,
@@ -244,6 +253,12 @@ struct tapan_priv {

	/* pointers to regulators required for chargepump */
	struct regulator *cp_regulators[CP_REG_MAX];

	/*
	 * list used to save/restore registers at start and
	 * end of impedance measurement
	 */
	struct list_head reg_save_restore;
};

static const u32 comp_shift[] = {
@@ -4937,6 +4952,255 @@ enum wcd9xxx_cdc_type tapan_get_cdc_type(void)
	return WCD9XXX_CDC_TYPE_TAPAN;
}

static void wcd9xxx_prepare_hph_pa(struct wcd9xxx_mbhc *mbhc,
				   struct list_head *lh)
{
	int i;
	struct snd_soc_codec *codec = mbhc->codec;
	u32 delay;

	const struct wcd9xxx_reg_mask_val reg_set_paon[] = {
		{WCD9XXX_A_CDC_CLSH_B1_CTL, 0x0F, 0x00},
		{WCD9XXX_A_RX_HPH_CHOP_CTL, 0xFF, 0xA4},
		{WCD9XXX_A_RX_HPH_OCP_CTL, 0xFF, 0x67},
		{WCD9XXX_A_RX_HPH_L_TEST, 0x1, 0x0},
		{WCD9XXX_A_RX_HPH_R_TEST, 0x1, 0x0},
		{WCD9XXX_A_RX_HPH_BIAS_WG_OCP, 0xFF, 0x1A},
		{WCD9XXX_A_RX_HPH_CNP_WG_CTL, 0xFF, 0xDB},
		{WCD9XXX_A_RX_HPH_CNP_WG_TIME, 0xFF, 0x2A},
		{TAPAN_A_CDC_CONN_RX2_B2_CTL, 0xFF, 0x10},
		{WCD9XXX_A_CDC_CLK_OTHR_CTL, 0xFF, 0x05},
		{WCD9XXX_A_CDC_RX1_B6_CTL, 0xFF, 0x81},
		{WCD9XXX_A_CDC_CLK_RX_B1_CTL, 0x03, 0x03},
		{WCD9XXX_A_RX_HPH_L_GAIN, 0xFF, 0x2C},
		{WCD9XXX_A_CDC_RX2_B6_CTL, 0xFF, 0x81},
		{WCD9XXX_A_RX_HPH_R_GAIN, 0xFF, 0x2C},
		{WCD9XXX_A_BUCK_CTRL_CCL_4, 0xFF, 0x50},
		{WCD9XXX_A_BUCK_CTRL_VCL_1, 0xFF, 0x08},
		{WCD9XXX_A_BUCK_CTRL_CCL_1, 0xFF, 0x5B},
		{WCD9XXX_A_NCP_CLK, 0xFF, 0x9C},
		{WCD9XXX_A_NCP_CLK, 0xFF, 0xFC},
		{WCD9XXX_A_BUCK_MODE_3, 0xFF, 0xCE},
		{WCD9XXX_A_BUCK_CTRL_CCL_3, 0xFF, 0x6B},
		{WCD9XXX_A_BUCK_CTRL_CCL_3, 0xFF, 0x6F},
		{TAPAN_A_RX_BUCK_BIAS1, 0xFF, 0x62},
		{TAPAN_A_RX_HPH_BIAS_PA, 0xFF, 0x7A},
		{TAPAN_A_CDC_CLK_RDAC_CLK_EN_CTL, 0xFF, 0x02},
		{TAPAN_A_CDC_CLK_RDAC_CLK_EN_CTL, 0xFF, 0x06},
		{WCD9XXX_A_RX_COM_BIAS, 0xFF, 0x80},
		{WCD9XXX_A_BUCK_MODE_3, 0xFF, 0xC6},
		{WCD9XXX_A_BUCK_MODE_4, 0xFF, 0xE6},
		{WCD9XXX_A_BUCK_MODE_5, 0xFF, 0x02},
		{WCD9XXX_A_BUCK_MODE_1, 0xFF, 0xA1},
		/* Delay 1ms */
		{WCD9XXX_A_NCP_EN, 0xFF, 0xFF},
		/* Delay 1ms */
		{WCD9XXX_A_BUCK_MODE_5, 0xFF, 0x03},
		{WCD9XXX_A_BUCK_MODE_5, 0xFF, 0x7B},
		{WCD9XXX_A_CDC_CLSH_B1_CTL, 0xFF, 0xE6},
		{WCD9XXX_A_RX_HPH_L_DAC_CTL, 0xFF, 0x40},
		{WCD9XXX_A_RX_HPH_L_DAC_CTL, 0xFF, 0xC0},
		{WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xFF, 0x40},
		{WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xFF, 0xC0},
		{WCD9XXX_A_NCP_STATIC, 0xFF, 0x08},
		{WCD9XXX_A_RX_HPH_L_DAC_CTL, 0x03, 0x01},
		{WCD9XXX_A_RX_HPH_R_DAC_CTL, 0x03, 0x01},
	};

	/*
	 * Configure PA in class-AB, -18dB gain,
	 * companding off, OCP off, Chopping ON
	 */
	for (i = 0; i < ARRAY_SIZE(reg_set_paon); i++) {
		/*
		 * Some of the codec registers like BUCK_MODE_1
		 * and NCP_EN requires 1ms wait time for them
		 * to take effect. Other register writes for
		 * PA configuration do not require any wait time.
		 */
		if (reg_set_paon[i].reg == WCD9XXX_A_BUCK_MODE_1 ||
		    reg_set_paon[i].reg == WCD9XXX_A_NCP_EN)
			delay = 1000;
		else
			delay = 0;
		wcd9xxx_soc_update_bits_push(codec, lh,
					     reg_set_paon[i].reg,
					     reg_set_paon[i].mask,
					     reg_set_paon[i].val, delay);
	}
	pr_debug("%s: PAs are prepared\n", __func__);
	return;
}

static int wcd9xxx_enable_static_pa(struct wcd9xxx_mbhc *mbhc, bool enable)
{
	struct snd_soc_codec *codec = mbhc->codec;
	int wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) *
				   TAPAN_WG_TIME_FACTOR_US;
	/*
	 * Tapan requires additional time to enable PA.
	 * It is observed during experiments that we need
	 * an additional wait time about 0.35 times of
	 * the WG_TIME
	 */
	wg_time += (int) (wg_time * 35) / 100;

	snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30,
			    enable ? 0x30 : 0x0);
	/* Wait for wave gen time to avoid pop noise */
	usleep_range(wg_time, wg_time + WCD9XXX_USLEEP_RANGE_MARGIN_US);
	pr_debug("%s: PAs are %s as static mode (wg_time %d)\n", __func__,
		 enable ? "enabled" : "disabled", wg_time);
	return 0;
}

static int tapan_setup_zdet(struct wcd9xxx_mbhc *mbhc,
			    enum mbhc_impedance_detect_stages stage)
{

	int ret = 0;
	struct snd_soc_codec *codec = mbhc->codec;
	struct tapan_priv *tapan = snd_soc_codec_get_drvdata(codec);
	const int mux_wait_us = 25;

	switch (stage) {

	case PRE_MEAS:
		INIT_LIST_HEAD(&tapan->reg_save_restore);
		/* Configure PA */
		wcd9xxx_prepare_hph_pa(mbhc, &tapan->reg_save_restore);

#define __wr(reg, mask, value)						  \
	do {								  \
		ret = wcd9xxx_soc_update_bits_push(codec,		  \
						   &tapan->reg_save_restore, \
						   reg, mask, value, 0);  \
		if (ret < 0)						  \
			return ret;					  \
	} while (0)

		/* Setup MBHC */
		__wr(WCD9XXX_A_MBHC_SCALING_MUX_1, 0x7F, 0x40);
		__wr(WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0);
		__wr(WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0xFF, 0x78);
		__wr(WCD9XXX_A_TX_7_MBHC_EN, 0xFF, 0xEC);
		__wr(WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0xFF, 0x45);
		__wr(WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL, 0xFF, 0x80);

		__wr(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xFF, 0x0A);
		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
		__wr(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xFF, 0x02);

		/* Enable Impedance Detection */
		__wr(WCD9XXX_A_MBHC_HPH, 0xFF, 0xC8);

		/*
		 * CnP setup for 0mV
		 * Route static data as input to noise shaper
		 */
		__wr(TAPAN_A_CDC_RX1_B3_CTL, 0xFF, 0x02);
		__wr(TAPAN_A_CDC_RX2_B3_CTL, 0xFF, 0x02);

		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_TEST,
				    0x02, 0x00);
		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_TEST,
				    0x02, 0x00);

		/* Reset the HPHL static data pointer */
		__wr(TAPAN_A_CDC_RX1_B2_CTL, 0xFF, 0x00);
		/* Four consecutive writes to set 0V as static data input */
		snd_soc_write(codec, TAPAN_A_CDC_RX1_B1_CTL, 0x00);
		snd_soc_write(codec, TAPAN_A_CDC_RX1_B1_CTL, 0x00);
		snd_soc_write(codec, TAPAN_A_CDC_RX1_B1_CTL, 0x00);
		snd_soc_write(codec, TAPAN_A_CDC_RX1_B1_CTL, 0x00);

		/* Reset the HPHR static data pointer */
		__wr(TAPAN_A_CDC_RX2_B2_CTL, 0xFF, 0x00);
		/* Four consecutive writes to set 0V as static data input */
		snd_soc_write(codec, TAPAN_A_CDC_RX2_B1_CTL, 0x00);
		snd_soc_write(codec, TAPAN_A_CDC_RX2_B1_CTL, 0x00);
		snd_soc_write(codec, TAPAN_A_CDC_RX2_B1_CTL, 0x00);
		snd_soc_write(codec, TAPAN_A_CDC_RX2_B1_CTL, 0x00);

		/* Enable the HPHL and HPHR PA */
		wcd9xxx_enable_static_pa(mbhc, true);
		break;
	case POST_MEAS:
		/* Turn off ICAL */
		snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xF0);

		wcd9xxx_enable_static_pa(mbhc, false);

		/*
		 * Setup CnP wavegen to ramp to the desired
		 * output using a 40ms ramp
		 */

		/* CnP wavegen current to 0.5uA */
		snd_soc_write(codec, WCD9XXX_A_RX_HPH_BIAS_WG_OCP, 0x1A);
		/* Set the current division ratio to 2000 */
		snd_soc_write(codec, WCD9XXX_A_RX_HPH_CNP_WG_CTL, 0xDF);
		/* Set the wavegen timer to max (60msec) */
		snd_soc_write(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME, 0xA0);
		/* Set the CnP reference current to sc_bias */
		snd_soc_write(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x6D);

		snd_soc_write(codec, TAPAN_A_CDC_RX1_B2_CTL, 0x00);
		/* Four consecutive writes to set -10mV as static data input */
		snd_soc_write(codec, TAPAN_A_CDC_RX1_B1_CTL, 0x00);
		snd_soc_write(codec, TAPAN_A_CDC_RX1_B1_CTL, 0x1F);
		snd_soc_write(codec, TAPAN_A_CDC_RX1_B1_CTL, 0x19);
		snd_soc_write(codec, TAPAN_A_CDC_RX1_B1_CTL, 0xAA);

		snd_soc_write(codec, TAPAN_A_CDC_RX2_B2_CTL, 0x00);
		/* Four consecutive writes to set -10mV as static data input */
		snd_soc_write(codec, TAPAN_A_CDC_RX2_B1_CTL, 0x00);
		snd_soc_write(codec, TAPAN_A_CDC_RX2_B1_CTL, 0x1F);
		snd_soc_write(codec, TAPAN_A_CDC_RX2_B1_CTL, 0x19);
		snd_soc_write(codec, TAPAN_A_CDC_RX2_B1_CTL, 0xAA);

		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_TEST,
				    0x02, 0x02);
		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_TEST,
				    0x02, 0x02);
		/* Enable the HPHL and HPHR PA and wait for 60mS */
		wcd9xxx_enable_static_pa(mbhc, true);

		snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
				    0x7F, 0x40);
		usleep_range(mux_wait_us,
				mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
		break;
	case PA_DISABLE:
		wcd9xxx_enable_static_pa(mbhc, false);
		wcd9xxx_restore_registers(codec, &tapan->reg_save_restore);
		break;
	}
#undef __wr

	return ret;
}

static void tapan_compute_impedance(s16 *l, s16 *r, uint32_t *zl, uint32_t *zr)
{
	int zln, zld;
	int zrn, zrd;
	int rl = 0, rr = 0;

	zln = (l[1] - l[0]) * TAPAN_ZDET_MUL_FACTOR;
	zld = (l[2] - l[0]);
	if (zld)
		rl = zln / zld;

	zrn = (r[1] - r[0]) * TAPAN_ZDET_MUL_FACTOR;
	zrd = (r[2] - r[0]);
	if (zrd)
		rr = zrn / zrd;

	*zl = rl;
	*zr = rr;
}

static const struct wcd9xxx_mbhc_cb mbhc_cb = {
	.enable_mux_bias_block = tapan_enable_mux_bias_block,
	.cfilt_fast_mode = tapan_put_cfilt_fast_mode,
@@ -4946,6 +5210,8 @@ static const struct wcd9xxx_mbhc_cb mbhc_cb = {
	.select_cfilt = tapan_select_cfilt,
	.free_irq = tapan_free_irq,
	.get_cdc_type = tapan_get_cdc_type,
	.setup_zdet = tapan_setup_zdet,
	.compute_impedance = tapan_compute_impedance,
};

int tapan_hs_detect(struct snd_soc_codec *codec,
@@ -5020,7 +5286,8 @@ static int tapan_post_reset_cb(struct wcd9xxx *wcd9xxx)
		rco_clk_rate = TAPAN_MCLK_CLK_9P6MHZ;

	ret = wcd9xxx_mbhc_init(&tapan->mbhc, &tapan->resmgr, codec, NULL,
				&mbhc_cb, rco_clk_rate, false);
				&mbhc_cb, rco_clk_rate,
				TAPAN_CDC_ZDET_SUPPORTED);
	if (ret)
		pr_err("%s: mbhc init failed %d\n", __func__, ret);
	else
@@ -5211,7 +5478,8 @@ static int tapan_codec_probe(struct snd_soc_codec *codec)
		rco_clk_rate = TAPAN_MCLK_CLK_9P6MHZ;

	ret = wcd9xxx_mbhc_init(&tapan->mbhc, &tapan->resmgr, codec, NULL,
				&mbhc_cb, rco_clk_rate, false);
				&mbhc_cb, rco_clk_rate,
				TAPAN_CDC_ZDET_SUPPORTED);

	if (ret) {
		pr_err("%s: mbhc init failed %d\n", __func__, ret);
+3 −1
Original line number Diff line number Diff line
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -24,6 +24,8 @@

#define TAPAN_REG_VAL(reg, val)		{reg, 0, val}

#define TAPAN_CDC_ZDET_SUPPORTED  true

extern const u8 tapan_reg_readable[TAPAN_CACHE_SIZE];
extern const u8 tapan_reset_reg_defaults[TAPAN_CACHE_SIZE];
struct tapan_codec_dai_data {
+203 −2
Original line number Diff line number Diff line
@@ -49,6 +49,9 @@

#define DAPM_MICBIAS2_EXTERNAL_STANDALONE "MIC BIAS2 External Standalone"

/* RX_HPH_CNP_WG_TIME increases by 0.24ms */
#define TAIKO_WG_TIME_FACTOR_US	240

static atomic_t kp_taiko_priv;
static int spkr_drv_wrnd_param_set(const char *val,
				   const struct kernel_param *kp);
@@ -509,6 +512,12 @@ struct taiko_priv {

	int (*machine_codec_event_cb)(struct snd_soc_codec *codec,
			enum wcd9xxx_codec_event);

	/*
	 * list used to save/restore registers at start and
	 * end of impedance measurement
	 */
	struct list_head reg_save_restore;
};

static const u32 comp_shift[] = {
@@ -6287,6 +6296,198 @@ static int taiko_device_down(struct wcd9xxx *wcd9xxx)
	return 0;
}

static int wcd9xxx_prepare_static_pa(struct wcd9xxx_mbhc *mbhc,
				     struct list_head *lh)
{
	int i;
	struct snd_soc_codec *codec = mbhc->codec;

	const struct wcd9xxx_reg_mask_val reg_set_paon[] = {
		{WCD9XXX_A_RX_HPH_OCP_CTL, 0x18, 0x00},
		{WCD9XXX_A_RX_HPH_L_TEST, 0x1, 0x0},
		{WCD9XXX_A_RX_HPH_R_TEST, 0x1, 0x0},
		{WCD9XXX_A_RX_HPH_BIAS_WG_OCP, 0xff, 0x1A},
		{WCD9XXX_A_RX_HPH_CNP_WG_CTL, 0xff, 0xDB},
		{WCD9XXX_A_RX_HPH_CNP_WG_TIME, 0xff, 0x15},
		{WCD9XXX_A_CDC_RX1_B6_CTL, 0xff, 0x81},
		{WCD9XXX_A_CDC_CLK_RX_B1_CTL, 0x01, 0x01},
		{WCD9XXX_A_RX_HPH_CHOP_CTL, 0xff, 0xA4},
		{WCD9XXX_A_RX_HPH_L_GAIN, 0xff, 0x2C},
		{WCD9XXX_A_CDC_RX2_B6_CTL, 0xff, 0x81},
		{WCD9XXX_A_CDC_CLK_RX_B1_CTL, 0x02, 0x02},
		{WCD9XXX_A_RX_HPH_R_GAIN, 0xff, 0x2C},
		{WCD9XXX_A_NCP_CLK, 0xff, 0xFC},
		{WCD9XXX_A_BUCK_CTRL_CCL_3, 0xff, 0x60},
		{WCD9XXX_A_RX_COM_BIAS, 0xff, 0x80},
		{WCD9XXX_A_BUCK_MODE_3, 0xff, 0xC6},
		{WCD9XXX_A_BUCK_MODE_4, 0xff, 0xE6},
		{WCD9XXX_A_BUCK_MODE_5, 0xff, 0x02},
		{WCD9XXX_A_BUCK_MODE_1, 0xff, 0xA1},
		{WCD9XXX_A_NCP_EN, 0xff, 0xFF},
		{WCD9XXX_A_BUCK_MODE_5, 0xff, 0x7B},
		{WCD9XXX_A_CDC_CLSH_B1_CTL, 0xff, 0xE6},
		{WCD9XXX_A_RX_HPH_L_DAC_CTL, 0xff, 0xC0},
		{WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xff, 0xC0},
	};

	for (i = 0; i < ARRAY_SIZE(reg_set_paon); i++)
		wcd9xxx_soc_update_bits_push(codec, lh,
					     reg_set_paon[i].reg,
					     reg_set_paon[i].mask,
					     reg_set_paon[i].val, 0);
	pr_debug("%s: PAs are prepared\n", __func__);

	return 0;
}

static int wcd9xxx_enable_static_pa(struct wcd9xxx_mbhc *mbhc, bool enable)
{
	struct snd_soc_codec *codec = mbhc->codec;
	const int wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) *
			    TAIKO_WG_TIME_FACTOR_US;

	snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30,
			    enable ? 0x30 : 0x0);
	/* Wait for wave gen time to avoid pop noise */
	usleep_range(wg_time, wg_time + WCD9XXX_USLEEP_RANGE_MARGIN_US);
	pr_debug("%s: PAs are %s as static mode (wg_time %d)\n", __func__,
		 enable ? "enabled" : "disabled", wg_time);
	return 0;
}

static int taiko_setup_zdet(struct wcd9xxx_mbhc *mbhc,
			    enum mbhc_impedance_detect_stages stage)
{
	int ret = 0;
	struct snd_soc_codec *codec = mbhc->codec;
	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
	const int ramp_wait_us = 18 * 1000;

#define __wr(reg, mask, value)						  \
	do {								  \
		ret = wcd9xxx_soc_update_bits_push(codec,		  \
						   &taiko->reg_save_restore, \
						   reg, mask, value, 0);  \
		if (ret < 0)						  \
			return ret;					  \
	} while (0)

	switch (stage) {

	case PRE_MEAS:
		INIT_LIST_HEAD(&taiko->reg_save_restore);
		wcd9xxx_prepare_static_pa(mbhc, &taiko->reg_save_restore);
		wcd9xxx_enable_static_pa(mbhc, true);

		/*
		 * save old value of registers and write the new value to
		 * restore old value back, WCD9XXX_A_CDC_PA_RAMP_B{1,2,3,4}_CTL
		 * registers don't need to be restored as those are solely used
		 * by impedance detection.
		 */
		/* Phase 1 */
		/* Reset the PA Ramp */
		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x1C);
		/*
		 * Connect the PA Ramp to PA chain and release reset with
		 * keep it connected.
		 */
		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x1F);
		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x03);
		/*
		 * Program the PA Ramp to FS_48K, L shift 1 and sample
		 * num to 24
		 */
		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B3_CTL,
			      0x3 << 4 | 0x6);
		/* 0x56 for 10mv.  0xC0 is for 50mv */
		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B4_CTL, 0xC0);
		/* Enable MBHC MUX, Set MUX current to 37.5uA and ADC7 */
		__wr(WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0);
		__wr(WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0);
		__wr(WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0xFF, 0x78);
		__wr(WCD9XXX_A_TX_7_MBHC_EN, 0xFF, 0x8C);
		/* Change NSA and NAVG */
		__wr(WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x4 << 4, 0x4 << 4);
		__wr(WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL, 0xFF, 0x10);
		/* Reset MBHC and set it up for STA */
		__wr(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xFF, 0x0A);
		__wr(WCD9XXX_A_CDC_MBHC_EN_CTL, 0xFF, 0x02);
		__wr(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xFF, 0x02);

		/* Set HPH_MBHC for zdet */
		__wr(WCD9XXX_A_MBHC_HPH, 0xB3, 0x80);
		break;
	case POST_MEAS:
		/* Phase 2 */
		/* Start the PA ramp on HPH L and R */
		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x05);
		/* Ramp generator takes ~17ms */
		usleep_range(ramp_wait_us,
				ramp_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);

		/* Disable Ical */
		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00);
		/* Ramp generator takes ~17ms */
		usleep_range(ramp_wait_us,
				ramp_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
		break;
	case PA_DISABLE:
		/* Ramp HPH L & R back to Zero */
		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x0A);
		/* Ramp generator takes ~17ms */
		usleep_range(ramp_wait_us,
				ramp_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00);

		/* Clean up starts */
		/* Turn off PA ramp generator */
		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x0);
		wcd9xxx_enable_static_pa(mbhc, false);
		wcd9xxx_restore_registers(codec, &taiko->reg_save_restore);
		break;
	}
#undef __wr

	return ret;
}

static void taiko_compute_impedance(s16 *l, s16 *r, uint32_t *zl, uint32_t *zr)
{

	int64_t rl, rr = 0; /* milliohm */
	const int alphal = 364; /* 0.005555 * 65536 = 364.05 */
	const int alphar = 364; /* 0.005555 * 65536 = 364.05 */
	const int beta = 3855; /* 0.011765 * 5 * 65536 = 3855.15 */
	const int rref = 11333; /* not scaled up */
	const int shift = 16;

	rl = (int)(l[0] - l[1]) * 1000 / (l[0] - l[2]);
	rl = rl * rref * alphal;
	rl = rl >> shift;
	rl = rl * beta;
	rl = rl >> shift;
	*zl = rl;

	rr = (int)(r[0] - r[1]) * 1000 / (r[0] - r[2]);
	rr = rr * rref  * alphar;
	rr = rr >> shift;
	rr = rr * beta;
	rr = rr >> shift;
	*zr = rr;
}

static enum wcd9xxx_cdc_type taiko_get_cdc_type(void)
{
	return WCD9XXX_CDC_TYPE_TAIKO;
}

static const struct wcd9xxx_mbhc_cb mbhc_cb = {
	.get_cdc_type = taiko_get_cdc_type,
	.setup_zdet = taiko_setup_zdet,
	.compute_impedance = taiko_compute_impedance,
};

static int taiko_post_reset_cb(struct wcd9xxx *wcd9xxx)
{
	int ret = 0;
@@ -6332,7 +6533,7 @@ static int taiko_post_reset_cb(struct wcd9xxx *wcd9xxx)

		ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec,
					taiko_enable_mbhc_micbias,
					NULL, rco_clk_rate, true);
					&mbhc_cb, rco_clk_rate, true);
		if (ret)
			pr_err("%s: mbhc init failed %d\n", __func__, ret);
		else
@@ -6521,7 +6722,7 @@ static int taiko_codec_probe(struct snd_soc_codec *codec)
	/* init and start mbhc */
	ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec,
				taiko_enable_mbhc_micbias,
				NULL, rco_clk_rate, true);
				&mbhc_cb, rco_clk_rate, true);
	if (ret) {
		pr_err("%s: mbhc init failed %d\n", __func__, ret);
		goto err_init;
+43 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
 */

#include <linux/module.h>
#include <linux/slab.h>
#include <sound/soc.h>
#include <linux/kernel.h>
#include <linux/delay.h>
@@ -32,6 +33,8 @@

#define MAX_IMPED_PARAMS 13

#define USLEEP_RANGE_MARGIN_US 100

struct wcd9xxx_imped_val {
	u32 imped_val;
	u8 index;
@@ -614,6 +617,46 @@ static void wcd9xxx_clsh_comp_req(struct snd_soc_codec *codec,
	}
}


int wcd9xxx_soc_update_bits_push(struct snd_soc_codec *codec,
					struct list_head *list,
					uint16_t reg, uint8_t mask,
					uint8_t value, int delay)
{
	int rc;
	struct wcd9xxx_register_save_node *node;

	node = kmalloc(sizeof(*node), GFP_KERNEL);
	if (unlikely(!node)) {
		pr_err("%s: Not enough memory\n", __func__);
		return -ENOMEM;
	}
	node->reg = reg;
	node->value = snd_soc_read(codec, reg);
	list_add(&node->lh, list);
	if (mask == 0xFF)
		rc = snd_soc_write(codec, reg, value);
	else
		rc = snd_soc_update_bits(codec, reg, mask, value);
	if (delay)
		usleep_range(delay, delay + USLEEP_RANGE_MARGIN_US);
	return rc;
}
EXPORT_SYMBOL(wcd9xxx_soc_update_bits_push);

void wcd9xxx_restore_registers(struct snd_soc_codec *codec,
			       struct list_head *lh)
{
	struct wcd9xxx_register_save_node *node, *nodetmp;

	list_for_each_entry_safe(node, nodetmp, lh, lh) {
		snd_soc_write(codec, node->reg, node->value);
		list_del(&node->lh);
		kfree(node);
	}
}
EXPORT_SYMBOL(wcd9xxx_restore_registers);

static void wcd9xxx_enable_buck_mode(struct snd_soc_codec *codec,
		u8 buck_vref)
{
+12 −0
Original line number Diff line number Diff line
@@ -81,4 +81,16 @@ enum wcd9xxx_codec_event {
	WCD9XXX_CODEC_EVENT_CODEC_UP = 0,
};

struct wcd9xxx_register_save_node {
	struct list_head lh;
	u16 reg;
	u16 value;
};

extern int wcd9xxx_soc_update_bits_push(struct snd_soc_codec *codec,
					struct list_head *lh,
					uint16_t reg, uint8_t mask,
					uint8_t value, int delay);
extern void wcd9xxx_restore_registers(struct snd_soc_codec *codec,
				      struct list_head *lh);
#endif
Loading