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

Commit b8c3cd1e 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: Update RCO and MCLK sequence for wcd9330"

parents dacafe8c dfc60524
Loading
Loading
Loading
Loading
+71 −13
Original line number Diff line number Diff line
@@ -434,7 +434,9 @@ struct tomtom_priv {

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

	int (*codec_ext_clk_en_cb)(struct snd_soc_codec *codec,
			int enable, bool dapm);
	int (*codec_get_ext_clk_cnt) (void);
	/*
	 * list used to save/restore registers at start and
	 * end of impedance measurement
@@ -2434,6 +2436,50 @@ static int tomtom_codec_enable_adc(struct snd_soc_dapm_widget *w,
	return 0;
}

/* tomtom_codec_internal_rco_ctrl( )
 * Make sure that BG_CLK_LOCK is not acquired. Exit if acquired to avoid
 * potential deadlock as ext_clk_en_cb() also tries to acquire the same
 * lock to enable MCLK for RCO calibration
 */
static int tomtom_codec_internal_rco_ctrl(struct snd_soc_codec *codec,
					  bool enable)
{
	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);

	if (mutex_is_locked(&tomtom->resmgr.codec_bg_clk_lock)) {
		dev_err(codec->dev, "%s: BG_CLK already acquired\n",
			__func__);
		return -EINVAL;
	}

	if (enable) {
		if (wcd9xxx_resmgr_get_clk_type(&tomtom->resmgr) ==
		    WCD9XXX_CLK_RCO) {
			WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
			wcd9xxx_resmgr_get_clk_block(&tomtom->resmgr,
						     WCD9XXX_CLK_RCO);
			WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
		} else {
			tomtom->codec_ext_clk_en_cb(codec, true, false);
			WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
			tomtom->resmgr.ext_clk_users =
					tomtom->codec_get_ext_clk_cnt();
			wcd9xxx_resmgr_get_clk_block(&tomtom->resmgr,
						     WCD9XXX_CLK_RCO);
			WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
			tomtom->codec_ext_clk_en_cb(codec, false, false);
		}

	} else {
		WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
		wcd9xxx_resmgr_put_clk_block(&tomtom->resmgr,
					     WCD9XXX_CLK_RCO);
		WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
	}

	return 0;
}

static int tomtom_codec_enable_aux_pga(struct snd_soc_dapm_widget *w,
	struct snd_kcontrol *kcontrol, int event)
{
@@ -2447,8 +2493,10 @@ static int tomtom_codec_enable_aux_pga(struct snd_soc_dapm_widget *w,
		WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
		wcd9xxx_resmgr_get_bandgap(&tomtom->resmgr,
					   WCD9XXX_BANDGAP_AUDIO_MODE);
		WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
		/* AUX PGA requires RCO or MCLK */
		wcd9xxx_resmgr_get_clk_block(&tomtom->resmgr, WCD9XXX_CLK_RCO);
		tomtom_codec_internal_rco_ctrl(codec, true);
		WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
		wcd9xxx_resmgr_enable_rx_bias(&tomtom->resmgr, 1);
		WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
		break;
@@ -2456,9 +2504,11 @@ static int tomtom_codec_enable_aux_pga(struct snd_soc_dapm_widget *w,
	case SND_SOC_DAPM_POST_PMD:
		WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
		wcd9xxx_resmgr_enable_rx_bias(&tomtom->resmgr, 0);
		WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
		tomtom_codec_internal_rco_ctrl(codec, false);
		WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
		wcd9xxx_resmgr_put_bandgap(&tomtom->resmgr,
					   WCD9XXX_BANDGAP_AUDIO_MODE);
		wcd9xxx_resmgr_put_clk_block(&tomtom->resmgr, WCD9XXX_CLK_RCO);
		WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
		break;
	}
@@ -3100,13 +3150,11 @@ static int __tomtom_codec_enable_ldo_h(struct snd_soc_dapm_widget *w,
			WCD9XXX_BG_CLK_LOCK(&priv->resmgr);
			wcd9xxx_resmgr_get_bandgap(&priv->resmgr,
						   WCD9XXX_BANDGAP_AUDIO_MODE);
			wcd9xxx_resmgr_get_clk_block(&priv->resmgr,
						     WCD9XXX_CLK_RCO);
			WCD9XXX_BG_CLK_UNLOCK(&priv->resmgr);
			tomtom_codec_internal_rco_ctrl(codec, true);
			snd_soc_update_bits(codec, TOMTOM_A_LDO_H_MODE_1,
					    1 << 7, 1 << 7);
			wcd9xxx_resmgr_put_clk_block(&priv->resmgr,
						     WCD9XXX_CLK_RCO);
			WCD9XXX_BG_CLK_UNLOCK(&priv->resmgr);
			tomtom_codec_internal_rco_ctrl(codec, false);
			pr_debug("%s: ldo_h_users %d\n", __func__,
				 priv->ldo_h_users);
			/* LDO enable requires 1ms to settle down */
@@ -3115,13 +3163,11 @@ static int __tomtom_codec_enable_ldo_h(struct snd_soc_dapm_widget *w,
		break;
	case SND_SOC_DAPM_POST_PMD:
		if (--priv->ldo_h_users == 0) {
			WCD9XXX_BG_CLK_LOCK(&priv->resmgr);
			wcd9xxx_resmgr_get_clk_block(&priv->resmgr,
						     WCD9XXX_CLK_RCO);
			tomtom_codec_internal_rco_ctrl(codec, true);
			snd_soc_update_bits(codec, TOMTOM_A_LDO_H_MODE_1,
					    1 << 7, 0);
			wcd9xxx_resmgr_put_clk_block(&priv->resmgr,
						     WCD9XXX_CLK_RCO);
			tomtom_codec_internal_rco_ctrl(codec, false);
			WCD9XXX_BG_CLK_LOCK(&priv->resmgr);
			wcd9xxx_resmgr_put_bandgap(&priv->resmgr,
						   WCD9XXX_BANDGAP_AUDIO_MODE);
			WCD9XXX_BG_CLK_UNLOCK(&priv->resmgr);
@@ -6519,6 +6565,18 @@ void tomtom_event_register(
}
EXPORT_SYMBOL(tomtom_event_register);

void tomtom_register_ext_clk_cb(
	int (*codec_ext_clk_en)(struct snd_soc_codec *codec,
				int enable, bool dapm),
	int (*get_ext_clk_cnt) (void),
	struct snd_soc_codec *codec)
{
	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
	tomtom->codec_ext_clk_en_cb =  codec_ext_clk_en;
	tomtom->codec_get_ext_clk_cnt = get_ext_clk_cnt;
}
EXPORT_SYMBOL(tomtom_register_ext_clk_cb);

static void tomtom_init_slim_slave_cfg(struct snd_soc_codec *codec)
{
	struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+5 −1
Original line number Diff line number Diff line
@@ -111,5 +111,9 @@ extern void tomtom_event_register(
	int (*machine_event_cb)(struct snd_soc_codec *codec,
				enum wcd9xxx_codec_event),
	struct snd_soc_codec *codec);

extern void tomtom_register_ext_clk_cb(
	int (*codec_ext_clk_en)(struct snd_soc_codec *codec,
				int enable, bool dapm),
	int (*get_ext_clk_cnt) (void),
	struct snd_soc_codec *codec);
#endif
+231 −65
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <linux/mfd/wcd9xxx/core.h>
#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
#include <uapi/linux/mfd/wcd9xxx/wcd9320_registers.h>
#include <linux/mfd/wcd9xxx/wcd9330_registers.h>
#include <linux/mfd/wcd9xxx/pdata.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -98,6 +99,11 @@ static char wcd9xxx_event_string[][64] = {
	"WCD9XXX_EVENT_LAST",
};

#define WCD9XXX_RCO_CALIBRATION_RETRY_COUNT 5
#define WCD9XXX_RCO_CALIBRATION_DELAY_US 5000
#define WCD9XXX_USLEEP_RANGE_MARGIN_US 100
#define WCD9XXX_RCO_CALIBRATION_DELAY_INC_US 1000

struct wcd9xxx_resmgr_cond_entry {
	unsigned short reg;
	int shift;
@@ -130,7 +136,7 @@ static void wcd9xxx_disable_bg(struct wcd9xxx_resmgr *resmgr)
	/* Disable bg */
	snd_soc_update_bits(resmgr->codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL,
			    0x03, 0x00);
	usleep_range(100, 100);
	usleep_range(100, 110);
	/* Notify bg mode change */
	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_OFF);
}
@@ -148,7 +154,7 @@ static void wcd9xxx_enable_bg(struct wcd9xxx_resmgr *resmgr)
	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x80);
	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x04, 0x04);
	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x01, 0x01);
	usleep_range(1000, 1000);
	usleep_range(1000, 1100);
	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x00);
}

@@ -193,14 +199,26 @@ static void wcd9xxx_disable_clock_block(struct wcd9xxx_resmgr *resmgr)
	else
		wcd9xxx_resmgr_notifier_call(resmgr,
					     WCD9XXX_EVENT_PRE_MCLK_OFF);
	/* Disable clock */
	if (resmgr->codec_type != WCD9XXX_CDC_TYPE_HELICON) {

	switch (resmgr->codec_type) {
	case WCD9XXX_CDC_TYPE_HELICON:
		break;
	case WCD9XXX_CDC_TYPE_TOMTOM:
		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x00);
		usleep_range(50, 55);
		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x02);
		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x40, 0x40);
		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x40, 0x00);
		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x01, 0x00);
		break;
	default:
		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x00);
		usleep_range(50, 50);
		usleep_range(50, 55);
		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x02);
		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x00);
		usleep_range(50, 50);
		break;
	}
	usleep_range(50, 55);
	/* Notify */
	if (resmgr->clk_type == WCD9XXX_CLK_RCO) {
		wcd9xxx_resmgr_notifier_call(resmgr,
@@ -267,7 +285,7 @@ void wcd9xxx_resmgr_post_ssr(struct wcd9xxx_resmgr *resmgr)
void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr,
				const enum wcd9xxx_bandgap_type choice)
{
	enum wcd9xxx_clock_type clock_save;
	enum wcd9xxx_clock_type clock_save = WCD9XXX_CLK_OFF;

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

@@ -284,11 +302,13 @@ void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr,
				WCD9XXX_BANDGAP_MBHC_MODE);

			/* BG mode can be changed only with clock off */
			if (resmgr->codec_type != WCD9XXX_CDC_TYPE_TOMTOM)
				clock_save = wcd9xxx_save_clock(resmgr);
			/* Swtich BG mode */
			wcd9xxx_disable_bg(resmgr);
			wcd9xxx_enable_bg_audio(resmgr);
			/* restore clock */
			if (resmgr->codec_type != WCD9XXX_CDC_TYPE_TOMTOM)
				wcd9xxx_restore_clock(resmgr, clock_save);
		} else if (resmgr->bg_audio_users == 1) {
			/* currently off, just enable it */
@@ -397,12 +417,12 @@ int wcd9xxx_resmgr_enable_config_mode(struct wcd9xxx_resmgr *resmgr, int enable)
		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x10, 0);
		/* bandgap mode to fast */
		snd_soc_write(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x17);
		usleep_range(5, 5);
		usleep_range(5, 10);
		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0x80);
		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0x80);
		usleep_range(10, 10);
		usleep_range(10, 20);
		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0);
		usleep_range(10000, 10000);
		usleep_range(10000, 10100);
		if (resmgr->codec_type != WCD9XXX_CDC_TYPE_HELICON)
			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
					0x08, 0x08);
@@ -423,72 +443,126 @@ int wcd9xxx_resmgr_enable_config_mode(struct wcd9xxx_resmgr *resmgr, int enable)
}

static void wcd9xxx_enable_clock_block(struct wcd9xxx_resmgr *resmgr,
				       int config_mode)
				enum wcd9xxx_clock_config_mode config_mode)
{
	struct snd_soc_codec *codec = resmgr->codec;
	unsigned long delay = WCD9XXX_RCO_CALIBRATION_DELAY_US;
	int num_retry = 0;

	pr_debug("%s: config_mode = %d\n", __func__, config_mode);

	/* transit to RCO requires mclk off */
	if (resmgr->codec_type != WCD9XXX_CDC_TYPE_HELICON)
	if (resmgr->codec_type != WCD9XXX_CDC_TYPE_HELICON &&
	    resmgr->codec_type != WCD9XXX_CDC_TYPE_TOMTOM)
		WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));

	if (config_mode) {
	if (config_mode == WCD9XXX_CFG_RCO) {
		/* Notify */
		wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_ON);
		/* enable RCO and switch to it */
		wcd9xxx_resmgr_enable_config_mode(resmgr, 1);
		if (resmgr->codec_type != WCD9XXX_CDC_TYPE_HELICON)
			snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
		usleep_range(1000, 1000);
		usleep_range(1000, 1100);
	} else if (config_mode == WCD9XXX_CFG_CAL_RCO) {
		snd_soc_update_bits(codec, TOMTOM_A_BIAS_OSC_BG_CTL,
				    0x01, 0x01);
		/* 1ms sleep required after BG enabled */
		usleep_range(1000, 1100);
		snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL, 0x80, 0x80);

		do {
			snd_soc_update_bits(codec,
					    TOMTOM_A_RCO_CALIBRATION_CTRL1,
					    0x80, 0x80);
			snd_soc_update_bits(codec,
					    TOMTOM_A_RCO_CALIBRATION_CTRL1,
					    0x80, 0x00);
			/* RCO calibration takes approx. 5ms */
			usleep_range(delay, delay +
					    WCD9XXX_USLEEP_RANGE_MARGIN_US);
			if (!(snd_soc_read(codec,
				TOMTOM_A_RCO_CALIBRATION_RESULT1) & 0x10))
				break;
			if (num_retry >= 3) {
				delay = delay +
					WCD9XXX_RCO_CALIBRATION_DELAY_INC_US;
			}
		} while (num_retry++ < WCD9XXX_RCO_CALIBRATION_RETRY_COUNT);
	} else {
		/* Notify */
		wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_MCLK_ON);
		/* switch to MCLK */
		if (resmgr->codec_type != WCD9XXX_CDC_TYPE_HELICON) {
			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
				0x08, 0x00);
		} else {

		switch (resmgr->codec_type) {
		case WCD9XXX_CDC_TYPE_HELICON:
			snd_soc_update_bits(codec,
					MSM8X10_WCD_A_CDC_CLK_PDM_CTL,
					0x03, 0x03);
			snd_soc_update_bits(codec,
					MSM8X10_WCD_A_CDC_TOP_CLK_CTL,
					0x0f, 0x0d);
		}

			/* if RCO is enabled, switch from it */
			if (snd_soc_read(codec, WCD9XXX_A_RC_OSC_FREQ) & 0x80)
				wcd9xxx_resmgr_enable_config_mode(resmgr, 0);
			break;
		case WCD9XXX_CDC_TYPE_TOMTOM:
			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
					    0x08, 0x00);
			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
					    0x40, 0x40);
			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
					    0x40, 0x00);
			/* clk source to ext clk and clk buff ref to VBG */
			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
					    0x0C, 0x04);
			break;
		default:
			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
					    0x08, 0x00);
			/* if RCO is enabled, switch from it */
			if (snd_soc_read(codec, WCD9XXX_A_RC_OSC_FREQ) & 0x80) {
			if (resmgr->codec_type != WCD9XXX_CDC_TYPE_HELICON)
				snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2,
					      0x02);
				wcd9xxx_resmgr_enable_config_mode(resmgr, 0);
			}
			/* clk source to ext clk and clk buff ref to VBG */
		if (resmgr->codec_type != WCD9XXX_CDC_TYPE_HELICON)
			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
					    0x0C, 0x04);
			break;
		}
	}

	if (config_mode != WCD9XXX_CFG_CAL_RCO) {
		if (resmgr->codec_type != WCD9XXX_CDC_TYPE_HELICON) {
		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x01, 0x01);
		/* sleep required by codec hardware to enable clock buffer */
			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
					    0x01, 0x01);
			/*
			 * sleep required by codec hardware to
			 * enable clock buffer
			 */
			usleep_range(1000, 1200);
		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x00);
			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2,
					    0x02, 0x00);
			/* on MCLK */
		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x04);
			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2,
					    0x04, 0x04);
			snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL,
					    0x01, 0x01);
		} else {
		snd_soc_update_bits(codec, MSM8X10_WCD_A_CDC_CLK_MCLK_CTL,
			snd_soc_update_bits(codec,
					    MSM8X10_WCD_A_CDC_CLK_MCLK_CTL,
					    0x01, 0x01);
		}
	usleep_range(50, 50);
	}
	usleep_range(50, 55);

	/* Notify */
	if (config_mode)
	if (config_mode == WCD9XXX_CFG_RCO)
		wcd9xxx_resmgr_notifier_call(resmgr,
					     WCD9XXX_EVENT_POST_RCO_ON);
	else
	else if (config_mode == WCD9XXX_CFG_MCLK)
		wcd9xxx_resmgr_notifier_call(resmgr,
					     WCD9XXX_EVENT_POST_MCLK_ON);
}
@@ -514,6 +588,8 @@ static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr,
void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
				  enum wcd9xxx_clock_type type)
{
	struct snd_soc_codec *codec = resmgr->codec;

	pr_debug("%s: current %d, requested %d, rco_users %d, mclk_users %d\n",
		 __func__, resmgr->clk_type, type,
		 resmgr->clk_rco_users, resmgr->clk_mclk_users);
@@ -523,25 +599,61 @@ void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
		if (++resmgr->clk_rco_users == 1 &&
		    resmgr->clk_type == WCD9XXX_CLK_OFF) {
			/* enable RCO and switch to it */
			wcd9xxx_enable_clock_block(resmgr, 1);
			wcd9xxx_enable_clock_block(resmgr, WCD9XXX_CFG_RCO);
			resmgr->clk_type = WCD9XXX_CLK_RCO;
		} else if (resmgr->clk_rco_users == 1 &&
			   resmgr->clk_type == WCD9XXX_CLK_MCLK &&
			   resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) {
			/*
			 * Enable RCO but do not switch CLK MUX to RCO
			 * unless ext_clk_users is 1, which indicates
			 * EXT CLK is enabled for RCO calibration
			 */
			wcd9xxx_enable_clock_block(resmgr, WCD9XXX_CFG_CAL_RCO);
			if (resmgr->ext_clk_users == 1) {
				/* Notify */
				wcd9xxx_resmgr_notifier_call(resmgr,
						WCD9XXX_EVENT_PRE_RCO_ON);
				/* CLK MUX to RCO */
				snd_soc_update_bits(codec,
						    WCD9XXX_A_CLK_BUFF_EN1,
						    0x08, 0x08);
				resmgr->clk_type = WCD9XXX_CLK_RCO;
				wcd9xxx_resmgr_notifier_call(resmgr,
						WCD9XXX_EVENT_POST_RCO_ON);
			}
		}
		break;
	case WCD9XXX_CLK_MCLK:
		if (++resmgr->clk_mclk_users == 1 &&
		    resmgr->clk_type == WCD9XXX_CLK_OFF) {
			/* switch to MCLK */
			wcd9xxx_enable_clock_block(resmgr, 0);
			wcd9xxx_enable_clock_block(resmgr, WCD9XXX_CFG_MCLK);
			resmgr->clk_type = WCD9XXX_CLK_MCLK;
		} else if (resmgr->clk_mclk_users == 1 &&
			   resmgr->clk_type == WCD9XXX_CLK_RCO) {
			/* RCO to MCLK switch, with RCO still powered on */
			if (resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) {
				snd_soc_update_bits(codec,
						WCD9XXX_A_BIAS_CENTRAL_BG_CTL,
						0x40, 0x00);
				/* Enable clock buffer */
				snd_soc_update_bits(codec,
						WCD9XXX_A_CLK_BUFF_EN1,
						0x01, 0x01);
				snd_soc_update_bits(codec,
						WCD9XXX_A_CLK_BUFF_EN1,
						0x08, 0x00);
			} else {
				/* if RCO is enabled, switch from it */
				WARN_ON(!(snd_soc_read(resmgr->codec,
					WCD9XXX_A_RC_OSC_FREQ) & 0x80));
				/* disable clock block */
				wcd9xxx_disable_clock_block(resmgr);
				/* switch to MCLK */
			wcd9xxx_enable_clock_block(resmgr, 0);
				wcd9xxx_enable_clock_block(resmgr,
							   WCD9XXX_CFG_MCLK);
			}
			resmgr->clk_type = WCD9XXX_CLK_MCLK;
		}
		break;
@@ -556,6 +668,8 @@ void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
				  enum wcd9xxx_clock_type type)
{
	struct snd_soc_codec *codec = resmgr->codec;

	pr_debug("%s: current %d, put %d\n", __func__, resmgr->clk_type, type);

	WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr);
@@ -564,14 +678,26 @@ void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
		if (--resmgr->clk_rco_users == 0 &&
		    resmgr->clk_type == WCD9XXX_CLK_RCO) {
			wcd9xxx_disable_clock_block(resmgr);
			if (resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) {
				/* Powerdown RCO */
				 snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL,
						     0x80, 0x00);
				 snd_soc_update_bits(codec,
						TOMTOM_A_BIAS_OSC_BG_CTL,
						0x01, 0x00);
			} else {
				/* if RCO is enabled, switch from it */
			if (snd_soc_read(resmgr->codec, WCD9XXX_A_RC_OSC_FREQ)
				if (snd_soc_read(resmgr->codec,
						 WCD9XXX_A_RC_OSC_FREQ)
						 & 0x80) {
					if (resmgr->codec_type !=
						WCD9XXX_CDC_TYPE_HELICON)
						snd_soc_write(resmgr->codec,
						WCD9XXX_A_CLK_BUFF_EN2, 0x02);
				wcd9xxx_resmgr_enable_config_mode(resmgr, 0);
							WCD9XXX_A_CLK_BUFF_EN2,
							0x02);
					wcd9xxx_resmgr_enable_config_mode(
								resmgr,	0);
				}
			}
			resmgr->clk_type = WCD9XXX_CLK_OFF;
		}
@@ -580,13 +706,44 @@ void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
		if (--resmgr->clk_mclk_users == 0 &&
		    resmgr->clk_rco_users == 0) {
			wcd9xxx_disable_clock_block(resmgr);

			if ((resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) &&
			    (snd_soc_read(codec, TOMTOM_A_RCO_CTRL) & 0x80)) {
				/* powerdown RCO*/
				 snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL,
						     0x80, 0x00);
				 snd_soc_update_bits(codec,
						TOMTOM_A_BIAS_OSC_BG_CTL,
						0x01, 0x00);
			}
			resmgr->clk_type = WCD9XXX_CLK_OFF;
		} else if (resmgr->clk_mclk_users == 0 &&
			   resmgr->clk_rco_users) {
			if (resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) {
				if (!(snd_soc_read(codec, TOMTOM_A_RCO_CTRL) &
				      0x80)) {
					dev_dbg(codec->dev, "%s: Enabling RCO\n",
						__func__);
					wcd9xxx_enable_clock_block(resmgr,
							WCD9XXX_CFG_CAL_RCO);
					snd_soc_update_bits(codec,
							WCD9XXX_A_CLK_BUFF_EN1,
							0x01, 0x00);
				} else {
					snd_soc_update_bits(codec,
							WCD9XXX_A_CLK_BUFF_EN1,
							0x08, 0x08);
					snd_soc_update_bits(codec,
							WCD9XXX_A_CLK_BUFF_EN1,
							0x01, 0x00);
				}
			} else {
				/* disable clock */
				wcd9xxx_disable_clock_block(resmgr);
				/* switch to RCO */
			wcd9xxx_enable_clock_block(resmgr, 1);
				wcd9xxx_enable_clock_block(resmgr,
							WCD9XXX_CFG_RCO);
			}
			resmgr->clk_type = WCD9XXX_CLK_RCO;
		}
		break;
@@ -602,6 +759,15 @@ void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
		 resmgr->clk_rco_users, resmgr->clk_mclk_users);
}

/*
 * wcd9xxx_resmgr_get_clk_type()
 * Returns clk type that is currently enabled
 */
int wcd9xxx_resmgr_get_clk_type(struct wcd9xxx_resmgr *resmgr)
{
	return resmgr->clk_type;
}

static void wcd9xxx_resmgr_update_cfilt_usage(struct wcd9xxx_resmgr *resmgr,
					      enum wcd9xxx_cfilt_sel cfilt_sel,
					      bool inc)
+8 −0
Original line number Diff line number Diff line
@@ -36,6 +36,12 @@ enum wcd9xxx_clock_type {
	WCD9XXX_CLK_MCLK,
};

enum wcd9xxx_clock_config_mode {
	WCD9XXX_CFG_MCLK = 0,
	WCD9XXX_CFG_RCO,
	WCD9XXX_CFG_CAL_RCO,
};

enum wcd9xxx_cfilt_sel {
	WCD9XXX_CFILT1_SEL,
	WCD9XXX_CFILT2_SEL,
@@ -133,6 +139,7 @@ struct wcd9xxx_resmgr {
	enum wcd9xxx_clock_type clk_type;
	u16 clk_rco_users;
	u16 clk_mclk_users;
	u16 ext_clk_users;

	/* cfilt users per cfilts */
	u16 cfilt_users[WCD9XXX_NUM_OF_CFILT];
@@ -189,6 +196,7 @@ void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr,
			      enum wcd9xxx_cfilt_sel cfilt_sel);
void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr,
			      enum wcd9xxx_cfilt_sel cfilt_sel);
int wcd9xxx_resmgr_get_clk_type(struct wcd9xxx_resmgr *resmgr);

void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr);
void wcd9xxx_resmgr_post_ssr(struct wcd9xxx_resmgr *resmgr);
+11 −2
Original line number Diff line number Diff line
@@ -1649,6 +1649,11 @@ static int apq8084_codec_event_cb(struct snd_soc_codec *codec,
	}
}

static int msm_snd_get_ext_clk_cnt(void)
{
	return clk_users;
}

static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
{
	int err;
@@ -1818,10 +1823,14 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
		goto out;
	}

	if (cdc_type)
	if (cdc_type) {
		tomtom_event_register(apq8084_codec_event_cb, rtd->codec);
	else
		tomtom_register_ext_clk_cb(msm_snd_enable_codec_ext_clk,
					   msm_snd_get_ext_clk_cnt,
					   rtd->codec);
	} else
		taiko_event_register(apq8084_codec_event_cb, rtd->codec);

	codec_reg_done = true;
	return 0;
out: