| OLD | NEW |
| 1 /* | 1 /* |
| 2 * ALSA SoC TWL6040 codec driver | 2 * ALSA SoC TWL6040 codec driver |
| 3 * | 3 * |
| 4 * Author: Misael Lopez Cruz <x0052729@ti.com> | 4 * Author: Misael Lopez Cruz <x0052729@ti.com> |
| 5 * | 5 * |
| 6 * This program is free software; you can redistribute it and/or | 6 * This program is free software; you can redistribute it and/or |
| 7 * modify it under the terms of the GNU General Public License | 7 * modify it under the terms of the GNU General Public License |
| 8 * version 2 as published by the Free Software Foundation. | 8 * version 2 as published by the Free Software Foundation. |
| 9 * | 9 * |
| 10 * This program is distributed in the hope that it will be useful, but | 10 * This program is distributed in the hope that it will be useful, but |
| (...skipping 16 matching lines...) Expand all Loading... |
| 27 #include <linux/i2c.h> | 27 #include <linux/i2c.h> |
| 28 #include <linux/gpio.h> | 28 #include <linux/gpio.h> |
| 29 #include <linux/platform_device.h> | 29 #include <linux/platform_device.h> |
| 30 #include <linux/slab.h> | 30 #include <linux/slab.h> |
| 31 #include <linux/i2c/twl.h> | 31 #include <linux/i2c/twl.h> |
| 32 | 32 |
| 33 #include <sound/core.h> | 33 #include <sound/core.h> |
| 34 #include <sound/pcm.h> | 34 #include <sound/pcm.h> |
| 35 #include <sound/pcm_params.h> | 35 #include <sound/pcm_params.h> |
| 36 #include <sound/soc.h> | 36 #include <sound/soc.h> |
| 37 #include <sound/soc-dapm.h> | |
| 38 #include <sound/initval.h> | 37 #include <sound/initval.h> |
| 39 #include <sound/tlv.h> | 38 #include <sound/tlv.h> |
| 40 | 39 |
| 41 #include "twl6040.h" | 40 #include "twl6040.h" |
| 42 | 41 |
| 43 #define TWL6040_RATES» (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) | 42 #define TWL6040_RATES» » SNDRV_PCM_RATE_8000_96000 |
| 44 #define TWL6040_FORMATS» (SNDRV_PCM_FMTBIT_S32_LE) | 43 #define TWL6040_FORMATS»(SNDRV_PCM_FMTBIT_S32_LE) |
| 44 |
| 45 #define TWL6040_OUTHS_0dB 0x00 |
| 46 #define TWL6040_OUTHS_M30dB 0x0F |
| 47 #define TWL6040_OUTHF_0dB 0x03 |
| 48 #define TWL6040_OUTHF_M52dB 0x1D |
| 49 |
| 50 #define TWL6040_RAMP_NONE» 0 |
| 51 #define TWL6040_RAMP_UP»» 1 |
| 52 #define TWL6040_RAMP_DOWN» 2 |
| 53 |
| 54 #define TWL6040_HSL_VOL_MASK» 0x0F |
| 55 #define TWL6040_HSL_VOL_SHIFT» 0 |
| 56 #define TWL6040_HSR_VOL_MASK» 0xF0 |
| 57 #define TWL6040_HSR_VOL_SHIFT» 4 |
| 58 #define TWL6040_HF_VOL_MASK» 0x1F |
| 59 #define TWL6040_HF_VOL_SHIFT» 0 |
| 60 |
| 61 struct twl6040_output { |
| 62 » u16 active; |
| 63 » u16 left_vol; |
| 64 » u16 right_vol; |
| 65 » u16 left_step; |
| 66 » u16 right_step; |
| 67 » unsigned int step_delay; |
| 68 » u16 ramp; |
| 69 » u16 mute; |
| 70 » struct completion ramp_done; |
| 71 }; |
| 72 |
| 73 struct twl6040_jack_data { |
| 74 » struct snd_soc_jack *jack; |
| 75 » int report; |
| 76 }; |
| 45 | 77 |
| 46 /* codec private data */ | 78 /* codec private data */ |
| 47 struct twl6040_data { | 79 struct twl6040_data { |
| 48 int audpwron; | 80 int audpwron; |
| 49 int naudint; | 81 int naudint; |
| 50 int codec_powered; | 82 int codec_powered; |
| 51 int pll; | 83 int pll; |
| 52 int non_lp; | 84 int non_lp; |
| 53 unsigned int sysclk; | 85 unsigned int sysclk; |
| 54 struct snd_pcm_hw_constraint_list *sysclk_constraints; | 86 struct snd_pcm_hw_constraint_list *sysclk_constraints; |
| 55 struct completion ready; | 87 struct completion ready; |
| 88 struct twl6040_jack_data hs_jack; |
| 89 struct snd_soc_codec *codec; |
| 90 struct workqueue_struct *workqueue; |
| 91 struct delayed_work delayed_work; |
| 92 struct mutex mutex; |
| 93 struct twl6040_output headset; |
| 94 struct twl6040_output handsfree; |
| 95 struct workqueue_struct *hf_workqueue; |
| 96 struct workqueue_struct *hs_workqueue; |
| 97 struct delayed_work hs_delayed_work; |
| 98 struct delayed_work hf_delayed_work; |
| 56 }; | 99 }; |
| 57 | 100 |
| 58 /* | 101 /* |
| 59 * twl6040 register cache & default register settings | 102 * twl6040 register cache & default register settings |
| 60 */ | 103 */ |
| 61 static const u8 twl6040_reg[TWL6040_CACHEREGNUM] = { | 104 static const u8 twl6040_reg[TWL6040_CACHEREGNUM] = { |
| 62 0x00, /* not used 0x00 */ | 105 0x00, /* not used 0x00 */ |
| 63 0x4B, /* TWL6040_ASICID (ro) 0x01 */ | 106 0x4B, /* TWL6040_ASICID (ro) 0x01 */ |
| 64 0x00, /* TWL6040_ASICREV (ro) 0x02 */ | 107 0x00, /* TWL6040_ASICREV (ro) 0x02 */ |
| 65 0x00, /* TWL6040_INTID 0x03 */ | 108 0x00, /* TWL6040_INTID 0x03 */ |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 194 * read from twl6040 hardware register | 237 * read from twl6040 hardware register |
| 195 */ | 238 */ |
| 196 static int twl6040_read_reg_volatile(struct snd_soc_codec *codec, | 239 static int twl6040_read_reg_volatile(struct snd_soc_codec *codec, |
| 197 unsigned int reg) | 240 unsigned int reg) |
| 198 { | 241 { |
| 199 u8 value; | 242 u8 value; |
| 200 | 243 |
| 201 if (reg >= TWL6040_CACHEREGNUM) | 244 if (reg >= TWL6040_CACHEREGNUM) |
| 202 return -EIO; | 245 return -EIO; |
| 203 | 246 |
| 204 » twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &value, reg); | 247 » twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &value, reg); |
| 205 twl6040_write_reg_cache(codec, reg, value); | 248 twl6040_write_reg_cache(codec, reg, value); |
| 206 | 249 |
| 207 return value; | 250 return value; |
| 208 } | 251 } |
| 209 | 252 |
| 210 /* | 253 /* |
| 211 * write to the twl6040 register space | 254 * write to the twl6040 register space |
| 212 */ | 255 */ |
| 213 static int twl6040_write(struct snd_soc_codec *codec, | 256 static int twl6040_write(struct snd_soc_codec *codec, |
| 214 unsigned int reg, unsigned int value) | 257 unsigned int reg, unsigned int value) |
| 215 { | 258 { |
| 216 if (reg >= TWL6040_CACHEREGNUM) | 259 if (reg >= TWL6040_CACHEREGNUM) |
| 217 return -EIO; | 260 return -EIO; |
| 218 | 261 |
| 219 twl6040_write_reg_cache(codec, reg, value); | 262 twl6040_write_reg_cache(codec, reg, value); |
| 220 » return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg); | 263 » return twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, value, reg); |
| 221 } | 264 } |
| 222 | 265 |
| 223 static void twl6040_init_vio_regs(struct snd_soc_codec *codec) | 266 static void twl6040_init_vio_regs(struct snd_soc_codec *codec) |
| 224 { | 267 { |
| 225 u8 *cache = codec->reg_cache; | 268 u8 *cache = codec->reg_cache; |
| 226 int reg, i; | 269 int reg, i; |
| 227 | 270 |
| 228 /* allow registers to be accessed by i2c */ | 271 /* allow registers to be accessed by i2c */ |
| 229 twl6040_write(codec, TWL6040_REG_ACCCTL, cache[TWL6040_REG_ACCCTL]); | 272 twl6040_write(codec, TWL6040_REG_ACCCTL, cache[TWL6040_REG_ACCCTL]); |
| 230 | 273 |
| (...skipping 16 matching lines...) Expand all Loading... |
| 247 { | 290 { |
| 248 u8 *cache = codec->reg_cache; | 291 u8 *cache = codec->reg_cache; |
| 249 int reg, i; | 292 int reg, i; |
| 250 | 293 |
| 251 for (i = 0; i < TWL6040_VDDREGNUM; i++) { | 294 for (i = 0; i < TWL6040_VDDREGNUM; i++) { |
| 252 reg = twl6040_vdd_reg[i]; | 295 reg = twl6040_vdd_reg[i]; |
| 253 twl6040_write(codec, reg, cache[reg]); | 296 twl6040_write(codec, reg, cache[reg]); |
| 254 } | 297 } |
| 255 } | 298 } |
| 256 | 299 |
| 300 /* |
| 301 * Ramp HS PGA volume to minimise pops at stream startup and shutdown. |
| 302 */ |
| 303 static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec, |
| 304 unsigned int left_step, unsigned int right_step) |
| 305 { |
| 306 |
| 307 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); |
| 308 struct twl6040_output *headset = &priv->headset; |
| 309 int left_complete = 0, right_complete = 0; |
| 310 u8 reg, val; |
| 311 |
| 312 /* left channel */ |
| 313 left_step = (left_step > 0xF) ? 0xF : left_step; |
| 314 reg = twl6040_read_reg_cache(codec, TWL6040_REG_HSGAIN); |
| 315 val = (~reg & TWL6040_HSL_VOL_MASK); |
| 316 |
| 317 if (headset->ramp == TWL6040_RAMP_UP) { |
| 318 /* ramp step up */ |
| 319 if (val < headset->left_vol) { |
| 320 val += left_step; |
| 321 reg &= ~TWL6040_HSL_VOL_MASK; |
| 322 twl6040_write(codec, TWL6040_REG_HSGAIN, |
| 323 (reg | (~val & TWL6040_HSL_VOL_MASK))); |
| 324 } else { |
| 325 left_complete = 1; |
| 326 } |
| 327 } else if (headset->ramp == TWL6040_RAMP_DOWN) { |
| 328 /* ramp step down */ |
| 329 if (val > 0x0) { |
| 330 val -= left_step; |
| 331 reg &= ~TWL6040_HSL_VOL_MASK; |
| 332 twl6040_write(codec, TWL6040_REG_HSGAIN, reg | |
| 333 (~val & TWL6040_HSL_VOL_MASK)); |
| 334 } else { |
| 335 left_complete = 1; |
| 336 } |
| 337 } |
| 338 |
| 339 /* right channel */ |
| 340 right_step = (right_step > 0xF) ? 0xF : right_step; |
| 341 reg = twl6040_read_reg_cache(codec, TWL6040_REG_HSGAIN); |
| 342 val = (~reg & TWL6040_HSR_VOL_MASK) >> TWL6040_HSR_VOL_SHIFT; |
| 343 |
| 344 if (headset->ramp == TWL6040_RAMP_UP) { |
| 345 /* ramp step up */ |
| 346 if (val < headset->right_vol) { |
| 347 val += right_step; |
| 348 reg &= ~TWL6040_HSR_VOL_MASK; |
| 349 twl6040_write(codec, TWL6040_REG_HSGAIN, |
| 350 (reg | (~val << TWL6040_HSR_VOL_SHIFT))); |
| 351 } else { |
| 352 right_complete = 1; |
| 353 } |
| 354 } else if (headset->ramp == TWL6040_RAMP_DOWN) { |
| 355 /* ramp step down */ |
| 356 if (val > 0x0) { |
| 357 val -= right_step; |
| 358 reg &= ~TWL6040_HSR_VOL_MASK; |
| 359 twl6040_write(codec, TWL6040_REG_HSGAIN, |
| 360 reg | (~val << TWL6040_HSR_VOL_SHIFT)); |
| 361 } else { |
| 362 right_complete = 1; |
| 363 } |
| 364 } |
| 365 |
| 366 return left_complete & right_complete; |
| 367 } |
| 368 |
| 369 /* |
| 370 * Ramp HF PGA volume to minimise pops at stream startup and shutdown. |
| 371 */ |
| 372 static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, |
| 373 unsigned int left_step, unsigned int right_step) |
| 374 { |
| 375 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); |
| 376 struct twl6040_output *handsfree = &priv->handsfree; |
| 377 int left_complete = 0, right_complete = 0; |
| 378 u16 reg, val; |
| 379 |
| 380 /* left channel */ |
| 381 left_step = (left_step > 0x1D) ? 0x1D : left_step; |
| 382 reg = twl6040_read_reg_cache(codec, TWL6040_REG_HFLGAIN); |
| 383 reg = 0x1D - reg; |
| 384 val = (reg & TWL6040_HF_VOL_MASK); |
| 385 if (handsfree->ramp == TWL6040_RAMP_UP) { |
| 386 /* ramp step up */ |
| 387 if (val < handsfree->left_vol) { |
| 388 val += left_step; |
| 389 reg &= ~TWL6040_HF_VOL_MASK; |
| 390 twl6040_write(codec, TWL6040_REG_HFLGAIN, |
| 391 reg | (0x1D - val)); |
| 392 } else { |
| 393 left_complete = 1; |
| 394 } |
| 395 } else if (handsfree->ramp == TWL6040_RAMP_DOWN) { |
| 396 /* ramp step down */ |
| 397 if (val > 0) { |
| 398 val -= left_step; |
| 399 reg &= ~TWL6040_HF_VOL_MASK; |
| 400 twl6040_write(codec, TWL6040_REG_HFLGAIN, |
| 401 reg | (0x1D - val)); |
| 402 } else { |
| 403 left_complete = 1; |
| 404 } |
| 405 } |
| 406 |
| 407 /* right channel */ |
| 408 right_step = (right_step > 0x1D) ? 0x1D : right_step; |
| 409 reg = twl6040_read_reg_cache(codec, TWL6040_REG_HFRGAIN); |
| 410 reg = 0x1D - reg; |
| 411 val = (reg & TWL6040_HF_VOL_MASK); |
| 412 if (handsfree->ramp == TWL6040_RAMP_UP) { |
| 413 /* ramp step up */ |
| 414 if (val < handsfree->right_vol) { |
| 415 val += right_step; |
| 416 reg &= ~TWL6040_HF_VOL_MASK; |
| 417 twl6040_write(codec, TWL6040_REG_HFRGAIN, |
| 418 reg | (0x1D - val)); |
| 419 } else { |
| 420 right_complete = 1; |
| 421 } |
| 422 } else if (handsfree->ramp == TWL6040_RAMP_DOWN) { |
| 423 /* ramp step down */ |
| 424 if (val > 0) { |
| 425 val -= right_step; |
| 426 reg &= ~TWL6040_HF_VOL_MASK; |
| 427 twl6040_write(codec, TWL6040_REG_HFRGAIN, |
| 428 reg | (0x1D - val)); |
| 429 } |
| 430 } |
| 431 |
| 432 return left_complete & right_complete; |
| 433 } |
| 434 |
| 435 /* |
| 436 * This work ramps both output PGAs at stream start/stop time to |
| 437 * minimise pop associated with DAPM power switching. |
| 438 */ |
| 439 static void twl6040_pga_hs_work(struct work_struct *work) |
| 440 { |
| 441 struct twl6040_data *priv = |
| 442 container_of(work, struct twl6040_data, hs_delayed_work.work); |
| 443 struct snd_soc_codec *codec = priv->codec; |
| 444 struct twl6040_output *headset = &priv->headset; |
| 445 unsigned int delay = headset->step_delay; |
| 446 int i, headset_complete; |
| 447 |
| 448 /* do we need to ramp at all ? */ |
| 449 if (headset->ramp == TWL6040_RAMP_NONE) |
| 450 return; |
| 451 |
| 452 /* HS PGA volumes have 4 bits of resolution to ramp */ |
| 453 for (i = 0; i <= 16; i++) { |
| 454 headset_complete = 1; |
| 455 if (headset->ramp != TWL6040_RAMP_NONE) |
| 456 headset_complete = twl6040_hs_ramp_step(codec, |
| 457 headset->left_step, |
| 458 headset->right_step); |
| 459 |
| 460 /* ramp finished ? */ |
| 461 if (headset_complete) |
| 462 break; |
| 463 |
| 464 /* |
| 465 * TODO: tune: delay is longer over 0dB |
| 466 * as increases are larger. |
| 467 */ |
| 468 if (i >= 8) |
| 469 schedule_timeout_interruptible(msecs_to_jiffies(delay + |
| 470 (delay >> 1))); |
| 471 else |
| 472 schedule_timeout_interruptible(msecs_to_jiffies(delay)); |
| 473 } |
| 474 |
| 475 if (headset->ramp == TWL6040_RAMP_DOWN) { |
| 476 headset->active = 0; |
| 477 complete(&headset->ramp_done); |
| 478 } else { |
| 479 headset->active = 1; |
| 480 } |
| 481 headset->ramp = TWL6040_RAMP_NONE; |
| 482 } |
| 483 |
| 484 static void twl6040_pga_hf_work(struct work_struct *work) |
| 485 { |
| 486 struct twl6040_data *priv = |
| 487 container_of(work, struct twl6040_data, hf_delayed_work.work); |
| 488 struct snd_soc_codec *codec = priv->codec; |
| 489 struct twl6040_output *handsfree = &priv->handsfree; |
| 490 unsigned int delay = handsfree->step_delay; |
| 491 int i, handsfree_complete; |
| 492 |
| 493 /* do we need to ramp at all ? */ |
| 494 if (handsfree->ramp == TWL6040_RAMP_NONE) |
| 495 return; |
| 496 |
| 497 /* HF PGA volumes have 5 bits of resolution to ramp */ |
| 498 for (i = 0; i <= 32; i++) { |
| 499 handsfree_complete = 1; |
| 500 if (handsfree->ramp != TWL6040_RAMP_NONE) |
| 501 handsfree_complete = twl6040_hf_ramp_step(codec, |
| 502 handsfree->left_step, |
| 503 handsfree->right_step); |
| 504 |
| 505 /* ramp finished ? */ |
| 506 if (handsfree_complete) |
| 507 break; |
| 508 |
| 509 /* |
| 510 * TODO: tune: delay is longer over 0dB |
| 511 * as increases are larger. |
| 512 */ |
| 513 if (i >= 16) |
| 514 schedule_timeout_interruptible(msecs_to_jiffies(delay + |
| 515 (delay >> 1))); |
| 516 else |
| 517 schedule_timeout_interruptible(msecs_to_jiffies(delay)); |
| 518 } |
| 519 |
| 520 |
| 521 if (handsfree->ramp == TWL6040_RAMP_DOWN) { |
| 522 handsfree->active = 0; |
| 523 complete(&handsfree->ramp_done); |
| 524 } else |
| 525 handsfree->active = 1; |
| 526 handsfree->ramp = TWL6040_RAMP_NONE; |
| 527 } |
| 528 |
| 529 static int pga_event(struct snd_soc_dapm_widget *w, |
| 530 struct snd_kcontrol *kcontrol, int event) |
| 531 { |
| 532 struct snd_soc_codec *codec = w->codec; |
| 533 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); |
| 534 struct twl6040_output *out; |
| 535 struct delayed_work *work; |
| 536 struct workqueue_struct *queue; |
| 537 |
| 538 switch (w->shift) { |
| 539 case 2: |
| 540 case 3: |
| 541 out = &priv->headset; |
| 542 work = &priv->hs_delayed_work; |
| 543 queue = priv->hs_workqueue; |
| 544 out->step_delay = 5; /* 5 ms between volume ramp steps */ |
| 545 break; |
| 546 case 4: |
| 547 out = &priv->handsfree; |
| 548 work = &priv->hf_delayed_work; |
| 549 queue = priv->hf_workqueue; |
| 550 out->step_delay = 5; /* 5 ms between volume ramp steps */ |
| 551 if (SND_SOC_DAPM_EVENT_ON(event)) |
| 552 priv->non_lp++; |
| 553 else |
| 554 priv->non_lp--; |
| 555 break; |
| 556 default: |
| 557 return -1; |
| 558 } |
| 559 |
| 560 switch (event) { |
| 561 case SND_SOC_DAPM_POST_PMU: |
| 562 if (out->active) |
| 563 break; |
| 564 |
| 565 /* don't use volume ramp for power-up */ |
| 566 out->left_step = out->left_vol; |
| 567 out->right_step = out->right_vol; |
| 568 |
| 569 if (!delayed_work_pending(work)) { |
| 570 out->ramp = TWL6040_RAMP_UP; |
| 571 queue_delayed_work(queue, work, |
| 572 msecs_to_jiffies(1)); |
| 573 } |
| 574 break; |
| 575 |
| 576 case SND_SOC_DAPM_PRE_PMD: |
| 577 if (!out->active) |
| 578 break; |
| 579 |
| 580 if (!delayed_work_pending(work)) { |
| 581 /* use volume ramp for power-down */ |
| 582 out->left_step = 1; |
| 583 out->right_step = 1; |
| 584 out->ramp = TWL6040_RAMP_DOWN; |
| 585 INIT_COMPLETION(out->ramp_done); |
| 586 |
| 587 queue_delayed_work(queue, work, |
| 588 msecs_to_jiffies(1)); |
| 589 |
| 590 wait_for_completion_timeout(&out->ramp_done, |
| 591 msecs_to_jiffies(2000)); |
| 592 } |
| 593 break; |
| 594 } |
| 595 |
| 596 return 0; |
| 597 } |
| 598 |
| 257 /* twl6040 codec manual power-up sequence */ | 599 /* twl6040 codec manual power-up sequence */ |
| 258 static void twl6040_power_up(struct snd_soc_codec *codec) | 600 static void twl6040_power_up(struct snd_soc_codec *codec) |
| 259 { | 601 { |
| 260 u8 ncpctl, ldoctl, lppllctl, accctl; | 602 u8 ncpctl, ldoctl, lppllctl, accctl; |
| 261 | 603 |
| 262 ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL); | 604 ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL); |
| 263 ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL); | 605 ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL); |
| 264 lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); | 606 lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); |
| 265 accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL); | 607 accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL); |
| 266 | 608 |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 375 if (SND_SOC_DAPM_EVENT_ON(event)) | 717 if (SND_SOC_DAPM_EVENT_ON(event)) |
| 376 priv->non_lp++; | 718 priv->non_lp++; |
| 377 else | 719 else |
| 378 priv->non_lp--; | 720 priv->non_lp--; |
| 379 | 721 |
| 380 msleep(1); | 722 msleep(1); |
| 381 | 723 |
| 382 return 0; | 724 return 0; |
| 383 } | 725 } |
| 384 | 726 |
| 727 static void twl6040_hs_jack_report(struct snd_soc_codec *codec, |
| 728 struct snd_soc_jack *jack, int report) |
| 729 { |
| 730 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); |
| 731 int status; |
| 732 |
| 733 mutex_lock(&priv->mutex); |
| 734 |
| 735 /* Sync status */ |
| 736 status = twl6040_read_reg_volatile(codec, TWL6040_REG_STATUS); |
| 737 if (status & TWL6040_PLUGCOMP) |
| 738 snd_soc_jack_report(jack, report, report); |
| 739 else |
| 740 snd_soc_jack_report(jack, 0, report); |
| 741 |
| 742 mutex_unlock(&priv->mutex); |
| 743 } |
| 744 |
| 745 void twl6040_hs_jack_detect(struct snd_soc_codec *codec, |
| 746 struct snd_soc_jack *jack, int report) |
| 747 { |
| 748 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); |
| 749 struct twl6040_jack_data *hs_jack = &priv->hs_jack; |
| 750 |
| 751 hs_jack->jack = jack; |
| 752 hs_jack->report = report; |
| 753 |
| 754 twl6040_hs_jack_report(codec, hs_jack->jack, hs_jack->report); |
| 755 } |
| 756 EXPORT_SYMBOL_GPL(twl6040_hs_jack_detect); |
| 757 |
| 758 static void twl6040_accessory_work(struct work_struct *work) |
| 759 { |
| 760 struct twl6040_data *priv = container_of(work, |
| 761 struct twl6040_data, delayed_work.work); |
| 762 struct snd_soc_codec *codec = priv->codec; |
| 763 struct twl6040_jack_data *hs_jack = &priv->hs_jack; |
| 764 |
| 765 twl6040_hs_jack_report(codec, hs_jack->jack, hs_jack->report); |
| 766 } |
| 767 |
| 385 /* audio interrupt handler */ | 768 /* audio interrupt handler */ |
| 386 static irqreturn_t twl6040_naudint_handler(int irq, void *data) | 769 static irqreturn_t twl6040_naudint_handler(int irq, void *data) |
| 387 { | 770 { |
| 388 struct snd_soc_codec *codec = data; | 771 struct snd_soc_codec *codec = data; |
| 389 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); | 772 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); |
| 390 u8 intid; | 773 u8 intid; |
| 391 | 774 |
| 392 » twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &intid, TWL6040_REG_INTID); | 775 » twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &intid, TWL6040_REG_INTID); |
| 393 | 776 |
| 394 » switch (intid) { | 777 » if (intid & TWL6040_THINT) |
| 395 » case TWL6040_THINT: | |
| 396 dev_alert(codec->dev, "die temp over-limit detection\n"); | 778 dev_alert(codec->dev, "die temp over-limit detection\n"); |
| 397 » » break; | 779 |
| 398 » case TWL6040_PLUGINT: | 780 » if ((intid & TWL6040_PLUGINT) || (intid & TWL6040_UNPLUGINT)) |
| 399 » case TWL6040_UNPLUGINT: | 781 » » queue_delayed_work(priv->workqueue, &priv->delayed_work, |
| 400 » case TWL6040_HOOKINT: | 782 » » » » » » » msecs_to_jiffies(200)); |
| 401 » » break; | 783 |
| 402 » case TWL6040_HFINT: | 784 » if (intid & TWL6040_HOOKINT) |
| 785 » » dev_info(codec->dev, "hook detection\n"); |
| 786 |
| 787 » if (intid & TWL6040_HFINT) |
| 403 dev_alert(codec->dev, "hf drivers over current detection\n"); | 788 dev_alert(codec->dev, "hf drivers over current detection\n"); |
| 404 » » break; | 789 |
| 405 » case TWL6040_VIBINT: | 790 » if (intid & TWL6040_VIBINT) |
| 406 dev_alert(codec->dev, "vib drivers over current detection\n"); | 791 dev_alert(codec->dev, "vib drivers over current detection\n"); |
| 407 » » break; | 792 |
| 408 » case TWL6040_READYINT: | 793 » if (intid & TWL6040_READYINT) |
| 409 complete(&priv->ready); | 794 complete(&priv->ready); |
| 410 break; | |
| 411 default: | |
| 412 dev_err(codec->dev, "unknown audio interrupt %d\n", intid); | |
| 413 break; | |
| 414 } | |
| 415 | 795 |
| 416 return IRQ_HANDLED; | 796 return IRQ_HANDLED; |
| 417 } | 797 } |
| 418 | 798 |
| 799 static int twl6040_put_volsw(struct snd_kcontrol *kcontrol, |
| 800 struct snd_ctl_elem_value *ucontrol) |
| 801 { |
| 802 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
| 803 struct twl6040_data *twl6040_priv = snd_soc_codec_get_drvdata(codec); |
| 804 struct twl6040_output *out = NULL; |
| 805 struct soc_mixer_control *mc = |
| 806 (struct soc_mixer_control *)kcontrol->private_value; |
| 807 int ret; |
| 808 unsigned int reg = mc->reg; |
| 809 |
| 810 /* For HS and HF we shadow the values and only actually write |
| 811 * them out when active in order to ensure the amplifier comes on |
| 812 * as quietly as possible. */ |
| 813 switch (reg) { |
| 814 case TWL6040_REG_HSGAIN: |
| 815 out = &twl6040_priv->headset; |
| 816 break; |
| 817 default: |
| 818 break; |
| 819 } |
| 820 |
| 821 if (out) { |
| 822 out->left_vol = ucontrol->value.integer.value[0]; |
| 823 out->right_vol = ucontrol->value.integer.value[1]; |
| 824 if (!out->active) |
| 825 return 1; |
| 826 } |
| 827 |
| 828 ret = snd_soc_put_volsw(kcontrol, ucontrol); |
| 829 if (ret < 0) |
| 830 return ret; |
| 831 |
| 832 return 1; |
| 833 } |
| 834 |
| 835 static int twl6040_get_volsw(struct snd_kcontrol *kcontrol, |
| 836 struct snd_ctl_elem_value *ucontrol) |
| 837 { |
| 838 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
| 839 struct twl6040_data *twl6040_priv = snd_soc_codec_get_drvdata(codec); |
| 840 struct twl6040_output *out = &twl6040_priv->headset; |
| 841 struct soc_mixer_control *mc = |
| 842 (struct soc_mixer_control *)kcontrol->private_value; |
| 843 unsigned int reg = mc->reg; |
| 844 |
| 845 switch (reg) { |
| 846 case TWL6040_REG_HSGAIN: |
| 847 out = &twl6040_priv->headset; |
| 848 ucontrol->value.integer.value[0] = out->left_vol; |
| 849 ucontrol->value.integer.value[1] = out->right_vol; |
| 850 return 0; |
| 851 |
| 852 default: |
| 853 break; |
| 854 } |
| 855 |
| 856 return snd_soc_get_volsw(kcontrol, ucontrol); |
| 857 } |
| 858 |
| 859 static int twl6040_put_volsw_2r_vu(struct snd_kcontrol *kcontrol, |
| 860 struct snd_ctl_elem_value *ucontrol) |
| 861 { |
| 862 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
| 863 struct twl6040_data *twl6040_priv = snd_soc_codec_get_drvdata(codec); |
| 864 struct twl6040_output *out = NULL; |
| 865 struct soc_mixer_control *mc = |
| 866 (struct soc_mixer_control *)kcontrol->private_value; |
| 867 int ret; |
| 868 unsigned int reg = mc->reg; |
| 869 |
| 870 /* For HS and HF we shadow the values and only actually write |
| 871 * them out when active in order to ensure the amplifier comes on |
| 872 * as quietly as possible. */ |
| 873 switch (reg) { |
| 874 case TWL6040_REG_HFLGAIN: |
| 875 case TWL6040_REG_HFRGAIN: |
| 876 out = &twl6040_priv->handsfree; |
| 877 break; |
| 878 default: |
| 879 break; |
| 880 } |
| 881 |
| 882 if (out) { |
| 883 out->left_vol = ucontrol->value.integer.value[0]; |
| 884 out->right_vol = ucontrol->value.integer.value[1]; |
| 885 if (!out->active) |
| 886 return 1; |
| 887 } |
| 888 |
| 889 ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); |
| 890 if (ret < 0) |
| 891 return ret; |
| 892 |
| 893 return 1; |
| 894 } |
| 895 |
| 896 static int twl6040_get_volsw_2r(struct snd_kcontrol *kcontrol, |
| 897 struct snd_ctl_elem_value *ucontrol) |
| 898 { |
| 899 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
| 900 struct twl6040_data *twl6040_priv = snd_soc_codec_get_drvdata(codec); |
| 901 struct twl6040_output *out = &twl6040_priv->handsfree; |
| 902 struct soc_mixer_control *mc = |
| 903 (struct soc_mixer_control *)kcontrol->private_value; |
| 904 unsigned int reg = mc->reg; |
| 905 |
| 906 /* If these are cached registers use the cache */ |
| 907 switch (reg) { |
| 908 case TWL6040_REG_HFLGAIN: |
| 909 case TWL6040_REG_HFRGAIN: |
| 910 out = &twl6040_priv->handsfree; |
| 911 ucontrol->value.integer.value[0] = out->left_vol; |
| 912 ucontrol->value.integer.value[1] = out->right_vol; |
| 913 return 0; |
| 914 |
| 915 default: |
| 916 break; |
| 917 } |
| 918 |
| 919 return snd_soc_get_volsw_2r(kcontrol, ucontrol); |
| 920 } |
| 921 |
| 922 /* double control with volume update */ |
| 923 #define SOC_TWL6040_DOUBLE_TLV(xname, xreg, shift_left, shift_right, xmax,\ |
| 924 xinvert, tlv_array)\ |
| 925 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ |
| 926 .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ |
| 927 SNDRV_CTL_ELEM_ACCESS_READWRITE,\ |
| 928 .tlv.p = (tlv_array), \ |
| 929 .info = snd_soc_info_volsw, .get = twl6040_get_volsw, \ |
| 930 .put = twl6040_put_volsw, \ |
| 931 .private_value = (unsigned long)&(struct soc_mixer_control) \ |
| 932 {.reg = xreg, .shift = shift_left, .rshift = shift_right,\ |
| 933 .max = xmax, .platform_max = xmax, .invert = xinvert} } |
| 934 |
| 935 /* double control with volume update */ |
| 936 #define SOC_TWL6040_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax,\ |
| 937 xinvert, tlv_array)\ |
| 938 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ |
| 939 .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ |
| 940 SNDRV_CTL_ELEM_ACCESS_READWRITE | \ |
| 941 SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ |
| 942 .tlv.p = (tlv_array), \ |
| 943 .info = snd_soc_info_volsw_2r, \ |
| 944 .get = twl6040_get_volsw_2r, .put = twl6040_put_volsw_2r_vu, \ |
| 945 .private_value = (unsigned long)&(struct soc_mixer_control) \ |
| 946 {.reg = reg_left, .rreg = reg_right, .shift = xshift, \ |
| 947 .rshift = xshift, .max = xmax, .invert = xinvert}, } |
| 948 |
| 419 /* | 949 /* |
| 420 * MICATT volume control: | 950 * MICATT volume control: |
| 421 * from -6 to 0 dB in 6 dB steps | 951 * from -6 to 0 dB in 6 dB steps |
| 422 */ | 952 */ |
| 423 static DECLARE_TLV_DB_SCALE(mic_preamp_tlv, -600, 600, 0); | 953 static DECLARE_TLV_DB_SCALE(mic_preamp_tlv, -600, 600, 0); |
| 424 | 954 |
| 425 /* | 955 /* |
| 426 * MICGAIN volume control: | 956 * MICGAIN volume control: |
| 427 * from 6 to 30 dB in 6 dB steps | 957 * from -6 to 30 dB in 6 dB steps |
| 428 */ | 958 */ |
| 429 static DECLARE_TLV_DB_SCALE(mic_amp_tlv, 600, 600, 0); | 959 static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -600, 600, 0); |
| 960 |
| 961 /* |
| 962 * AFMGAIN volume control: |
| 963 * from 18 to 24 dB in 6 dB steps |
| 964 */ |
| 965 static DECLARE_TLV_DB_SCALE(afm_amp_tlv, 1800, 600, 0); |
| 430 | 966 |
| 431 /* | 967 /* |
| 432 * HSGAIN volume control: | 968 * HSGAIN volume control: |
| 433 * from -30 to 0 dB in 2 dB steps | 969 * from -30 to 0 dB in 2 dB steps |
| 434 */ | 970 */ |
| 435 static DECLARE_TLV_DB_SCALE(hs_tlv, -3000, 200, 0); | 971 static DECLARE_TLV_DB_SCALE(hs_tlv, -3000, 200, 0); |
| 436 | 972 |
| 437 /* | 973 /* |
| 438 * HFGAIN volume control: | 974 * HFGAIN volume control: |
| 439 * from -52 to 6 dB in 2 dB steps | 975 * from -52 to 6 dB in 2 dB steps |
| 440 */ | 976 */ |
| 441 static DECLARE_TLV_DB_SCALE(hf_tlv, -5200, 200, 0); | 977 static DECLARE_TLV_DB_SCALE(hf_tlv, -5200, 200, 0); |
| 442 | 978 |
| 443 /* | 979 /* |
| 444 * EPGAIN volume control: | 980 * EPGAIN volume control: |
| 445 * from -24 to 6 dB in 2 dB steps | 981 * from -24 to 6 dB in 2 dB steps |
| 446 */ | 982 */ |
| 447 static DECLARE_TLV_DB_SCALE(ep_tlv, -2400, 200, 0); | 983 static DECLARE_TLV_DB_SCALE(ep_tlv, -2400, 200, 0); |
| 448 | 984 |
| 449 /* Left analog microphone selection */ | 985 /* Left analog microphone selection */ |
| 450 static const char *twl6040_amicl_texts[] = | 986 static const char *twl6040_amicl_texts[] = |
| 451 {"Headset Mic", "Main Mic", "Aux/FM Left", "Off"}; | 987 {"Headset Mic", "Main Mic", "Aux/FM Left", "Off"}; |
| 452 | 988 |
| 453 /* Right analog microphone selection */ | 989 /* Right analog microphone selection */ |
| 454 static const char *twl6040_amicr_texts[] = | 990 static const char *twl6040_amicr_texts[] = |
| 455 {"Headset Mic", "Sub Mic", "Aux/FM Right", "Off"}; | 991 {"Headset Mic", "Sub Mic", "Aux/FM Right", "Off"}; |
| 456 | 992 |
| 457 static const struct soc_enum twl6040_enum[] = { | 993 static const struct soc_enum twl6040_enum[] = { |
| 458 » SOC_ENUM_SINGLE(TWL6040_REG_MICLCTL, 3, 3, twl6040_amicl_texts), | 994 » SOC_ENUM_SINGLE(TWL6040_REG_MICLCTL, 3, 4, twl6040_amicl_texts), |
| 459 » SOC_ENUM_SINGLE(TWL6040_REG_MICRCTL, 3, 3, twl6040_amicr_texts), | 995 » SOC_ENUM_SINGLE(TWL6040_REG_MICRCTL, 3, 4, twl6040_amicr_texts), |
| 996 }; |
| 997 |
| 998 static const char *twl6040_hs_texts[] = { |
| 999 » "Off", "HS DAC", "Line-In amp" |
| 1000 }; |
| 1001 |
| 1002 static const struct soc_enum twl6040_hs_enum[] = { |
| 1003 » SOC_ENUM_SINGLE(TWL6040_REG_HSLCTL, 5, ARRAY_SIZE(twl6040_hs_texts), |
| 1004 » » » twl6040_hs_texts), |
| 1005 » SOC_ENUM_SINGLE(TWL6040_REG_HSRCTL, 5, ARRAY_SIZE(twl6040_hs_texts), |
| 1006 » » » twl6040_hs_texts), |
| 1007 }; |
| 1008 |
| 1009 static const char *twl6040_hf_texts[] = { |
| 1010 » "Off", "HF DAC", "Line-In amp" |
| 1011 }; |
| 1012 |
| 1013 static const struct soc_enum twl6040_hf_enum[] = { |
| 1014 » SOC_ENUM_SINGLE(TWL6040_REG_HFLCTL, 2, ARRAY_SIZE(twl6040_hf_texts), |
| 1015 » » » twl6040_hf_texts), |
| 1016 » SOC_ENUM_SINGLE(TWL6040_REG_HFRCTL, 2, ARRAY_SIZE(twl6040_hf_texts), |
| 1017 » » » twl6040_hf_texts), |
| 460 }; | 1018 }; |
| 461 | 1019 |
| 462 static const struct snd_kcontrol_new amicl_control = | 1020 static const struct snd_kcontrol_new amicl_control = |
| 463 SOC_DAPM_ENUM("Route", twl6040_enum[0]); | 1021 SOC_DAPM_ENUM("Route", twl6040_enum[0]); |
| 464 | 1022 |
| 465 static const struct snd_kcontrol_new amicr_control = | 1023 static const struct snd_kcontrol_new amicr_control = |
| 466 SOC_DAPM_ENUM("Route", twl6040_enum[1]); | 1024 SOC_DAPM_ENUM("Route", twl6040_enum[1]); |
| 467 | 1025 |
| 468 /* Headset DAC playback switches */ | 1026 /* Headset DAC playback switches */ |
| 469 static const struct snd_kcontrol_new hsdacl_switch_controls = | 1027 static const struct snd_kcontrol_new hsl_mux_controls = |
| 470 » SOC_DAPM_SINGLE("Switch", TWL6040_REG_HSLCTL, 5, 1, 0); | 1028 » SOC_DAPM_ENUM("Route", twl6040_hs_enum[0]); |
| 471 | 1029 |
| 472 static const struct snd_kcontrol_new hsdacr_switch_controls = | 1030 static const struct snd_kcontrol_new hsr_mux_controls = |
| 473 » SOC_DAPM_SINGLE("Switch", TWL6040_REG_HSRCTL, 5, 1, 0); | 1031 » SOC_DAPM_ENUM("Route", twl6040_hs_enum[1]); |
| 474 | 1032 |
| 475 /* Handsfree DAC playback switches */ | 1033 /* Handsfree DAC playback switches */ |
| 476 static const struct snd_kcontrol_new hfdacl_switch_controls = | 1034 static const struct snd_kcontrol_new hfl_mux_controls = |
| 477 » SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFLCTL, 2, 1, 0); | 1035 » SOC_DAPM_ENUM("Route", twl6040_hf_enum[0]); |
| 478 | 1036 |
| 479 static const struct snd_kcontrol_new hfdacr_switch_controls = | 1037 static const struct snd_kcontrol_new hfr_mux_controls = |
| 480 » SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFRCTL, 2, 1, 0); | 1038 » SOC_DAPM_ENUM("Route", twl6040_hf_enum[1]); |
| 481 | 1039 |
| 482 static const struct snd_kcontrol_new ep_driver_switch_controls = | 1040 static const struct snd_kcontrol_new ep_driver_switch_controls = |
| 483 SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0); | 1041 SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0); |
| 484 | 1042 |
| 485 static const struct snd_kcontrol_new twl6040_snd_controls[] = { | 1043 static const struct snd_kcontrol_new twl6040_snd_controls[] = { |
| 486 /* Capture gains */ | 1044 /* Capture gains */ |
| 487 SOC_DOUBLE_TLV("Capture Preamplifier Volume", | 1045 SOC_DOUBLE_TLV("Capture Preamplifier Volume", |
| 488 TWL6040_REG_MICGAIN, 6, 7, 1, 1, mic_preamp_tlv), | 1046 TWL6040_REG_MICGAIN, 6, 7, 1, 1, mic_preamp_tlv), |
| 489 SOC_DOUBLE_TLV("Capture Volume", | 1047 SOC_DOUBLE_TLV("Capture Volume", |
| 490 TWL6040_REG_MICGAIN, 0, 3, 4, 0, mic_amp_tlv), | 1048 TWL6040_REG_MICGAIN, 0, 3, 4, 0, mic_amp_tlv), |
| 491 | 1049 |
| 1050 /* AFM gains */ |
| 1051 SOC_DOUBLE_TLV("Aux FM Volume", |
| 1052 TWL6040_REG_LINEGAIN, 0, 4, 0xF, 0, afm_amp_tlv), |
| 1053 |
| 492 /* Playback gains */ | 1054 /* Playback gains */ |
| 493 » SOC_DOUBLE_TLV("Headset Playback Volume", | 1055 » SOC_TWL6040_DOUBLE_TLV("Headset Playback Volume", |
| 494 TWL6040_REG_HSGAIN, 0, 4, 0xF, 1, hs_tlv), | 1056 TWL6040_REG_HSGAIN, 0, 4, 0xF, 1, hs_tlv), |
| 495 » SOC_DOUBLE_R_TLV("Handsfree Playback Volume", | 1057 » SOC_TWL6040_DOUBLE_R_TLV("Handsfree Playback Volume", |
| 496 TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv), | 1058 TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv), |
| 497 SOC_SINGLE_TLV("Earphone Playback Volume", | 1059 SOC_SINGLE_TLV("Earphone Playback Volume", |
| 498 TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv), | 1060 TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv), |
| 499 }; | 1061 }; |
| 500 | 1062 |
| 501 static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { | 1063 static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { |
| 502 /* Inputs */ | 1064 /* Inputs */ |
| 503 SND_SOC_DAPM_INPUT("MAINMIC"), | 1065 SND_SOC_DAPM_INPUT("MAINMIC"), |
| 504 SND_SOC_DAPM_INPUT("HSMIC"), | 1066 SND_SOC_DAPM_INPUT("HSMIC"), |
| 505 SND_SOC_DAPM_INPUT("SUBMIC"), | 1067 SND_SOC_DAPM_INPUT("SUBMIC"), |
| (...skipping 12 matching lines...) Expand all Loading... |
| 518 SND_SOC_NOPM, 0, 0, &amicl_control), | 1080 SND_SOC_NOPM, 0, 0, &amicl_control), |
| 519 SND_SOC_DAPM_MUX("Analog Right Capture Route", | 1081 SND_SOC_DAPM_MUX("Analog Right Capture Route", |
| 520 SND_SOC_NOPM, 0, 0, &amicr_control), | 1082 SND_SOC_NOPM, 0, 0, &amicr_control), |
| 521 | 1083 |
| 522 /* Analog capture PGAs */ | 1084 /* Analog capture PGAs */ |
| 523 SND_SOC_DAPM_PGA("MicAmpL", | 1085 SND_SOC_DAPM_PGA("MicAmpL", |
| 524 TWL6040_REG_MICLCTL, 0, 0, NULL, 0), | 1086 TWL6040_REG_MICLCTL, 0, 0, NULL, 0), |
| 525 SND_SOC_DAPM_PGA("MicAmpR", | 1087 SND_SOC_DAPM_PGA("MicAmpR", |
| 526 TWL6040_REG_MICRCTL, 0, 0, NULL, 0), | 1088 TWL6040_REG_MICRCTL, 0, 0, NULL, 0), |
| 527 | 1089 |
| 1090 /* Auxiliary FM PGAs */ |
| 1091 SND_SOC_DAPM_PGA("AFMAmpL", |
| 1092 TWL6040_REG_MICLCTL, 1, 0, NULL, 0), |
| 1093 SND_SOC_DAPM_PGA("AFMAmpR", |
| 1094 TWL6040_REG_MICRCTL, 1, 0, NULL, 0), |
| 1095 |
| 528 /* ADCs */ | 1096 /* ADCs */ |
| 529 SND_SOC_DAPM_ADC("ADC Left", "Left Front Capture", | 1097 SND_SOC_DAPM_ADC("ADC Left", "Left Front Capture", |
| 530 TWL6040_REG_MICLCTL, 2, 0), | 1098 TWL6040_REG_MICLCTL, 2, 0), |
| 531 SND_SOC_DAPM_ADC("ADC Right", "Right Front Capture", | 1099 SND_SOC_DAPM_ADC("ADC Right", "Right Front Capture", |
| 532 TWL6040_REG_MICRCTL, 2, 0), | 1100 TWL6040_REG_MICRCTL, 2, 0), |
| 533 | 1101 |
| 534 /* Microphone bias */ | 1102 /* Microphone bias */ |
| 535 SND_SOC_DAPM_MICBIAS("Headset Mic Bias", | 1103 SND_SOC_DAPM_MICBIAS("Headset Mic Bias", |
| 536 TWL6040_REG_AMICBCTL, 0, 0), | 1104 TWL6040_REG_AMICBCTL, 0, 0), |
| 537 SND_SOC_DAPM_MICBIAS("Main Mic Bias", | 1105 SND_SOC_DAPM_MICBIAS("Main Mic Bias", |
| (...skipping 14 matching lines...) Expand all Loading... |
| 552 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | 1120 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), |
| 553 SND_SOC_DAPM_DAC_E("HFDAC Left", "Handsfree Playback", | 1121 SND_SOC_DAPM_DAC_E("HFDAC Left", "Handsfree Playback", |
| 554 TWL6040_REG_HFLCTL, 0, 0, | 1122 TWL6040_REG_HFLCTL, 0, 0, |
| 555 twl6040_power_mode_event, | 1123 twl6040_power_mode_event, |
| 556 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | 1124 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), |
| 557 SND_SOC_DAPM_DAC_E("HFDAC Right", "Handsfree Playback", | 1125 SND_SOC_DAPM_DAC_E("HFDAC Right", "Handsfree Playback", |
| 558 TWL6040_REG_HFRCTL, 0, 0, | 1126 TWL6040_REG_HFRCTL, 0, 0, |
| 559 twl6040_power_mode_event, | 1127 twl6040_power_mode_event, |
| 560 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | 1128 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), |
| 561 | 1129 |
| 562 » /* Analog playback switches */ | 1130 » SND_SOC_DAPM_MUX("HF Left Playback", |
| 563 » SND_SOC_DAPM_SWITCH("HSDAC Left Playback", | 1131 » » » SND_SOC_NOPM, 0, 0, &hfl_mux_controls), |
| 564 » » » SND_SOC_NOPM, 0, 0, &hsdacl_switch_controls), | 1132 » SND_SOC_DAPM_MUX("HF Right Playback", |
| 565 » SND_SOC_DAPM_SWITCH("HSDAC Right Playback", | 1133 » » » SND_SOC_NOPM, 0, 0, &hfr_mux_controls), |
| 566 » » » SND_SOC_NOPM, 0, 0, &hsdacr_switch_controls), | 1134 » /* Analog playback Muxes */ |
| 567 » SND_SOC_DAPM_SWITCH("HFDAC Left Playback", | 1135 » SND_SOC_DAPM_MUX("HS Left Playback", |
| 568 » » » SND_SOC_NOPM, 0, 0, &hfdacl_switch_controls), | 1136 » » » SND_SOC_NOPM, 0, 0, &hsl_mux_controls), |
| 569 » SND_SOC_DAPM_SWITCH("HFDAC Right Playback", | 1137 » SND_SOC_DAPM_MUX("HS Right Playback", |
| 570 » » » SND_SOC_NOPM, 0, 0, &hfdacr_switch_controls), | 1138 » » » SND_SOC_NOPM, 0, 0, &hsr_mux_controls), |
| 571 | 1139 |
| 572 /* Analog playback drivers */ | 1140 /* Analog playback drivers */ |
| 573 » SND_SOC_DAPM_PGA_E("Handsfree Left Driver", | 1141 » SND_SOC_DAPM_OUT_DRV_E("Handsfree Left Driver", |
| 574 TWL6040_REG_HFLCTL, 4, 0, NULL, 0, | 1142 TWL6040_REG_HFLCTL, 4, 0, NULL, 0, |
| 575 » » » twl6040_power_mode_event, | 1143 » » » pga_event, |
| 576 » » » SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | 1144 » » » SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
| 577 » SND_SOC_DAPM_PGA_E("Handsfree Right Driver", | 1145 » SND_SOC_DAPM_OUT_DRV_E("Handsfree Right Driver", |
| 578 TWL6040_REG_HFRCTL, 4, 0, NULL, 0, | 1146 TWL6040_REG_HFRCTL, 4, 0, NULL, 0, |
| 579 » » » twl6040_power_mode_event, | 1147 » » » pga_event, |
| 580 » » » SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | 1148 » » » SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
| 581 » SND_SOC_DAPM_PGA("Headset Left Driver", | 1149 » SND_SOC_DAPM_OUT_DRV_E("Headset Left Driver", |
| 582 » » » TWL6040_REG_HSLCTL, 2, 0, NULL, 0), | 1150 » » » TWL6040_REG_HSLCTL, 2, 0, NULL, 0, |
| 583 » SND_SOC_DAPM_PGA("Headset Right Driver", | 1151 » » » pga_event, |
| 584 » » » TWL6040_REG_HSRCTL, 2, 0, NULL, 0), | 1152 » » » SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
| 1153 » SND_SOC_DAPM_OUT_DRV_E("Headset Right Driver", |
| 1154 » » » TWL6040_REG_HSRCTL, 2, 0, NULL, 0, |
| 1155 » » » pga_event, |
| 1156 » » » SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
| 585 SND_SOC_DAPM_SWITCH_E("Earphone Driver", | 1157 SND_SOC_DAPM_SWITCH_E("Earphone Driver", |
| 586 SND_SOC_NOPM, 0, 0, &ep_driver_switch_controls, | 1158 SND_SOC_NOPM, 0, 0, &ep_driver_switch_controls, |
| 587 twl6040_power_mode_event, | 1159 twl6040_power_mode_event, |
| 588 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | 1160 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), |
| 589 | 1161 |
| 590 /* Analog playback PGAs */ | 1162 /* Analog playback PGAs */ |
| 591 SND_SOC_DAPM_PGA("HFDAC Left PGA", | 1163 SND_SOC_DAPM_PGA("HFDAC Left PGA", |
| 592 TWL6040_REG_HFLCTL, 1, 0, NULL, 0), | 1164 TWL6040_REG_HFLCTL, 1, 0, NULL, 0), |
| 593 SND_SOC_DAPM_PGA("HFDAC Right PGA", | 1165 SND_SOC_DAPM_PGA("HFDAC Right PGA", |
| 594 TWL6040_REG_HFRCTL, 1, 0, NULL, 0), | 1166 TWL6040_REG_HFRCTL, 1, 0, NULL, 0), |
| 595 | 1167 |
| 596 }; | 1168 }; |
| 597 | 1169 |
| 598 static const struct snd_soc_dapm_route intercon[] = { | 1170 static const struct snd_soc_dapm_route intercon[] = { |
| 599 /* Capture path */ | 1171 /* Capture path */ |
| 600 {"Analog Left Capture Route", "Headset Mic", "HSMIC"}, | 1172 {"Analog Left Capture Route", "Headset Mic", "HSMIC"}, |
| 601 {"Analog Left Capture Route", "Main Mic", "MAINMIC"}, | 1173 {"Analog Left Capture Route", "Main Mic", "MAINMIC"}, |
| 602 {"Analog Left Capture Route", "Aux/FM Left", "AFML"}, | 1174 {"Analog Left Capture Route", "Aux/FM Left", "AFML"}, |
| 603 | 1175 |
| 604 {"Analog Right Capture Route", "Headset Mic", "HSMIC"}, | 1176 {"Analog Right Capture Route", "Headset Mic", "HSMIC"}, |
| 605 {"Analog Right Capture Route", "Sub Mic", "SUBMIC"}, | 1177 {"Analog Right Capture Route", "Sub Mic", "SUBMIC"}, |
| 606 {"Analog Right Capture Route", "Aux/FM Right", "AFMR"}, | 1178 {"Analog Right Capture Route", "Aux/FM Right", "AFMR"}, |
| 607 | 1179 |
| 608 {"MicAmpL", NULL, "Analog Left Capture Route"}, | 1180 {"MicAmpL", NULL, "Analog Left Capture Route"}, |
| 609 {"MicAmpR", NULL, "Analog Right Capture Route"}, | 1181 {"MicAmpR", NULL, "Analog Right Capture Route"}, |
| 610 | 1182 |
| 611 {"ADC Left", NULL, "MicAmpL"}, | 1183 {"ADC Left", NULL, "MicAmpL"}, |
| 612 {"ADC Right", NULL, "MicAmpR"}, | 1184 {"ADC Right", NULL, "MicAmpR"}, |
| 613 | 1185 |
| 614 » /* Headset playback path */ | 1186 » /* AFM path */ |
| 615 » {"HSDAC Left Playback", "Switch", "HSDAC Left"}, | 1187 » {"AFMAmpL", "NULL", "AFML"}, |
| 616 » {"HSDAC Right Playback", "Switch", "HSDAC Right"}, | 1188 » {"AFMAmpR", "NULL", "AFMR"}, |
| 617 | 1189 |
| 618 » {"Headset Left Driver", NULL, "HSDAC Left Playback"}, | 1190 » {"HS Left Playback", "HS DAC", "HSDAC Left"}, |
| 619 » {"Headset Right Driver", NULL, "HSDAC Right Playback"}, | 1191 » {"HS Left Playback", "Line-In amp", "AFMAmpL"}, |
| 1192 |
| 1193 » {"HS Right Playback", "HS DAC", "HSDAC Right"}, |
| 1194 » {"HS Right Playback", "Line-In amp", "AFMAmpR"}, |
| 1195 |
| 1196 » {"Headset Left Driver", "NULL", "HS Left Playback"}, |
| 1197 » {"Headset Right Driver", "NULL", "HS Right Playback"}, |
| 620 | 1198 |
| 621 {"HSOL", NULL, "Headset Left Driver"}, | 1199 {"HSOL", NULL, "Headset Left Driver"}, |
| 622 {"HSOR", NULL, "Headset Right Driver"}, | 1200 {"HSOR", NULL, "Headset Right Driver"}, |
| 623 | 1201 |
| 624 /* Earphone playback path */ | 1202 /* Earphone playback path */ |
| 625 {"Earphone Driver", "Switch", "HSDAC Left"}, | 1203 {"Earphone Driver", "Switch", "HSDAC Left"}, |
| 626 {"EP", NULL, "Earphone Driver"}, | 1204 {"EP", NULL, "Earphone Driver"}, |
| 627 | 1205 |
| 628 » /* Handsfree playback path */ | 1206 » {"HF Left Playback", "HF DAC", "HFDAC Left"}, |
| 629 » {"HFDAC Left Playback", "Switch", "HFDAC Left"}, | 1207 » {"HF Left Playback", "Line-In amp", "AFMAmpL"}, |
| 630 » {"HFDAC Right Playback", "Switch", "HFDAC Right"}, | |
| 631 | 1208 |
| 632 » {"HFDAC Left PGA", NULL, "HFDAC Left Playback"}, | 1209 » {"HF Right Playback", "HF DAC", "HFDAC Right"}, |
| 633 » {"HFDAC Right PGA", NULL, "HFDAC Right Playback"}, | 1210 » {"HF Right Playback", "Line-In amp", "AFMAmpR"}, |
| 1211 |
| 1212 » {"HFDAC Left PGA", NULL, "HF Left Playback"}, |
| 1213 » {"HFDAC Right PGA", NULL, "HF Right Playback"}, |
| 634 | 1214 |
| 635 {"Handsfree Left Driver", "Switch", "HFDAC Left PGA"}, | 1215 {"Handsfree Left Driver", "Switch", "HFDAC Left PGA"}, |
| 636 {"Handsfree Right Driver", "Switch", "HFDAC Right PGA"}, | 1216 {"Handsfree Right Driver", "Switch", "HFDAC Right PGA"}, |
| 637 | 1217 |
| 638 {"HFL", NULL, "Handsfree Left Driver"}, | 1218 {"HFL", NULL, "Handsfree Left Driver"}, |
| 639 {"HFR", NULL, "Handsfree Right Driver"}, | 1219 {"HFR", NULL, "Handsfree Right Driver"}, |
| 640 }; | 1220 }; |
| 641 | 1221 |
| 642 static int twl6040_add_widgets(struct snd_soc_codec *codec) | 1222 static int twl6040_add_widgets(struct snd_soc_codec *codec) |
| 643 { | 1223 { |
| 644 » snd_soc_dapm_new_controls(codec, twl6040_dapm_widgets, | 1224 » struct snd_soc_dapm_context *dapm = &codec->dapm; |
| 1225 |
| 1226 » snd_soc_dapm_new_controls(dapm, twl6040_dapm_widgets, |
| 645 ARRAY_SIZE(twl6040_dapm_widgets)); | 1227 ARRAY_SIZE(twl6040_dapm_widgets)); |
| 646 | 1228 » snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); |
| 647 » snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); | 1229 » snd_soc_dapm_new_widgets(dapm); |
| 648 | |
| 649 » snd_soc_dapm_new_widgets(codec); | |
| 650 | 1230 |
| 651 return 0; | 1231 return 0; |
| 652 } | 1232 } |
| 653 | 1233 |
| 654 static int twl6040_power_up_completion(struct snd_soc_codec *codec, | 1234 static int twl6040_power_up_completion(struct snd_soc_codec *codec, |
| 655 int naudint) | 1235 int naudint) |
| 656 { | 1236 { |
| 657 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); | 1237 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); |
| 658 int time_left; | 1238 int time_left; |
| 659 u8 intid; | 1239 u8 intid; |
| 660 | 1240 |
| 661 time_left = wait_for_completion_timeout(&priv->ready, | 1241 time_left = wait_for_completion_timeout(&priv->ready, |
| 662 » » » » msecs_to_jiffies(48)); | 1242 » » » » msecs_to_jiffies(144)); |
| 663 | 1243 |
| 664 if (!time_left) { | 1244 if (!time_left) { |
| 665 » » twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &intid, | 1245 » » twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &intid, |
| 666 TWL6040_REG_INTID); | 1246 TWL6040_REG_INTID); |
| 667 if (!(intid & TWL6040_READYINT)) { | 1247 if (!(intid & TWL6040_READYINT)) { |
| 668 dev_err(codec->dev, "timeout waiting for READYINT\n"); | 1248 dev_err(codec->dev, "timeout waiting for READYINT\n"); |
| 669 return -ETIMEDOUT; | 1249 return -ETIMEDOUT; |
| 670 } | 1250 } |
| 671 } | 1251 } |
| 672 | 1252 |
| 673 priv->codec_powered = 1; | 1253 priv->codec_powered = 1; |
| 674 | 1254 |
| 675 return 0; | 1255 return 0; |
| (...skipping 30 matching lines...) Expand all Loading... |
| 706 twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL); | 1286 twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL); |
| 707 twl6040_read_reg_volatile(codec, TWL6040_REG_LPPLLCTL); | 1287 twl6040_read_reg_volatile(codec, TWL6040_REG_LPPLLCTL); |
| 708 } else { | 1288 } else { |
| 709 /* use manual power-up sequence */ | 1289 /* use manual power-up sequence */ |
| 710 twl6040_power_up(codec); | 1290 twl6040_power_up(codec); |
| 711 priv->codec_powered = 1; | 1291 priv->codec_powered = 1; |
| 712 } | 1292 } |
| 713 | 1293 |
| 714 /* initialize vdd/vss registers with reg_cache */ | 1294 /* initialize vdd/vss registers with reg_cache */ |
| 715 twl6040_init_vdd_regs(codec); | 1295 twl6040_init_vdd_regs(codec); |
| 1296 |
| 1297 /* Set external boost GPO */ |
| 1298 twl6040_write(codec, TWL6040_REG_GPOCTL, 0x02); |
| 1299 |
| 1300 /* Set initial minimal gain values */ |
| 1301 twl6040_write(codec, TWL6040_REG_HSGAIN, 0xFF); |
| 1302 twl6040_write(codec, TWL6040_REG_EARCTL, 0x1E); |
| 1303 twl6040_write(codec, TWL6040_REG_HFLGAIN, 0x1D); |
| 1304 twl6040_write(codec, TWL6040_REG_HFRGAIN, 0x1D); |
| 716 break; | 1305 break; |
| 717 case SND_SOC_BIAS_OFF: | 1306 case SND_SOC_BIAS_OFF: |
| 718 if (!priv->codec_powered) | 1307 if (!priv->codec_powered) |
| 719 break; | 1308 break; |
| 720 | 1309 |
| 721 if (gpio_is_valid(audpwron)) { | 1310 if (gpio_is_valid(audpwron)) { |
| 722 /* use AUDPWRON line */ | 1311 /* use AUDPWRON line */ |
| 723 gpio_set_value(audpwron, 0); | 1312 gpio_set_value(audpwron, 0); |
| 724 | 1313 |
| 725 /* power-down sequence latency */ | 1314 /* power-down sequence latency */ |
| 726 udelay(500); | 1315 udelay(500); |
| 727 | 1316 |
| 728 /* sync registers updated during power-down sequence */ | 1317 /* sync registers updated during power-down sequence */ |
| 729 twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL); | 1318 twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL); |
| 730 twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL); | 1319 twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL); |
| 731 twl6040_write_reg_cache(codec, TWL6040_REG_LPPLLCTL, | 1320 twl6040_write_reg_cache(codec, TWL6040_REG_LPPLLCTL, |
| 732 0x00); | 1321 0x00); |
| 733 } else { | 1322 } else { |
| 734 /* use manual power-down sequence */ | 1323 /* use manual power-down sequence */ |
| 735 twl6040_power_down(codec); | 1324 twl6040_power_down(codec); |
| 736 } | 1325 } |
| 737 | 1326 |
| 738 priv->codec_powered = 0; | 1327 priv->codec_powered = 0; |
| 739 break; | 1328 break; |
| 740 } | 1329 } |
| 741 | 1330 |
| 742 » codec->bias_level = level; | 1331 » codec->dapm.bias_level = level; |
| 743 | 1332 |
| 744 return 0; | 1333 return 0; |
| 745 } | 1334 } |
| 746 | 1335 |
| 747 /* set of rates for each pll: low-power and high-performance */ | 1336 /* set of rates for each pll: low-power and high-performance */ |
| 748 | 1337 |
| 749 static unsigned int lp_rates[] = { | 1338 static unsigned int lp_rates[] = { |
| 750 88200, | 1339 88200, |
| 751 96000, | 1340 96000, |
| 752 }; | 1341 }; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 765 .list = hp_rates, | 1354 .list = hp_rates, |
| 766 }; | 1355 }; |
| 767 | 1356 |
| 768 static int twl6040_startup(struct snd_pcm_substream *substream, | 1357 static int twl6040_startup(struct snd_pcm_substream *substream, |
| 769 struct snd_soc_dai *dai) | 1358 struct snd_soc_dai *dai) |
| 770 { | 1359 { |
| 771 struct snd_soc_pcm_runtime *rtd = substream->private_data; | 1360 struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| 772 struct snd_soc_codec *codec = rtd->codec; | 1361 struct snd_soc_codec *codec = rtd->codec; |
| 773 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); | 1362 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); |
| 774 | 1363 |
| 775 if (!priv->sysclk) { | |
| 776 dev_err(codec->dev, | |
| 777 "no mclk configured, call set_sysclk() on init\n"); | |
| 778 return -EINVAL; | |
| 779 } | |
| 780 | |
| 781 /* | |
| 782 * capture is not supported at 17.64 MHz, | |
| 783 * it's reserved for headset low-power playback scenario | |
| 784 */ | |
| 785 if ((priv->sysclk == 17640000) && substream->stream) { | |
| 786 dev_err(codec->dev, | |
| 787 "capture mode is not supported at %dHz\n", | |
| 788 priv->sysclk); | |
| 789 return -EINVAL; | |
| 790 } | |
| 791 | |
| 792 snd_pcm_hw_constraint_list(substream->runtime, 0, | 1364 snd_pcm_hw_constraint_list(substream->runtime, 0, |
| 793 SNDRV_PCM_HW_PARAM_RATE, | 1365 SNDRV_PCM_HW_PARAM_RATE, |
| 794 priv->sysclk_constraints); | 1366 priv->sysclk_constraints); |
| 795 | 1367 |
| 796 return 0; | 1368 return 0; |
| 797 } | 1369 } |
| 798 | 1370 |
| 799 static int twl6040_hw_params(struct snd_pcm_substream *substream, | 1371 static int twl6040_hw_params(struct snd_pcm_substream *substream, |
| 800 struct snd_pcm_hw_params *params, | 1372 struct snd_pcm_hw_params *params, |
| 801 struct snd_soc_dai *dai) | 1373 struct snd_soc_dai *dai) |
| 802 { | 1374 { |
| 803 struct snd_soc_pcm_runtime *rtd = substream->private_data; | 1375 struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| 804 struct snd_soc_codec *codec = rtd->codec; | 1376 struct snd_soc_codec *codec = rtd->codec; |
| 805 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); | 1377 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); |
| 806 u8 lppllctl; | 1378 u8 lppllctl; |
| 807 int rate; | 1379 int rate; |
| 808 | 1380 |
| 809 /* nothing to do for high-perf pll, it supports only 48 kHz */ | 1381 /* nothing to do for high-perf pll, it supports only 48 kHz */ |
| 810 if (priv->pll == TWL6040_HPPLL_ID) | 1382 if (priv->pll == TWL6040_HPPLL_ID) |
| 811 return 0; | 1383 return 0; |
| 812 | 1384 |
| 813 lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); | 1385 lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); |
| 814 | 1386 |
| 815 rate = params_rate(params); | 1387 rate = params_rate(params); |
| 816 switch (rate) { | 1388 switch (rate) { |
| 1389 case 11250: |
| 1390 case 22500: |
| 1391 case 44100: |
| 817 case 88200: | 1392 case 88200: |
| 818 lppllctl |= TWL6040_LPLLFIN; | 1393 lppllctl |= TWL6040_LPLLFIN; |
| 819 priv->sysclk = 17640000; | 1394 priv->sysclk = 17640000; |
| 820 break; | 1395 break; |
| 1396 case 8000: |
| 1397 case 16000: |
| 1398 case 32000: |
| 1399 case 48000: |
| 821 case 96000: | 1400 case 96000: |
| 822 lppllctl &= ~TWL6040_LPLLFIN; | 1401 lppllctl &= ~TWL6040_LPLLFIN; |
| 823 priv->sysclk = 19200000; | 1402 priv->sysclk = 19200000; |
| 824 break; | 1403 break; |
| 825 default: | 1404 default: |
| 826 dev_err(codec->dev, "unsupported rate %d\n", rate); | 1405 dev_err(codec->dev, "unsupported rate %d\n", rate); |
| 827 return -EINVAL; | 1406 return -EINVAL; |
| 828 } | 1407 } |
| 829 | 1408 |
| 830 twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); | 1409 twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); |
| 831 | 1410 |
| 832 return 0; | 1411 return 0; |
| 833 } | 1412 } |
| 834 | 1413 |
| 835 static int twl6040_trigger(struct snd_pcm_substream *substream, | 1414 static int twl6040_prepare(struct snd_pcm_substream *substream, |
| 836 » » » int cmd, struct snd_soc_dai *dai) | 1415 » » » struct snd_soc_dai *dai) |
| 837 { | 1416 { |
| 838 struct snd_soc_pcm_runtime *rtd = substream->private_data; | 1417 struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| 839 struct snd_soc_codec *codec = rtd->codec; | 1418 struct snd_soc_codec *codec = rtd->codec; |
| 840 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); | 1419 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); |
| 841 | 1420 |
| 842 » switch (cmd) { | 1421 » if (!priv->sysclk) { |
| 843 » case SNDRV_PCM_TRIGGER_START: | 1422 » » dev_err(codec->dev, |
| 844 » case SNDRV_PCM_TRIGGER_RESUME: | 1423 » » » "no mclk configured, call set_sysclk() on init\n"); |
| 845 » » /* | 1424 » » return -EINVAL; |
| 846 » » * low-power playback mode is restricted | 1425 » } |
| 847 » » * for headset path only | 1426 |
| 848 » » */ | 1427 » /* |
| 849 » » if ((priv->sysclk == 17640000) && priv->non_lp) { | 1428 » * capture is not supported at 17.64 MHz, |
| 1429 » * it's reserved for headset low-power playback scenario |
| 1430 » */ |
| 1431 » if ((priv->sysclk == 17640000) && |
| 1432 » » » substream->stream == SNDRV_PCM_STREAM_CAPTURE) { |
| 1433 » » dev_err(codec->dev, |
| 1434 » » » "capture mode is not supported at %dHz\n", |
| 1435 » » » priv->sysclk); |
| 1436 » » return -EINVAL; |
| 1437 » } |
| 1438 |
| 1439 » if ((priv->sysclk == 17640000) && priv->non_lp) { |
| 850 dev_err(codec->dev, | 1440 dev_err(codec->dev, |
| 851 "some enabled paths aren't supported at %dHz\n", | 1441 "some enabled paths aren't supported at %dHz\n", |
| 852 priv->sysclk); | 1442 priv->sysclk); |
| 853 return -EPERM; | 1443 return -EPERM; |
| 854 } | |
| 855 break; | |
| 856 default: | |
| 857 break; | |
| 858 } | 1444 } |
| 859 | |
| 860 return 0; | 1445 return 0; |
| 861 } | 1446 } |
| 862 | 1447 |
| 863 static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai, | 1448 static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai, |
| 864 int clk_id, unsigned int freq, int dir) | 1449 int clk_id, unsigned int freq, int dir) |
| 865 { | 1450 { |
| 866 struct snd_soc_codec *codec = codec_dai->codec; | 1451 struct snd_soc_codec *codec = codec_dai->codec; |
| 867 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); | 1452 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); |
| 868 u8 hppllctl, lppllctl; | 1453 u8 hppllctl, lppllctl; |
| 869 | 1454 |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 963 dev_err(codec->dev, "unknown clk_id %d\n", clk_id); | 1548 dev_err(codec->dev, "unknown clk_id %d\n", clk_id); |
| 964 return -EINVAL; | 1549 return -EINVAL; |
| 965 } | 1550 } |
| 966 | 1551 |
| 967 return 0; | 1552 return 0; |
| 968 } | 1553 } |
| 969 | 1554 |
| 970 static struct snd_soc_dai_ops twl6040_dai_ops = { | 1555 static struct snd_soc_dai_ops twl6040_dai_ops = { |
| 971 .startup = twl6040_startup, | 1556 .startup = twl6040_startup, |
| 972 .hw_params = twl6040_hw_params, | 1557 .hw_params = twl6040_hw_params, |
| 973 » .trigger» = twl6040_trigger, | 1558 » .prepare» = twl6040_prepare, |
| 974 .set_sysclk = twl6040_set_dai_sysclk, | 1559 .set_sysclk = twl6040_set_dai_sysclk, |
| 975 }; | 1560 }; |
| 976 | 1561 |
| 977 static struct snd_soc_dai_driver twl6040_dai = { | 1562 static struct snd_soc_dai_driver twl6040_dai = { |
| 978 .name = "twl6040-hifi", | 1563 .name = "twl6040-hifi", |
| 979 .playback = { | 1564 .playback = { |
| 980 .stream_name = "Playback", | 1565 .stream_name = "Playback", |
| 981 .channels_min = 1, | 1566 .channels_min = 1, |
| 982 .channels_max = 4, | 1567 .channels_max = 4, |
| 983 .rates = TWL6040_RATES, | 1568 .rates = TWL6040_RATES, |
| (...skipping 13 matching lines...) Expand all Loading... |
| 997 static int twl6040_suspend(struct snd_soc_codec *codec, pm_message_t state) | 1582 static int twl6040_suspend(struct snd_soc_codec *codec, pm_message_t state) |
| 998 { | 1583 { |
| 999 twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); | 1584 twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); |
| 1000 | 1585 |
| 1001 return 0; | 1586 return 0; |
| 1002 } | 1587 } |
| 1003 | 1588 |
| 1004 static int twl6040_resume(struct snd_soc_codec *codec) | 1589 static int twl6040_resume(struct snd_soc_codec *codec) |
| 1005 { | 1590 { |
| 1006 twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 1591 twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
| 1592 twl6040_set_bias_level(codec, codec->dapm.suspend_bias_level); |
| 1007 | 1593 |
| 1008 return 0; | 1594 return 0; |
| 1009 } | 1595 } |
| 1010 #else | 1596 #else |
| 1011 #define twl6040_suspend NULL | 1597 #define twl6040_suspend NULL |
| 1012 #define twl6040_resume NULL | 1598 #define twl6040_resume NULL |
| 1013 #endif | 1599 #endif |
| 1014 | 1600 |
| 1015 static int twl6040_probe(struct snd_soc_codec *codec) | 1601 static int twl6040_probe(struct snd_soc_codec *codec) |
| 1016 { | 1602 { |
| 1017 struct twl4030_codec_data *twl_codec = codec->dev->platform_data; | 1603 struct twl4030_codec_data *twl_codec = codec->dev->platform_data; |
| 1018 struct twl6040_data *priv; | 1604 struct twl6040_data *priv; |
| 1019 int audpwron, naudint; | 1605 int audpwron, naudint; |
| 1020 int ret = 0; | 1606 int ret = 0; |
| 1607 u8 icrev, intmr = TWL6040_ALLINT_MSK; |
| 1021 | 1608 |
| 1022 priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL); | 1609 priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL); |
| 1023 if (priv == NULL) | 1610 if (priv == NULL) |
| 1024 return -ENOMEM; | 1611 return -ENOMEM; |
| 1025 snd_soc_codec_set_drvdata(codec, priv); | 1612 snd_soc_codec_set_drvdata(codec, priv); |
| 1026 | 1613 |
| 1027 » if (twl_codec) { | 1614 » priv->codec = codec; |
| 1615 |
| 1616 » twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &icrev, TWL6040_REG_ASICREV); |
| 1617 |
| 1618 » if (twl_codec && (icrev > 0)) |
| 1028 audpwron = twl_codec->audpwron_gpio; | 1619 audpwron = twl_codec->audpwron_gpio; |
| 1620 else |
| 1621 audpwron = -EINVAL; |
| 1622 |
| 1623 if (twl_codec) |
| 1029 naudint = twl_codec->naudint_irq; | 1624 naudint = twl_codec->naudint_irq; |
| 1030 » } else { | 1625 » else |
| 1031 » » audpwron = -EINVAL; | |
| 1032 naudint = 0; | 1626 naudint = 0; |
| 1033 } | |
| 1034 | 1627 |
| 1035 priv->audpwron = audpwron; | 1628 priv->audpwron = audpwron; |
| 1036 priv->naudint = naudint; | 1629 priv->naudint = naudint; |
| 1630 priv->workqueue = create_singlethread_workqueue("twl6040-codec"); |
| 1631 |
| 1632 if (!priv->workqueue) |
| 1633 goto work_err; |
| 1634 |
| 1635 INIT_DELAYED_WORK(&priv->delayed_work, twl6040_accessory_work); |
| 1636 |
| 1637 mutex_init(&priv->mutex); |
| 1037 | 1638 |
| 1038 init_completion(&priv->ready); | 1639 init_completion(&priv->ready); |
| 1640 init_completion(&priv->headset.ramp_done); |
| 1641 init_completion(&priv->handsfree.ramp_done); |
| 1039 | 1642 |
| 1040 if (gpio_is_valid(audpwron)) { | 1643 if (gpio_is_valid(audpwron)) { |
| 1041 ret = gpio_request(audpwron, "audpwron"); | 1644 ret = gpio_request(audpwron, "audpwron"); |
| 1042 if (ret) | 1645 if (ret) |
| 1043 goto gpio1_err; | 1646 goto gpio1_err; |
| 1044 | 1647 |
| 1045 ret = gpio_direction_output(audpwron, 0); | 1648 ret = gpio_direction_output(audpwron, 0); |
| 1046 if (ret) | 1649 if (ret) |
| 1047 goto gpio2_err; | 1650 goto gpio2_err; |
| 1048 | 1651 |
| 1049 priv->codec_powered = 0; | 1652 priv->codec_powered = 0; |
| 1653 |
| 1654 /* enable only codec ready interrupt */ |
| 1655 intmr &= ~(TWL6040_READYMSK | TWL6040_PLUGMSK); |
| 1656 |
| 1657 /* reset interrupt status to allow correct power up sequence */ |
| 1658 twl6040_read_reg_volatile(codec, TWL6040_REG_INTID); |
| 1050 } | 1659 } |
| 1660 twl6040_write(codec, TWL6040_REG_INTMR, intmr); |
| 1051 | 1661 |
| 1052 if (naudint) { | 1662 if (naudint) { |
| 1053 /* audio interrupt */ | 1663 /* audio interrupt */ |
| 1054 ret = request_threaded_irq(naudint, NULL, | 1664 ret = request_threaded_irq(naudint, NULL, |
| 1055 twl6040_naudint_handler, | 1665 twl6040_naudint_handler, |
| 1056 IRQF_TRIGGER_LOW | IRQF_ONESHOT, | 1666 IRQF_TRIGGER_LOW | IRQF_ONESHOT, |
| 1057 "twl6040_codec", codec); | 1667 "twl6040_codec", codec); |
| 1058 if (ret) | 1668 if (ret) |
| 1059 goto gpio2_err; | 1669 goto gpio2_err; |
| 1060 } else { | |
| 1061 if (gpio_is_valid(audpwron)) { | |
| 1062 /* enable only codec ready interrupt */ | |
| 1063 twl6040_write_reg_cache(codec, TWL6040_REG_INTMR, | |
| 1064 ~TWL6040_READYMSK & TWL6040_ALLINT_MSK); | |
| 1065 } else { | |
| 1066 /* no interrupts at all */ | |
| 1067 twl6040_write_reg_cache(codec, TWL6040_REG_INTMR, | |
| 1068 TWL6040_ALLINT_MSK); | |
| 1069 } | |
| 1070 } | 1670 } |
| 1071 | 1671 |
| 1072 /* init vio registers */ | 1672 /* init vio registers */ |
| 1073 twl6040_init_vio_regs(codec); | 1673 twl6040_init_vio_regs(codec); |
| 1074 | 1674 |
| 1675 priv->hf_workqueue = create_singlethread_workqueue("twl6040-hf"); |
| 1676 if (priv->hf_workqueue == NULL) { |
| 1677 ret = -ENOMEM; |
| 1678 goto irq_err; |
| 1679 } |
| 1680 priv->hs_workqueue = create_singlethread_workqueue("twl6040-hs"); |
| 1681 if (priv->hs_workqueue == NULL) { |
| 1682 ret = -ENOMEM; |
| 1683 goto wq_err; |
| 1684 } |
| 1685 |
| 1686 INIT_DELAYED_WORK(&priv->hs_delayed_work, twl6040_pga_hs_work); |
| 1687 INIT_DELAYED_WORK(&priv->hf_delayed_work, twl6040_pga_hf_work); |
| 1688 |
| 1075 /* power on device */ | 1689 /* power on device */ |
| 1076 ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 1690 ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
| 1077 if (ret) | 1691 if (ret) |
| 1078 » » goto irq_err; | 1692 » » goto bias_err; |
| 1079 | 1693 |
| 1080 snd_soc_add_controls(codec, twl6040_snd_controls, | 1694 snd_soc_add_controls(codec, twl6040_snd_controls, |
| 1081 ARRAY_SIZE(twl6040_snd_controls)); | 1695 ARRAY_SIZE(twl6040_snd_controls)); |
| 1082 twl6040_add_widgets(codec); | 1696 twl6040_add_widgets(codec); |
| 1083 | 1697 |
| 1084 return 0; | 1698 return 0; |
| 1085 | 1699 |
| 1700 bias_err: |
| 1701 destroy_workqueue(priv->hs_workqueue); |
| 1702 wq_err: |
| 1703 destroy_workqueue(priv->hf_workqueue); |
| 1086 irq_err: | 1704 irq_err: |
| 1087 if (naudint) | 1705 if (naudint) |
| 1088 free_irq(naudint, codec); | 1706 free_irq(naudint, codec); |
| 1089 gpio2_err: | 1707 gpio2_err: |
| 1090 if (gpio_is_valid(audpwron)) | 1708 if (gpio_is_valid(audpwron)) |
| 1091 gpio_free(audpwron); | 1709 gpio_free(audpwron); |
| 1092 gpio1_err: | 1710 gpio1_err: |
| 1711 destroy_workqueue(priv->workqueue); |
| 1712 work_err: |
| 1093 kfree(priv); | 1713 kfree(priv); |
| 1094 return ret; | 1714 return ret; |
| 1095 } | 1715 } |
| 1096 | 1716 |
| 1097 static int twl6040_remove(struct snd_soc_codec *codec) | 1717 static int twl6040_remove(struct snd_soc_codec *codec) |
| 1098 { | 1718 { |
| 1099 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); | 1719 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); |
| 1100 int audpwron = priv->audpwron; | 1720 int audpwron = priv->audpwron; |
| 1101 int naudint = priv->naudint; | 1721 int naudint = priv->naudint; |
| 1102 | 1722 |
| 1103 twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); | 1723 twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); |
| 1104 | 1724 |
| 1105 if (gpio_is_valid(audpwron)) | 1725 if (gpio_is_valid(audpwron)) |
| 1106 gpio_free(audpwron); | 1726 gpio_free(audpwron); |
| 1107 | 1727 |
| 1108 if (naudint) | 1728 if (naudint) |
| 1109 free_irq(naudint, codec); | 1729 free_irq(naudint, codec); |
| 1110 | 1730 |
| 1731 destroy_workqueue(priv->workqueue); |
| 1732 destroy_workqueue(priv->hf_workqueue); |
| 1733 destroy_workqueue(priv->hs_workqueue); |
| 1111 kfree(priv); | 1734 kfree(priv); |
| 1112 | 1735 |
| 1113 return 0; | 1736 return 0; |
| 1114 } | 1737 } |
| 1115 | 1738 |
| 1116 static struct snd_soc_codec_driver soc_codec_dev_twl6040 = { | 1739 static struct snd_soc_codec_driver soc_codec_dev_twl6040 = { |
| 1117 .probe = twl6040_probe, | 1740 .probe = twl6040_probe, |
| 1118 .remove = twl6040_remove, | 1741 .remove = twl6040_remove, |
| 1119 .suspend = twl6040_suspend, | 1742 .suspend = twl6040_suspend, |
| 1120 .resume = twl6040_resume, | 1743 .resume = twl6040_resume, |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1155 | 1778 |
| 1156 static void __exit twl6040_codec_exit(void) | 1779 static void __exit twl6040_codec_exit(void) |
| 1157 { | 1780 { |
| 1158 platform_driver_unregister(&twl6040_codec_driver); | 1781 platform_driver_unregister(&twl6040_codec_driver); |
| 1159 } | 1782 } |
| 1160 module_exit(twl6040_codec_exit); | 1783 module_exit(twl6040_codec_exit); |
| 1161 | 1784 |
| 1162 MODULE_DESCRIPTION("ASoC TWL6040 codec driver"); | 1785 MODULE_DESCRIPTION("ASoC TWL6040 codec driver"); |
| 1163 MODULE_AUTHOR("Misael Lopez Cruz"); | 1786 MODULE_AUTHOR("Misael Lopez Cruz"); |
| 1164 MODULE_LICENSE("GPL"); | 1787 MODULE_LICENSE("GPL"); |
| OLD | NEW |