diff options
Diffstat (limited to 'sound/soc/codecs/wm8971.c')
| -rw-r--r-- | sound/soc/codecs/wm8971.c | 99 |
1 files changed, 30 insertions, 69 deletions
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index 39ddb9b8834c..f9cbabdc6238 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c | |||
| @@ -31,11 +31,11 @@ | |||
| 31 | 31 | ||
| 32 | #define WM8971_REG_COUNT 43 | 32 | #define WM8971_REG_COUNT 43 |
| 33 | 33 | ||
| 34 | static struct workqueue_struct *wm8971_workq = NULL; | ||
| 35 | |||
| 36 | /* codec private data */ | 34 | /* codec private data */ |
| 37 | struct wm8971_priv { | 35 | struct wm8971_priv { |
| 38 | unsigned int sysclk; | 36 | unsigned int sysclk; |
| 37 | struct delayed_work charge_work; | ||
| 38 | struct regmap *regmap; | ||
| 39 | }; | 39 | }; |
| 40 | 40 | ||
| 41 | /* | 41 | /* |
| @@ -552,9 +552,19 @@ static int wm8971_mute(struct snd_soc_dai *dai, int mute) | |||
| 552 | return 0; | 552 | return 0; |
| 553 | } | 553 | } |
| 554 | 554 | ||
| 555 | static void wm8971_charge_work(struct work_struct *work) | ||
| 556 | { | ||
| 557 | struct wm8971_priv *wm8971 = | ||
| 558 | container_of(work, struct wm8971_priv, charge_work.work); | ||
| 559 | |||
| 560 | /* Set to 500k */ | ||
| 561 | regmap_update_bits(wm8971->regmap, WM8971_PWR1, 0x0180, 0x0100); | ||
| 562 | } | ||
| 563 | |||
| 555 | static int wm8971_set_bias_level(struct snd_soc_codec *codec, | 564 | static int wm8971_set_bias_level(struct snd_soc_codec *codec, |
| 556 | enum snd_soc_bias_level level) | 565 | enum snd_soc_bias_level level) |
| 557 | { | 566 | { |
| 567 | struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec); | ||
| 558 | u16 pwr_reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e; | 568 | u16 pwr_reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e; |
| 559 | 569 | ||
| 560 | switch (level) { | 570 | switch (level) { |
| @@ -563,15 +573,24 @@ static int wm8971_set_bias_level(struct snd_soc_codec *codec, | |||
| 563 | snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x00c1); | 573 | snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x00c1); |
| 564 | break; | 574 | break; |
| 565 | case SND_SOC_BIAS_PREPARE: | 575 | case SND_SOC_BIAS_PREPARE: |
| 576 | /* Wait until fully charged */ | ||
| 577 | flush_delayed_work(&wm8971->charge_work); | ||
| 566 | break; | 578 | break; |
| 567 | case SND_SOC_BIAS_STANDBY: | 579 | case SND_SOC_BIAS_STANDBY: |
| 568 | if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) | 580 | if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { |
| 569 | snd_soc_cache_sync(codec); | 581 | snd_soc_cache_sync(codec); |
| 582 | /* charge output caps - set vmid to 5k for quick power up */ | ||
| 583 | snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x01c0); | ||
| 584 | queue_delayed_work(system_power_efficient_wq, | ||
| 585 | &wm8971->charge_work, msecs_to_jiffies(1000)); | ||
| 586 | } else { | ||
| 587 | /* mute dac and set vmid to 500k, enable VREF */ | ||
| 588 | snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x0140); | ||
| 589 | } | ||
| 570 | 590 | ||
| 571 | /* mute dac and set vmid to 500k, enable VREF */ | ||
| 572 | snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x0140); | ||
| 573 | break; | 591 | break; |
| 574 | case SND_SOC_BIAS_OFF: | 592 | case SND_SOC_BIAS_OFF: |
| 593 | cancel_delayed_work_sync(&wm8971->charge_work); | ||
| 575 | snd_soc_write(codec, WM8971_PWR1, 0x0001); | 594 | snd_soc_write(codec, WM8971_PWR1, 0x0001); |
| 576 | break; | 595 | break; |
| 577 | } | 596 | } |
| @@ -610,58 +629,14 @@ static struct snd_soc_dai_driver wm8971_dai = { | |||
| 610 | .ops = &wm8971_dai_ops, | 629 | .ops = &wm8971_dai_ops, |
| 611 | }; | 630 | }; |
| 612 | 631 | ||
| 613 | static void wm8971_work(struct work_struct *work) | ||
| 614 | { | ||
| 615 | struct snd_soc_dapm_context *dapm = | ||
| 616 | container_of(work, struct snd_soc_dapm_context, | ||
| 617 | delayed_work.work); | ||
| 618 | struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); | ||
| 619 | wm8971_set_bias_level(codec, codec->dapm.bias_level); | ||
| 620 | } | ||
| 621 | |||
| 622 | static int wm8971_suspend(struct snd_soc_codec *codec) | ||
| 623 | { | ||
| 624 | wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
| 625 | return 0; | ||
| 626 | } | ||
| 627 | |||
| 628 | static int wm8971_resume(struct snd_soc_codec *codec) | ||
| 629 | { | ||
| 630 | u16 reg; | ||
| 631 | |||
| 632 | wm8971_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
| 633 | |||
| 634 | /* charge wm8971 caps */ | ||
| 635 | if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) { | ||
| 636 | reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e; | ||
| 637 | snd_soc_write(codec, WM8971_PWR1, reg | 0x01c0); | ||
| 638 | codec->dapm.bias_level = SND_SOC_BIAS_ON; | ||
| 639 | queue_delayed_work(wm8971_workq, &codec->dapm.delayed_work, | ||
| 640 | msecs_to_jiffies(1000)); | ||
| 641 | } | ||
| 642 | |||
| 643 | return 0; | ||
| 644 | } | ||
| 645 | |||
| 646 | static int wm8971_probe(struct snd_soc_codec *codec) | 632 | static int wm8971_probe(struct snd_soc_codec *codec) |
| 647 | { | 633 | { |
| 648 | int ret = 0; | 634 | struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec); |
| 649 | u16 reg; | ||
| 650 | 635 | ||
| 651 | INIT_DELAYED_WORK(&codec->dapm.delayed_work, wm8971_work); | 636 | INIT_DELAYED_WORK(&wm8971->charge_work, wm8971_charge_work); |
| 652 | wm8971_workq = create_workqueue("wm8971"); | ||
| 653 | if (wm8971_workq == NULL) | ||
| 654 | return -ENOMEM; | ||
| 655 | 637 | ||
| 656 | wm8971_reset(codec); | 638 | wm8971_reset(codec); |
| 657 | 639 | ||
| 658 | /* charge output caps - set vmid to 5k for quick power up */ | ||
| 659 | reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e; | ||
| 660 | snd_soc_write(codec, WM8971_PWR1, reg | 0x01c0); | ||
| 661 | codec->dapm.bias_level = SND_SOC_BIAS_STANDBY; | ||
| 662 | queue_delayed_work(wm8971_workq, &codec->dapm.delayed_work, | ||
| 663 | msecs_to_jiffies(1000)); | ||
| 664 | |||
| 665 | /* set the update bits */ | 640 | /* set the update bits */ |
| 666 | snd_soc_update_bits(codec, WM8971_LDAC, 0x0100, 0x0100); | 641 | snd_soc_update_bits(codec, WM8971_LDAC, 0x0100, 0x0100); |
| 667 | snd_soc_update_bits(codec, WM8971_RDAC, 0x0100, 0x0100); | 642 | snd_soc_update_bits(codec, WM8971_RDAC, 0x0100, 0x0100); |
| @@ -672,26 +647,13 @@ static int wm8971_probe(struct snd_soc_codec *codec) | |||
| 672 | snd_soc_update_bits(codec, WM8971_LINVOL, 0x0100, 0x0100); | 647 | snd_soc_update_bits(codec, WM8971_LINVOL, 0x0100, 0x0100); |
| 673 | snd_soc_update_bits(codec, WM8971_RINVOL, 0x0100, 0x0100); | 648 | snd_soc_update_bits(codec, WM8971_RINVOL, 0x0100, 0x0100); |
| 674 | 649 | ||
| 675 | return ret; | ||
| 676 | } | ||
| 677 | |||
| 678 | |||
| 679 | /* power down chip */ | ||
| 680 | static int wm8971_remove(struct snd_soc_codec *codec) | ||
| 681 | { | ||
| 682 | wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
| 683 | |||
| 684 | if (wm8971_workq) | ||
| 685 | destroy_workqueue(wm8971_workq); | ||
| 686 | return 0; | 650 | return 0; |
| 687 | } | 651 | } |
| 688 | 652 | ||
| 689 | static struct snd_soc_codec_driver soc_codec_dev_wm8971 = { | 653 | static struct snd_soc_codec_driver soc_codec_dev_wm8971 = { |
| 690 | .probe = wm8971_probe, | 654 | .probe = wm8971_probe, |
| 691 | .remove = wm8971_remove, | ||
| 692 | .suspend = wm8971_suspend, | ||
| 693 | .resume = wm8971_resume, | ||
| 694 | .set_bias_level = wm8971_set_bias_level, | 655 | .set_bias_level = wm8971_set_bias_level, |
| 656 | .suspend_bias_off = true, | ||
| 695 | 657 | ||
| 696 | .controls = wm8971_snd_controls, | 658 | .controls = wm8971_snd_controls, |
| 697 | .num_controls = ARRAY_SIZE(wm8971_snd_controls), | 659 | .num_controls = ARRAY_SIZE(wm8971_snd_controls), |
| @@ -715,7 +677,6 @@ static int wm8971_i2c_probe(struct i2c_client *i2c, | |||
| 715 | const struct i2c_device_id *id) | 677 | const struct i2c_device_id *id) |
| 716 | { | 678 | { |
| 717 | struct wm8971_priv *wm8971; | 679 | struct wm8971_priv *wm8971; |
| 718 | struct regmap *regmap; | ||
| 719 | int ret; | 680 | int ret; |
| 720 | 681 | ||
| 721 | wm8971 = devm_kzalloc(&i2c->dev, sizeof(struct wm8971_priv), | 682 | wm8971 = devm_kzalloc(&i2c->dev, sizeof(struct wm8971_priv), |
| @@ -723,9 +684,9 @@ static int wm8971_i2c_probe(struct i2c_client *i2c, | |||
| 723 | if (wm8971 == NULL) | 684 | if (wm8971 == NULL) |
| 724 | return -ENOMEM; | 685 | return -ENOMEM; |
| 725 | 686 | ||
| 726 | regmap = devm_regmap_init_i2c(i2c, &wm8971_regmap); | 687 | wm8971->regmap = devm_regmap_init_i2c(i2c, &wm8971_regmap); |
| 727 | if (IS_ERR(regmap)) | 688 | if (IS_ERR(wm8971->regmap)) |
| 728 | return PTR_ERR(regmap); | 689 | return PTR_ERR(wm8971->regmap); |
| 729 | 690 | ||
| 730 | i2c_set_clientdata(i2c, wm8971); | 691 | i2c_set_clientdata(i2c, wm8971); |
| 731 | 692 | ||
