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

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

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

parents c61f5e37 23163fa7
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