diff options
-rw-r--r-- | sound/soc/codecs/wm8993.c | 8 | ||||
-rw-r--r-- | sound/soc/codecs/wm_hubs.c | 142 | ||||
-rw-r--r-- | sound/soc/codecs/wm_hubs.h | 6 |
3 files changed, 130 insertions, 26 deletions
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 828d8174d5b7..bacfc2f20d70 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c | |||
@@ -213,6 +213,7 @@ static struct { | |||
213 | }; | 213 | }; |
214 | 214 | ||
215 | struct wm8993_priv { | 215 | struct wm8993_priv { |
216 | struct wm_hubs_data hubs_data; | ||
216 | u16 reg_cache[WM8993_REGISTER_COUNT]; | 217 | u16 reg_cache[WM8993_REGISTER_COUNT]; |
217 | struct wm8993_platform_data pdata; | 218 | struct wm8993_platform_data pdata; |
218 | struct snd_soc_codec codec; | 219 | struct snd_soc_codec codec; |
@@ -997,6 +998,11 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec, | |||
997 | 998 | ||
998 | case SND_SOC_BIAS_STANDBY: | 999 | case SND_SOC_BIAS_STANDBY: |
999 | if (codec->bias_level == SND_SOC_BIAS_OFF) { | 1000 | if (codec->bias_level == SND_SOC_BIAS_OFF) { |
1001 | /* Tune DC servo configuration */ | ||
1002 | snd_soc_write(codec, 0x44, 3); | ||
1003 | snd_soc_write(codec, 0x56, 3); | ||
1004 | snd_soc_write(codec, 0x44, 0); | ||
1005 | |||
1000 | /* Bring up VMID with fast soft start */ | 1006 | /* Bring up VMID with fast soft start */ |
1001 | snd_soc_update_bits(codec, WM8993_ANTIPOP2, | 1007 | snd_soc_update_bits(codec, WM8993_ANTIPOP2, |
1002 | WM8993_STARTUP_BIAS_ENA | | 1008 | WM8993_STARTUP_BIAS_ENA | |
@@ -1591,6 +1597,8 @@ static int wm8993_i2c_probe(struct i2c_client *i2c, | |||
1591 | codec->num_dai = 1; | 1597 | codec->num_dai = 1; |
1592 | codec->private_data = wm8993; | 1598 | codec->private_data = wm8993; |
1593 | 1599 | ||
1600 | wm8993->hubs_data.hp_startup_mode = 1; | ||
1601 | |||
1594 | memcpy(wm8993->reg_cache, wm8993_reg_defaults, | 1602 | memcpy(wm8993->reg_cache, wm8993_reg_defaults, |
1595 | sizeof(wm8993->reg_cache)); | 1603 | sizeof(wm8993->reg_cache)); |
1596 | 1604 | ||
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index a67319d9ca7e..0ad9f5d536c6 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c | |||
@@ -68,24 +68,77 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec) | |||
68 | int count = 0; | 68 | int count = 0; |
69 | 69 | ||
70 | dev_dbg(codec->dev, "Waiting for DC servo...\n"); | 70 | dev_dbg(codec->dev, "Waiting for DC servo...\n"); |
71 | |||
71 | do { | 72 | do { |
72 | count++; | 73 | count++; |
73 | msleep(1); | 74 | msleep(1); |
74 | reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_0); | 75 | reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_0); |
75 | dev_dbg(codec->dev, "DC servo status: %x\n", reg); | 76 | dev_dbg(codec->dev, "DC servo: %x\n", reg); |
76 | } while ((reg & WM8993_DCS_CAL_COMPLETE_MASK) | 77 | } while (reg & WM8993_DCS_DATAPATH_BUSY); |
77 | != WM8993_DCS_CAL_COMPLETE_MASK && count < 1000); | ||
78 | 78 | ||
79 | if ((reg & WM8993_DCS_CAL_COMPLETE_MASK) | 79 | if (reg & WM8993_DCS_DATAPATH_BUSY) |
80 | != WM8993_DCS_CAL_COMPLETE_MASK) | ||
81 | dev_err(codec->dev, "Timed out waiting for DC Servo\n"); | 80 | dev_err(codec->dev, "Timed out waiting for DC Servo\n"); |
82 | } | 81 | } |
83 | 82 | ||
84 | /* | 83 | /* |
84 | * Startup calibration of the DC servo | ||
85 | */ | ||
86 | static void calibrate_dc_servo(struct snd_soc_codec *codec) | ||
87 | { | ||
88 | struct wm_hubs_data *hubs = codec->private_data; | ||
89 | u16 reg, dcs_cfg; | ||
90 | |||
91 | /* Set for 32 series updates */ | ||
92 | snd_soc_update_bits(codec, WM8993_DC_SERVO_1, | ||
93 | WM8993_DCS_SERIES_NO_01_MASK, | ||
94 | 32 << WM8993_DCS_SERIES_NO_01_SHIFT); | ||
95 | |||
96 | /* Enable the DC servo. Write all bits to avoid triggering startup | ||
97 | * or write calibration. | ||
98 | */ | ||
99 | snd_soc_update_bits(codec, WM8993_DC_SERVO_0, | ||
100 | 0xFFFF, | ||
101 | WM8993_DCS_ENA_CHAN_0 | | ||
102 | WM8993_DCS_ENA_CHAN_1 | | ||
103 | WM8993_DCS_TRIG_SERIES_1 | | ||
104 | WM8993_DCS_TRIG_SERIES_0); | ||
105 | |||
106 | wait_for_dc_servo(codec); | ||
107 | |||
108 | /* Apply correction to DC servo result */ | ||
109 | if (hubs->dcs_codes) { | ||
110 | dev_dbg(codec->dev, "Applying %d code DC servo correction\n", | ||
111 | hubs->dcs_codes); | ||
112 | |||
113 | /* HPOUT1L */ | ||
114 | reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1) & | ||
115 | WM8993_DCS_INTEG_CHAN_0_MASK;; | ||
116 | reg += hubs->dcs_codes; | ||
117 | dcs_cfg = reg << WM8993_DCS_DAC_WR_VAL_1_SHIFT; | ||
118 | |||
119 | /* HPOUT1R */ | ||
120 | reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2) & | ||
121 | WM8993_DCS_INTEG_CHAN_1_MASK; | ||
122 | reg += hubs->dcs_codes; | ||
123 | dcs_cfg |= reg; | ||
124 | |||
125 | /* Do it */ | ||
126 | snd_soc_write(codec, WM8993_DC_SERVO_3, dcs_cfg); | ||
127 | snd_soc_update_bits(codec, WM8993_DC_SERVO_0, | ||
128 | WM8993_DCS_TRIG_DAC_WR_0 | | ||
129 | WM8993_DCS_TRIG_DAC_WR_1, | ||
130 | WM8993_DCS_TRIG_DAC_WR_0 | | ||
131 | WM8993_DCS_TRIG_DAC_WR_1); | ||
132 | |||
133 | wait_for_dc_servo(codec); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | /* | ||
85 | * Update the DC servo calibration on gain changes | 138 | * Update the DC servo calibration on gain changes |
86 | */ | 139 | */ |
87 | static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol, | 140 | static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol, |
88 | struct snd_ctl_elem_value *ucontrol) | 141 | struct snd_ctl_elem_value *ucontrol) |
89 | { | 142 | { |
90 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 143 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
91 | int ret; | 144 | int ret; |
@@ -251,6 +304,47 @@ SOC_SINGLE_TLV("LINEOUT2 Volume", WM8993_LINE_OUTPUTS_VOLUME, 0, 1, 1, | |||
251 | line_tlv), | 304 | line_tlv), |
252 | }; | 305 | }; |
253 | 306 | ||
307 | static int hp_supply_event(struct snd_soc_dapm_widget *w, | ||
308 | struct snd_kcontrol *kcontrol, int event) | ||
309 | { | ||
310 | struct snd_soc_codec *codec = w->codec; | ||
311 | struct wm_hubs_data *hubs = codec->private_data; | ||
312 | |||
313 | switch (event) { | ||
314 | case SND_SOC_DAPM_PRE_PMU: | ||
315 | switch (hubs->hp_startup_mode) { | ||
316 | case 0: | ||
317 | break; | ||
318 | case 1: | ||
319 | /* Enable the headphone amp */ | ||
320 | snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1, | ||
321 | WM8993_HPOUT1L_ENA | | ||
322 | WM8993_HPOUT1R_ENA, | ||
323 | WM8993_HPOUT1L_ENA | | ||
324 | WM8993_HPOUT1R_ENA); | ||
325 | |||
326 | /* Enable the second stage */ | ||
327 | snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0, | ||
328 | WM8993_HPOUT1L_DLY | | ||
329 | WM8993_HPOUT1R_DLY, | ||
330 | WM8993_HPOUT1L_DLY | | ||
331 | WM8993_HPOUT1R_DLY); | ||
332 | break; | ||
333 | default: | ||
334 | dev_err(codec->dev, "Unknown HP startup mode %d\n", | ||
335 | hubs->hp_startup_mode); | ||
336 | break; | ||
337 | } | ||
338 | |||
339 | case SND_SOC_DAPM_PRE_PMD: | ||
340 | snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1, | ||
341 | WM8993_CP_ENA, 0); | ||
342 | break; | ||
343 | } | ||
344 | |||
345 | return 0; | ||
346 | } | ||
347 | |||
254 | static int hp_event(struct snd_soc_dapm_widget *w, | 348 | static int hp_event(struct snd_soc_dapm_widget *w, |
255 | struct snd_kcontrol *kcontrol, int event) | 349 | struct snd_kcontrol *kcontrol, int event) |
256 | { | 350 | { |
@@ -271,14 +365,11 @@ static int hp_event(struct snd_soc_dapm_widget *w, | |||
271 | reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY; | 365 | reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY; |
272 | snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg); | 366 | snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg); |
273 | 367 | ||
274 | /* Start the DC servo */ | 368 | /* Smallest supported update interval */ |
275 | snd_soc_update_bits(codec, WM8993_DC_SERVO_0, | 369 | snd_soc_update_bits(codec, WM8993_DC_SERVO_1, |
276 | 0xFFFF, | 370 | WM8993_DCS_TIMER_PERIOD_01_MASK, 1); |
277 | WM8993_DCS_ENA_CHAN_0 | | 371 | |
278 | WM8993_DCS_ENA_CHAN_1 | | 372 | calibrate_dc_servo(codec); |
279 | WM8993_DCS_TRIG_STARTUP_1 | | ||
280 | WM8993_DCS_TRIG_STARTUP_0); | ||
281 | wait_for_dc_servo(codec); | ||
282 | 373 | ||
283 | reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT | | 374 | reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT | |
284 | WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT; | 375 | WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT; |
@@ -286,23 +377,19 @@ static int hp_event(struct snd_soc_dapm_widget *w, | |||
286 | break; | 377 | break; |
287 | 378 | ||
288 | case SND_SOC_DAPM_PRE_PMD: | 379 | case SND_SOC_DAPM_PRE_PMD: |
289 | reg &= ~(WM8993_HPOUT1L_RMV_SHORT | | 380 | snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0, |
290 | WM8993_HPOUT1L_DLY | | 381 | WM8993_HPOUT1L_DLY | |
291 | WM8993_HPOUT1L_OUTP | | 382 | WM8993_HPOUT1R_DLY | |
292 | WM8993_HPOUT1R_RMV_SHORT | | 383 | WM8993_HPOUT1L_RMV_SHORT | |
293 | WM8993_HPOUT1R_DLY | | 384 | WM8993_HPOUT1R_RMV_SHORT, 0); |
294 | WM8993_HPOUT1R_OUTP); | ||
295 | 385 | ||
296 | snd_soc_update_bits(codec, WM8993_DC_SERVO_0, | 386 | snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0, |
297 | 0xffff, 0); | 387 | WM8993_HPOUT1L_OUTP | |
388 | WM8993_HPOUT1R_OUTP, 0); | ||
298 | 389 | ||
299 | snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg); | ||
300 | snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1, | 390 | snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1, |
301 | WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA, | 391 | WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA, |
302 | 0); | 392 | 0); |
303 | |||
304 | snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1, | ||
305 | WM8993_CP_ENA, 0); | ||
306 | break; | 393 | break; |
307 | } | 394 | } |
308 | 395 | ||
@@ -473,6 +560,8 @@ SND_SOC_DAPM_MIXER("Right Output Mixer", WM8993_POWER_MANAGEMENT_3, 4, 0, | |||
473 | SND_SOC_DAPM_PGA("Left Output PGA", WM8993_POWER_MANAGEMENT_3, 7, 0, NULL, 0), | 560 | SND_SOC_DAPM_PGA("Left Output PGA", WM8993_POWER_MANAGEMENT_3, 7, 0, NULL, 0), |
474 | SND_SOC_DAPM_PGA("Right Output PGA", WM8993_POWER_MANAGEMENT_3, 6, 0, NULL, 0), | 561 | SND_SOC_DAPM_PGA("Right Output PGA", WM8993_POWER_MANAGEMENT_3, 6, 0, NULL, 0), |
475 | 562 | ||
563 | SND_SOC_DAPM_SUPPLY("Headphone Supply", SND_SOC_NOPM, 0, 0, hp_supply_event, | ||
564 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), | ||
476 | SND_SOC_DAPM_PGA_E("Headphone PGA", SND_SOC_NOPM, 0, 0, | 565 | SND_SOC_DAPM_PGA_E("Headphone PGA", SND_SOC_NOPM, 0, 0, |
477 | NULL, 0, | 566 | NULL, 0, |
478 | hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), | 567 | hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
@@ -626,6 +715,7 @@ static const struct snd_soc_dapm_route analogue_routes[] = { | |||
626 | { "Headphone PGA", NULL, "Left Headphone Mux" }, | 715 | { "Headphone PGA", NULL, "Left Headphone Mux" }, |
627 | { "Headphone PGA", NULL, "Right Headphone Mux" }, | 716 | { "Headphone PGA", NULL, "Right Headphone Mux" }, |
628 | { "Headphone PGA", NULL, "CLK_SYS" }, | 717 | { "Headphone PGA", NULL, "CLK_SYS" }, |
718 | { "Headphone PGA", NULL, "Headphone Supply" }, | ||
629 | 719 | ||
630 | { "HPOUT1L", NULL, "Headphone PGA" }, | 720 | { "HPOUT1L", NULL, "Headphone PGA" }, |
631 | { "HPOUT1R", NULL, "Headphone PGA" }, | 721 | { "HPOUT1R", NULL, "Headphone PGA" }, |
diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h index 36d3fba1de8b..420104fe9c90 100644 --- a/sound/soc/codecs/wm_hubs.h +++ b/sound/soc/codecs/wm_hubs.h | |||
@@ -18,6 +18,12 @@ struct snd_soc_codec; | |||
18 | 18 | ||
19 | extern const unsigned int wm_hubs_spkmix_tlv[]; | 19 | extern const unsigned int wm_hubs_spkmix_tlv[]; |
20 | 20 | ||
21 | /* This *must* be the first element of the codec->private_data struct */ | ||
22 | struct wm_hubs_data { | ||
23 | int dcs_codes; | ||
24 | int hp_startup_mode; | ||
25 | }; | ||
26 | |||
21 | extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *); | 27 | extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *); |
22 | extern int wm_hubs_add_analogue_routes(struct snd_soc_codec *, int, int); | 28 | extern int wm_hubs_add_analogue_routes(struct snd_soc_codec *, int, int); |
23 | extern int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *, | 29 | extern int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *, |