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

Commit dfc60524 authored by Phani Kumar Uppalapati's avatar Phani Kumar Uppalapati Committed by Gerrit - the friendly Code Review server
Browse files

ASoC: wcd9xxx: Update RCO and MCLK sequence for wcd9330



RCO and MCLK enablement for WCD9330 audio codec has
few differences in the register sequence. Modify
resource manager as per the new register write
sequence for WCD9330 to enable these clocks.

Change-Id: I833bbcb0dd95a861a7850c6167d18ad3b8847ce3
Signed-off-by: default avatarPhani Kumar Uppalapati <phaniu@codeaurora.org>
parent f4488910
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: