| OLD | NEW |
| 1 /* | 1 /* |
| 2 * ALSA SoC WM9090 driver | 2 * ALSA SoC WM9090 driver |
| 3 * | 3 * |
| 4 * Copyright 2009, 2010 Wolfson Microelectronics | 4 * Copyright 2009, 2010 Wolfson Microelectronics |
| 5 * | 5 * |
| 6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | 6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> |
| 7 * | 7 * |
| 8 * This program is free software; you can redistribute it and/or | 8 * This program is free software; you can redistribute it and/or |
| 9 * modify it under the terms of the GNU General Public License | 9 * modify it under the terms of the GNU General Public License |
| 10 * version 2 as published by the Free Software Foundation. | 10 * version 2 as published by the Free Software Foundation. |
| (...skipping 10 matching lines...) Expand all Loading... |
| 21 */ | 21 */ |
| 22 | 22 |
| 23 #include <linux/module.h> | 23 #include <linux/module.h> |
| 24 #include <linux/errno.h> | 24 #include <linux/errno.h> |
| 25 #include <linux/device.h> | 25 #include <linux/device.h> |
| 26 #include <linux/i2c.h> | 26 #include <linux/i2c.h> |
| 27 #include <linux/delay.h> | 27 #include <linux/delay.h> |
| 28 #include <linux/slab.h> | 28 #include <linux/slab.h> |
| 29 #include <sound/initval.h> | 29 #include <sound/initval.h> |
| 30 #include <sound/soc.h> | 30 #include <sound/soc.h> |
| 31 #include <sound/soc-dapm.h> | |
| 32 #include <sound/tlv.h> | 31 #include <sound/tlv.h> |
| 33 #include <sound/wm9090.h> | 32 #include <sound/wm9090.h> |
| 34 | 33 |
| 35 #include "wm9090.h" | 34 #include "wm9090.h" |
| 36 | 35 |
| 37 static const u16 wm9090_reg_defaults[] = { | 36 static const u16 wm9090_reg_defaults[] = { |
| 38 0x9093, /* R0 - Software Reset */ | 37 0x9093, /* R0 - Software Reset */ |
| 39 0x0006, /* R1 - Power Management (1) */ | 38 0x0006, /* R1 - Power Management (1) */ |
| 40 0x6000, /* R2 - Power Management (2) */ | 39 0x6000, /* R2 - Power Management (2) */ |
| 41 0x0000, /* R3 - Power Management (3) */ | 40 0x0000, /* R3 - Power Management (3) */ |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 138 0x0200, /* R100 - AGC Control 2 */ | 137 0x0200, /* R100 - AGC Control 2 */ |
| 139 }; | 138 }; |
| 140 | 139 |
| 141 /* This struct is used to save the context */ | 140 /* This struct is used to save the context */ |
| 142 struct wm9090_priv { | 141 struct wm9090_priv { |
| 143 struct mutex mutex; | 142 struct mutex mutex; |
| 144 struct wm9090_platform_data pdata; | 143 struct wm9090_platform_data pdata; |
| 145 void *control_data; | 144 void *control_data; |
| 146 }; | 145 }; |
| 147 | 146 |
| 148 static int wm9090_volatile(unsigned int reg) | 147 static int wm9090_volatile(struct snd_soc_codec *codec, unsigned int reg) |
| 149 { | 148 { |
| 150 switch (reg) { | 149 switch (reg) { |
| 151 case WM9090_SOFTWARE_RESET: | 150 case WM9090_SOFTWARE_RESET: |
| 152 case WM9090_DC_SERVO_0: | 151 case WM9090_DC_SERVO_0: |
| 153 case WM9090_DC_SERVO_READBACK_0: | 152 case WM9090_DC_SERVO_READBACK_0: |
| 154 case WM9090_DC_SERVO_READBACK_1: | 153 case WM9090_DC_SERVO_READBACK_1: |
| 155 case WM9090_DC_SERVO_READBACK_2: | 154 case WM9090_DC_SERVO_READBACK_2: |
| 156 return 1; | 155 return 1; |
| 157 | 156 |
| 158 default: | 157 default: |
| (...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 435 { "MIXOUTR", "IN2B Switch", "IN2B PGA" }, | 434 { "MIXOUTR", "IN2B Switch", "IN2B PGA" }, |
| 436 }; | 435 }; |
| 437 | 436 |
| 438 static const struct snd_soc_dapm_route audio_map_in2_diff[] = { | 437 static const struct snd_soc_dapm_route audio_map_in2_diff[] = { |
| 439 { "IN2A PGA", NULL, "IN2-" }, | 438 { "IN2A PGA", NULL, "IN2-" }, |
| 440 }; | 439 }; |
| 441 | 440 |
| 442 static int wm9090_add_controls(struct snd_soc_codec *codec) | 441 static int wm9090_add_controls(struct snd_soc_codec *codec) |
| 443 { | 442 { |
| 444 struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec); | 443 struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec); |
| 444 struct snd_soc_dapm_context *dapm = &codec->dapm; |
| 445 int i; | 445 int i; |
| 446 | 446 |
| 447 » snd_soc_dapm_new_controls(codec, wm9090_dapm_widgets, | 447 » snd_soc_dapm_new_controls(dapm, wm9090_dapm_widgets, |
| 448 ARRAY_SIZE(wm9090_dapm_widgets)); | 448 ARRAY_SIZE(wm9090_dapm_widgets)); |
| 449 | 449 |
| 450 » snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | 450 » snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); |
| 451 | 451 |
| 452 snd_soc_add_controls(codec, wm9090_controls, | 452 snd_soc_add_controls(codec, wm9090_controls, |
| 453 ARRAY_SIZE(wm9090_controls)); | 453 ARRAY_SIZE(wm9090_controls)); |
| 454 | 454 |
| 455 if (wm9090->pdata.lin1_diff) { | 455 if (wm9090->pdata.lin1_diff) { |
| 456 » » snd_soc_dapm_add_routes(codec, audio_map_in1_diff, | 456 » » snd_soc_dapm_add_routes(dapm, audio_map_in1_diff, |
| 457 ARRAY_SIZE(audio_map_in1_diff)); | 457 ARRAY_SIZE(audio_map_in1_diff)); |
| 458 } else { | 458 } else { |
| 459 » » snd_soc_dapm_add_routes(codec, audio_map_in1_se, | 459 » » snd_soc_dapm_add_routes(dapm, audio_map_in1_se, |
| 460 ARRAY_SIZE(audio_map_in1_se)); | 460 ARRAY_SIZE(audio_map_in1_se)); |
| 461 snd_soc_add_controls(codec, wm9090_in1_se_controls, | 461 snd_soc_add_controls(codec, wm9090_in1_se_controls, |
| 462 ARRAY_SIZE(wm9090_in1_se_controls)); | 462 ARRAY_SIZE(wm9090_in1_se_controls)); |
| 463 } | 463 } |
| 464 | 464 |
| 465 if (wm9090->pdata.lin2_diff) { | 465 if (wm9090->pdata.lin2_diff) { |
| 466 » » snd_soc_dapm_add_routes(codec, audio_map_in2_diff, | 466 » » snd_soc_dapm_add_routes(dapm, audio_map_in2_diff, |
| 467 ARRAY_SIZE(audio_map_in2_diff)); | 467 ARRAY_SIZE(audio_map_in2_diff)); |
| 468 } else { | 468 } else { |
| 469 » » snd_soc_dapm_add_routes(codec, audio_map_in2_se, | 469 » » snd_soc_dapm_add_routes(dapm, audio_map_in2_se, |
| 470 ARRAY_SIZE(audio_map_in2_se)); | 470 ARRAY_SIZE(audio_map_in2_se)); |
| 471 snd_soc_add_controls(codec, wm9090_in2_se_controls, | 471 snd_soc_add_controls(codec, wm9090_in2_se_controls, |
| 472 ARRAY_SIZE(wm9090_in2_se_controls)); | 472 ARRAY_SIZE(wm9090_in2_se_controls)); |
| 473 } | 473 } |
| 474 | 474 |
| 475 if (wm9090->pdata.agc_ena) { | 475 if (wm9090->pdata.agc_ena) { |
| 476 for (i = 0; i < ARRAY_SIZE(wm9090->pdata.agc); i++) | 476 for (i = 0; i < ARRAY_SIZE(wm9090->pdata.agc); i++) |
| 477 snd_soc_write(codec, WM9090_AGC_CONTROL_0 + i, | 477 snd_soc_write(codec, WM9090_AGC_CONTROL_0 + i, |
| 478 wm9090->pdata.agc[i]); | 478 wm9090->pdata.agc[i]); |
| 479 snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_3, | 479 snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_3, |
| (...skipping 26 matching lines...) Expand all Loading... |
| 506 WM9090_VMID_ENA); | 506 WM9090_VMID_ENA); |
| 507 snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_1, | 507 snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_1, |
| 508 WM9090_BIAS_ENA | | 508 WM9090_BIAS_ENA | |
| 509 WM9090_VMID_RES_MASK, | 509 WM9090_VMID_RES_MASK, |
| 510 WM9090_BIAS_ENA | | 510 WM9090_BIAS_ENA | |
| 511 1 << WM9090_VMID_RES_SHIFT); | 511 1 << WM9090_VMID_RES_SHIFT); |
| 512 msleep(1); /* Probably an overestimate */ | 512 msleep(1); /* Probably an overestimate */ |
| 513 break; | 513 break; |
| 514 | 514 |
| 515 case SND_SOC_BIAS_STANDBY: | 515 case SND_SOC_BIAS_STANDBY: |
| 516 » » if (codec->bias_level == SND_SOC_BIAS_OFF) { | 516 » » if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { |
| 517 /* Restore the register cache */ | 517 /* Restore the register cache */ |
| 518 for (i = 1; i < codec->driver->reg_cache_size; i++) { | 518 for (i = 1; i < codec->driver->reg_cache_size; i++) { |
| 519 if (reg_cache[i] == wm9090_reg_defaults[i]) | 519 if (reg_cache[i] == wm9090_reg_defaults[i]) |
| 520 continue; | 520 continue; |
| 521 » » » » if (wm9090_volatile(i)) | 521 » » » » if (wm9090_volatile(codec, i)) |
| 522 continue; | 522 continue; |
| 523 | 523 |
| 524 ret = snd_soc_write(codec, i, reg_cache[i]); | 524 ret = snd_soc_write(codec, i, reg_cache[i]); |
| 525 if (ret != 0) | 525 if (ret != 0) |
| 526 dev_warn(codec->dev, | 526 dev_warn(codec->dev, |
| 527 "Failed to restore register %d:
%d\n", | 527 "Failed to restore register %d:
%d\n", |
| 528 i, ret); | 528 i, ret); |
| 529 } | 529 } |
| 530 } | 530 } |
| 531 | 531 |
| 532 /* We keep VMID off during standby since the combination of | 532 /* We keep VMID off during standby since the combination of |
| 533 * ground referenced outputs and class D speaker mean that | 533 * ground referenced outputs and class D speaker mean that |
| 534 * latency is not an issue. | 534 * latency is not an issue. |
| 535 */ | 535 */ |
| 536 snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_1, | 536 snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_1, |
| 537 WM9090_BIAS_ENA | WM9090_VMID_RES_MASK, 0); | 537 WM9090_BIAS_ENA | WM9090_VMID_RES_MASK, 0); |
| 538 snd_soc_update_bits(codec, WM9090_ANTIPOP2, | 538 snd_soc_update_bits(codec, WM9090_ANTIPOP2, |
| 539 WM9090_VMID_ENA, 0); | 539 WM9090_VMID_ENA, 0); |
| 540 break; | 540 break; |
| 541 | 541 |
| 542 case SND_SOC_BIAS_OFF: | 542 case SND_SOC_BIAS_OFF: |
| 543 break; | 543 break; |
| 544 } | 544 } |
| 545 | 545 |
| 546 » codec->bias_level = level; | 546 » codec->dapm.bias_level = level; |
| 547 | 547 |
| 548 return 0; | 548 return 0; |
| 549 } | 549 } |
| 550 | 550 |
| 551 static int wm9090_probe(struct snd_soc_codec *codec) | 551 static int wm9090_probe(struct snd_soc_codec *codec) |
| 552 { | 552 { |
| 553 struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec); | 553 struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec); |
| 554 u16 *reg_cache = codec->reg_cache; | |
| 555 int ret; | 554 int ret; |
| 556 | 555 |
| 557 codec->control_data = wm9090->control_data; | 556 codec->control_data = wm9090->control_data; |
| 558 ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C); | 557 ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C); |
| 559 if (ret != 0) { | 558 if (ret != 0) { |
| 560 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); | 559 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); |
| 561 return ret; | 560 return ret; |
| 562 } | 561 } |
| 563 | 562 |
| 564 ret = snd_soc_read(codec, WM9090_SOFTWARE_RESET); | 563 ret = snd_soc_read(codec, WM9090_SOFTWARE_RESET); |
| 565 if (ret < 0) | 564 if (ret < 0) |
| 566 return ret; | 565 return ret; |
| 567 if (ret != wm9090_reg_defaults[WM9090_SOFTWARE_RESET]) { | 566 if (ret != wm9090_reg_defaults[WM9090_SOFTWARE_RESET]) { |
| 568 dev_err(codec->dev, "Device is not a WM9090, ID=%x\n", ret); | 567 dev_err(codec->dev, "Device is not a WM9090, ID=%x\n", ret); |
| 569 return -EINVAL; | 568 return -EINVAL; |
| 570 } | 569 } |
| 571 | 570 |
| 572 ret = snd_soc_write(codec, WM9090_SOFTWARE_RESET, 0); | 571 ret = snd_soc_write(codec, WM9090_SOFTWARE_RESET, 0); |
| 573 if (ret < 0) | 572 if (ret < 0) |
| 574 return ret; | 573 return ret; |
| 575 | 574 |
| 576 /* Configure some defaults; they will be written out when we | 575 /* Configure some defaults; they will be written out when we |
| 577 * bring the bias up. | 576 * bring the bias up. |
| 578 */ | 577 */ |
| 579 » reg_cache[WM9090_IN1_LINE_INPUT_A_VOLUME] |= WM9090_IN1_VU | 578 » snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_A_VOLUME, |
| 580 » » | WM9090_IN1A_ZC; | 579 » » » WM9090_IN1_VU | WM9090_IN1A_ZC, |
| 581 » reg_cache[WM9090_IN1_LINE_INPUT_B_VOLUME] |= WM9090_IN1_VU | 580 » » » WM9090_IN1_VU | WM9090_IN1A_ZC); |
| 582 » » | WM9090_IN1B_ZC; | 581 » snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_B_VOLUME, |
| 583 » reg_cache[WM9090_IN2_LINE_INPUT_A_VOLUME] |= WM9090_IN2_VU | 582 » » » WM9090_IN1_VU | WM9090_IN1B_ZC, |
| 584 » » | WM9090_IN2A_ZC; | 583 » » » WM9090_IN1_VU | WM9090_IN1B_ZC); |
| 585 » reg_cache[WM9090_IN2_LINE_INPUT_B_VOLUME] |= WM9090_IN2_VU | 584 » snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_A_VOLUME, |
| 586 » » | WM9090_IN2B_ZC; | 585 » » » WM9090_IN2_VU | WM9090_IN2A_ZC, |
| 587 » reg_cache[WM9090_SPEAKER_VOLUME_LEFT] |= | 586 » » » WM9090_IN2_VU | WM9090_IN2A_ZC); |
| 588 » » WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC; | 587 » snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_B_VOLUME, |
| 589 » reg_cache[WM9090_LEFT_OUTPUT_VOLUME] |= | 588 » » » WM9090_IN2_VU | WM9090_IN2B_ZC, |
| 590 » » WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC; | 589 » » » WM9090_IN2_VU | WM9090_IN2B_ZC); |
| 591 » reg_cache[WM9090_RIGHT_OUTPUT_VOLUME] |= | 590 » snd_soc_update_bits(codec, WM9090_SPEAKER_VOLUME_LEFT, |
| 592 » » WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC; | 591 » » » WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC, |
| 592 » » » WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC); |
| 593 » snd_soc_update_bits(codec, WM9090_LEFT_OUTPUT_VOLUME, |
| 594 » » » WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC, |
| 595 » » » WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC); |
| 596 » snd_soc_update_bits(codec, WM9090_RIGHT_OUTPUT_VOLUME, |
| 597 » » » WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC, |
| 598 » » » WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC); |
| 593 | 599 |
| 594 » reg_cache[WM9090_CLOCKING_1] |= WM9090_TOCLK_ENA; | 600 » snd_soc_update_bits(codec, WM9090_CLOCKING_1, |
| 601 » » » WM9090_TOCLK_ENA, WM9090_TOCLK_ENA); |
| 595 | 602 |
| 596 wm9090_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 603 wm9090_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
| 597 | 604 |
| 598 wm9090_add_controls(codec); | 605 wm9090_add_controls(codec); |
| 599 | 606 |
| 600 return 0; | 607 return 0; |
| 601 } | 608 } |
| 602 | 609 |
| 603 #ifdef CONFIG_PM | 610 #ifdef CONFIG_PM |
| 604 static int wm9090_suspend(struct snd_soc_codec *codec, pm_message_t state) | 611 static int wm9090_suspend(struct snd_soc_codec *codec, pm_message_t state) |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 699 | 706 |
| 700 static void __exit wm9090_exit(void) | 707 static void __exit wm9090_exit(void) |
| 701 { | 708 { |
| 702 i2c_del_driver(&wm9090_i2c_driver); | 709 i2c_del_driver(&wm9090_i2c_driver); |
| 703 } | 710 } |
| 704 module_exit(wm9090_exit); | 711 module_exit(wm9090_exit); |
| 705 | 712 |
| 706 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); | 713 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); |
| 707 MODULE_DESCRIPTION("WM9090 ASoC driver"); | 714 MODULE_DESCRIPTION("WM9090 ASoC driver"); |
| 708 MODULE_LICENSE("GPL"); | 715 MODULE_LICENSE("GPL"); |
| OLD | NEW |