aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2012-05-01 18:45:09 +0100
committerAndreas Blaesius <skate4life@gmx.de>2016-04-30 11:03:59 +0200
commit588793f20870ad65bfef73ea88702a4811710a78 (patch)
treeaea828b66467e52d832a56bdd875b5f2ac25107e /sound
parentd8c39aa41e85c4c615888c0e57dc16d3c53449d3 (diff)
downloadkernel_samsung_espresso10-588793f20870ad65bfef73ea88702a4811710a78.zip
kernel_samsung_espresso10-588793f20870ad65bfef73ea88702a4811710a78.tar.gz
kernel_samsung_espresso10-588793f20870ad65bfef73ea88702a4811710a78.tar.bz2
ASoC: wm_hubs: Cache multiple DCS offsets
Rather than invalidating the cached DCS value every time the headphone gain changes store multiple values, indexed by gain. This allows the optimisation we get from the cache to take effect more often. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/wm_hubs.c74
-rw-r--r--sound/soc/codecs/wm_hubs.h3
2 files changed, 67 insertions, 10 deletions
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index 4859f07..0278727 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -144,12 +144,69 @@ static bool wm_hubs_dac_hp_direct(struct snd_soc_codec *codec)
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
*/
static void calibrate_dc_servo(struct snd_soc_codec *codec)
{
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ struct wm_hubs_dcs_cache *cache;
s8 offset;
u16 reg, reg_l, reg_r, dcs_cfg, dcs_reg;
@@ -164,10 +221,11 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
/* If we're using a digital only path and have a previously
* 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,
WM8993_DCS_TRIG_DAC_WR_0 |
WM8993_DCS_TRIG_DAC_WR_1);
@@ -242,8 +300,8 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
/* Save the callibrated offset if we're in class W mode and
* 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);
}
/*
@@ -258,9 +316,6 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
ret = snd_soc_put_volsw_2r(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
* callibration would be likely to introduce further offsets. */
if (hubs->dcs_codes_l || hubs->dcs_codes_r || hubs->no_series_update)
@@ -1068,6 +1123,7 @@ int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec,
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = &codec->dapm;
+ INIT_LIST_HEAD(&hubs->dcs_cache);
init_completion(&hubs->dcs_done);
snd_soc_dapm_add_routes(dapm, analogue_routes,
diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h
index 71861fc..da2dc89 100644
--- a/sound/soc/codecs/wm_hubs.h
+++ b/sound/soc/codecs/wm_hubs.h
@@ -16,6 +16,7 @@
#include <linux/completion.h>
#include <linux/interrupt.h>
+#include <linux/list.h>
#include <sound/control.h>
struct snd_soc_codec;
@@ -32,7 +33,7 @@ struct wm_hubs_data {
int no_series_update;
bool no_cache_dac_hp_direct;
- u16 dac_hp_direct_dcs;
+ struct list_head dcs_cache;
bool (*check_class_w_digital)(struct snd_soc_codec *);
bool lineout1_se;