Loading sound/soc/codecs/wcd9330.c +71 −13 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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) { Loading @@ -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; Loading @@ -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; } Loading Loading @@ -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 */ Loading @@ -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); Loading Loading @@ -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); Loading sound/soc/codecs/wcd9330.h +5 −1 Original line number Diff line number Diff line Loading @@ -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 sound/soc/codecs/wcd9xxx-resmgr.c +231 −65 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; Loading Loading @@ -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); } Loading @@ -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); } Loading Loading @@ -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, Loading Loading @@ -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); Loading @@ -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 */ Loading Loading @@ -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); Loading @@ -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); } Loading @@ -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); Loading @@ -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; Loading @@ -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); Loading @@ -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; } Loading @@ -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; Loading @@ -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) Loading sound/soc/codecs/wcd9xxx-resmgr.h +8 −0 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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]; Loading Loading @@ -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); Loading sound/soc/msm/apq8084.c +11 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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: Loading Loading
sound/soc/codecs/wcd9330.c +71 −13 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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) { Loading @@ -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; Loading @@ -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; } Loading Loading @@ -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 */ Loading @@ -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); Loading Loading @@ -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); Loading
sound/soc/codecs/wcd9330.h +5 −1 Original line number Diff line number Diff line Loading @@ -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
sound/soc/codecs/wcd9xxx-resmgr.c +231 −65 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; Loading Loading @@ -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); } Loading @@ -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); } Loading Loading @@ -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, Loading Loading @@ -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); Loading @@ -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 */ Loading Loading @@ -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); Loading @@ -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); } Loading @@ -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); Loading @@ -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; Loading @@ -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); Loading @@ -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; } Loading @@ -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; Loading @@ -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) Loading
sound/soc/codecs/wcd9xxx-resmgr.h +8 −0 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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]; Loading Loading @@ -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); Loading
sound/soc/msm/apq8084.c +11 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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: Loading