| OLD | NEW |
| 1 /* | 1 /* |
| 2 * wm8960.c -- WM8960 ALSA SoC Audio driver | 2 * wm8960.c -- WM8960 ALSA SoC Audio driver |
| 3 * | 3 * |
| 4 * Author: Liam Girdwood | 4 * Author: Liam Girdwood |
| 5 * | 5 * |
| 6 * This program is free software; you can redistribute it and/or modify | 6 * This program is free software; you can redistribute it and/or modify |
| 7 * it under the terms of the GNU General Public License version 2 as | 7 * it under the terms of the GNU General Public License version 2 as |
| 8 * published by the Free Software Foundation. | 8 * published by the Free Software Foundation. |
| 9 */ | 9 */ |
| 10 | 10 |
| 11 #include <linux/module.h> | 11 #include <linux/module.h> |
| 12 #include <linux/moduleparam.h> | 12 #include <linux/moduleparam.h> |
| 13 #include <linux/init.h> | 13 #include <linux/init.h> |
| 14 #include <linux/delay.h> | 14 #include <linux/delay.h> |
| 15 #include <linux/pm.h> | 15 #include <linux/pm.h> |
| 16 #include <linux/i2c.h> | 16 #include <linux/i2c.h> |
| 17 #include <linux/platform_device.h> | 17 #include <linux/platform_device.h> |
| 18 #include <linux/slab.h> | 18 #include <linux/slab.h> |
| 19 #include <sound/core.h> | 19 #include <sound/core.h> |
| 20 #include <sound/pcm.h> | 20 #include <sound/pcm.h> |
| 21 #include <sound/pcm_params.h> | 21 #include <sound/pcm_params.h> |
| 22 #include <sound/soc.h> | 22 #include <sound/soc.h> |
| 23 #include <sound/soc-dapm.h> | |
| 24 #include <sound/initval.h> | 23 #include <sound/initval.h> |
| 25 #include <sound/tlv.h> | 24 #include <sound/tlv.h> |
| 26 #include <sound/wm8960.h> | 25 #include <sound/wm8960.h> |
| 27 | 26 |
| 28 #include "wm8960.h" | 27 #include "wm8960.h" |
| 29 | 28 |
| 30 #define AUDIO_NAME "wm8960" | 29 #define AUDIO_NAME "wm8960" |
| 31 | 30 |
| 32 /* R25 - Power 1 */ | 31 /* R25 - Power 1 */ |
| 33 #define WM8960_VMID_MASK 0x180 | 32 #define WM8960_VMID_MASK 0x180 |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 65 0x0000, 0x0000, 0x0000, 0x0000, | 64 0x0000, 0x0000, 0x0000, 0x0000, |
| 66 0x0100, 0x0100, 0x0050, 0x0050, | 65 0x0100, 0x0100, 0x0050, 0x0050, |
| 67 0x0050, 0x0050, 0x0000, 0x0000, | 66 0x0050, 0x0050, 0x0000, 0x0000, |
| 68 0x0000, 0x0000, 0x0040, 0x0000, | 67 0x0000, 0x0000, 0x0040, 0x0000, |
| 69 0x0000, 0x0050, 0x0050, 0x0000, | 68 0x0000, 0x0050, 0x0050, 0x0000, |
| 70 0x0002, 0x0037, 0x004d, 0x0080, | 69 0x0002, 0x0037, 0x004d, 0x0080, |
| 71 0x0008, 0x0031, 0x0026, 0x00e9, | 70 0x0008, 0x0031, 0x0026, 0x00e9, |
| 72 }; | 71 }; |
| 73 | 72 |
| 74 struct wm8960_priv { | 73 struct wm8960_priv { |
| 75 u16 reg_cache[WM8960_CACHEREGNUM]; | |
| 76 enum snd_soc_control_type control_type; | 74 enum snd_soc_control_type control_type; |
| 77 void *control_data; | 75 void *control_data; |
| 78 int (*set_bias_level)(struct snd_soc_codec *, | 76 int (*set_bias_level)(struct snd_soc_codec *, |
| 79 enum snd_soc_bias_level level); | 77 enum snd_soc_bias_level level); |
| 80 struct snd_soc_dapm_widget *lout1; | 78 struct snd_soc_dapm_widget *lout1; |
| 81 struct snd_soc_dapm_widget *rout1; | 79 struct snd_soc_dapm_widget *rout1; |
| 82 struct snd_soc_dapm_widget *out3; | 80 struct snd_soc_dapm_widget *out3; |
| 83 bool deemph; | 81 bool deemph; |
| 84 int playback_fs; | 82 int playback_fs; |
| 85 }; | 83 }; |
| (...skipping 296 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 382 { "HP_R", NULL, "OUT3 VMID" }, | 380 { "HP_R", NULL, "OUT3 VMID" }, |
| 383 | 381 |
| 384 { "OUT3 VMID", NULL, "Left Output Mixer" }, | 382 { "OUT3 VMID", NULL, "Left Output Mixer" }, |
| 385 { "OUT3 VMID", NULL, "Right Output Mixer" }, | 383 { "OUT3 VMID", NULL, "Right Output Mixer" }, |
| 386 }; | 384 }; |
| 387 | 385 |
| 388 static int wm8960_add_widgets(struct snd_soc_codec *codec) | 386 static int wm8960_add_widgets(struct snd_soc_codec *codec) |
| 389 { | 387 { |
| 390 struct wm8960_data *pdata = codec->dev->platform_data; | 388 struct wm8960_data *pdata = codec->dev->platform_data; |
| 391 struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); | 389 struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); |
| 390 struct snd_soc_dapm_context *dapm = &codec->dapm; |
| 392 struct snd_soc_dapm_widget *w; | 391 struct snd_soc_dapm_widget *w; |
| 393 | 392 |
| 394 » snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets, | 393 » snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets, |
| 395 ARRAY_SIZE(wm8960_dapm_widgets)); | 394 ARRAY_SIZE(wm8960_dapm_widgets)); |
| 396 | 395 |
| 397 » snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); | 396 » snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths)); |
| 398 | 397 |
| 399 /* In capless mode OUT3 is used to provide VMID for the | 398 /* In capless mode OUT3 is used to provide VMID for the |
| 400 * headphone outputs, otherwise it is used as a mono mixer. | 399 * headphone outputs, otherwise it is used as a mono mixer. |
| 401 */ | 400 */ |
| 402 if (pdata && pdata->capless) { | 401 if (pdata && pdata->capless) { |
| 403 » » snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets_capless, | 402 » » snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_capless, |
| 404 ARRAY_SIZE(wm8960_dapm_widgets_capless
)); | 403 ARRAY_SIZE(wm8960_dapm_widgets_capless
)); |
| 405 | 404 |
| 406 » » snd_soc_dapm_add_routes(codec, audio_paths_capless, | 405 » » snd_soc_dapm_add_routes(dapm, audio_paths_capless, |
| 407 ARRAY_SIZE(audio_paths_capless)); | 406 ARRAY_SIZE(audio_paths_capless)); |
| 408 } else { | 407 } else { |
| 409 » » snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets_out3, | 408 » » snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_out3, |
| 410 ARRAY_SIZE(wm8960_dapm_widgets_out3)); | 409 ARRAY_SIZE(wm8960_dapm_widgets_out3)); |
| 411 | 410 |
| 412 » » snd_soc_dapm_add_routes(codec, audio_paths_out3, | 411 » » snd_soc_dapm_add_routes(dapm, audio_paths_out3, |
| 413 ARRAY_SIZE(audio_paths_out3)); | 412 ARRAY_SIZE(audio_paths_out3)); |
| 414 } | 413 } |
| 415 | 414 |
| 416 /* We need to power up the headphone output stage out of | 415 /* We need to power up the headphone output stage out of |
| 417 * sequence for capless mode. To save scanning the widget | 416 * sequence for capless mode. To save scanning the widget |
| 418 * list each time to find the desired power state do so now | 417 * list each time to find the desired power state do so now |
| 419 * and save the result. | 418 * and save the result. |
| 420 */ | 419 */ |
| 421 » list_for_each_entry(w, &codec->dapm_widgets, list) { | 420 » list_for_each_entry(w, &codec->card->widgets, list) { |
| 421 » » if (w->dapm != &codec->dapm) |
| 422 » » » continue; |
| 422 if (strcmp(w->name, "LOUT1 PGA") == 0) | 423 if (strcmp(w->name, "LOUT1 PGA") == 0) |
| 423 wm8960->lout1 = w; | 424 wm8960->lout1 = w; |
| 424 if (strcmp(w->name, "ROUT1 PGA") == 0) | 425 if (strcmp(w->name, "ROUT1 PGA") == 0) |
| 425 wm8960->rout1 = w; | 426 wm8960->rout1 = w; |
| 426 if (strcmp(w->name, "OUT3 VMID") == 0) | 427 if (strcmp(w->name, "OUT3 VMID") == 0) |
| 427 wm8960->out3 = w; | 428 wm8960->out3 = w; |
| 428 } | 429 } |
| 429 | 430 |
| 430 return 0; | 431 return 0; |
| 431 } | 432 } |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 566 | 567 |
| 567 case SND_SOC_BIAS_PREPARE: | 568 case SND_SOC_BIAS_PREPARE: |
| 568 /* Set VMID to 2x50k */ | 569 /* Set VMID to 2x50k */ |
| 569 reg = snd_soc_read(codec, WM8960_POWER1); | 570 reg = snd_soc_read(codec, WM8960_POWER1); |
| 570 reg &= ~0x180; | 571 reg &= ~0x180; |
| 571 reg |= 0x80; | 572 reg |= 0x80; |
| 572 snd_soc_write(codec, WM8960_POWER1, reg); | 573 snd_soc_write(codec, WM8960_POWER1, reg); |
| 573 break; | 574 break; |
| 574 | 575 |
| 575 case SND_SOC_BIAS_STANDBY: | 576 case SND_SOC_BIAS_STANDBY: |
| 576 » » if (codec->bias_level == SND_SOC_BIAS_OFF) { | 577 » » if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { |
| 577 /* Enable anti-pop features */ | 578 /* Enable anti-pop features */ |
| 578 snd_soc_write(codec, WM8960_APOP1, | 579 snd_soc_write(codec, WM8960_APOP1, |
| 579 WM8960_POBCTRL | WM8960_SOFT_ST | | 580 WM8960_POBCTRL | WM8960_SOFT_ST | |
| 580 WM8960_BUFDCOPEN | WM8960_BUFIOEN); | 581 WM8960_BUFDCOPEN | WM8960_BUFIOEN); |
| 581 | 582 |
| 582 /* Enable & ramp VMID at 2x50k */ | 583 /* Enable & ramp VMID at 2x50k */ |
| 583 reg = snd_soc_read(codec, WM8960_POWER1); | 584 reg = snd_soc_read(codec, WM8960_POWER1); |
| 584 reg |= 0x80; | 585 reg |= 0x80; |
| 585 snd_soc_write(codec, WM8960_POWER1, reg); | 586 snd_soc_write(codec, WM8960_POWER1, reg); |
| 586 msleep(100); | 587 msleep(100); |
| (...skipping 17 matching lines...) Expand all Loading... |
| 604 snd_soc_write(codec, WM8960_APOP1, | 605 snd_soc_write(codec, WM8960_APOP1, |
| 605 WM8960_POBCTRL | WM8960_SOFT_ST | | 606 WM8960_POBCTRL | WM8960_SOFT_ST | |
| 606 WM8960_BUFDCOPEN | WM8960_BUFIOEN); | 607 WM8960_BUFDCOPEN | WM8960_BUFIOEN); |
| 607 | 608 |
| 608 /* Disable VMID and VREF, let them discharge */ | 609 /* Disable VMID and VREF, let them discharge */ |
| 609 snd_soc_write(codec, WM8960_POWER1, 0); | 610 snd_soc_write(codec, WM8960_POWER1, 0); |
| 610 msleep(600); | 611 msleep(600); |
| 611 break; | 612 break; |
| 612 } | 613 } |
| 613 | 614 |
| 614 » codec->bias_level = level; | 615 » codec->dapm.bias_level = level; |
| 615 | 616 |
| 616 return 0; | 617 return 0; |
| 617 } | 618 } |
| 618 | 619 |
| 619 static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, | 620 static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, |
| 620 enum snd_soc_bias_level level) | 621 enum snd_soc_bias_level level) |
| 621 { | 622 { |
| 622 struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); | 623 struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); |
| 623 int reg; | 624 int reg; |
| 624 | 625 |
| 625 switch (level) { | 626 switch (level) { |
| 626 case SND_SOC_BIAS_ON: | 627 case SND_SOC_BIAS_ON: |
| 627 break; | 628 break; |
| 628 | 629 |
| 629 case SND_SOC_BIAS_PREPARE: | 630 case SND_SOC_BIAS_PREPARE: |
| 630 » » switch (codec->bias_level) { | 631 » » switch (codec->dapm.bias_level) { |
| 631 case SND_SOC_BIAS_STANDBY: | 632 case SND_SOC_BIAS_STANDBY: |
| 632 /* Enable anti pop mode */ | 633 /* Enable anti pop mode */ |
| 633 snd_soc_update_bits(codec, WM8960_APOP1, | 634 snd_soc_update_bits(codec, WM8960_APOP1, |
| 634 WM8960_POBCTRL | WM8960_SOFT_ST | | 635 WM8960_POBCTRL | WM8960_SOFT_ST | |
| 635 WM8960_BUFDCOPEN, | 636 WM8960_BUFDCOPEN, |
| 636 WM8960_POBCTRL | WM8960_SOFT_ST | | 637 WM8960_POBCTRL | WM8960_SOFT_ST | |
| 637 WM8960_BUFDCOPEN); | 638 WM8960_BUFDCOPEN); |
| 638 | 639 |
| 639 /* Enable LOUT1, ROUT1 and OUT3 if they're enabled */ | 640 /* Enable LOUT1, ROUT1 and OUT3 if they're enabled */ |
| 640 reg = 0; | 641 reg = 0; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 675 snd_soc_update_bits(codec, WM8960_POWER1, | 676 snd_soc_update_bits(codec, WM8960_POWER1, |
| 676 WM8960_VREF | WM8960_VMID_MASK, 0); | 677 WM8960_VREF | WM8960_VMID_MASK, 0); |
| 677 break; | 678 break; |
| 678 | 679 |
| 679 default: | 680 default: |
| 680 break; | 681 break; |
| 681 } | 682 } |
| 682 break; | 683 break; |
| 683 | 684 |
| 684 case SND_SOC_BIAS_STANDBY: | 685 case SND_SOC_BIAS_STANDBY: |
| 685 » » switch (codec->bias_level) { | 686 » » switch (codec->dapm.bias_level) { |
| 686 case SND_SOC_BIAS_PREPARE: | 687 case SND_SOC_BIAS_PREPARE: |
| 687 /* Disable HP discharge */ | 688 /* Disable HP discharge */ |
| 688 snd_soc_update_bits(codec, WM8960_APOP2, | 689 snd_soc_update_bits(codec, WM8960_APOP2, |
| 689 WM8960_DISOP | WM8960_DRES_MASK, | 690 WM8960_DISOP | WM8960_DRES_MASK, |
| 690 0); | 691 0); |
| 691 | 692 |
| 692 /* Disable anti-pop features */ | 693 /* Disable anti-pop features */ |
| 693 snd_soc_update_bits(codec, WM8960_APOP1, | 694 snd_soc_update_bits(codec, WM8960_APOP1, |
| 694 WM8960_POBCTRL | WM8960_SOFT_ST | | 695 WM8960_POBCTRL | WM8960_SOFT_ST | |
| 695 WM8960_BUFDCOPEN, | 696 WM8960_BUFDCOPEN, |
| 696 WM8960_POBCTRL | WM8960_SOFT_ST | | 697 WM8960_POBCTRL | WM8960_SOFT_ST | |
| 697 WM8960_BUFDCOPEN); | 698 WM8960_BUFDCOPEN); |
| 698 break; | 699 break; |
| 699 | 700 |
| 700 default: | 701 default: |
| 701 break; | 702 break; |
| 702 } | 703 } |
| 703 break; | 704 break; |
| 704 | 705 |
| 705 case SND_SOC_BIAS_OFF: | 706 case SND_SOC_BIAS_OFF: |
| 706 break; | 707 break; |
| 707 } | 708 } |
| 708 | 709 |
| 709 » codec->bias_level = level; | 710 » codec->dapm.bias_level = level; |
| 710 | 711 |
| 711 return 0; | 712 return 0; |
| 712 } | 713 } |
| 713 | 714 |
| 714 /* PLL divisors */ | 715 /* PLL divisors */ |
| 715 struct _pll_div { | 716 struct _pll_div { |
| 716 u32 pre_div:1; | 717 u32 pre_div:1; |
| 717 u32 n:4; | 718 u32 n:4; |
| 718 u32 k:24; | 719 u32 k:24; |
| 719 }; | 720 }; |
| (...skipping 345 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1065 { | 1066 { |
| 1066 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 1067 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
| 1067 i2c_del_driver(&wm8960_i2c_driver); | 1068 i2c_del_driver(&wm8960_i2c_driver); |
| 1068 #endif | 1069 #endif |
| 1069 } | 1070 } |
| 1070 module_exit(wm8960_exit); | 1071 module_exit(wm8960_exit); |
| 1071 | 1072 |
| 1072 MODULE_DESCRIPTION("ASoC WM8960 driver"); | 1073 MODULE_DESCRIPTION("ASoC WM8960 driver"); |
| 1073 MODULE_AUTHOR("Liam Girdwood"); | 1074 MODULE_AUTHOR("Liam Girdwood"); |
| 1074 MODULE_LICENSE("GPL"); | 1075 MODULE_LICENSE("GPL"); |
| OLD | NEW |