|
@@ -143,12 +143,69 @@ static bool wm_hubs_dac_hp_direct(struct snd_soc_codec *codec)
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+struct wm_hubs_dcs_cache {
|
|
|
|
+ struct list_head list;
|
|
|
|
+ unsigned int left;
|
|
|
|
+ unsigned int right;
|
|
|
|
+ u16 dcs_cfg;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static bool wm_hubs_dcs_cache_get(struct snd_soc_codec *codec,
|
|
|
|
+ struct wm_hubs_dcs_cache **entry)
|
|
|
|
+{
|
|
|
|
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
|
|
|
|
+ struct wm_hubs_dcs_cache *cache;
|
|
|
|
+ unsigned int left, right;
|
|
|
|
+
|
|
|
|
+ left = snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME);
|
|
|
|
+ left &= WM8993_HPOUT1L_VOL_MASK;
|
|
|
|
+
|
|
|
|
+ right = snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME);
|
|
|
|
+ right &= WM8993_HPOUT1R_VOL_MASK;
|
|
|
|
+
|
|
|
|
+ list_for_each_entry(cache, &hubs->dcs_cache, list) {
|
|
|
|
+ if (cache->left != left || cache->right != right)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ *entry = cache;
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void wm_hubs_dcs_cache_set(struct snd_soc_codec *codec, u16 dcs_cfg)
|
|
|
|
+{
|
|
|
|
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
|
|
|
|
+ struct wm_hubs_dcs_cache *cache;
|
|
|
|
+
|
|
|
|
+ if (hubs->no_cache_dac_hp_direct)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ cache = devm_kzalloc(codec->dev, sizeof(*cache), GFP_KERNEL);
|
|
|
|
+ if (!cache) {
|
|
|
|
+ dev_err(codec->dev, "Failed to allocate DCS cache entry\n");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cache->left = snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME);
|
|
|
|
+ cache->left &= WM8993_HPOUT1L_VOL_MASK;
|
|
|
|
+
|
|
|
|
+ cache->right = snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME);
|
|
|
|
+ cache->right &= WM8993_HPOUT1R_VOL_MASK;
|
|
|
|
+
|
|
|
|
+ cache->dcs_cfg = dcs_cfg;
|
|
|
|
+
|
|
|
|
+ list_add_tail(&cache->list, &hubs->dcs_cache);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Startup calibration of the DC servo
|
|
* Startup calibration of the DC servo
|
|
*/
|
|
*/
|
|
static void calibrate_dc_servo(struct snd_soc_codec *codec)
|
|
static void calibrate_dc_servo(struct snd_soc_codec *codec)
|
|
{
|
|
{
|
|
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
|
|
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
|
|
|
|
+ struct wm_hubs_dcs_cache *cache;
|
|
s8 offset;
|
|
s8 offset;
|
|
u16 reg, reg_l, reg_r, dcs_cfg, dcs_reg;
|
|
u16 reg, reg_l, reg_r, dcs_cfg, dcs_reg;
|
|
|
|
|
|
@@ -163,10 +220,11 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
|
|
|
|
|
|
/* If we're using a digital only path and have a previously
|
|
/* If we're using a digital only path and have a previously
|
|
* callibrated DC servo offset stored then use that. */
|
|
* callibrated DC servo offset stored then use that. */
|
|
- if (wm_hubs_dac_hp_direct(codec) && hubs->dac_hp_direct_dcs) {
|
|
|
|
- dev_dbg(codec->dev, "Using cached DC servo offset %x\n",
|
|
|
|
- hubs->dac_hp_direct_dcs);
|
|
|
|
- snd_soc_write(codec, dcs_reg, hubs->dac_hp_direct_dcs);
|
|
|
|
|
|
+ if (wm_hubs_dac_hp_direct(codec) &&
|
|
|
|
+ wm_hubs_dcs_cache_get(codec, &cache)) {
|
|
|
|
+ dev_dbg(codec->dev, "Using cached DCS offset %x for %d,%d\n",
|
|
|
|
+ cache->dcs_cfg, cache->left, cache->right);
|
|
|
|
+ snd_soc_write(codec, dcs_reg, cache->dcs_cfg);
|
|
wait_for_dc_servo(codec,
|
|
wait_for_dc_servo(codec,
|
|
WM8993_DCS_TRIG_DAC_WR_0 |
|
|
WM8993_DCS_TRIG_DAC_WR_0 |
|
|
WM8993_DCS_TRIG_DAC_WR_1);
|
|
WM8993_DCS_TRIG_DAC_WR_1);
|
|
@@ -241,8 +299,8 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
|
|
|
|
|
|
/* Save the callibrated offset if we're in class W mode and
|
|
/* Save the callibrated offset if we're in class W mode and
|
|
* therefore don't have any analogue signal mixed in. */
|
|
* therefore don't have any analogue signal mixed in. */
|
|
- if (wm_hubs_dac_hp_direct(codec) && !hubs->no_cache_dac_hp_direct)
|
|
|
|
- hubs->dac_hp_direct_dcs = dcs_cfg;
|
|
|
|
|
|
+ if (wm_hubs_dac_hp_direct(codec))
|
|
|
|
+ wm_hubs_dcs_cache_set(codec, dcs_cfg);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -257,9 +315,6 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
|
|
|
|
|
|
ret = snd_soc_put_volsw(kcontrol, ucontrol);
|
|
ret = snd_soc_put_volsw(kcontrol, ucontrol);
|
|
|
|
|
|
- /* Updating the analogue gains invalidates the DC servo cache */
|
|
|
|
- hubs->dac_hp_direct_dcs = 0;
|
|
|
|
-
|
|
|
|
/* If we're applying an offset correction then updating the
|
|
/* If we're applying an offset correction then updating the
|
|
* callibration would be likely to introduce further offsets. */
|
|
* callibration would be likely to introduce further offsets. */
|
|
if (hubs->dcs_codes_l || hubs->dcs_codes_r || hubs->no_series_update)
|
|
if (hubs->dcs_codes_l || hubs->dcs_codes_r || hubs->no_series_update)
|
|
@@ -1057,6 +1112,7 @@ int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec,
|
|
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
|
|
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
|
|
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
|
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
|
|
|
|
|
|
|
+ INIT_LIST_HEAD(&hubs->dcs_cache);
|
|
init_completion(&hubs->dcs_done);
|
|
init_completion(&hubs->dcs_done);
|
|
|
|
|
|
snd_soc_dapm_add_routes(dapm, analogue_routes,
|
|
snd_soc_dapm_add_routes(dapm, analogue_routes,
|