diff options
Diffstat (limited to 'sound/soc/codecs/wm8940.c')
-rw-r--r-- | sound/soc/codecs/wm8940.c | 221 |
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 | ||
42 | struct wm8940_priv { | 42 | struct 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 | ||
47 | static int wm8940_volatile_register(struct snd_soc_codec *codec, | 47 | static 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 | |||
57 | static 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 | ||
58 | static u16 wm8940_reg_defaults[] = { | 106 | static 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 | ||
118 | static const char *wm8940_companding[] = { "Off", "NC", "u-law", "A-law" }; | 156 | static 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 | ||
267 | static const struct snd_soc_dapm_route audio_map[] = { | 305 | static 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 | ||
299 | static 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 | |||
310 | error_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 | ||
316 | static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai, | 339 | static 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) | |||
446 | static int wm8940_set_bias_level(struct snd_soc_codec *codec, | 469 | static 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 | ||
685 | static int wm8940_probe(struct snd_soc_codec *codec) | 709 | static 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 | |||
765 | static 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 | ||
745 | static int wm8940_i2c_probe(struct i2c_client *i2c, | 777 | static 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); |