aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm8940.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/wm8940.c')
-rw-r--r--sound/soc/codecs/wm8940.c221
1 files changed, 128 insertions, 93 deletions
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index b1591c61c254..b404c26c1753 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -28,7 +28,7 @@
28#include <linux/delay.h> 28#include <linux/delay.h>
29#include <linux/pm.h> 29#include <linux/pm.h>
30#include <linux/i2c.h> 30#include <linux/i2c.h>
31#include <linux/spi/spi.h> 31#include <linux/regmap.h>
32#include <linux/slab.h> 32#include <linux/slab.h>
33#include <sound/core.h> 33#include <sound/core.h>
34#include <sound/pcm.h> 34#include <sound/pcm.h>
@@ -41,78 +41,116 @@
41 41
42struct wm8940_priv { 42struct wm8940_priv {
43 unsigned int sysclk; 43 unsigned int sysclk;
44 enum snd_soc_control_type control_type; 44 struct regmap *regmap;
45}; 45};
46 46
47static int wm8940_volatile_register(struct snd_soc_codec *codec, 47static bool wm8940_volatile_register(struct device *dev, unsigned int reg)
48 unsigned int reg)
49{ 48{
50 switch (reg) { 49 switch (reg) {
51 case WM8940_SOFTRESET: 50 case WM8940_SOFTRESET:
52 return 1; 51 return true;
53 default: 52 default:
54 return 0; 53 return false;
54 }
55}
56
57static bool wm8940_readable_register(struct device *dev, unsigned int reg)
58{
59 switch (reg) {
60 case WM8940_SOFTRESET:
61 case WM8940_POWER1:
62 case WM8940_POWER2:
63 case WM8940_POWER3:
64 case WM8940_IFACE:
65 case WM8940_COMPANDINGCTL:
66 case WM8940_CLOCK:
67 case WM8940_ADDCNTRL:
68 case WM8940_GPIO:
69 case WM8940_CTLINT:
70 case WM8940_DAC:
71 case WM8940_DACVOL:
72 case WM8940_ADC:
73 case WM8940_ADCVOL:
74 case WM8940_NOTCH1:
75 case WM8940_NOTCH2:
76 case WM8940_NOTCH3:
77 case WM8940_NOTCH4:
78 case WM8940_NOTCH5:
79 case WM8940_NOTCH6:
80 case WM8940_NOTCH7:
81 case WM8940_NOTCH8:
82 case WM8940_DACLIM1:
83 case WM8940_DACLIM2:
84 case WM8940_ALC1:
85 case WM8940_ALC2:
86 case WM8940_ALC3:
87 case WM8940_NOISEGATE:
88 case WM8940_PLLN:
89 case WM8940_PLLK1:
90 case WM8940_PLLK2:
91 case WM8940_PLLK3:
92 case WM8940_ALC4:
93 case WM8940_INPUTCTL:
94 case WM8940_PGAGAIN:
95 case WM8940_ADCBOOST:
96 case WM8940_OUTPUTCTL:
97 case WM8940_SPKMIX:
98 case WM8940_SPKVOL:
99 case WM8940_MONOMIX:
100 return true;
101 default:
102 return false;
55 } 103 }
56} 104}
57 105
58static u16 wm8940_reg_defaults[] = { 106static const struct reg_default wm8940_reg_defaults[] = {
59 0x8940, /* Soft Reset */ 107 { 0x1, 0x0000 }, /* Power 1 */
60 0x0000, /* Power 1 */ 108 { 0x2, 0x0000 }, /* Power 2 */
61 0x0000, /* Power 2 */ 109 { 0x3, 0x0000 }, /* Power 3 */
62 0x0000, /* Power 3 */ 110 { 0x4, 0x0010 }, /* Interface Control */
63 0x0010, /* Interface Control */ 111 { 0x5, 0x0000 }, /* Companding Control */
64 0x0000, /* Companding Control */ 112 { 0x6, 0x0140 }, /* Clock Control */
65 0x0140, /* Clock Control */ 113 { 0x7, 0x0000 }, /* Additional Controls */
66 0x0000, /* Additional Controls */ 114 { 0x8, 0x0000 }, /* GPIO Control */
67 0x0000, /* GPIO Control */ 115 { 0x9, 0x0002 }, /* Auto Increment Control */
68 0x0002, /* Auto Increment Control */ 116 { 0xa, 0x0000 }, /* DAC Control */
69 0x0000, /* DAC Control */ 117 { 0xb, 0x00FF }, /* DAC Volume */
70 0x00FF, /* DAC Volume */ 118
71 0, 119 { 0xe, 0x0100 }, /* ADC Control */
72 0, 120 { 0xf, 0x00FF }, /* ADC Volume */
73 0x0100, /* ADC Control */ 121 { 0x10, 0x0000 }, /* Notch Filter 1 Control 1 */
74 0x00FF, /* ADC Volume */ 122 { 0x11, 0x0000 }, /* Notch Filter 1 Control 2 */
75 0x0000, /* Notch Filter 1 Control 1 */ 123 { 0x12, 0x0000 }, /* Notch Filter 2 Control 1 */
76 0x0000, /* Notch Filter 1 Control 2 */ 124 { 0x13, 0x0000 }, /* Notch Filter 2 Control 2 */
77 0x0000, /* Notch Filter 2 Control 1 */ 125 { 0x14, 0x0000 }, /* Notch Filter 3 Control 1 */
78 0x0000, /* Notch Filter 2 Control 2 */ 126 { 0x15, 0x0000 }, /* Notch Filter 3 Control 2 */
79 0x0000, /* Notch Filter 3 Control 1 */ 127 { 0x16, 0x0000 }, /* Notch Filter 4 Control 1 */
80 0x0000, /* Notch Filter 3 Control 2 */ 128 { 0x17, 0x0000 }, /* Notch Filter 4 Control 2 */
81 0x0000, /* Notch Filter 4 Control 1 */ 129 { 0x18, 0x0032 }, /* DAC Limit Control 1 */
82 0x0000, /* Notch Filter 4 Control 2 */ 130 { 0x19, 0x0000 }, /* DAC Limit Control 2 */
83 0x0032, /* DAC Limit Control 1 */ 131
84 0x0000, /* DAC Limit Control 2 */ 132 { 0x20, 0x0038 }, /* ALC Control 1 */
85 0, 133 { 0x21, 0x000B }, /* ALC Control 2 */
86 0, 134 { 0x22, 0x0032 }, /* ALC Control 3 */
87 0, 135 { 0x23, 0x0000 }, /* Noise Gate */
88 0, 136 { 0x24, 0x0041 }, /* PLLN */
89 0, 137 { 0x25, 0x000C }, /* PLLK1 */
90 0, 138 { 0x26, 0x0093 }, /* PLLK2 */
91 0x0038, /* ALC Control 1 */ 139 { 0x27, 0x00E9 }, /* PLLK3 */
92 0x000B, /* ALC Control 2 */ 140
93 0x0032, /* ALC Control 3 */ 141 { 0x2a, 0x0030 }, /* ALC Control 4 */
94 0x0000, /* Noise Gate */ 142
95 0x0041, /* PLLN */ 143 { 0x2c, 0x0002 }, /* Input Control */
96 0x000C, /* PLLK1 */ 144 { 0x2d, 0x0050 }, /* PGA Gain */
97 0x0093, /* PLLK2 */ 145
98 0x00E9, /* PLLK3 */ 146 { 0x2f, 0x0002 }, /* ADC Boost Control */
99 0, 147
100 0, 148 { 0x31, 0x0002 }, /* Output Control */
101 0x0030, /* ALC Control 4 */ 149 { 0x32, 0x0000 }, /* Speaker Mixer Control */
102 0, 150
103 0x0002, /* Input Control */ 151 { 0x36, 0x0079 }, /* Speaker Volume */
104 0x0050, /* PGA Gain */ 152
105 0, 153 { 0x38, 0x0000 }, /* Mono Mixer Control */
106 0x0002, /* ADC Boost Control */
107 0,
108 0x0002, /* Output Control */
109 0x0000, /* Speaker Mixer Control */
110 0,
111 0,
112 0,
113 0x0079, /* Speaker Volume */
114 0,
115 0x0000, /* Mono Mixer Control */
116}; 154};
117 155
118static const char *wm8940_companding[] = { "Off", "NC", "u-law", "A-law" }; 156static const char *wm8940_companding[] = { "Off", "NC", "u-law", "A-law" };
@@ -264,7 +302,7 @@ static const struct snd_soc_dapm_widget wm8940_dapm_widgets[] = {
264 SND_SOC_DAPM_INPUT("AUX"), 302 SND_SOC_DAPM_INPUT("AUX"),
265}; 303};
266 304
267static const struct snd_soc_dapm_route audio_map[] = { 305static const struct snd_soc_dapm_route wm8940_dapm_routes[] = {
268 /* Mono output mixer */ 306 /* Mono output mixer */
269 {"Mono Mixer", "PCM Playback Switch", "DAC"}, 307 {"Mono Mixer", "PCM Playback Switch", "DAC"},
270 {"Mono Mixer", "Aux Playback Switch", "Aux Input"}, 308 {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
@@ -296,21 +334,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
296 {"ADC", NULL, "Boost Mixer"}, 334 {"ADC", NULL, "Boost Mixer"},
297}; 335};
298 336
299static int wm8940_add_widgets(struct snd_soc_codec *codec)
300{
301 struct snd_soc_dapm_context *dapm = &codec->dapm;
302 int ret;
303
304 ret = snd_soc_dapm_new_controls(dapm, wm8940_dapm_widgets,
305 ARRAY_SIZE(wm8940_dapm_widgets));
306 if (ret)
307 goto error_ret;
308 ret = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
309
310error_ret:
311 return ret;
312}
313
314#define wm8940_reset(c) snd_soc_write(c, WM8940_SOFTRESET, 0); 337#define wm8940_reset(c) snd_soc_write(c, WM8940_SOFTRESET, 0);
315 338
316static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai, 339static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai,
@@ -446,6 +469,7 @@ static int wm8940_mute(struct snd_soc_dai *dai, int mute)
446static int wm8940_set_bias_level(struct snd_soc_codec *codec, 469static int wm8940_set_bias_level(struct snd_soc_codec *codec,
447 enum snd_soc_bias_level level) 470 enum snd_soc_bias_level level)
448{ 471{
472 struct wm8940_priv *wm8940 = snd_soc_codec_get_drvdata(codec);
449 u16 val; 473 u16 val;
450 u16 pwr_reg = snd_soc_read(codec, WM8940_POWER1) & 0x1F0; 474 u16 pwr_reg = snd_soc_read(codec, WM8940_POWER1) & 0x1F0;
451 int ret = 0; 475 int ret = 0;
@@ -469,7 +493,7 @@ static int wm8940_set_bias_level(struct snd_soc_codec *codec,
469 break; 493 break;
470 case SND_SOC_BIAS_STANDBY: 494 case SND_SOC_BIAS_STANDBY:
471 if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { 495 if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
472 ret = snd_soc_cache_sync(codec); 496 ret = regcache_sync(wm8940->regmap);
473 if (ret < 0) { 497 if (ret < 0) {
474 dev_err(codec->dev, "Failed to sync cache: %d\n", ret); 498 dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
475 return ret; 499 return ret;
@@ -684,12 +708,11 @@ static int wm8940_resume(struct snd_soc_codec *codec)
684 708
685static int wm8940_probe(struct snd_soc_codec *codec) 709static int wm8940_probe(struct snd_soc_codec *codec)
686{ 710{
687 struct wm8940_priv *wm8940 = snd_soc_codec_get_drvdata(codec);
688 struct wm8940_setup_data *pdata = codec->dev->platform_data; 711 struct wm8940_setup_data *pdata = codec->dev->platform_data;
689 int ret; 712 int ret;
690 u16 reg; 713 u16 reg;
691 714
692 ret = snd_soc_codec_set_cache_io(codec, 8, 16, wm8940->control_type); 715 ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP);
693 if (ret < 0) { 716 if (ret < 0) {
694 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); 717 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
695 return ret; 718 return ret;
@@ -716,11 +739,6 @@ static int wm8940_probe(struct snd_soc_codec *codec)
716 return ret; 739 return ret;
717 } 740 }
718 741
719 ret = snd_soc_add_codec_controls(codec, wm8940_snd_controls,
720 ARRAY_SIZE(wm8940_snd_controls));
721 if (ret)
722 return ret;
723 ret = wm8940_add_widgets(codec);
724 return ret; 742 return ret;
725} 743}
726 744
@@ -736,10 +754,24 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8940 = {
736 .suspend = wm8940_suspend, 754 .suspend = wm8940_suspend,
737 .resume = wm8940_resume, 755 .resume = wm8940_resume,
738 .set_bias_level = wm8940_set_bias_level, 756 .set_bias_level = wm8940_set_bias_level,
739 .reg_cache_size = ARRAY_SIZE(wm8940_reg_defaults), 757 .controls = wm8940_snd_controls,
740 .reg_word_size = sizeof(u16), 758 .num_controls = ARRAY_SIZE(wm8940_snd_controls),
741 .reg_cache_default = wm8940_reg_defaults, 759 .dapm_widgets = wm8940_dapm_widgets,
742 .volatile_register = wm8940_volatile_register, 760 .num_dapm_widgets = ARRAY_SIZE(wm8940_dapm_widgets),
761 .dapm_routes = wm8940_dapm_routes,
762 .num_dapm_routes = ARRAY_SIZE(wm8940_dapm_routes),
763};
764
765static const struct regmap_config wm8940_regmap = {
766 .reg_bits = 8,
767 .val_bits = 16,
768
769 .max_register = WM8940_MONOMIX,
770 .reg_defaults = wm8940_reg_defaults,
771 .num_reg_defaults = ARRAY_SIZE(wm8940_reg_defaults),
772
773 .readable_reg = wm8940_readable_register,
774 .volatile_reg = wm8940_volatile_register,
743}; 775};
744 776
745static int wm8940_i2c_probe(struct i2c_client *i2c, 777static int wm8940_i2c_probe(struct i2c_client *i2c,
@@ -753,8 +785,11 @@ static int wm8940_i2c_probe(struct i2c_client *i2c,
753 if (wm8940 == NULL) 785 if (wm8940 == NULL)
754 return -ENOMEM; 786 return -ENOMEM;
755 787
788 wm8940->regmap = devm_regmap_init_i2c(i2c, &wm8940_regmap);
789 if (IS_ERR(wm8940->regmap))
790 return PTR_ERR(wm8940->regmap);
791
756 i2c_set_clientdata(i2c, wm8940); 792 i2c_set_clientdata(i2c, wm8940);
757 wm8940->control_type = SND_SOC_I2C;
758 793
759 ret = snd_soc_register_codec(&i2c->dev, 794 ret = snd_soc_register_codec(&i2c->dev,
760 &soc_codec_dev_wm8940, &wm8940_dai, 1); 795 &soc_codec_dev_wm8940, &wm8940_dai, 1);