diff options
Diffstat (limited to 'sound/soc/codecs/wm9712.c')
-rw-r--r-- | sound/soc/codecs/wm9712.c | 209 |
1 files changed, 129 insertions, 80 deletions
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index c5eb746087b4..52a211be5b47 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c | |||
@@ -23,6 +23,12 @@ | |||
23 | #include <sound/tlv.h> | 23 | #include <sound/tlv.h> |
24 | #include "wm9712.h" | 24 | #include "wm9712.h" |
25 | 25 | ||
26 | struct wm9712_priv { | ||
27 | struct snd_ac97 *ac97; | ||
28 | unsigned int hp_mixer[2]; | ||
29 | struct mutex lock; | ||
30 | }; | ||
31 | |||
26 | static unsigned int ac97_read(struct snd_soc_codec *codec, | 32 | static unsigned int ac97_read(struct snd_soc_codec *codec, |
27 | unsigned int reg); | 33 | unsigned int reg); |
28 | static int ac97_write(struct snd_soc_codec *codec, | 34 | static int ac97_write(struct snd_soc_codec *codec, |
@@ -48,12 +54,10 @@ static const u16 wm9712_reg[] = { | |||
48 | 0x0000, 0x0000, 0x0000, 0x0000, /* 6e */ | 54 | 0x0000, 0x0000, 0x0000, 0x0000, /* 6e */ |
49 | 0x0000, 0x0000, 0x0000, 0x0006, /* 76 */ | 55 | 0x0000, 0x0000, 0x0000, 0x0006, /* 76 */ |
50 | 0x0001, 0x0000, 0x574d, 0x4c12, /* 7e */ | 56 | 0x0001, 0x0000, 0x574d, 0x4c12, /* 7e */ |
51 | 0x0000, 0x0000 /* virtual hp mixers */ | ||
52 | }; | 57 | }; |
53 | 58 | ||
54 | /* virtual HP mixers regs */ | 59 | #define HPL_MIXER 0x0 |
55 | #define HPL_MIXER 0x80 | 60 | #define HPR_MIXER 0x1 |
56 | #define HPR_MIXER 0x82 | ||
57 | 61 | ||
58 | static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"}; | 62 | static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"}; |
59 | static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"}; | 63 | static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"}; |
@@ -157,75 +161,108 @@ SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv), | |||
157 | SOC_SINGLE_TLV("Mic Boost Volume", AC97_MIC, 7, 1, 0, boost_tlv), | 161 | SOC_SINGLE_TLV("Mic Boost Volume", AC97_MIC, 7, 1, 0, boost_tlv), |
158 | }; | 162 | }; |
159 | 163 | ||
164 | static const unsigned int wm9712_mixer_mute_regs[] = { | ||
165 | AC97_VIDEO, | ||
166 | AC97_PCM, | ||
167 | AC97_LINE, | ||
168 | AC97_PHONE, | ||
169 | AC97_CD, | ||
170 | AC97_PC_BEEP, | ||
171 | }; | ||
172 | |||
160 | /* We have to create a fake left and right HP mixers because | 173 | /* We have to create a fake left and right HP mixers because |
161 | * the codec only has a single control that is shared by both channels. | 174 | * the codec only has a single control that is shared by both channels. |
162 | * This makes it impossible to determine the audio path. | 175 | * This makes it impossible to determine the audio path. |
163 | */ | 176 | */ |
164 | static int mixer_event(struct snd_soc_dapm_widget *w, | 177 | static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, |
165 | struct snd_kcontrol *k, int event) | 178 | struct snd_ctl_elem_value *ucontrol) |
166 | { | 179 | { |
167 | u16 l, r, beep, line, phone, mic, pcm, aux; | 180 | struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); |
168 | 181 | struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); | |
169 | l = ac97_read(w->codec, HPL_MIXER); | 182 | struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); |
170 | r = ac97_read(w->codec, HPR_MIXER); | 183 | unsigned int val = ucontrol->value.enumerated.item[0]; |
171 | beep = ac97_read(w->codec, AC97_PC_BEEP); | 184 | struct soc_mixer_control *mc = |
172 | mic = ac97_read(w->codec, AC97_VIDEO); | 185 | (struct soc_mixer_control *)kcontrol->private_value; |
173 | phone = ac97_read(w->codec, AC97_PHONE); | 186 | unsigned int mixer, mask, shift, old; |
174 | line = ac97_read(w->codec, AC97_LINE); | 187 | struct snd_soc_dapm_update update; |
175 | pcm = ac97_read(w->codec, AC97_PCM); | 188 | bool change; |
176 | aux = ac97_read(w->codec, AC97_CD); | 189 | |
177 | 190 | mixer = mc->shift >> 8; | |
178 | if (l & 0x1 || r & 0x1) | 191 | shift = mc->shift & 0xff; |
179 | ac97_write(w->codec, AC97_VIDEO, mic & 0x7fff); | 192 | mask = 1 << shift; |
193 | |||
194 | mutex_lock(&wm9712->lock); | ||
195 | old = wm9712->hp_mixer[mixer]; | ||
196 | if (ucontrol->value.enumerated.item[0]) | ||
197 | wm9712->hp_mixer[mixer] |= mask; | ||
180 | else | 198 | else |
181 | ac97_write(w->codec, AC97_VIDEO, mic | 0x8000); | 199 | wm9712->hp_mixer[mixer] &= ~mask; |
200 | |||
201 | change = old != wm9712->hp_mixer[mixer]; | ||
202 | if (change) { | ||
203 | update.kcontrol = kcontrol; | ||
204 | update.reg = wm9712_mixer_mute_regs[shift]; | ||
205 | update.mask = 0x8000; | ||
206 | if ((wm9712->hp_mixer[0] & mask) || | ||
207 | (wm9712->hp_mixer[1] & mask)) | ||
208 | update.val = 0x0; | ||
209 | else | ||
210 | update.val = 0x8000; | ||
211 | |||
212 | snd_soc_dapm_mixer_update_power(dapm, kcontrol, val, | ||
213 | &update); | ||
214 | } | ||
182 | 215 | ||
183 | if (l & 0x2 || r & 0x2) | 216 | mutex_unlock(&wm9712->lock); |
184 | ac97_write(w->codec, AC97_PCM, pcm & 0x7fff); | ||
185 | else | ||
186 | ac97_write(w->codec, AC97_PCM, pcm | 0x8000); | ||
187 | 217 | ||
188 | if (l & 0x4 || r & 0x4) | 218 | return change; |
189 | ac97_write(w->codec, AC97_LINE, line & 0x7fff); | 219 | } |
190 | else | ||
191 | ac97_write(w->codec, AC97_LINE, line | 0x8000); | ||
192 | 220 | ||
193 | if (l & 0x8 || r & 0x8) | 221 | static int wm9712_hp_mixer_get(struct snd_kcontrol *kcontrol, |
194 | ac97_write(w->codec, AC97_PHONE, phone & 0x7fff); | 222 | struct snd_ctl_elem_value *ucontrol) |
195 | else | 223 | { |
196 | ac97_write(w->codec, AC97_PHONE, phone | 0x8000); | 224 | struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); |
225 | struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); | ||
226 | struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); | ||
227 | struct soc_mixer_control *mc = | ||
228 | (struct soc_mixer_control *)kcontrol->private_value; | ||
229 | unsigned int shift, mixer; | ||
197 | 230 | ||
198 | if (l & 0x10 || r & 0x10) | 231 | mixer = mc->shift >> 8; |
199 | ac97_write(w->codec, AC97_CD, aux & 0x7fff); | 232 | shift = mc->shift & 0xff; |
200 | else | ||
201 | ac97_write(w->codec, AC97_CD, aux | 0x8000); | ||
202 | 233 | ||
203 | if (l & 0x20 || r & 0x20) | 234 | ucontrol->value.enumerated.item[0] = |
204 | ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff); | 235 | (wm9712->hp_mixer[mixer] >> shift) & 1; |
205 | else | ||
206 | ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000); | ||
207 | 236 | ||
208 | return 0; | 237 | return 0; |
209 | } | 238 | } |
210 | 239 | ||
240 | #define WM9712_HP_MIXER_CTRL(xname, xmixer, xshift) { \ | ||
241 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | ||
242 | .info = snd_soc_info_volsw, \ | ||
243 | .get = wm9712_hp_mixer_get, .put = wm9712_hp_mixer_put, \ | ||
244 | .private_value = SOC_SINGLE_VALUE(SND_SOC_NOPM, \ | ||
245 | (xmixer << 8) | xshift, 1, 0, 0) \ | ||
246 | } | ||
247 | |||
211 | /* Left Headphone Mixers */ | 248 | /* Left Headphone Mixers */ |
212 | static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = { | 249 | static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = { |
213 | SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPL_MIXER, 5, 1, 0), | 250 | WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPL_MIXER, 5), |
214 | SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 4, 1, 0), | 251 | WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 4), |
215 | SOC_DAPM_SINGLE("Phone Bypass Switch", HPL_MIXER, 3, 1, 0), | 252 | WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPL_MIXER, 3), |
216 | SOC_DAPM_SINGLE("Line Bypass Switch", HPL_MIXER, 2, 1, 0), | 253 | WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPL_MIXER, 2), |
217 | SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 1, 1, 0), | 254 | WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 1), |
218 | SOC_DAPM_SINGLE("Mic Sidetone Switch", HPL_MIXER, 0, 1, 0), | 255 | WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPL_MIXER, 0), |
219 | }; | 256 | }; |
220 | 257 | ||
221 | /* Right Headphone Mixers */ | 258 | /* Right Headphone Mixers */ |
222 | static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = { | 259 | static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = { |
223 | SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPR_MIXER, 5, 1, 0), | 260 | WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPR_MIXER, 5), |
224 | SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 4, 1, 0), | 261 | WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 4), |
225 | SOC_DAPM_SINGLE("Phone Bypass Switch", HPR_MIXER, 3, 1, 0), | 262 | WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPR_MIXER, 3), |
226 | SOC_DAPM_SINGLE("Line Bypass Switch", HPR_MIXER, 2, 1, 0), | 263 | WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPR_MIXER, 2), |
227 | SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 1, 1, 0), | 264 | WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 1), |
228 | SOC_DAPM_SINGLE("Mic Sidetone Switch", HPR_MIXER, 0, 1, 0), | 265 | WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPR_MIXER, 0), |
229 | }; | 266 | }; |
230 | 267 | ||
231 | /* Speaker Mixer */ | 268 | /* Speaker Mixer */ |
@@ -299,12 +336,10 @@ SND_SOC_DAPM_MUX("Right Mic Select Source", SND_SOC_NOPM, 0, 0, | |||
299 | SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0, | 336 | SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0, |
300 | &wm9712_diff_sel_controls), | 337 | &wm9712_diff_sel_controls), |
301 | SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), | 338 | SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), |
302 | SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_INT_PAGING, 9, 1, | 339 | SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_INT_PAGING, 9, 1, |
303 | &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls), | 340 | &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls)), |
304 | mixer_event, SND_SOC_DAPM_POST_REG), | 341 | SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_INT_PAGING, 8, 1, |
305 | SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_INT_PAGING, 8, 1, | 342 | &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls)), |
306 | &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls), | ||
307 | mixer_event, SND_SOC_DAPM_POST_REG), | ||
308 | SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1, | 343 | SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1, |
309 | &wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)), | 344 | &wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)), |
310 | SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1, | 345 | SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1, |
@@ -450,12 +485,13 @@ static const struct snd_soc_dapm_route wm9712_audio_map[] = { | |||
450 | static unsigned int ac97_read(struct snd_soc_codec *codec, | 485 | static unsigned int ac97_read(struct snd_soc_codec *codec, |
451 | unsigned int reg) | 486 | unsigned int reg) |
452 | { | 487 | { |
488 | struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); | ||
453 | u16 *cache = codec->reg_cache; | 489 | u16 *cache = codec->reg_cache; |
454 | 490 | ||
455 | if (reg == AC97_RESET || reg == AC97_GPIO_STATUS || | 491 | if (reg == AC97_RESET || reg == AC97_GPIO_STATUS || |
456 | reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 || | 492 | reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 || |
457 | reg == AC97_REC_GAIN) | 493 | reg == AC97_REC_GAIN) |
458 | return soc_ac97_ops->read(codec->ac97, reg); | 494 | return soc_ac97_ops->read(wm9712->ac97, reg); |
459 | else { | 495 | else { |
460 | reg = reg >> 1; | 496 | reg = reg >> 1; |
461 | 497 | ||
@@ -469,10 +505,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec, | |||
469 | static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, | 505 | static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, |
470 | unsigned int val) | 506 | unsigned int val) |
471 | { | 507 | { |
508 | struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); | ||
472 | u16 *cache = codec->reg_cache; | 509 | u16 *cache = codec->reg_cache; |
473 | 510 | ||
474 | if (reg < 0x7c) | 511 | soc_ac97_ops->write(wm9712->ac97, reg, val); |
475 | soc_ac97_ops->write(codec->ac97, reg, val); | ||
476 | reg = reg >> 1; | 512 | reg = reg >> 1; |
477 | if (reg < (ARRAY_SIZE(wm9712_reg))) | 513 | if (reg < (ARRAY_SIZE(wm9712_reg))) |
478 | cache[reg] = val; | 514 | cache[reg] = val; |
@@ -532,7 +568,6 @@ static const struct snd_soc_dai_ops wm9712_dai_ops_aux = { | |||
532 | static struct snd_soc_dai_driver wm9712_dai[] = { | 568 | static struct snd_soc_dai_driver wm9712_dai[] = { |
533 | { | 569 | { |
534 | .name = "wm9712-hifi", | 570 | .name = "wm9712-hifi", |
535 | .ac97_control = 1, | ||
536 | .playback = { | 571 | .playback = { |
537 | .stream_name = "HiFi Playback", | 572 | .stream_name = "HiFi Playback", |
538 | .channels_min = 1, | 573 | .channels_min = 1, |
@@ -581,21 +616,23 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec, | |||
581 | 616 | ||
582 | static int wm9712_reset(struct snd_soc_codec *codec, int try_warm) | 617 | static int wm9712_reset(struct snd_soc_codec *codec, int try_warm) |
583 | { | 618 | { |
619 | struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); | ||
620 | |||
584 | if (try_warm && soc_ac97_ops->warm_reset) { | 621 | if (try_warm && soc_ac97_ops->warm_reset) { |
585 | soc_ac97_ops->warm_reset(codec->ac97); | 622 | soc_ac97_ops->warm_reset(wm9712->ac97); |
586 | if (ac97_read(codec, 0) == wm9712_reg[0]) | 623 | if (ac97_read(codec, 0) == wm9712_reg[0]) |
587 | return 1; | 624 | return 1; |
588 | } | 625 | } |
589 | 626 | ||
590 | soc_ac97_ops->reset(codec->ac97); | 627 | soc_ac97_ops->reset(wm9712->ac97); |
591 | if (soc_ac97_ops->warm_reset) | 628 | if (soc_ac97_ops->warm_reset) |
592 | soc_ac97_ops->warm_reset(codec->ac97); | 629 | soc_ac97_ops->warm_reset(wm9712->ac97); |
593 | if (ac97_read(codec, 0) != wm9712_reg[0]) | 630 | if (ac97_read(codec, 0) != wm9712_reg[0]) |
594 | goto err; | 631 | goto err; |
595 | return 0; | 632 | return 0; |
596 | 633 | ||
597 | err: | 634 | err: |
598 | printk(KERN_ERR "WM9712 AC97 reset failed\n"); | 635 | dev_err(codec->dev, "Failed to reset: AC97 link error\n"); |
599 | return -EIO; | 636 | return -EIO; |
600 | } | 637 | } |
601 | 638 | ||
@@ -607,14 +644,13 @@ static int wm9712_soc_suspend(struct snd_soc_codec *codec) | |||
607 | 644 | ||
608 | static int wm9712_soc_resume(struct snd_soc_codec *codec) | 645 | static int wm9712_soc_resume(struct snd_soc_codec *codec) |
609 | { | 646 | { |
647 | struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); | ||
610 | int i, ret; | 648 | int i, ret; |
611 | u16 *cache = codec->reg_cache; | 649 | u16 *cache = codec->reg_cache; |
612 | 650 | ||
613 | ret = wm9712_reset(codec, 1); | 651 | ret = wm9712_reset(codec, 1); |
614 | if (ret < 0) { | 652 | if (ret < 0) |
615 | printk(KERN_ERR "could not reset AC97 codec\n"); | ||
616 | return ret; | 653 | return ret; |
617 | } | ||
618 | 654 | ||
619 | wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 655 | wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
620 | 656 | ||
@@ -624,7 +660,7 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec) | |||
624 | if (i == AC97_INT_PAGING || i == AC97_POWERDOWN || | 660 | if (i == AC97_INT_PAGING || i == AC97_POWERDOWN || |
625 | (i > 0x58 && i != 0x5c)) | 661 | (i > 0x58 && i != 0x5c)) |
626 | continue; | 662 | continue; |
627 | soc_ac97_ops->write(codec->ac97, i, cache[i>>1]); | 663 | soc_ac97_ops->write(wm9712->ac97, i, cache[i>>1]); |
628 | } | 664 | } |
629 | } | 665 | } |
630 | 666 | ||
@@ -633,37 +669,37 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec) | |||
633 | 669 | ||
634 | static int wm9712_soc_probe(struct snd_soc_codec *codec) | 670 | static int wm9712_soc_probe(struct snd_soc_codec *codec) |
635 | { | 671 | { |
672 | struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); | ||
636 | int ret = 0; | 673 | int ret = 0; |
637 | 674 | ||
638 | ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0); | 675 | wm9712->ac97 = snd_soc_new_ac97_codec(codec); |
639 | if (ret < 0) { | 676 | if (IS_ERR(wm9712->ac97)) { |
640 | printk(KERN_ERR "wm9712: failed to register AC97 codec\n"); | 677 | ret = PTR_ERR(wm9712->ac97); |
678 | dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret); | ||
641 | return ret; | 679 | return ret; |
642 | } | 680 | } |
643 | 681 | ||
644 | ret = wm9712_reset(codec, 0); | 682 | ret = wm9712_reset(codec, 0); |
645 | if (ret < 0) { | 683 | if (ret < 0) |
646 | printk(KERN_ERR "Failed to reset WM9712: AC97 link error\n"); | ||
647 | goto reset_err; | 684 | goto reset_err; |
648 | } | ||
649 | 685 | ||
650 | /* set alc mux to none */ | 686 | /* set alc mux to none */ |
651 | ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000); | 687 | ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000); |
652 | 688 | ||
653 | wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 689 | wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
654 | snd_soc_add_codec_controls(codec, wm9712_snd_ac97_controls, | ||
655 | ARRAY_SIZE(wm9712_snd_ac97_controls)); | ||
656 | 690 | ||
657 | return 0; | 691 | return 0; |
658 | 692 | ||
659 | reset_err: | 693 | reset_err: |
660 | snd_soc_free_ac97_codec(codec); | 694 | snd_soc_free_ac97_codec(wm9712->ac97); |
661 | return ret; | 695 | return ret; |
662 | } | 696 | } |
663 | 697 | ||
664 | static int wm9712_soc_remove(struct snd_soc_codec *codec) | 698 | static int wm9712_soc_remove(struct snd_soc_codec *codec) |
665 | { | 699 | { |
666 | snd_soc_free_ac97_codec(codec); | 700 | struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); |
701 | |||
702 | snd_soc_free_ac97_codec(wm9712->ac97); | ||
667 | return 0; | 703 | return 0; |
668 | } | 704 | } |
669 | 705 | ||
@@ -679,6 +715,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = { | |||
679 | .reg_word_size = sizeof(u16), | 715 | .reg_word_size = sizeof(u16), |
680 | .reg_cache_step = 2, | 716 | .reg_cache_step = 2, |
681 | .reg_cache_default = wm9712_reg, | 717 | .reg_cache_default = wm9712_reg, |
718 | |||
719 | .controls = wm9712_snd_ac97_controls, | ||
720 | .num_controls = ARRAY_SIZE(wm9712_snd_ac97_controls), | ||
682 | .dapm_widgets = wm9712_dapm_widgets, | 721 | .dapm_widgets = wm9712_dapm_widgets, |
683 | .num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets), | 722 | .num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets), |
684 | .dapm_routes = wm9712_audio_map, | 723 | .dapm_routes = wm9712_audio_map, |
@@ -687,6 +726,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = { | |||
687 | 726 | ||
688 | static int wm9712_probe(struct platform_device *pdev) | 727 | static int wm9712_probe(struct platform_device *pdev) |
689 | { | 728 | { |
729 | struct wm9712_priv *wm9712; | ||
730 | |||
731 | wm9712 = devm_kzalloc(&pdev->dev, sizeof(*wm9712), GFP_KERNEL); | ||
732 | if (wm9712 == NULL) | ||
733 | return -ENOMEM; | ||
734 | |||
735 | mutex_init(&wm9712->lock); | ||
736 | |||
737 | platform_set_drvdata(pdev, wm9712); | ||
738 | |||
690 | return snd_soc_register_codec(&pdev->dev, | 739 | return snd_soc_register_codec(&pdev->dev, |
691 | &soc_codec_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai)); | 740 | &soc_codec_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai)); |
692 | } | 741 | } |