aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2010-10-27 16:48:36 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2010-10-28 14:34:03 -0400
commitfec6dd833e733b5d9588a1f1e4d81118b79b5774 (patch)
treefd321d8252fa61dd6df15166c41e262f4c8955ae /sound
parentd906401114861585c990ff0290c002b5d22fc71a (diff)
ASoC: Store DC offset correction for wm_hubs devices in class W mode
Providing the analogue configuration of the output path remains the same the DC offset corrected by the DC servo will remain identical so we can skip the callibration, reducing the startup time for the headphone output. Implement this for the wm_hubs devices as has been done for several other CODECs. Don't do this if we have any analogue paths enabled since offsets may be being introduced by the analogue paths which could vary outside the control of the driver. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/wm8993.c2
-rw-r--r--sound/soc/codecs/wm8994.c3
-rw-r--r--sound/soc/codecs/wm_hubs.c69
-rw-r--r--sound/soc/codecs/wm_hubs.h3
4 files changed, 54 insertions, 23 deletions
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index 589e3fa24734..67fe5ccc6082 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -735,6 +735,7 @@ static int class_w_put(struct snd_kcontrol *kcontrol,
735 0); 735 0);
736 } 736 }
737 wm8993->class_w_users++; 737 wm8993->class_w_users++;
738 wm8993->hubs_data.class_w = true;
738 } 739 }
739 740
740 /* Implement the change */ 741 /* Implement the change */
@@ -751,6 +752,7 @@ static int class_w_put(struct snd_kcontrol *kcontrol,
751 WM8993_CP_DYN_V); 752 WM8993_CP_DYN_V);
752 } 753 }
753 wm8993->class_w_users--; 754 wm8993->class_w_users--;
755 wm8993->hubs_data.class_w = false;
754 } 756 }
755 757
756 dev_dbg(codec->dev, "Indirect DAC use count now %d\n", 758 dev_dbg(codec->dev, "Indirect DAC use count now %d\n",
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 0db59c3aa5d4..3f70dee048b0 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -2228,6 +2228,7 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w,
2228 2228
2229static void wm8994_update_class_w(struct snd_soc_codec *codec) 2229static void wm8994_update_class_w(struct snd_soc_codec *codec)
2230{ 2230{
2231 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
2231 int enable = 1; 2232 int enable = 1;
2232 int source = 0; /* GCC flow analysis can't track enable */ 2233 int source = 0; /* GCC flow analysis can't track enable */
2233 int reg, reg_r; 2234 int reg, reg_r;
@@ -2278,11 +2279,13 @@ static void wm8994_update_class_w(struct snd_soc_codec *codec)
2278 WM8994_CP_DYN_PWR | 2279 WM8994_CP_DYN_PWR |
2279 WM8994_CP_DYN_SRC_SEL_MASK, 2280 WM8994_CP_DYN_SRC_SEL_MASK,
2280 source | WM8994_CP_DYN_PWR); 2281 source | WM8994_CP_DYN_PWR);
2282 wm8994->hubs.class_w = true;
2281 2283
2282 } else { 2284 } else {
2283 dev_dbg(codec->dev, "Class W disabled\n"); 2285 dev_dbg(codec->dev, "Class W disabled\n");
2284 snd_soc_update_bits(codec, WM8994_CLASS_W_1, 2286 snd_soc_update_bits(codec, WM8994_CLASS_W_1,
2285 WM8994_CP_DYN_PWR, 0); 2287 WM8994_CP_DYN_PWR, 0);
2288 wm8994->hubs.class_w = false;
2286 } 2289 }
2287} 2290}
2288 2291
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index 2cb81538cd91..31c2a5724d85 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -94,6 +94,18 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
94 struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); 94 struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
95 u16 reg, reg_l, reg_r, dcs_cfg; 95 u16 reg, reg_l, reg_r, dcs_cfg;
96 96
97 /* If we're using a digital only path and have a previously
98 * callibrated DC servo offset stored then use that. */
99 if (hubs->class_w && hubs->class_w_dcs) {
100 dev_dbg(codec->dev, "Using cached DC servo offset %x\n",
101 hubs->class_w_dcs);
102 snd_soc_write(codec, WM8993_DC_SERVO_3, hubs->class_w_dcs);
103 wait_for_dc_servo(codec,
104 WM8993_DCS_TRIG_DAC_WR_0 |
105 WM8993_DCS_TRIG_DAC_WR_1);
106 return;
107 }
108
97 /* Set for 32 series updates */ 109 /* Set for 32 series updates */
98 snd_soc_update_bits(codec, WM8993_DC_SERVO_1, 110 snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
99 WM8993_DCS_SERIES_NO_01_MASK, 111 WM8993_DCS_SERIES_NO_01_MASK,
@@ -101,34 +113,34 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
101 wait_for_dc_servo(codec, 113 wait_for_dc_servo(codec,
102 WM8993_DCS_TRIG_SERIES_0 | WM8993_DCS_TRIG_SERIES_1); 114 WM8993_DCS_TRIG_SERIES_0 | WM8993_DCS_TRIG_SERIES_1);
103 115
116 /* Different chips in the family support different readback
117 * methods.
118 */
119 switch (hubs->dcs_readback_mode) {
120 case 0:
121 reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1)
122 & WM8993_DCS_INTEG_CHAN_0_MASK;;
123 reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2)
124 & WM8993_DCS_INTEG_CHAN_1_MASK;
125 break;
126 case 1:
127 reg = snd_soc_read(codec, WM8993_DC_SERVO_3);
128 reg_l = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
129 >> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
130 reg_r = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
131 break;
132 default:
133 WARN(1, "Unknown DCS readback method");
134 break;
135 }
136
137 dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r);
138
104 /* Apply correction to DC servo result */ 139 /* Apply correction to DC servo result */
105 if (hubs->dcs_codes) { 140 if (hubs->dcs_codes) {
106 dev_dbg(codec->dev, "Applying %d code DC servo correction\n", 141 dev_dbg(codec->dev, "Applying %d code DC servo correction\n",
107 hubs->dcs_codes); 142 hubs->dcs_codes);
108 143
109 /* Different chips in the family support different
110 * readback methods.
111 */
112 switch (hubs->dcs_readback_mode) {
113 case 0:
114 reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1)
115 & WM8993_DCS_INTEG_CHAN_0_MASK;;
116 reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2)
117 & WM8993_DCS_INTEG_CHAN_1_MASK;
118 break;
119 case 1:
120 reg = snd_soc_read(codec, WM8993_DC_SERVO_3);
121 reg_l = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
122 >> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
123 reg_r = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
124 break;
125 default:
126 WARN(1, "Unknown DCS readback method");
127 break;
128 }
129
130 dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r);
131
132 /* HPOUT1L */ 144 /* HPOUT1L */
133 if (reg_l + hubs->dcs_codes > 0 && 145 if (reg_l + hubs->dcs_codes > 0 &&
134 reg_l + hubs->dcs_codes < 0xff) 146 reg_l + hubs->dcs_codes < 0xff)
@@ -148,7 +160,15 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
148 wait_for_dc_servo(codec, 160 wait_for_dc_servo(codec,
149 WM8993_DCS_TRIG_DAC_WR_0 | 161 WM8993_DCS_TRIG_DAC_WR_0 |
150 WM8993_DCS_TRIG_DAC_WR_1); 162 WM8993_DCS_TRIG_DAC_WR_1);
163 } else {
164 dcs_cfg = reg_l << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
165 dcs_cfg |= reg_r;
151 } 166 }
167
168 /* Save the callibrated offset if we're in class W mode and
169 * therefore don't have any analogue signal mixed in. */
170 if (hubs->class_w)
171 hubs->class_w_dcs = dcs_cfg;
152} 172}
153 173
154/* 174/*
@@ -163,6 +183,9 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
163 183
164 ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); 184 ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
165 185
186 /* Updating the analogue gains invalidates the DC servo cache */
187 hubs->class_w_dcs = 0;
188
166 /* If we're applying an offset correction then updating the 189 /* If we're applying an offset correction then updating the
167 * callibration would be likely to introduce further offsets. */ 190 * callibration would be likely to introduce further offsets. */
168 if (hubs->dcs_codes) 191 if (hubs->dcs_codes)
diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h
index e51c16683589..f8a5e976b5e6 100644
--- a/sound/soc/codecs/wm_hubs.h
+++ b/sound/soc/codecs/wm_hubs.h
@@ -23,6 +23,9 @@ struct wm_hubs_data {
23 int dcs_codes; 23 int dcs_codes;
24 int dcs_readback_mode; 24 int dcs_readback_mode;
25 int hp_startup_mode; 25 int hp_startup_mode;
26
27 bool class_w;
28 u16 class_w_dcs;
26}; 29};
27 30
28extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *); 31extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *);