aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm9712.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/wm9712.c')
-rw-r--r--sound/soc/codecs/wm9712.c209
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
26struct wm9712_priv {
27 struct snd_ac97 *ac97;
28 unsigned int hp_mixer[2];
29 struct mutex lock;
30};
31
26static unsigned int ac97_read(struct snd_soc_codec *codec, 32static unsigned int ac97_read(struct snd_soc_codec *codec,
27 unsigned int reg); 33 unsigned int reg);
28static int ac97_write(struct snd_soc_codec *codec, 34static 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
58static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"}; 62static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"};
59static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"}; 63static 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),
157SOC_SINGLE_TLV("Mic Boost Volume", AC97_MIC, 7, 1, 0, boost_tlv), 161SOC_SINGLE_TLV("Mic Boost Volume", AC97_MIC, 7, 1, 0, boost_tlv),
158}; 162};
159 163
164static 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 */
164static int mixer_event(struct snd_soc_dapm_widget *w, 177static 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) 221static 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 */
212static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = { 249static 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 */
222static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = { 259static 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,
299SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0, 336SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0,
300 &wm9712_diff_sel_controls), 337 &wm9712_diff_sel_controls),
301SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), 338SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
302SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_INT_PAGING, 9, 1, 339SND_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), 341SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_INT_PAGING, 8, 1,
305SND_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),
308SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1, 343SND_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)),
310SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1, 345SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1,
@@ -450,12 +485,13 @@ static const struct snd_soc_dapm_route wm9712_audio_map[] = {
450static unsigned int ac97_read(struct snd_soc_codec *codec, 485static 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,
469static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, 505static 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 = {
532static struct snd_soc_dai_driver wm9712_dai[] = { 568static 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
582static int wm9712_reset(struct snd_soc_codec *codec, int try_warm) 617static 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
597err: 634err:
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
608static int wm9712_soc_resume(struct snd_soc_codec *codec) 645static 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
634static int wm9712_soc_probe(struct snd_soc_codec *codec) 670static 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
659reset_err: 693reset_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
664static int wm9712_soc_remove(struct snd_soc_codec *codec) 698static 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
688static int wm9712_probe(struct platform_device *pdev) 727static 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}