| OLD | NEW |
| 1 /* | 1 /* |
| 2 * wm8988.c -- WM8988 ALSA SoC audio driver | 2 * wm8988.c -- WM8988 ALSA SoC audio driver |
| 3 * | 3 * |
| 4 * Copyright 2009 Wolfson Microelectronics plc | 4 * Copyright 2009 Wolfson Microelectronics plc |
| 5 * Copyright 2005 Openedhand Ltd. | 5 * Copyright 2005 Openedhand Ltd. |
| 6 * | 6 * |
| 7 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | 7 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> |
| 8 * | 8 * |
| 9 * This program is free software; you can redistribute it and/or modify | 9 * This program is free software; you can redistribute it and/or modify |
| 10 * it under the terms of the GNU General Public License version 2 as | 10 * it under the terms of the GNU General Public License version 2 as |
| 11 * published by the Free Software Foundation. | 11 * published by the Free Software Foundation. |
| 12 */ | 12 */ |
| 13 | 13 |
| 14 #include <linux/module.h> | 14 #include <linux/module.h> |
| 15 #include <linux/moduleparam.h> | 15 #include <linux/moduleparam.h> |
| 16 #include <linux/init.h> | 16 #include <linux/init.h> |
| 17 #include <linux/delay.h> | 17 #include <linux/delay.h> |
| 18 #include <linux/pm.h> | 18 #include <linux/pm.h> |
| 19 #include <linux/i2c.h> | 19 #include <linux/i2c.h> |
| 20 #include <linux/spi/spi.h> | 20 #include <linux/spi/spi.h> |
| 21 #include <linux/platform_device.h> | 21 #include <linux/platform_device.h> |
| 22 #include <linux/slab.h> | 22 #include <linux/slab.h> |
| 23 #include <sound/core.h> | 23 #include <sound/core.h> |
| 24 #include <sound/pcm.h> | 24 #include <sound/pcm.h> |
| 25 #include <sound/pcm_params.h> | 25 #include <sound/pcm_params.h> |
| 26 #include <sound/tlv.h> | 26 #include <sound/tlv.h> |
| 27 #include <sound/soc.h> | 27 #include <sound/soc.h> |
| 28 #include <sound/soc-dapm.h> | |
| 29 #include <sound/initval.h> | 28 #include <sound/initval.h> |
| 30 | 29 |
| 31 #include "wm8988.h" | 30 #include "wm8988.h" |
| 32 | 31 |
| 33 /* | 32 /* |
| 34 * wm8988 register cache | 33 * wm8988 register cache |
| 35 * We can't read the WM8988 register space when we | 34 * We can't read the WM8988 register space when we |
| 36 * are using 2 wire for device control, so we cache them instead. | 35 * are using 2 wire for device control, so we cache them instead. |
| 37 */ | 36 */ |
| 38 static const u16 wm8988_reg[] = { | 37 static const u16 wm8988_reg[] = { |
| 39 0x0097, 0x0097, 0x0079, 0x0079, /* 0 */ | 38 0x0097, 0x0097, 0x0079, 0x0079, /* 0 */ |
| 40 0x0000, 0x0008, 0x0000, 0x000a, /* 4 */ | 39 0x0000, 0x0008, 0x0000, 0x000a, /* 4 */ |
| 41 0x0000, 0x0000, 0x00ff, 0x00ff, /* 8 */ | 40 0x0000, 0x0000, 0x00ff, 0x00ff, /* 8 */ |
| 42 0x000f, 0x000f, 0x0000, 0x0000, /* 12 */ | 41 0x000f, 0x000f, 0x0000, 0x0000, /* 12 */ |
| 43 0x0000, 0x007b, 0x0000, 0x0032, /* 16 */ | 42 0x0000, 0x007b, 0x0000, 0x0032, /* 16 */ |
| 44 0x0000, 0x00c3, 0x00c3, 0x00c0, /* 20 */ | 43 0x0000, 0x00c3, 0x00c3, 0x00c0, /* 20 */ |
| 45 0x0000, 0x0000, 0x0000, 0x0000, /* 24 */ | 44 0x0000, 0x0000, 0x0000, 0x0000, /* 24 */ |
| 46 0x0000, 0x0000, 0x0000, 0x0000, /* 28 */ | 45 0x0000, 0x0000, 0x0000, 0x0000, /* 28 */ |
| 47 0x0000, 0x0000, 0x0050, 0x0050, /* 32 */ | 46 0x0000, 0x0000, 0x0050, 0x0050, /* 32 */ |
| 48 0x0050, 0x0050, 0x0050, 0x0050, /* 36 */ | 47 0x0050, 0x0050, 0x0050, 0x0050, /* 36 */ |
| 49 0x0079, 0x0079, 0x0079, /* 40 */ | 48 0x0079, 0x0079, 0x0079, /* 40 */ |
| 50 }; | 49 }; |
| 51 | 50 |
| 52 /* codec private data */ | 51 /* codec private data */ |
| 53 struct wm8988_priv { | 52 struct wm8988_priv { |
| 54 unsigned int sysclk; | 53 unsigned int sysclk; |
| 55 enum snd_soc_control_type control_type; | 54 enum snd_soc_control_type control_type; |
| 56 struct snd_pcm_hw_constraint_list *sysclk_constraints; | 55 struct snd_pcm_hw_constraint_list *sysclk_constraints; |
| 57 u16 reg_cache[WM8988_NUM_REG]; | |
| 58 }; | 56 }; |
| 59 | 57 |
| 60 | 58 |
| 61 #define wm8988_reset(c) snd_soc_write(c, WM8988_RESET, 0) | 59 #define wm8988_reset(c) snd_soc_write(c, WM8988_RESET, 0) |
| 62 | 60 |
| 63 /* | 61 /* |
| 64 * WM8988 Controls | 62 * WM8988 Controls |
| 65 */ | 63 */ |
| 66 | 64 |
| 67 static const char *bass_boost_txt[] = {"Linear Control", "Adaptive Boost"}; | 65 static const char *bass_boost_txt[] = {"Linear Control", "Adaptive Boost"}; |
| (...skipping 602 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 670 switch (level) { | 668 switch (level) { |
| 671 case SND_SOC_BIAS_ON: | 669 case SND_SOC_BIAS_ON: |
| 672 break; | 670 break; |
| 673 | 671 |
| 674 case SND_SOC_BIAS_PREPARE: | 672 case SND_SOC_BIAS_PREPARE: |
| 675 /* VREF, VMID=2x50k, digital enabled */ | 673 /* VREF, VMID=2x50k, digital enabled */ |
| 676 snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x00c0); | 674 snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x00c0); |
| 677 break; | 675 break; |
| 678 | 676 |
| 679 case SND_SOC_BIAS_STANDBY: | 677 case SND_SOC_BIAS_STANDBY: |
| 680 » » if (codec->bias_level == SND_SOC_BIAS_OFF) { | 678 » » if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { |
| 681 /* VREF, VMID=2x5k */ | 679 /* VREF, VMID=2x5k */ |
| 682 snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x1c1); | 680 snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x1c1); |
| 683 | 681 |
| 684 /* Charge caps */ | 682 /* Charge caps */ |
| 685 msleep(100); | 683 msleep(100); |
| 686 } | 684 } |
| 687 | 685 |
| 688 /* VREF, VMID=2*500k, digital stopped */ | 686 /* VREF, VMID=2*500k, digital stopped */ |
| 689 snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x0141); | 687 snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x0141); |
| 690 break; | 688 break; |
| 691 | 689 |
| 692 case SND_SOC_BIAS_OFF: | 690 case SND_SOC_BIAS_OFF: |
| 693 snd_soc_write(codec, WM8988_PWR1, 0x0000); | 691 snd_soc_write(codec, WM8988_PWR1, 0x0000); |
| 694 break; | 692 break; |
| 695 } | 693 } |
| 696 » codec->bias_level = level; | 694 » codec->dapm.bias_level = level; |
| 697 return 0; | 695 return 0; |
| 698 } | 696 } |
| 699 | 697 |
| 700 #define WM8988_RATES SNDRV_PCM_RATE_8000_96000 | 698 #define WM8988_RATES SNDRV_PCM_RATE_8000_96000 |
| 701 | 699 |
| 702 #define WM8988_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ | 700 #define WM8988_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ |
| 703 SNDRV_PCM_FMTBIT_S24_LE) | 701 SNDRV_PCM_FMTBIT_S24_LE) |
| 704 | 702 |
| 705 static struct snd_soc_dai_ops wm8988_ops = { | 703 static struct snd_soc_dai_ops wm8988_ops = { |
| 706 .startup = wm8988_pcm_startup, | 704 .startup = wm8988_pcm_startup, |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 752 } | 750 } |
| 753 | 751 |
| 754 wm8988_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 752 wm8988_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
| 755 | 753 |
| 756 return 0; | 754 return 0; |
| 757 } | 755 } |
| 758 | 756 |
| 759 static int wm8988_probe(struct snd_soc_codec *codec) | 757 static int wm8988_probe(struct snd_soc_codec *codec) |
| 760 { | 758 { |
| 761 struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec); | 759 struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec); |
| 760 struct snd_soc_dapm_context *dapm = &codec->dapm; |
| 762 int ret = 0; | 761 int ret = 0; |
| 763 u16 reg; | 762 u16 reg; |
| 764 | 763 |
| 765 ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8988->control_type); | 764 ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8988->control_type); |
| 766 if (ret < 0) { | 765 if (ret < 0) { |
| 767 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); | 766 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); |
| 768 return ret; | 767 return ret; |
| 769 } | 768 } |
| 770 | 769 |
| 771 ret = wm8988_reset(codec); | 770 ret = wm8988_reset(codec); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 783 snd_soc_write(codec, WM8988_ROUT1V, reg | 0x0100); | 782 snd_soc_write(codec, WM8988_ROUT1V, reg | 0x0100); |
| 784 reg = snd_soc_read(codec, WM8988_ROUT2V); | 783 reg = snd_soc_read(codec, WM8988_ROUT2V); |
| 785 snd_soc_write(codec, WM8988_ROUT2V, reg | 0x0100); | 784 snd_soc_write(codec, WM8988_ROUT2V, reg | 0x0100); |
| 786 reg = snd_soc_read(codec, WM8988_RINVOL); | 785 reg = snd_soc_read(codec, WM8988_RINVOL); |
| 787 snd_soc_write(codec, WM8988_RINVOL, reg | 0x0100); | 786 snd_soc_write(codec, WM8988_RINVOL, reg | 0x0100); |
| 788 | 787 |
| 789 wm8988_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 788 wm8988_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
| 790 | 789 |
| 791 snd_soc_add_controls(codec, wm8988_snd_controls, | 790 snd_soc_add_controls(codec, wm8988_snd_controls, |
| 792 ARRAY_SIZE(wm8988_snd_controls)); | 791 ARRAY_SIZE(wm8988_snd_controls)); |
| 793 » snd_soc_dapm_new_controls(codec, wm8988_dapm_widgets, | 792 » snd_soc_dapm_new_controls(dapm, wm8988_dapm_widgets, |
| 794 ARRAY_SIZE(wm8988_dapm_widgets)); | 793 ARRAY_SIZE(wm8988_dapm_widgets)); |
| 795 » snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | 794 » snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); |
| 796 | 795 |
| 797 return 0; | 796 return 0; |
| 798 } | 797 } |
| 799 | 798 |
| 800 static int wm8988_remove(struct snd_soc_codec *codec) | 799 static int wm8988_remove(struct snd_soc_codec *codec) |
| 801 { | 800 { |
| 802 wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF); | 801 wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF); |
| 803 return 0; | 802 return 0; |
| 804 } | 803 } |
| 805 | 804 |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 925 #if defined(CONFIG_SPI_MASTER) | 924 #if defined(CONFIG_SPI_MASTER) |
| 926 spi_unregister_driver(&wm8988_spi_driver); | 925 spi_unregister_driver(&wm8988_spi_driver); |
| 927 #endif | 926 #endif |
| 928 } | 927 } |
| 929 module_exit(wm8988_exit); | 928 module_exit(wm8988_exit); |
| 930 | 929 |
| 931 | 930 |
| 932 MODULE_DESCRIPTION("ASoC WM8988 driver"); | 931 MODULE_DESCRIPTION("ASoC WM8988 driver"); |
| 933 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); | 932 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); |
| 934 MODULE_LICENSE("GPL"); | 933 MODULE_LICENSE("GPL"); |
| OLD | NEW |