diff options
Diffstat (limited to 'sound/soc/codecs/twl6040.c')
-rw-r--r-- | sound/soc/codecs/twl6040.c | 805 |
1 files changed, 365 insertions, 440 deletions
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 443032b3b329..73e11f022ded 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c | |||
@@ -57,6 +57,13 @@ | |||
57 | #define TWL6040_HF_VOL_MASK 0x1F | 57 | #define TWL6040_HF_VOL_MASK 0x1F |
58 | #define TWL6040_HF_VOL_SHIFT 0 | 58 | #define TWL6040_HF_VOL_SHIFT 0 |
59 | 59 | ||
60 | /* Shadow register used by the driver */ | ||
61 | #define TWL6040_REG_SW_SHADOW 0x2F | ||
62 | #define TWL6040_CACHEREGNUM (TWL6040_REG_SW_SHADOW + 1) | ||
63 | |||
64 | /* TWL6040_REG_SW_SHADOW (0x2F) fields */ | ||
65 | #define TWL6040_EAR_PATH_ENABLE 0x01 | ||
66 | |||
60 | struct twl6040_output { | 67 | struct twl6040_output { |
61 | u16 active; | 68 | u16 active; |
62 | u16 left_vol; | 69 | u16 left_vol; |
@@ -65,12 +72,13 @@ struct twl6040_output { | |||
65 | u16 right_step; | 72 | u16 right_step; |
66 | unsigned int step_delay; | 73 | unsigned int step_delay; |
67 | u16 ramp; | 74 | u16 ramp; |
68 | u16 mute; | 75 | struct delayed_work work; |
69 | struct completion ramp_done; | 76 | struct completion ramp_done; |
70 | }; | 77 | }; |
71 | 78 | ||
72 | struct twl6040_jack_data { | 79 | struct twl6040_jack_data { |
73 | struct snd_soc_jack *jack; | 80 | struct snd_soc_jack *jack; |
81 | struct delayed_work work; | ||
74 | int report; | 82 | int report; |
75 | }; | 83 | }; |
76 | 84 | ||
@@ -79,7 +87,6 @@ struct twl6040_data { | |||
79 | int plug_irq; | 87 | int plug_irq; |
80 | int codec_powered; | 88 | int codec_powered; |
81 | int pll; | 89 | int pll; |
82 | int non_lp; | ||
83 | int pll_power_mode; | 90 | int pll_power_mode; |
84 | int hs_power_mode; | 91 | int hs_power_mode; |
85 | int hs_power_mode_locked; | 92 | int hs_power_mode_locked; |
@@ -92,104 +99,68 @@ struct twl6040_data { | |||
92 | struct twl6040_jack_data hs_jack; | 99 | struct twl6040_jack_data hs_jack; |
93 | struct snd_soc_codec *codec; | 100 | struct snd_soc_codec *codec; |
94 | struct workqueue_struct *workqueue; | 101 | struct workqueue_struct *workqueue; |
95 | struct delayed_work delayed_work; | ||
96 | struct mutex mutex; | 102 | struct mutex mutex; |
97 | struct twl6040_output headset; | 103 | struct twl6040_output headset; |
98 | struct twl6040_output handsfree; | 104 | struct twl6040_output handsfree; |
99 | struct workqueue_struct *hf_workqueue; | ||
100 | struct workqueue_struct *hs_workqueue; | ||
101 | struct delayed_work hs_delayed_work; | ||
102 | struct delayed_work hf_delayed_work; | ||
103 | }; | 105 | }; |
104 | 106 | ||
105 | /* | 107 | /* |
106 | * twl6040 register cache & default register settings | 108 | * twl6040 register cache & default register settings |
107 | */ | 109 | */ |
108 | static const u8 twl6040_reg[TWL6040_CACHEREGNUM] = { | 110 | static const u8 twl6040_reg[TWL6040_CACHEREGNUM] = { |
109 | 0x00, /* not used 0x00 */ | 111 | 0x00, /* not used 0x00 */ |
110 | 0x4B, /* TWL6040_ASICID (ro) 0x01 */ | 112 | 0x4B, /* REG_ASICID 0x01 (ro) */ |
111 | 0x00, /* TWL6040_ASICREV (ro) 0x02 */ | 113 | 0x00, /* REG_ASICREV 0x02 (ro) */ |
112 | 0x00, /* TWL6040_INTID 0x03 */ | 114 | 0x00, /* REG_INTID 0x03 */ |
113 | 0x00, /* TWL6040_INTMR 0x04 */ | 115 | 0x00, /* REG_INTMR 0x04 */ |
114 | 0x00, /* TWL6040_NCPCTRL 0x05 */ | 116 | 0x00, /* REG_NCPCTRL 0x05 */ |
115 | 0x00, /* TWL6040_LDOCTL 0x06 */ | 117 | 0x00, /* REG_LDOCTL 0x06 */ |
116 | 0x60, /* TWL6040_HPPLLCTL 0x07 */ | 118 | 0x60, /* REG_HPPLLCTL 0x07 */ |
117 | 0x00, /* TWL6040_LPPLLCTL 0x08 */ | 119 | 0x00, /* REG_LPPLLCTL 0x08 */ |
118 | 0x4A, /* TWL6040_LPPLLDIV 0x09 */ | 120 | 0x4A, /* REG_LPPLLDIV 0x09 */ |
119 | 0x00, /* TWL6040_AMICBCTL 0x0A */ | 121 | 0x00, /* REG_AMICBCTL 0x0A */ |
120 | 0x00, /* TWL6040_DMICBCTL 0x0B */ | 122 | 0x00, /* REG_DMICBCTL 0x0B */ |
121 | 0x18, /* TWL6040_MICLCTL 0x0C - No input selected on Left Mic */ | 123 | 0x00, /* REG_MICLCTL 0x0C */ |
122 | 0x18, /* TWL6040_MICRCTL 0x0D - No input selected on Right Mic */ | 124 | 0x00, /* REG_MICRCTL 0x0D */ |
123 | 0x00, /* TWL6040_MICGAIN 0x0E */ | 125 | 0x00, /* REG_MICGAIN 0x0E */ |
124 | 0x1B, /* TWL6040_LINEGAIN 0x0F */ | 126 | 0x1B, /* REG_LINEGAIN 0x0F */ |
125 | 0x00, /* TWL6040_HSLCTL 0x10 */ | 127 | 0x00, /* REG_HSLCTL 0x10 */ |
126 | 0x00, /* TWL6040_HSRCTL 0x11 */ | 128 | 0x00, /* REG_HSRCTL 0x11 */ |
127 | 0x00, /* TWL6040_HSGAIN 0x12 */ | 129 | 0x00, /* REG_HSGAIN 0x12 */ |
128 | 0x00, /* TWL6040_EARCTL 0x13 */ | 130 | 0x00, /* REG_EARCTL 0x13 */ |
129 | 0x00, /* TWL6040_HFLCTL 0x14 */ | 131 | 0x00, /* REG_HFLCTL 0x14 */ |
130 | 0x00, /* TWL6040_HFLGAIN 0x15 */ | 132 | 0x00, /* REG_HFLGAIN 0x15 */ |
131 | 0x00, /* TWL6040_HFRCTL 0x16 */ | 133 | 0x00, /* REG_HFRCTL 0x16 */ |
132 | 0x00, /* TWL6040_HFRGAIN 0x17 */ | 134 | 0x00, /* REG_HFRGAIN 0x17 */ |
133 | 0x00, /* TWL6040_VIBCTLL 0x18 */ | 135 | 0x00, /* REG_VIBCTLL 0x18 */ |
134 | 0x00, /* TWL6040_VIBDATL 0x19 */ | 136 | 0x00, /* REG_VIBDATL 0x19 */ |
135 | 0x00, /* TWL6040_VIBCTLR 0x1A */ | 137 | 0x00, /* REG_VIBCTLR 0x1A */ |
136 | 0x00, /* TWL6040_VIBDATR 0x1B */ | 138 | 0x00, /* REG_VIBDATR 0x1B */ |
137 | 0x00, /* TWL6040_HKCTL1 0x1C */ | 139 | 0x00, /* REG_HKCTL1 0x1C */ |
138 | 0x00, /* TWL6040_HKCTL2 0x1D */ | 140 | 0x00, /* REG_HKCTL2 0x1D */ |
139 | 0x00, /* TWL6040_GPOCTL 0x1E */ | 141 | 0x00, /* REG_GPOCTL 0x1E */ |
140 | 0x00, /* TWL6040_ALB 0x1F */ | 142 | 0x00, /* REG_ALB 0x1F */ |
141 | 0x00, /* TWL6040_DLB 0x20 */ | 143 | 0x00, /* REG_DLB 0x20 */ |
142 | 0x00, /* not used 0x21 */ | 144 | 0x00, /* not used 0x21 */ |
143 | 0x00, /* not used 0x22 */ | 145 | 0x00, /* not used 0x22 */ |
144 | 0x00, /* not used 0x23 */ | 146 | 0x00, /* not used 0x23 */ |
145 | 0x00, /* not used 0x24 */ | 147 | 0x00, /* not used 0x24 */ |
146 | 0x00, /* not used 0x25 */ | 148 | 0x00, /* not used 0x25 */ |
147 | 0x00, /* not used 0x26 */ | 149 | 0x00, /* not used 0x26 */ |
148 | 0x00, /* not used 0x27 */ | 150 | 0x00, /* not used 0x27 */ |
149 | 0x00, /* TWL6040_TRIM1 0x28 */ | 151 | 0x00, /* REG_TRIM1 0x28 */ |
150 | 0x00, /* TWL6040_TRIM2 0x29 */ | 152 | 0x00, /* REG_TRIM2 0x29 */ |
151 | 0x00, /* TWL6040_TRIM3 0x2A */ | 153 | 0x00, /* REG_TRIM3 0x2A */ |
152 | 0x00, /* TWL6040_HSOTRIM 0x2B */ | 154 | 0x00, /* REG_HSOTRIM 0x2B */ |
153 | 0x00, /* TWL6040_HFOTRIM 0x2C */ | 155 | 0x00, /* REG_HFOTRIM 0x2C */ |
154 | 0x09, /* TWL6040_ACCCTL 0x2D */ | 156 | 0x09, /* REG_ACCCTL 0x2D */ |
155 | 0x00, /* TWL6040_STATUS (ro) 0x2E */ | 157 | 0x00, /* REG_STATUS 0x2E (ro) */ |
156 | }; | 158 | |
157 | 159 | 0x00, /* REG_SW_SHADOW 0x2F - Shadow, non HW register */ | |
158 | /* | ||
159 | * twl6040 vio/gnd registers: | ||
160 | * registers under vio/gnd supply can be accessed | ||
161 | * before the power-up sequence, after NRESPWRON goes high | ||
162 | */ | ||
163 | static const int twl6040_vio_reg[TWL6040_VIOREGNUM] = { | ||
164 | TWL6040_REG_ASICID, | ||
165 | TWL6040_REG_ASICREV, | ||
166 | TWL6040_REG_INTID, | ||
167 | TWL6040_REG_INTMR, | ||
168 | TWL6040_REG_NCPCTL, | ||
169 | TWL6040_REG_LDOCTL, | ||
170 | TWL6040_REG_AMICBCTL, | ||
171 | TWL6040_REG_DMICBCTL, | ||
172 | TWL6040_REG_HKCTL1, | ||
173 | TWL6040_REG_HKCTL2, | ||
174 | TWL6040_REG_GPOCTL, | ||
175 | TWL6040_REG_TRIM1, | ||
176 | TWL6040_REG_TRIM2, | ||
177 | TWL6040_REG_TRIM3, | ||
178 | TWL6040_REG_HSOTRIM, | ||
179 | TWL6040_REG_HFOTRIM, | ||
180 | TWL6040_REG_ACCCTL, | ||
181 | TWL6040_REG_STATUS, | ||
182 | }; | 160 | }; |
183 | 161 | ||
184 | /* | 162 | /* List of registers to be restored after power up */ |
185 | * twl6040 vdd/vss registers: | 163 | static const int twl6040_restore_list[] = { |
186 | * registers under vdd/vss supplies can only be accessed | ||
187 | * after the power-up sequence | ||
188 | */ | ||
189 | static const int twl6040_vdd_reg[TWL6040_VDDREGNUM] = { | ||
190 | TWL6040_REG_HPPLLCTL, | ||
191 | TWL6040_REG_LPPLLCTL, | ||
192 | TWL6040_REG_LPPLLDIV, | ||
193 | TWL6040_REG_MICLCTL, | 164 | TWL6040_REG_MICLCTL, |
194 | TWL6040_REG_MICRCTL, | 165 | TWL6040_REG_MICRCTL, |
195 | TWL6040_REG_MICGAIN, | 166 | TWL6040_REG_MICGAIN, |
@@ -202,12 +173,6 @@ static const int twl6040_vdd_reg[TWL6040_VDDREGNUM] = { | |||
202 | TWL6040_REG_HFLGAIN, | 173 | TWL6040_REG_HFLGAIN, |
203 | TWL6040_REG_HFRCTL, | 174 | TWL6040_REG_HFRCTL, |
204 | TWL6040_REG_HFRGAIN, | 175 | TWL6040_REG_HFRGAIN, |
205 | TWL6040_REG_VIBCTLL, | ||
206 | TWL6040_REG_VIBDATL, | ||
207 | TWL6040_REG_VIBCTLR, | ||
208 | TWL6040_REG_VIBDATR, | ||
209 | TWL6040_REG_ALB, | ||
210 | TWL6040_REG_DLB, | ||
211 | }; | 176 | }; |
212 | 177 | ||
213 | /* set of rates for each pll: low-power and high-performance */ | 178 | /* set of rates for each pll: low-power and high-performance */ |
@@ -275,8 +240,12 @@ static int twl6040_read_reg_volatile(struct snd_soc_codec *codec, | |||
275 | if (reg >= TWL6040_CACHEREGNUM) | 240 | if (reg >= TWL6040_CACHEREGNUM) |
276 | return -EIO; | 241 | return -EIO; |
277 | 242 | ||
278 | value = twl6040_reg_read(twl6040, reg); | 243 | if (likely(reg < TWL6040_REG_SW_SHADOW)) { |
279 | twl6040_write_reg_cache(codec, reg, value); | 244 | value = twl6040_reg_read(twl6040, reg); |
245 | twl6040_write_reg_cache(codec, reg, value); | ||
246 | } else { | ||
247 | value = twl6040_read_reg_cache(codec, reg); | ||
248 | } | ||
280 | 249 | ||
281 | return value; | 250 | return value; |
282 | } | 251 | } |
@@ -293,59 +262,51 @@ static int twl6040_write(struct snd_soc_codec *codec, | |||
293 | return -EIO; | 262 | return -EIO; |
294 | 263 | ||
295 | twl6040_write_reg_cache(codec, reg, value); | 264 | twl6040_write_reg_cache(codec, reg, value); |
296 | return twl6040_reg_write(twl6040, reg, value); | 265 | if (likely(reg < TWL6040_REG_SW_SHADOW)) |
266 | return twl6040_reg_write(twl6040, reg, value); | ||
267 | else | ||
268 | return 0; | ||
297 | } | 269 | } |
298 | 270 | ||
299 | static void twl6040_init_vio_regs(struct snd_soc_codec *codec) | 271 | static void twl6040_init_chip(struct snd_soc_codec *codec) |
300 | { | 272 | { |
301 | u8 *cache = codec->reg_cache; | 273 | struct twl6040 *twl6040 = codec->control_data; |
302 | int reg, i; | 274 | u8 val; |
303 | 275 | ||
304 | for (i = 0; i < TWL6040_VIOREGNUM; i++) { | 276 | /* Update reg_cache: ASICREV, and TRIM values */ |
305 | reg = twl6040_vio_reg[i]; | 277 | val = twl6040_get_revid(twl6040); |
306 | /* | 278 | twl6040_write_reg_cache(codec, TWL6040_REG_ASICREV, val); |
307 | * skip read-only registers (ASICID, ASICREV, STATUS) | 279 | |
308 | * and registers shared among MFD children | 280 | twl6040_read_reg_volatile(codec, TWL6040_REG_TRIM1); |
309 | */ | 281 | twl6040_read_reg_volatile(codec, TWL6040_REG_TRIM2); |
310 | switch (reg) { | 282 | twl6040_read_reg_volatile(codec, TWL6040_REG_TRIM3); |
311 | case TWL6040_REG_ASICID: | 283 | twl6040_read_reg_volatile(codec, TWL6040_REG_HSOTRIM); |
312 | case TWL6040_REG_ASICREV: | 284 | twl6040_read_reg_volatile(codec, TWL6040_REG_HFOTRIM); |
313 | case TWL6040_REG_INTID: | 285 | |
314 | case TWL6040_REG_INTMR: | 286 | /* Change chip defaults */ |
315 | case TWL6040_REG_NCPCTL: | 287 | /* No imput selected for microphone amplifiers */ |
316 | case TWL6040_REG_LDOCTL: | 288 | twl6040_write_reg_cache(codec, TWL6040_REG_MICLCTL, 0x18); |
317 | case TWL6040_REG_GPOCTL: | 289 | twl6040_write_reg_cache(codec, TWL6040_REG_MICRCTL, 0x18); |
318 | case TWL6040_REG_ACCCTL: | 290 | |
319 | case TWL6040_REG_STATUS: | 291 | /* |
320 | continue; | 292 | * We need to lower the default gain values, so the ramp code |
321 | default: | 293 | * can work correctly for the first playback. |
322 | break; | 294 | * This reduces the pop noise heard at the first playback. |
323 | } | 295 | */ |
324 | twl6040_write(codec, reg, cache[reg]); | 296 | twl6040_write_reg_cache(codec, TWL6040_REG_HSGAIN, 0xff); |
325 | } | 297 | twl6040_write_reg_cache(codec, TWL6040_REG_EARCTL, 0x1e); |
298 | twl6040_write_reg_cache(codec, TWL6040_REG_HFLGAIN, 0x1d); | ||
299 | twl6040_write_reg_cache(codec, TWL6040_REG_HFRGAIN, 0x1d); | ||
300 | twl6040_write_reg_cache(codec, TWL6040_REG_LINEGAIN, 0); | ||
326 | } | 301 | } |
327 | 302 | ||
328 | static void twl6040_init_vdd_regs(struct snd_soc_codec *codec) | 303 | static void twl6040_restore_regs(struct snd_soc_codec *codec) |
329 | { | 304 | { |
330 | u8 *cache = codec->reg_cache; | 305 | u8 *cache = codec->reg_cache; |
331 | int reg, i; | 306 | int reg, i; |
332 | 307 | ||
333 | for (i = 0; i < TWL6040_VDDREGNUM; i++) { | 308 | for (i = 0; i < ARRAY_SIZE(twl6040_restore_list); i++) { |
334 | reg = twl6040_vdd_reg[i]; | 309 | reg = twl6040_restore_list[i]; |
335 | /* skip vibra and PLL registers */ | ||
336 | switch (reg) { | ||
337 | case TWL6040_REG_VIBCTLL: | ||
338 | case TWL6040_REG_VIBDATL: | ||
339 | case TWL6040_REG_VIBCTLR: | ||
340 | case TWL6040_REG_VIBDATR: | ||
341 | case TWL6040_REG_HPPLLCTL: | ||
342 | case TWL6040_REG_LPPLLCTL: | ||
343 | case TWL6040_REG_LPPLLDIV: | ||
344 | continue; | ||
345 | default: | ||
346 | break; | ||
347 | } | ||
348 | |||
349 | twl6040_write(codec, reg, cache[reg]); | 310 | twl6040_write(codec, reg, cache[reg]); |
350 | } | 311 | } |
351 | } | 312 | } |
@@ -524,18 +485,17 @@ static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, | |||
524 | static void twl6040_pga_hs_work(struct work_struct *work) | 485 | static void twl6040_pga_hs_work(struct work_struct *work) |
525 | { | 486 | { |
526 | struct twl6040_data *priv = | 487 | struct twl6040_data *priv = |
527 | container_of(work, struct twl6040_data, hs_delayed_work.work); | 488 | container_of(work, struct twl6040_data, headset.work.work); |
528 | struct snd_soc_codec *codec = priv->codec; | 489 | struct snd_soc_codec *codec = priv->codec; |
529 | struct twl6040_output *headset = &priv->headset; | 490 | struct twl6040_output *headset = &priv->headset; |
530 | unsigned int delay = headset->step_delay; | ||
531 | int i, headset_complete; | 491 | int i, headset_complete; |
532 | 492 | ||
533 | /* do we need to ramp at all ? */ | 493 | /* do we need to ramp at all ? */ |
534 | if (headset->ramp == TWL6040_RAMP_NONE) | 494 | if (headset->ramp == TWL6040_RAMP_NONE) |
535 | return; | 495 | return; |
536 | 496 | ||
537 | /* HS PGA volumes have 4 bits of resolution to ramp */ | 497 | /* HS PGA gain range: 0x0 - 0xf (0 - 15) */ |
538 | for (i = 0; i <= 16; i++) { | 498 | for (i = 0; i < 16; i++) { |
539 | headset_complete = twl6040_hs_ramp_step(codec, | 499 | headset_complete = twl6040_hs_ramp_step(codec, |
540 | headset->left_step, | 500 | headset->left_step, |
541 | headset->right_step); | 501 | headset->right_step); |
@@ -544,15 +504,8 @@ static void twl6040_pga_hs_work(struct work_struct *work) | |||
544 | if (headset_complete) | 504 | if (headset_complete) |
545 | break; | 505 | break; |
546 | 506 | ||
547 | /* | 507 | schedule_timeout_interruptible( |
548 | * TODO: tune: delay is longer over 0dB | 508 | msecs_to_jiffies(headset->step_delay)); |
549 | * as increases are larger. | ||
550 | */ | ||
551 | if (i >= 8) | ||
552 | schedule_timeout_interruptible(msecs_to_jiffies(delay + | ||
553 | (delay >> 1))); | ||
554 | else | ||
555 | schedule_timeout_interruptible(msecs_to_jiffies(delay)); | ||
556 | } | 509 | } |
557 | 510 | ||
558 | if (headset->ramp == TWL6040_RAMP_DOWN) { | 511 | if (headset->ramp == TWL6040_RAMP_DOWN) { |
@@ -567,18 +520,18 @@ static void twl6040_pga_hs_work(struct work_struct *work) | |||
567 | static void twl6040_pga_hf_work(struct work_struct *work) | 520 | static void twl6040_pga_hf_work(struct work_struct *work) |
568 | { | 521 | { |
569 | struct twl6040_data *priv = | 522 | struct twl6040_data *priv = |
570 | container_of(work, struct twl6040_data, hf_delayed_work.work); | 523 | container_of(work, struct twl6040_data, handsfree.work.work); |
571 | struct snd_soc_codec *codec = priv->codec; | 524 | struct snd_soc_codec *codec = priv->codec; |
572 | struct twl6040_output *handsfree = &priv->handsfree; | 525 | struct twl6040_output *handsfree = &priv->handsfree; |
573 | unsigned int delay = handsfree->step_delay; | ||
574 | int i, handsfree_complete; | 526 | int i, handsfree_complete; |
575 | 527 | ||
576 | /* do we need to ramp at all ? */ | 528 | /* do we need to ramp at all ? */ |
577 | if (handsfree->ramp == TWL6040_RAMP_NONE) | 529 | if (handsfree->ramp == TWL6040_RAMP_NONE) |
578 | return; | 530 | return; |
579 | 531 | ||
580 | /* HF PGA volumes have 5 bits of resolution to ramp */ | 532 | /* |
581 | for (i = 0; i <= 32; i++) { | 533 | * HF PGA gain range: 0x00 - 0x1d (0 - 29) */ |
534 | for (i = 0; i < 30; i++) { | ||
582 | handsfree_complete = twl6040_hf_ramp_step(codec, | 535 | handsfree_complete = twl6040_hf_ramp_step(codec, |
583 | handsfree->left_step, | 536 | handsfree->left_step, |
584 | handsfree->right_step); | 537 | handsfree->right_step); |
@@ -587,15 +540,8 @@ static void twl6040_pga_hf_work(struct work_struct *work) | |||
587 | if (handsfree_complete) | 540 | if (handsfree_complete) |
588 | break; | 541 | break; |
589 | 542 | ||
590 | /* | 543 | schedule_timeout_interruptible( |
591 | * TODO: tune: delay is longer over 0dB | 544 | msecs_to_jiffies(handsfree->step_delay)); |
592 | * as increases are larger. | ||
593 | */ | ||
594 | if (i >= 16) | ||
595 | schedule_timeout_interruptible(msecs_to_jiffies(delay + | ||
596 | (delay >> 1))); | ||
597 | else | ||
598 | schedule_timeout_interruptible(msecs_to_jiffies(delay)); | ||
599 | } | 545 | } |
600 | 546 | ||
601 | 547 | ||
@@ -607,36 +553,40 @@ static void twl6040_pga_hf_work(struct work_struct *work) | |||
607 | handsfree->ramp = TWL6040_RAMP_NONE; | 553 | handsfree->ramp = TWL6040_RAMP_NONE; |
608 | } | 554 | } |
609 | 555 | ||
610 | static int pga_event(struct snd_soc_dapm_widget *w, | 556 | static int out_drv_event(struct snd_soc_dapm_widget *w, |
611 | struct snd_kcontrol *kcontrol, int event) | 557 | struct snd_kcontrol *kcontrol, int event) |
612 | { | 558 | { |
613 | struct snd_soc_codec *codec = w->codec; | 559 | struct snd_soc_codec *codec = w->codec; |
614 | struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); | 560 | struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); |
615 | struct twl6040_output *out; | 561 | struct twl6040_output *out; |
616 | struct delayed_work *work; | 562 | struct delayed_work *work; |
617 | struct workqueue_struct *queue; | ||
618 | 563 | ||
619 | switch (w->shift) { | 564 | switch (w->shift) { |
620 | case 2: | 565 | case 2: /* Headset output driver */ |
621 | case 3: | ||
622 | out = &priv->headset; | 566 | out = &priv->headset; |
623 | work = &priv->hs_delayed_work; | 567 | work = &out->work; |
624 | queue = priv->hs_workqueue; | 568 | /* |
569 | * Make sure, that we do not mess up variables for already | ||
570 | * executing work. | ||
571 | */ | ||
572 | cancel_delayed_work_sync(work); | ||
573 | |||
625 | out->left_step = priv->hs_left_step; | 574 | out->left_step = priv->hs_left_step; |
626 | out->right_step = priv->hs_right_step; | 575 | out->right_step = priv->hs_right_step; |
627 | out->step_delay = 5; /* 5 ms between volume ramp steps */ | 576 | out->step_delay = 5; /* 5 ms between volume ramp steps */ |
628 | break; | 577 | break; |
629 | case 4: | 578 | case 4: /* Handsfree output driver */ |
630 | out = &priv->handsfree; | 579 | out = &priv->handsfree; |
631 | work = &priv->hf_delayed_work; | 580 | work = &out->work; |
632 | queue = priv->hf_workqueue; | 581 | /* |
582 | * Make sure, that we do not mess up variables for already | ||
583 | * executing work. | ||
584 | */ | ||
585 | cancel_delayed_work_sync(work); | ||
586 | |||
633 | out->left_step = priv->hf_left_step; | 587 | out->left_step = priv->hf_left_step; |
634 | out->right_step = priv->hf_right_step; | 588 | out->right_step = priv->hf_right_step; |
635 | out->step_delay = 5; /* 5 ms between volume ramp steps */ | 589 | out->step_delay = 5; /* 5 ms between volume ramp steps */ |
636 | if (SND_SOC_DAPM_EVENT_ON(event)) | ||
637 | priv->non_lp++; | ||
638 | else | ||
639 | priv->non_lp--; | ||
640 | break; | 590 | break; |
641 | default: | 591 | default: |
642 | return -1; | 592 | return -1; |
@@ -648,31 +598,25 @@ static int pga_event(struct snd_soc_dapm_widget *w, | |||
648 | break; | 598 | break; |
649 | 599 | ||
650 | /* don't use volume ramp for power-up */ | 600 | /* don't use volume ramp for power-up */ |
601 | out->ramp = TWL6040_RAMP_UP; | ||
651 | out->left_step = out->left_vol; | 602 | out->left_step = out->left_vol; |
652 | out->right_step = out->right_vol; | 603 | out->right_step = out->right_vol; |
653 | 604 | ||
654 | if (!delayed_work_pending(work)) { | 605 | queue_delayed_work(priv->workqueue, work, msecs_to_jiffies(1)); |
655 | out->ramp = TWL6040_RAMP_UP; | ||
656 | queue_delayed_work(queue, work, | ||
657 | msecs_to_jiffies(1)); | ||
658 | } | ||
659 | break; | 606 | break; |
660 | 607 | ||
661 | case SND_SOC_DAPM_PRE_PMD: | 608 | case SND_SOC_DAPM_PRE_PMD: |
662 | if (!out->active) | 609 | if (!out->active) |
663 | break; | 610 | break; |
664 | 611 | ||
665 | if (!delayed_work_pending(work)) { | 612 | /* use volume ramp for power-down */ |
666 | /* use volume ramp for power-down */ | 613 | out->ramp = TWL6040_RAMP_DOWN; |
667 | out->ramp = TWL6040_RAMP_DOWN; | 614 | INIT_COMPLETION(out->ramp_done); |
668 | INIT_COMPLETION(out->ramp_done); | ||
669 | 615 | ||
670 | queue_delayed_work(queue, work, | 616 | queue_delayed_work(priv->workqueue, work, msecs_to_jiffies(1)); |
671 | msecs_to_jiffies(1)); | ||
672 | 617 | ||
673 | wait_for_completion_timeout(&out->ramp_done, | 618 | wait_for_completion_timeout(&out->ramp_done, |
674 | msecs_to_jiffies(2000)); | 619 | msecs_to_jiffies(2000)); |
675 | } | ||
676 | break; | 620 | break; |
677 | } | 621 | } |
678 | 622 | ||
@@ -683,7 +627,7 @@ static int pga_event(struct snd_soc_dapm_widget *w, | |||
683 | static int headset_power_mode(struct snd_soc_codec *codec, int high_perf) | 627 | static int headset_power_mode(struct snd_soc_codec *codec, int high_perf) |
684 | { | 628 | { |
685 | int hslctl, hsrctl; | 629 | int hslctl, hsrctl; |
686 | int mask = TWL6040_HSDRVMODEL | TWL6040_HSDACMODEL; | 630 | int mask = TWL6040_HSDRVMODE | TWL6040_HSDACMODE; |
687 | 631 | ||
688 | hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL); | 632 | hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL); |
689 | hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL); | 633 | hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL); |
@@ -705,11 +649,31 @@ static int headset_power_mode(struct snd_soc_codec *codec, int high_perf) | |||
705 | static int twl6040_hs_dac_event(struct snd_soc_dapm_widget *w, | 649 | static int twl6040_hs_dac_event(struct snd_soc_dapm_widget *w, |
706 | struct snd_kcontrol *kcontrol, int event) | 650 | struct snd_kcontrol *kcontrol, int event) |
707 | { | 651 | { |
652 | struct snd_soc_codec *codec = w->codec; | ||
653 | u8 hslctl, hsrctl; | ||
654 | |||
655 | /* | ||
656 | * Workaround for Headset DC offset caused pop noise: | ||
657 | * Both HS DAC need to be turned on (before the HS driver) and off at | ||
658 | * the same time. | ||
659 | */ | ||
660 | hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL); | ||
661 | hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL); | ||
662 | if (SND_SOC_DAPM_EVENT_ON(event)) { | ||
663 | hslctl |= TWL6040_HSDACENA; | ||
664 | hsrctl |= TWL6040_HSDACENA; | ||
665 | } else { | ||
666 | hslctl &= ~TWL6040_HSDACENA; | ||
667 | hsrctl &= ~TWL6040_HSDACENA; | ||
668 | } | ||
669 | twl6040_write(codec, TWL6040_REG_HSLCTL, hslctl); | ||
670 | twl6040_write(codec, TWL6040_REG_HSRCTL, hsrctl); | ||
671 | |||
708 | msleep(1); | 672 | msleep(1); |
709 | return 0; | 673 | return 0; |
710 | } | 674 | } |
711 | 675 | ||
712 | static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w, | 676 | static int twl6040_ep_drv_event(struct snd_soc_dapm_widget *w, |
713 | struct snd_kcontrol *kcontrol, int event) | 677 | struct snd_kcontrol *kcontrol, int event) |
714 | { | 678 | { |
715 | struct snd_soc_codec *codec = w->codec; | 679 | struct snd_soc_codec *codec = w->codec; |
@@ -717,18 +681,12 @@ static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w, | |||
717 | int ret = 0; | 681 | int ret = 0; |
718 | 682 | ||
719 | if (SND_SOC_DAPM_EVENT_ON(event)) { | 683 | if (SND_SOC_DAPM_EVENT_ON(event)) { |
720 | priv->non_lp++; | 684 | /* Earphone doesn't support low power mode */ |
721 | if (!strcmp(w->name, "Earphone Driver")) { | 685 | priv->hs_power_mode_locked = 1; |
722 | /* Earphone doesn't support low power mode */ | 686 | ret = headset_power_mode(codec, 1); |
723 | priv->hs_power_mode_locked = 1; | ||
724 | ret = headset_power_mode(codec, 1); | ||
725 | } | ||
726 | } else { | 687 | } else { |
727 | priv->non_lp--; | 688 | priv->hs_power_mode_locked = 0; |
728 | if (!strcmp(w->name, "Earphone Driver")) { | 689 | ret = headset_power_mode(codec, priv->hs_power_mode); |
729 | priv->hs_power_mode_locked = 0; | ||
730 | ret = headset_power_mode(codec, priv->hs_power_mode); | ||
731 | } | ||
732 | } | 690 | } |
733 | 691 | ||
734 | msleep(1); | 692 | msleep(1); |
@@ -770,7 +728,7 @@ EXPORT_SYMBOL_GPL(twl6040_hs_jack_detect); | |||
770 | static void twl6040_accessory_work(struct work_struct *work) | 728 | static void twl6040_accessory_work(struct work_struct *work) |
771 | { | 729 | { |
772 | struct twl6040_data *priv = container_of(work, | 730 | struct twl6040_data *priv = container_of(work, |
773 | struct twl6040_data, delayed_work.work); | 731 | struct twl6040_data, hs_jack.work.work); |
774 | struct snd_soc_codec *codec = priv->codec; | 732 | struct snd_soc_codec *codec = priv->codec; |
775 | struct twl6040_jack_data *hs_jack = &priv->hs_jack; | 733 | struct twl6040_jack_data *hs_jack = &priv->hs_jack; |
776 | 734 | ||
@@ -781,15 +739,10 @@ static void twl6040_accessory_work(struct work_struct *work) | |||
781 | static irqreturn_t twl6040_audio_handler(int irq, void *data) | 739 | static irqreturn_t twl6040_audio_handler(int irq, void *data) |
782 | { | 740 | { |
783 | struct snd_soc_codec *codec = data; | 741 | struct snd_soc_codec *codec = data; |
784 | struct twl6040 *twl6040 = codec->control_data; | ||
785 | struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); | 742 | struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); |
786 | u8 intid; | ||
787 | |||
788 | intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); | ||
789 | 743 | ||
790 | if ((intid & TWL6040_PLUGINT) || (intid & TWL6040_UNPLUGINT)) | 744 | queue_delayed_work(priv->workqueue, &priv->hs_jack.work, |
791 | queue_delayed_work(priv->workqueue, &priv->delayed_work, | 745 | msecs_to_jiffies(200)); |
792 | msecs_to_jiffies(200)); | ||
793 | 746 | ||
794 | return IRQ_HANDLED; | 747 | return IRQ_HANDLED; |
795 | } | 748 | } |
@@ -803,25 +756,27 @@ static int twl6040_put_volsw(struct snd_kcontrol *kcontrol, | |||
803 | struct soc_mixer_control *mc = | 756 | struct soc_mixer_control *mc = |
804 | (struct soc_mixer_control *)kcontrol->private_value; | 757 | (struct soc_mixer_control *)kcontrol->private_value; |
805 | int ret; | 758 | int ret; |
806 | unsigned int reg = mc->reg; | ||
807 | 759 | ||
808 | /* For HS and HF we shadow the values and only actually write | 760 | /* For HS and HF we shadow the values and only actually write |
809 | * them out when active in order to ensure the amplifier comes on | 761 | * them out when active in order to ensure the amplifier comes on |
810 | * as quietly as possible. */ | 762 | * as quietly as possible. */ |
811 | switch (reg) { | 763 | switch (mc->reg) { |
812 | case TWL6040_REG_HSGAIN: | 764 | case TWL6040_REG_HSGAIN: |
813 | out = &twl6040_priv->headset; | 765 | out = &twl6040_priv->headset; |
814 | break; | 766 | break; |
815 | default: | 767 | case TWL6040_REG_HFLGAIN: |
768 | out = &twl6040_priv->handsfree; | ||
816 | break; | 769 | break; |
770 | default: | ||
771 | dev_warn(codec->dev, "%s: Unexpected register: 0x%02x\n", | ||
772 | __func__, mc->reg); | ||
773 | return -EINVAL; | ||
817 | } | 774 | } |
818 | 775 | ||
819 | if (out) { | 776 | out->left_vol = ucontrol->value.integer.value[0]; |
820 | out->left_vol = ucontrol->value.integer.value[0]; | 777 | out->right_vol = ucontrol->value.integer.value[1]; |
821 | out->right_vol = ucontrol->value.integer.value[1]; | 778 | if (!out->active) |
822 | if (!out->active) | 779 | return 1; |
823 | return 1; | ||
824 | } | ||
825 | 780 | ||
826 | ret = snd_soc_put_volsw(kcontrol, ucontrol); | 781 | ret = snd_soc_put_volsw(kcontrol, ucontrol); |
827 | if (ret < 0) | 782 | if (ret < 0) |
@@ -838,112 +793,42 @@ static int twl6040_get_volsw(struct snd_kcontrol *kcontrol, | |||
838 | struct twl6040_output *out = &twl6040_priv->headset; | 793 | struct twl6040_output *out = &twl6040_priv->headset; |
839 | struct soc_mixer_control *mc = | 794 | struct soc_mixer_control *mc = |
840 | (struct soc_mixer_control *)kcontrol->private_value; | 795 | (struct soc_mixer_control *)kcontrol->private_value; |
841 | unsigned int reg = mc->reg; | ||
842 | 796 | ||
843 | switch (reg) { | 797 | switch (mc->reg) { |
844 | case TWL6040_REG_HSGAIN: | 798 | case TWL6040_REG_HSGAIN: |
845 | out = &twl6040_priv->headset; | 799 | out = &twl6040_priv->headset; |
846 | ucontrol->value.integer.value[0] = out->left_vol; | ||
847 | ucontrol->value.integer.value[1] = out->right_vol; | ||
848 | return 0; | ||
849 | |||
850 | default: | ||
851 | break; | 800 | break; |
852 | } | ||
853 | |||
854 | return snd_soc_get_volsw(kcontrol, ucontrol); | ||
855 | } | ||
856 | |||
857 | static int twl6040_put_volsw_2r_vu(struct snd_kcontrol *kcontrol, | ||
858 | struct snd_ctl_elem_value *ucontrol) | ||
859 | { | ||
860 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
861 | struct twl6040_data *twl6040_priv = snd_soc_codec_get_drvdata(codec); | ||
862 | struct twl6040_output *out = NULL; | ||
863 | struct soc_mixer_control *mc = | ||
864 | (struct soc_mixer_control *)kcontrol->private_value; | ||
865 | int ret; | ||
866 | unsigned int reg = mc->reg; | ||
867 | |||
868 | /* For HS and HF we shadow the values and only actually write | ||
869 | * them out when active in order to ensure the amplifier comes on | ||
870 | * as quietly as possible. */ | ||
871 | switch (reg) { | ||
872 | case TWL6040_REG_HFLGAIN: | 801 | case TWL6040_REG_HFLGAIN: |
873 | case TWL6040_REG_HFRGAIN: | ||
874 | out = &twl6040_priv->handsfree; | 802 | out = &twl6040_priv->handsfree; |
875 | break; | 803 | break; |
876 | default: | 804 | default: |
877 | break; | 805 | dev_warn(codec->dev, "%s: Unexpected register: 0x%02x\n", |
878 | } | 806 | __func__, mc->reg); |
879 | 807 | return -EINVAL; | |
880 | if (out) { | ||
881 | out->left_vol = ucontrol->value.integer.value[0]; | ||
882 | out->right_vol = ucontrol->value.integer.value[1]; | ||
883 | if (!out->active) | ||
884 | return 1; | ||
885 | } | 808 | } |
886 | 809 | ||
887 | ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); | 810 | ucontrol->value.integer.value[0] = out->left_vol; |
888 | if (ret < 0) | 811 | ucontrol->value.integer.value[1] = out->right_vol; |
889 | return ret; | 812 | return 0; |
890 | |||
891 | return 1; | ||
892 | } | 813 | } |
893 | 814 | ||
894 | static int twl6040_get_volsw_2r(struct snd_kcontrol *kcontrol, | 815 | static int twl6040_soc_dapm_put_vibra_enum(struct snd_kcontrol *kcontrol, |
895 | struct snd_ctl_elem_value *ucontrol) | 816 | struct snd_ctl_elem_value *ucontrol) |
896 | { | 817 | { |
897 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 818 | struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); |
898 | struct twl6040_data *twl6040_priv = snd_soc_codec_get_drvdata(codec); | 819 | struct snd_soc_dapm_widget *widget = wlist->widgets[0]; |
899 | struct twl6040_output *out = &twl6040_priv->handsfree; | 820 | struct snd_soc_codec *codec = widget->codec; |
900 | struct soc_mixer_control *mc = | 821 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; |
901 | (struct soc_mixer_control *)kcontrol->private_value; | 822 | unsigned int val; |
902 | unsigned int reg = mc->reg; | 823 | |
903 | 824 | /* Do not allow changes while Input/FF efect is running */ | |
904 | /* If these are cached registers use the cache */ | 825 | val = twl6040_read_reg_volatile(codec, e->reg); |
905 | switch (reg) { | 826 | if (val & TWL6040_VIBENA && !(val & TWL6040_VIBSEL)) |
906 | case TWL6040_REG_HFLGAIN: | 827 | return -EBUSY; |
907 | case TWL6040_REG_HFRGAIN: | 828 | |
908 | out = &twl6040_priv->handsfree; | 829 | return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); |
909 | ucontrol->value.integer.value[0] = out->left_vol; | ||
910 | ucontrol->value.integer.value[1] = out->right_vol; | ||
911 | return 0; | ||
912 | |||
913 | default: | ||
914 | break; | ||
915 | } | ||
916 | |||
917 | return snd_soc_get_volsw_2r(kcontrol, ucontrol); | ||
918 | } | 830 | } |
919 | 831 | ||
920 | /* double control with volume update */ | ||
921 | #define SOC_TWL6040_DOUBLE_TLV(xname, xreg, shift_left, shift_right, xmax,\ | ||
922 | xinvert, tlv_array)\ | ||
923 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ | ||
924 | .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ | ||
925 | SNDRV_CTL_ELEM_ACCESS_READWRITE,\ | ||
926 | .tlv.p = (tlv_array), \ | ||
927 | .info = snd_soc_info_volsw, .get = twl6040_get_volsw, \ | ||
928 | .put = twl6040_put_volsw, \ | ||
929 | .private_value = (unsigned long)&(struct soc_mixer_control) \ | ||
930 | {.reg = xreg, .shift = shift_left, .rshift = shift_right,\ | ||
931 | .max = xmax, .platform_max = xmax, .invert = xinvert} } | ||
932 | |||
933 | /* double control with volume update */ | ||
934 | #define SOC_TWL6040_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax,\ | ||
935 | xinvert, tlv_array)\ | ||
936 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ | ||
937 | .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ | ||
938 | SNDRV_CTL_ELEM_ACCESS_READWRITE | \ | ||
939 | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ | ||
940 | .tlv.p = (tlv_array), \ | ||
941 | .info = snd_soc_info_volsw_2r, \ | ||
942 | .get = twl6040_get_volsw_2r, .put = twl6040_put_volsw_2r_vu, \ | ||
943 | .private_value = (unsigned long)&(struct soc_mixer_control) \ | ||
944 | {.reg = reg_left, .rreg = reg_right, .shift = xshift, \ | ||
945 | .rshift = xshift, .max = xmax, .invert = xinvert}, } | ||
946 | |||
947 | /* | 832 | /* |
948 | * MICATT volume control: | 833 | * MICATT volume control: |
949 | * from -6 to 0 dB in 6 dB steps | 834 | * from -6 to 0 dB in 6 dB steps |
@@ -1015,6 +900,19 @@ static const struct soc_enum twl6040_hf_enum[] = { | |||
1015 | twl6040_hf_texts), | 900 | twl6040_hf_texts), |
1016 | }; | 901 | }; |
1017 | 902 | ||
903 | static const char *twl6040_vibrapath_texts[] = { | ||
904 | "Input FF", "Audio PDM" | ||
905 | }; | ||
906 | |||
907 | static const struct soc_enum twl6040_vibra_enum[] = { | ||
908 | SOC_ENUM_SINGLE(TWL6040_REG_VIBCTLL, 1, | ||
909 | ARRAY_SIZE(twl6040_vibrapath_texts), | ||
910 | twl6040_vibrapath_texts), | ||
911 | SOC_ENUM_SINGLE(TWL6040_REG_VIBCTLR, 1, | ||
912 | ARRAY_SIZE(twl6040_vibrapath_texts), | ||
913 | twl6040_vibrapath_texts), | ||
914 | }; | ||
915 | |||
1018 | static const struct snd_kcontrol_new amicl_control = | 916 | static const struct snd_kcontrol_new amicl_control = |
1019 | SOC_DAPM_ENUM("Route", twl6040_enum[0]); | 917 | SOC_DAPM_ENUM("Route", twl6040_enum[0]); |
1020 | 918 | ||
@@ -1035,8 +933,25 @@ static const struct snd_kcontrol_new hfl_mux_controls = | |||
1035 | static const struct snd_kcontrol_new hfr_mux_controls = | 933 | static const struct snd_kcontrol_new hfr_mux_controls = |
1036 | SOC_DAPM_ENUM("Route", twl6040_hf_enum[1]); | 934 | SOC_DAPM_ENUM("Route", twl6040_hf_enum[1]); |
1037 | 935 | ||
1038 | static const struct snd_kcontrol_new ep_driver_switch_controls = | 936 | static const struct snd_kcontrol_new ep_path_enable_control = |
1039 | SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0); | 937 | SOC_DAPM_SINGLE("Switch", TWL6040_REG_SW_SHADOW, 0, 1, 0); |
938 | |||
939 | static const struct snd_kcontrol_new auxl_switch_control = | ||
940 | SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFLCTL, 6, 1, 0); | ||
941 | |||
942 | static const struct snd_kcontrol_new auxr_switch_control = | ||
943 | SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFRCTL, 6, 1, 0); | ||
944 | |||
945 | /* Vibra playback switches */ | ||
946 | static const struct snd_kcontrol_new vibral_mux_controls = | ||
947 | SOC_DAPM_ENUM_EXT("Route", twl6040_vibra_enum[0], | ||
948 | snd_soc_dapm_get_enum_double, | ||
949 | twl6040_soc_dapm_put_vibra_enum); | ||
950 | |||
951 | static const struct snd_kcontrol_new vibrar_mux_controls = | ||
952 | SOC_DAPM_ENUM_EXT("Route", twl6040_vibra_enum[1], | ||
953 | snd_soc_dapm_get_enum_double, | ||
954 | twl6040_soc_dapm_put_vibra_enum); | ||
1040 | 955 | ||
1041 | /* Headset power mode */ | 956 | /* Headset power mode */ |
1042 | static const char *twl6040_power_mode_texts[] = { | 957 | static const char *twl6040_power_mode_texts[] = { |
@@ -1105,6 +1020,15 @@ int twl6040_get_clk_id(struct snd_soc_codec *codec) | |||
1105 | } | 1020 | } |
1106 | EXPORT_SYMBOL_GPL(twl6040_get_clk_id); | 1021 | EXPORT_SYMBOL_GPL(twl6040_get_clk_id); |
1107 | 1022 | ||
1023 | int twl6040_get_trim_value(struct snd_soc_codec *codec, enum twl6040_trim trim) | ||
1024 | { | ||
1025 | if (unlikely(trim >= TWL6040_TRIM_INVAL)) | ||
1026 | return -EINVAL; | ||
1027 | |||
1028 | return twl6040_read_reg_cache(codec, TWL6040_REG_TRIM1 + trim); | ||
1029 | } | ||
1030 | EXPORT_SYMBOL_GPL(twl6040_get_trim_value); | ||
1031 | |||
1108 | static const struct snd_kcontrol_new twl6040_snd_controls[] = { | 1032 | static const struct snd_kcontrol_new twl6040_snd_controls[] = { |
1109 | /* Capture gains */ | 1033 | /* Capture gains */ |
1110 | SOC_DOUBLE_TLV("Capture Preamplifier Volume", | 1034 | SOC_DOUBLE_TLV("Capture Preamplifier Volume", |
@@ -1117,10 +1041,12 @@ static const struct snd_kcontrol_new twl6040_snd_controls[] = { | |||
1117 | TWL6040_REG_LINEGAIN, 0, 3, 7, 0, afm_amp_tlv), | 1041 | TWL6040_REG_LINEGAIN, 0, 3, 7, 0, afm_amp_tlv), |
1118 | 1042 | ||
1119 | /* Playback gains */ | 1043 | /* Playback gains */ |
1120 | SOC_TWL6040_DOUBLE_TLV("Headset Playback Volume", | 1044 | SOC_DOUBLE_EXT_TLV("Headset Playback Volume", |
1121 | TWL6040_REG_HSGAIN, 0, 4, 0xF, 1, hs_tlv), | 1045 | TWL6040_REG_HSGAIN, 0, 4, 0xF, 1, twl6040_get_volsw, |
1122 | SOC_TWL6040_DOUBLE_R_TLV("Handsfree Playback Volume", | 1046 | twl6040_put_volsw, hs_tlv), |
1123 | TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv), | 1047 | SOC_DOUBLE_R_EXT_TLV("Handsfree Playback Volume", |
1048 | TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, | ||
1049 | twl6040_get_volsw, twl6040_put_volsw, hf_tlv), | ||
1124 | SOC_SINGLE_TLV("Earphone Playback Volume", | 1050 | SOC_SINGLE_TLV("Earphone Playback Volume", |
1125 | TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv), | 1051 | TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv), |
1126 | 1052 | ||
@@ -1146,6 +1072,10 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { | |||
1146 | SND_SOC_DAPM_OUTPUT("HFL"), | 1072 | SND_SOC_DAPM_OUTPUT("HFL"), |
1147 | SND_SOC_DAPM_OUTPUT("HFR"), | 1073 | SND_SOC_DAPM_OUTPUT("HFR"), |
1148 | SND_SOC_DAPM_OUTPUT("EP"), | 1074 | SND_SOC_DAPM_OUTPUT("EP"), |
1075 | SND_SOC_DAPM_OUTPUT("AUXL"), | ||
1076 | SND_SOC_DAPM_OUTPUT("AUXR"), | ||
1077 | SND_SOC_DAPM_OUTPUT("VIBRAL"), | ||
1078 | SND_SOC_DAPM_OUTPUT("VIBRAR"), | ||
1149 | 1079 | ||
1150 | /* Analog input muxes for the capture amplifiers */ | 1080 | /* Analog input muxes for the capture amplifiers */ |
1151 | SND_SOC_DAPM_MUX("Analog Left Capture Route", | 1081 | SND_SOC_DAPM_MUX("Analog Left Capture Route", |
@@ -1182,59 +1112,76 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { | |||
1182 | TWL6040_REG_DMICBCTL, 4, 0), | 1112 | TWL6040_REG_DMICBCTL, 4, 0), |
1183 | 1113 | ||
1184 | /* DACs */ | 1114 | /* DACs */ |
1185 | SND_SOC_DAPM_DAC_E("HSDAC Left", "Headset Playback", | 1115 | SND_SOC_DAPM_DAC("HSDAC Left", "Headset Playback", SND_SOC_NOPM, 0, 0), |
1186 | TWL6040_REG_HSLCTL, 0, 0, | 1116 | SND_SOC_DAPM_DAC("HSDAC Right", "Headset Playback", SND_SOC_NOPM, 0, 0), |
1187 | twl6040_hs_dac_event, | 1117 | SND_SOC_DAPM_DAC("HFDAC Left", "Handsfree Playback", |
1188 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | 1118 | TWL6040_REG_HFLCTL, 0, 0), |
1189 | SND_SOC_DAPM_DAC_E("HSDAC Right", "Headset Playback", | 1119 | SND_SOC_DAPM_DAC("HFDAC Right", "Handsfree Playback", |
1190 | TWL6040_REG_HSRCTL, 0, 0, | 1120 | TWL6040_REG_HFRCTL, 0, 0), |
1191 | twl6040_hs_dac_event, | 1121 | /* Virtual DAC for vibra path (DL4 channel) */ |
1192 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | 1122 | SND_SOC_DAPM_DAC("VIBRA DAC", "Vibra Playback", |
1193 | SND_SOC_DAPM_DAC_E("HFDAC Left", "Handsfree Playback", | 1123 | SND_SOC_NOPM, 0, 0), |
1194 | TWL6040_REG_HFLCTL, 0, 0, | 1124 | |
1195 | twl6040_power_mode_event, | 1125 | SND_SOC_DAPM_MUX("Handsfree Left Playback", |
1196 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | ||
1197 | SND_SOC_DAPM_DAC_E("HFDAC Right", "Handsfree Playback", | ||
1198 | TWL6040_REG_HFRCTL, 0, 0, | ||
1199 | twl6040_power_mode_event, | ||
1200 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | ||
1201 | |||
1202 | SND_SOC_DAPM_MUX("HF Left Playback", | ||
1203 | SND_SOC_NOPM, 0, 0, &hfl_mux_controls), | 1126 | SND_SOC_NOPM, 0, 0, &hfl_mux_controls), |
1204 | SND_SOC_DAPM_MUX("HF Right Playback", | 1127 | SND_SOC_DAPM_MUX("Handsfree Right Playback", |
1205 | SND_SOC_NOPM, 0, 0, &hfr_mux_controls), | 1128 | SND_SOC_NOPM, 0, 0, &hfr_mux_controls), |
1206 | /* Analog playback Muxes */ | 1129 | /* Analog playback Muxes */ |
1207 | SND_SOC_DAPM_MUX("HS Left Playback", | 1130 | SND_SOC_DAPM_MUX("Headset Left Playback", |
1208 | SND_SOC_NOPM, 0, 0, &hsl_mux_controls), | 1131 | SND_SOC_NOPM, 0, 0, &hsl_mux_controls), |
1209 | SND_SOC_DAPM_MUX("HS Right Playback", | 1132 | SND_SOC_DAPM_MUX("Headset Right Playback", |
1210 | SND_SOC_NOPM, 0, 0, &hsr_mux_controls), | 1133 | SND_SOC_NOPM, 0, 0, &hsr_mux_controls), |
1211 | 1134 | ||
1135 | SND_SOC_DAPM_MUX("Vibra Left Playback", SND_SOC_NOPM, 0, 0, | ||
1136 | &vibral_mux_controls), | ||
1137 | SND_SOC_DAPM_MUX("Vibra Right Playback", SND_SOC_NOPM, 0, 0, | ||
1138 | &vibrar_mux_controls), | ||
1139 | |||
1140 | SND_SOC_DAPM_SWITCH("Earphone Playback", SND_SOC_NOPM, 0, 0, | ||
1141 | &ep_path_enable_control), | ||
1142 | SND_SOC_DAPM_SWITCH("AUXL Playback", SND_SOC_NOPM, 0, 0, | ||
1143 | &auxl_switch_control), | ||
1144 | SND_SOC_DAPM_SWITCH("AUXR Playback", SND_SOC_NOPM, 0, 0, | ||
1145 | &auxr_switch_control), | ||
1146 | |||
1212 | /* Analog playback drivers */ | 1147 | /* Analog playback drivers */ |
1213 | SND_SOC_DAPM_OUT_DRV_E("Handsfree Left Driver", | 1148 | SND_SOC_DAPM_OUT_DRV_E("HF Left Driver", |
1214 | TWL6040_REG_HFLCTL, 4, 0, NULL, 0, | 1149 | TWL6040_REG_HFLCTL, 4, 0, NULL, 0, |
1215 | pga_event, | 1150 | out_drv_event, |
1216 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), | 1151 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
1217 | SND_SOC_DAPM_OUT_DRV_E("Handsfree Right Driver", | 1152 | SND_SOC_DAPM_OUT_DRV_E("HF Right Driver", |
1218 | TWL6040_REG_HFRCTL, 4, 0, NULL, 0, | 1153 | TWL6040_REG_HFRCTL, 4, 0, NULL, 0, |
1219 | pga_event, | 1154 | out_drv_event, |
1220 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), | 1155 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
1221 | SND_SOC_DAPM_OUT_DRV_E("Headset Left Driver", | 1156 | SND_SOC_DAPM_OUT_DRV_E("HS Left Driver", |
1222 | TWL6040_REG_HSLCTL, 2, 0, NULL, 0, | 1157 | TWL6040_REG_HSLCTL, 2, 0, NULL, 0, |
1223 | pga_event, | 1158 | out_drv_event, |
1224 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), | 1159 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
1225 | SND_SOC_DAPM_OUT_DRV_E("Headset Right Driver", | 1160 | SND_SOC_DAPM_OUT_DRV_E("HS Right Driver", |
1226 | TWL6040_REG_HSRCTL, 2, 0, NULL, 0, | 1161 | TWL6040_REG_HSRCTL, 2, 0, NULL, 0, |
1227 | pga_event, | 1162 | out_drv_event, |
1228 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), | 1163 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
1229 | SND_SOC_DAPM_SWITCH_E("Earphone Driver", | 1164 | SND_SOC_DAPM_OUT_DRV_E("Earphone Driver", |
1230 | SND_SOC_NOPM, 0, 0, &ep_driver_switch_controls, | 1165 | TWL6040_REG_EARCTL, 0, 0, NULL, 0, |
1231 | twl6040_power_mode_event, | 1166 | twl6040_ep_drv_event, |
1232 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | 1167 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
1168 | SND_SOC_DAPM_OUT_DRV("Vibra Left Driver", | ||
1169 | TWL6040_REG_VIBCTLL, 0, 0, NULL, 0), | ||
1170 | SND_SOC_DAPM_OUT_DRV("Vibra Right Driver", | ||
1171 | TWL6040_REG_VIBCTLR, 0, 0, NULL, 0), | ||
1172 | |||
1173 | SND_SOC_DAPM_SUPPLY("Vibra Left Control", TWL6040_REG_VIBCTLL, 2, 0, | ||
1174 | NULL, 0), | ||
1175 | SND_SOC_DAPM_SUPPLY("Vibra Right Control", TWL6040_REG_VIBCTLR, 2, 0, | ||
1176 | NULL, 0), | ||
1177 | SND_SOC_DAPM_SUPPLY_S("HSDAC Power", 1, SND_SOC_NOPM, 0, 0, | ||
1178 | twl6040_hs_dac_event, | ||
1179 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | ||
1233 | 1180 | ||
1234 | /* Analog playback PGAs */ | 1181 | /* Analog playback PGAs */ |
1235 | SND_SOC_DAPM_PGA("HFDAC Left PGA", | 1182 | SND_SOC_DAPM_PGA("HF Left PGA", |
1236 | TWL6040_REG_HFLCTL, 1, 0, NULL, 0), | 1183 | TWL6040_REG_HFLCTL, 1, 0, NULL, 0), |
1237 | SND_SOC_DAPM_PGA("HFDAC Right PGA", | 1184 | SND_SOC_DAPM_PGA("HF Right PGA", |
1238 | TWL6040_REG_HFRCTL, 1, 0, NULL, 0), | 1185 | TWL6040_REG_HFRCTL, 1, 0, NULL, 0), |
1239 | 1186 | ||
1240 | }; | 1187 | }; |
@@ -1256,52 +1203,62 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
1256 | {"ADC Right", NULL, "MicAmpR"}, | 1203 | {"ADC Right", NULL, "MicAmpR"}, |
1257 | 1204 | ||
1258 | /* AFM path */ | 1205 | /* AFM path */ |
1259 | {"AFMAmpL", "NULL", "AFML"}, | 1206 | {"AFMAmpL", NULL, "AFML"}, |
1260 | {"AFMAmpR", "NULL", "AFMR"}, | 1207 | {"AFMAmpR", NULL, "AFMR"}, |
1208 | |||
1209 | {"HSDAC Left", NULL, "HSDAC Power"}, | ||
1210 | {"HSDAC Right", NULL, "HSDAC Power"}, | ||
1261 | 1211 | ||
1262 | {"HS Left Playback", "HS DAC", "HSDAC Left"}, | 1212 | {"Headset Left Playback", "HS DAC", "HSDAC Left"}, |
1263 | {"HS Left Playback", "Line-In amp", "AFMAmpL"}, | 1213 | {"Headset Left Playback", "Line-In amp", "AFMAmpL"}, |
1264 | 1214 | ||
1265 | {"HS Right Playback", "HS DAC", "HSDAC Right"}, | 1215 | {"Headset Right Playback", "HS DAC", "HSDAC Right"}, |
1266 | {"HS Right Playback", "Line-In amp", "AFMAmpR"}, | 1216 | {"Headset Right Playback", "Line-In amp", "AFMAmpR"}, |
1267 | 1217 | ||
1268 | {"Headset Left Driver", "NULL", "HS Left Playback"}, | 1218 | {"HS Left Driver", NULL, "Headset Left Playback"}, |
1269 | {"Headset Right Driver", "NULL", "HS Right Playback"}, | 1219 | {"HS Right Driver", NULL, "Headset Right Playback"}, |
1270 | 1220 | ||
1271 | {"HSOL", NULL, "Headset Left Driver"}, | 1221 | {"HSOL", NULL, "HS Left Driver"}, |
1272 | {"HSOR", NULL, "Headset Right Driver"}, | 1222 | {"HSOR", NULL, "HS Right Driver"}, |
1273 | 1223 | ||
1274 | /* Earphone playback path */ | 1224 | /* Earphone playback path */ |
1275 | {"Earphone Driver", "Switch", "HSDAC Left"}, | 1225 | {"Earphone Playback", "Switch", "HSDAC Left"}, |
1226 | {"Earphone Driver", NULL, "Earphone Playback"}, | ||
1276 | {"EP", NULL, "Earphone Driver"}, | 1227 | {"EP", NULL, "Earphone Driver"}, |
1277 | 1228 | ||
1278 | {"HF Left Playback", "HF DAC", "HFDAC Left"}, | 1229 | {"Handsfree Left Playback", "HF DAC", "HFDAC Left"}, |
1279 | {"HF Left Playback", "Line-In amp", "AFMAmpL"}, | 1230 | {"Handsfree Left Playback", "Line-In amp", "AFMAmpL"}, |
1280 | 1231 | ||
1281 | {"HF Right Playback", "HF DAC", "HFDAC Right"}, | 1232 | {"Handsfree Right Playback", "HF DAC", "HFDAC Right"}, |
1282 | {"HF Right Playback", "Line-In amp", "AFMAmpR"}, | 1233 | {"Handsfree Right Playback", "Line-In amp", "AFMAmpR"}, |
1283 | 1234 | ||
1284 | {"HFDAC Left PGA", NULL, "HF Left Playback"}, | 1235 | {"HF Left PGA", NULL, "Handsfree Left Playback"}, |
1285 | {"HFDAC Right PGA", NULL, "HF Right Playback"}, | 1236 | {"HF Right PGA", NULL, "Handsfree Right Playback"}, |
1286 | 1237 | ||
1287 | {"Handsfree Left Driver", "Switch", "HFDAC Left PGA"}, | 1238 | {"HF Left Driver", NULL, "HF Left PGA"}, |
1288 | {"Handsfree Right Driver", "Switch", "HFDAC Right PGA"}, | 1239 | {"HF Right Driver", NULL, "HF Right PGA"}, |
1289 | 1240 | ||
1290 | {"HFL", NULL, "Handsfree Left Driver"}, | 1241 | {"HFL", NULL, "HF Left Driver"}, |
1291 | {"HFR", NULL, "Handsfree Right Driver"}, | 1242 | {"HFR", NULL, "HF Right Driver"}, |
1292 | }; | ||
1293 | 1243 | ||
1294 | static int twl6040_add_widgets(struct snd_soc_codec *codec) | 1244 | {"AUXL Playback", "Switch", "HF Left PGA"}, |
1295 | { | 1245 | {"AUXR Playback", "Switch", "HF Right PGA"}, |
1296 | struct snd_soc_dapm_context *dapm = &codec->dapm; | ||
1297 | 1246 | ||
1298 | snd_soc_dapm_new_controls(dapm, twl6040_dapm_widgets, | 1247 | {"AUXL", NULL, "AUXL Playback"}, |
1299 | ARRAY_SIZE(twl6040_dapm_widgets)); | 1248 | {"AUXR", NULL, "AUXR Playback"}, |
1300 | snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); | ||
1301 | snd_soc_dapm_new_widgets(dapm); | ||
1302 | 1249 | ||
1303 | return 0; | 1250 | /* Vibrator paths */ |
1304 | } | 1251 | {"Vibra Left Playback", "Audio PDM", "VIBRA DAC"}, |
1252 | {"Vibra Right Playback", "Audio PDM", "VIBRA DAC"}, | ||
1253 | |||
1254 | {"Vibra Left Driver", NULL, "Vibra Left Playback"}, | ||
1255 | {"Vibra Right Driver", NULL, "Vibra Right Playback"}, | ||
1256 | {"Vibra Left Driver", NULL, "Vibra Left Control"}, | ||
1257 | {"Vibra Right Driver", NULL, "Vibra Right Control"}, | ||
1258 | |||
1259 | {"VIBRAL", NULL, "Vibra Left Driver"}, | ||
1260 | {"VIBRAR", NULL, "Vibra Right Driver"}, | ||
1261 | }; | ||
1305 | 1262 | ||
1306 | static int twl6040_set_bias_level(struct snd_soc_codec *codec, | 1263 | static int twl6040_set_bias_level(struct snd_soc_codec *codec, |
1307 | enum snd_soc_bias_level level) | 1264 | enum snd_soc_bias_level level) |
@@ -1325,8 +1282,7 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, | |||
1325 | 1282 | ||
1326 | priv->codec_powered = 1; | 1283 | priv->codec_powered = 1; |
1327 | 1284 | ||
1328 | /* initialize vdd/vss registers with reg_cache */ | 1285 | twl6040_restore_regs(codec); |
1329 | twl6040_init_vdd_regs(codec); | ||
1330 | 1286 | ||
1331 | /* Set external boost GPO */ | 1287 | /* Set external boost GPO */ |
1332 | twl6040_write(codec, TWL6040_REG_GPOCTL, 0x02); | 1288 | twl6040_write(codec, TWL6040_REG_GPOCTL, 0x02); |
@@ -1380,13 +1336,6 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream, | |||
1380 | rate); | 1336 | rate); |
1381 | return -EINVAL; | 1337 | return -EINVAL; |
1382 | } | 1338 | } |
1383 | /* Capture is not supported with 17.64MHz sysclk */ | ||
1384 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { | ||
1385 | dev_err(codec->dev, | ||
1386 | "capture mode is not supported at %dHz\n", | ||
1387 | rate); | ||
1388 | return -EINVAL; | ||
1389 | } | ||
1390 | priv->sysclk = 17640000; | 1339 | priv->sysclk = 17640000; |
1391 | break; | 1340 | break; |
1392 | case 8000: | 1341 | case 8000: |
@@ -1419,13 +1368,6 @@ static int twl6040_prepare(struct snd_pcm_substream *substream, | |||
1419 | return -EINVAL; | 1368 | return -EINVAL; |
1420 | } | 1369 | } |
1421 | 1370 | ||
1422 | if ((priv->sysclk == 17640000) && priv->non_lp) { | ||
1423 | dev_err(codec->dev, | ||
1424 | "some enabled paths aren't supported at %dHz\n", | ||
1425 | priv->sysclk); | ||
1426 | return -EPERM; | ||
1427 | } | ||
1428 | |||
1429 | ret = twl6040_set_pll(twl6040, priv->pll, priv->clk_in, priv->sysclk); | 1371 | ret = twl6040_set_pll(twl6040, priv->pll, priv->clk_in, priv->sysclk); |
1430 | if (ret) { | 1372 | if (ret) { |
1431 | dev_err(codec->dev, "Can not set PLL (%d)\n", ret); | 1373 | dev_err(codec->dev, "Can not set PLL (%d)\n", ret); |
@@ -1464,11 +1406,11 @@ static struct snd_soc_dai_ops twl6040_dai_ops = { | |||
1464 | 1406 | ||
1465 | static struct snd_soc_dai_driver twl6040_dai[] = { | 1407 | static struct snd_soc_dai_driver twl6040_dai[] = { |
1466 | { | 1408 | { |
1467 | .name = "twl6040-hifi", | 1409 | .name = "twl6040-legacy", |
1468 | .playback = { | 1410 | .playback = { |
1469 | .stream_name = "Playback", | 1411 | .stream_name = "Playback", |
1470 | .channels_min = 1, | 1412 | .channels_min = 1, |
1471 | .channels_max = 2, | 1413 | .channels_max = 5, |
1472 | .rates = TWL6040_RATES, | 1414 | .rates = TWL6040_RATES, |
1473 | .formats = TWL6040_FORMATS, | 1415 | .formats = TWL6040_FORMATS, |
1474 | }, | 1416 | }, |
@@ -1518,8 +1460,8 @@ static struct snd_soc_dai_driver twl6040_dai[] = { | |||
1518 | .name = "twl6040-vib", | 1460 | .name = "twl6040-vib", |
1519 | .playback = { | 1461 | .playback = { |
1520 | .stream_name = "Vibra Playback", | 1462 | .stream_name = "Vibra Playback", |
1521 | .channels_min = 2, | 1463 | .channels_min = 1, |
1522 | .channels_max = 2, | 1464 | .channels_max = 1, |
1523 | .rates = SNDRV_PCM_RATE_CONTINUOUS, | 1465 | .rates = SNDRV_PCM_RATE_CONTINUOUS, |
1524 | .formats = TWL6040_FORMATS, | 1466 | .formats = TWL6040_FORMATS, |
1525 | }, | 1467 | }, |
@@ -1562,6 +1504,7 @@ static int twl6040_probe(struct snd_soc_codec *codec) | |||
1562 | 1504 | ||
1563 | priv->codec = codec; | 1505 | priv->codec = codec; |
1564 | codec->control_data = dev_get_drvdata(codec->dev->parent); | 1506 | codec->control_data = dev_get_drvdata(codec->dev->parent); |
1507 | codec->ignore_pmdown_time = 1; | ||
1565 | 1508 | ||
1566 | if (pdata && pdata->hs_left_step && pdata->hs_right_step) { | 1509 | if (pdata && pdata->hs_left_step && pdata->hs_right_step) { |
1567 | priv->hs_left_step = pdata->hs_left_step; | 1510 | priv->hs_left_step = pdata->hs_left_step; |
@@ -1586,33 +1529,21 @@ static int twl6040_probe(struct snd_soc_codec *codec) | |||
1586 | goto work_err; | 1529 | goto work_err; |
1587 | } | 1530 | } |
1588 | 1531 | ||
1589 | priv->workqueue = create_singlethread_workqueue("twl6040-codec"); | 1532 | priv->workqueue = alloc_workqueue("twl6040-codec", 0, 0); |
1590 | if (!priv->workqueue) { | 1533 | if (!priv->workqueue) { |
1591 | ret = -ENOMEM; | 1534 | ret = -ENOMEM; |
1592 | goto work_err; | 1535 | goto work_err; |
1593 | } | 1536 | } |
1594 | 1537 | ||
1595 | INIT_DELAYED_WORK(&priv->delayed_work, twl6040_accessory_work); | 1538 | INIT_DELAYED_WORK(&priv->hs_jack.work, twl6040_accessory_work); |
1539 | INIT_DELAYED_WORK(&priv->headset.work, twl6040_pga_hs_work); | ||
1540 | INIT_DELAYED_WORK(&priv->handsfree.work, twl6040_pga_hf_work); | ||
1596 | 1541 | ||
1597 | mutex_init(&priv->mutex); | 1542 | mutex_init(&priv->mutex); |
1598 | 1543 | ||
1599 | init_completion(&priv->headset.ramp_done); | 1544 | init_completion(&priv->headset.ramp_done); |
1600 | init_completion(&priv->handsfree.ramp_done); | 1545 | init_completion(&priv->handsfree.ramp_done); |
1601 | 1546 | ||
1602 | priv->hf_workqueue = create_singlethread_workqueue("twl6040-hf"); | ||
1603 | if (priv->hf_workqueue == NULL) { | ||
1604 | ret = -ENOMEM; | ||
1605 | goto hfwq_err; | ||
1606 | } | ||
1607 | priv->hs_workqueue = create_singlethread_workqueue("twl6040-hs"); | ||
1608 | if (priv->hs_workqueue == NULL) { | ||
1609 | ret = -ENOMEM; | ||
1610 | goto hswq_err; | ||
1611 | } | ||
1612 | |||
1613 | INIT_DELAYED_WORK(&priv->hs_delayed_work, twl6040_pga_hs_work); | ||
1614 | INIT_DELAYED_WORK(&priv->hf_delayed_work, twl6040_pga_hf_work); | ||
1615 | |||
1616 | ret = request_threaded_irq(priv->plug_irq, NULL, twl6040_audio_handler, | 1547 | ret = request_threaded_irq(priv->plug_irq, NULL, twl6040_audio_handler, |
1617 | 0, "twl6040_irq_plug", codec); | 1548 | 0, "twl6040_irq_plug", codec); |
1618 | if (ret) { | 1549 | if (ret) { |
@@ -1620,27 +1551,16 @@ static int twl6040_probe(struct snd_soc_codec *codec) | |||
1620 | goto plugirq_err; | 1551 | goto plugirq_err; |
1621 | } | 1552 | } |
1622 | 1553 | ||
1623 | /* init vio registers */ | 1554 | twl6040_init_chip(codec); |
1624 | twl6040_init_vio_regs(codec); | ||
1625 | 1555 | ||
1626 | /* power on device */ | 1556 | /* power on device */ |
1627 | ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 1557 | ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
1628 | if (ret) | 1558 | if (!ret) |
1629 | goto bias_err; | 1559 | return 0; |
1630 | |||
1631 | snd_soc_add_controls(codec, twl6040_snd_controls, | ||
1632 | ARRAY_SIZE(twl6040_snd_controls)); | ||
1633 | twl6040_add_widgets(codec); | ||
1634 | |||
1635 | return 0; | ||
1636 | 1560 | ||
1637 | bias_err: | 1561 | /* Error path */ |
1638 | free_irq(priv->plug_irq, codec); | 1562 | free_irq(priv->plug_irq, codec); |
1639 | plugirq_err: | 1563 | plugirq_err: |
1640 | destroy_workqueue(priv->hs_workqueue); | ||
1641 | hswq_err: | ||
1642 | destroy_workqueue(priv->hf_workqueue); | ||
1643 | hfwq_err: | ||
1644 | destroy_workqueue(priv->workqueue); | 1564 | destroy_workqueue(priv->workqueue); |
1645 | work_err: | 1565 | work_err: |
1646 | kfree(priv); | 1566 | kfree(priv); |
@@ -1654,8 +1574,6 @@ static int twl6040_remove(struct snd_soc_codec *codec) | |||
1654 | twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); | 1574 | twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); |
1655 | free_irq(priv->plug_irq, codec); | 1575 | free_irq(priv->plug_irq, codec); |
1656 | destroy_workqueue(priv->workqueue); | 1576 | destroy_workqueue(priv->workqueue); |
1657 | destroy_workqueue(priv->hf_workqueue); | ||
1658 | destroy_workqueue(priv->hs_workqueue); | ||
1659 | kfree(priv); | 1577 | kfree(priv); |
1660 | 1578 | ||
1661 | return 0; | 1579 | return 0; |
@@ -1672,6 +1590,13 @@ static struct snd_soc_codec_driver soc_codec_dev_twl6040 = { | |||
1672 | .reg_cache_size = ARRAY_SIZE(twl6040_reg), | 1590 | .reg_cache_size = ARRAY_SIZE(twl6040_reg), |
1673 | .reg_word_size = sizeof(u8), | 1591 | .reg_word_size = sizeof(u8), |
1674 | .reg_cache_default = twl6040_reg, | 1592 | .reg_cache_default = twl6040_reg, |
1593 | |||
1594 | .controls = twl6040_snd_controls, | ||
1595 | .num_controls = ARRAY_SIZE(twl6040_snd_controls), | ||
1596 | .dapm_widgets = twl6040_dapm_widgets, | ||
1597 | .num_dapm_widgets = ARRAY_SIZE(twl6040_dapm_widgets), | ||
1598 | .dapm_routes = intercon, | ||
1599 | .num_dapm_routes = ARRAY_SIZE(intercon), | ||
1675 | }; | 1600 | }; |
1676 | 1601 | ||
1677 | static int __devinit twl6040_codec_probe(struct platform_device *pdev) | 1602 | static int __devinit twl6040_codec_probe(struct platform_device *pdev) |