aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm_hubs.c
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2012-05-01 13:45:09 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2012-05-01 14:21:07 -0400
commit94aa733a477dd5fe6eb153e5b6741488d743fab5 (patch)
tree676e6e4c3a7d8623360ddf246829fd21079cc002 /sound/soc/codecs/wm_hubs.c
parent6264f668d51647be68bcfde8a56042bd646a7855 (diff)
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/soc/codecs/wm_hubs.c')
-rw-r--r--sound/soc/codecs/wm_hubs.c74
1 files changed, 65 insertions, 9 deletions
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index 484ec22ca6f4..c424a1e50638 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -143,12 +143,69 @@ static bool wm_hubs_dac_hp_direct(struct snd_soc_codec *codec)
143 return true; 143 return true;
144} 144}
145 145
146struct wm_hubs_dcs_cache {
147 struct list_head list;
148 unsigned int left;
149 unsigned int right;
150 u16 dcs_cfg;
151};
152
153static bool wm_hubs_dcs_cache_get(struct snd_soc_codec *codec,
154 struct wm_hubs_dcs_cache **entry)
155{
156 struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
157 struct wm_hubs_dcs_cache *cache;
158 unsigned int left, right;
159
160 left = snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME);
161 left &= WM8993_HPOUT1L_VOL_MASK;
162
163 right = snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME);
164 right &= WM8993_HPOUT1R_VOL_MASK;
165
166 list_for_each_entry(cache, &hubs->dcs_cache, list) {
167 if (cache->left != left || cache->right != right)
168 continue;
169
170 *entry = cache;
171 return true;
172 }
173
174 return false;
175}
176
177static void wm_hubs_dcs_cache_set(struct snd_soc_codec *codec, u16 dcs_cfg)
178{
179 struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
180 struct wm_hubs_dcs_cache *cache;
181
182 if (hubs->no_cache_dac_hp_direct)
183 return;
184
185 cache = devm_kzalloc(codec->dev, sizeof(*cache), GFP_KERNEL);
186 if (!cache) {
187 dev_err(codec->dev, "Failed to allocate DCS cache entry\n");
188 return;
189 }
190
191 cache->left = snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME);
192 cache->left &= WM8993_HPOUT1L_VOL_MASK;
193
194 cache->right = snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME);
195 cache->right &= WM8993_HPOUT1R_VOL_MASK;
196
197 cache->dcs_cfg = dcs_cfg;
198
199 list_add_tail(&cache->list, &hubs->dcs_cache);
200}
201
146/* 202/*
147 * Startup calibration of the DC servo 203 * Startup calibration of the DC servo
148 */ 204 */
149static void calibrate_dc_servo(struct snd_soc_codec *codec) 205static void calibrate_dc_servo(struct snd_soc_codec *codec)
150{ 206{
151 struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); 207 struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
208 struct wm_hubs_dcs_cache *cache;
152 s8 offset; 209 s8 offset;
153 u16 reg, reg_l, reg_r, dcs_cfg, dcs_reg; 210 u16 reg, reg_l, reg_r, dcs_cfg, dcs_reg;
154 211
@@ -163,10 +220,11 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
163 220
164 /* If we're using a digital only path and have a previously 221 /* If we're using a digital only path and have a previously
165 * callibrated DC servo offset stored then use that. */ 222 * callibrated DC servo offset stored then use that. */
166 if (wm_hubs_dac_hp_direct(codec) && hubs->dac_hp_direct_dcs) { 223 if (wm_hubs_dac_hp_direct(codec) &&
167 dev_dbg(codec->dev, "Using cached DC servo offset %x\n", 224 wm_hubs_dcs_cache_get(codec, &cache)) {
168 hubs->dac_hp_direct_dcs); 225 dev_dbg(codec->dev, "Using cached DCS offset %x for %d,%d\n",
169 snd_soc_write(codec, dcs_reg, hubs->dac_hp_direct_dcs); 226 cache->dcs_cfg, cache->left, cache->right);
227 snd_soc_write(codec, dcs_reg, cache->dcs_cfg);
170 wait_for_dc_servo(codec, 228 wait_for_dc_servo(codec,
171 WM8993_DCS_TRIG_DAC_WR_0 | 229 WM8993_DCS_TRIG_DAC_WR_0 |
172 WM8993_DCS_TRIG_DAC_WR_1); 230 WM8993_DCS_TRIG_DAC_WR_1);
@@ -241,8 +299,8 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
241 299
242 /* Save the callibrated offset if we're in class W mode and 300 /* Save the callibrated offset if we're in class W mode and
243 * therefore don't have any analogue signal mixed in. */ 301 * therefore don't have any analogue signal mixed in. */
244 if (wm_hubs_dac_hp_direct(codec) && !hubs->no_cache_dac_hp_direct) 302 if (wm_hubs_dac_hp_direct(codec))
245 hubs->dac_hp_direct_dcs = dcs_cfg; 303 wm_hubs_dcs_cache_set(codec, dcs_cfg);
246} 304}
247 305
248/* 306/*
@@ -257,9 +315,6 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
257 315
258 ret = snd_soc_put_volsw(kcontrol, ucontrol); 316 ret = snd_soc_put_volsw(kcontrol, ucontrol);
259 317
260 /* Updating the analogue gains invalidates the DC servo cache */
261 hubs->dac_hp_direct_dcs = 0;
262
263 /* If we're applying an offset correction then updating the 318 /* If we're applying an offset correction then updating the
264 * callibration would be likely to introduce further offsets. */ 319 * callibration would be likely to introduce further offsets. */
265 if (hubs->dcs_codes_l || hubs->dcs_codes_r || hubs->no_series_update) 320 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,
1057 struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); 1112 struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
1058 struct snd_soc_dapm_context *dapm = &codec->dapm; 1113 struct snd_soc_dapm_context *dapm = &codec->dapm;
1059 1114
1115 INIT_LIST_HEAD(&hubs->dcs_cache);
1060 init_completion(&hubs->dcs_done); 1116 init_completion(&hubs->dcs_done);
1061 1117
1062 snd_soc_dapm_add_routes(dapm, analogue_routes, 1118 snd_soc_dapm_add_routes(dapm, analogue_routes,