diff options
Diffstat (limited to 'sound/soc/codecs/wm_hubs.c')
-rw-r--r-- | sound/soc/codecs/wm_hubs.c | 148 |
1 files changed, 122 insertions, 26 deletions
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index d73c30536a2c..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" }, |
@@ -753,6 +843,12 @@ int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec, | |||
753 | WM8993_LINEOUT2_MODE, | 843 | WM8993_LINEOUT2_MODE, |
754 | WM8993_LINEOUT2_MODE); | 844 | WM8993_LINEOUT2_MODE); |
755 | 845 | ||
846 | /* If the line outputs are differential then we aren't presenting | ||
847 | * VMID as an output and can disable it. | ||
848 | */ | ||
849 | if (lineout1_diff && lineout2_diff) | ||
850 | codec->idle_bias_off = 1; | ||
851 | |||
756 | if (lineout1fb) | 852 | if (lineout1fb) |
757 | snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL, | 853 | snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL, |
758 | WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB); | 854 | WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB); |