aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm_hubs.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/wm_hubs.c')
-rw-r--r--sound/soc/codecs/wm_hubs.c148
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 */
86static 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 */
87static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol, 140static 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
307static 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
254static int hp_event(struct snd_soc_dapm_widget *w, 348static 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,
473SND_SOC_DAPM_PGA("Left Output PGA", WM8993_POWER_MANAGEMENT_3, 7, 0, NULL, 0), 560SND_SOC_DAPM_PGA("Left Output PGA", WM8993_POWER_MANAGEMENT_3, 7, 0, NULL, 0),
474SND_SOC_DAPM_PGA("Right Output PGA", WM8993_POWER_MANAGEMENT_3, 6, 0, NULL, 0), 561SND_SOC_DAPM_PGA("Right Output PGA", WM8993_POWER_MANAGEMENT_3, 6, 0, NULL, 0),
475 562
563SND_SOC_DAPM_SUPPLY("Headphone Supply", SND_SOC_NOPM, 0, 0, hp_supply_event,
564 SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
476SND_SOC_DAPM_PGA_E("Headphone PGA", SND_SOC_NOPM, 0, 0, 565SND_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);