diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-07-05 00:58:16 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-07-05 10:41:18 -0400 |
commit | afd6d36a0ded1691c6710ebddabae06e5bb9583b (patch) | |
tree | 8f4017417a6994a44ec2c82e3daf454791a418b4 /sound/soc/codecs/wm8960.c | |
parent | 4faaa8d968df08bf2e75a481f99e7c5c1d0142ab (diff) |
ASoC: Automatically manage DAC deemphasis rate for WM8960
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'sound/soc/codecs/wm8960.c')
-rw-r--r-- | sound/soc/codecs/wm8960.c | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 6be3f4645b71..743d9a708a22 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c | |||
@@ -79,6 +79,8 @@ struct wm8960_priv { | |||
79 | struct snd_soc_dapm_widget *lout1; | 79 | struct snd_soc_dapm_widget *lout1; |
80 | struct snd_soc_dapm_widget *rout1; | 80 | struct snd_soc_dapm_widget *rout1; |
81 | struct snd_soc_dapm_widget *out3; | 81 | struct snd_soc_dapm_widget *out3; |
82 | bool deemph; | ||
83 | int playback_fs; | ||
82 | }; | 84 | }; |
83 | 85 | ||
84 | #define wm8960_reset(c) snd_soc_write(c, WM8960_RESET, 0) | 86 | #define wm8960_reset(c) snd_soc_write(c, WM8960_RESET, 0) |
@@ -100,6 +102,59 @@ static const struct soc_enum wm8960_enum[] = { | |||
100 | SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode), | 102 | SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode), |
101 | }; | 103 | }; |
102 | 104 | ||
105 | static const int deemph_settings[] = { 0, 32000, 44100, 48000 }; | ||
106 | |||
107 | static int wm8960_set_deemph(struct snd_soc_codec *codec) | ||
108 | { | ||
109 | struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); | ||
110 | int val, i, best; | ||
111 | |||
112 | /* If we're using deemphasis select the nearest available sample | ||
113 | * rate. | ||
114 | */ | ||
115 | if (wm8960->deemph) { | ||
116 | best = 1; | ||
117 | for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) { | ||
118 | if (abs(deemph_settings[i] - wm8960->playback_fs) < | ||
119 | abs(deemph_settings[best] - wm8960->playback_fs)) | ||
120 | best = i; | ||
121 | } | ||
122 | |||
123 | val = best << 1; | ||
124 | } else { | ||
125 | val = 0; | ||
126 | } | ||
127 | |||
128 | dev_dbg(codec->dev, "Set deemphasis %d\n", val); | ||
129 | |||
130 | return snd_soc_update_bits(codec, WM8960_DACCTL1, | ||
131 | 0x6, val); | ||
132 | } | ||
133 | |||
134 | static int wm8960_get_deemph(struct snd_kcontrol *kcontrol, | ||
135 | struct snd_ctl_elem_value *ucontrol) | ||
136 | { | ||
137 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
138 | struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); | ||
139 | |||
140 | return wm8960->deemph; | ||
141 | } | ||
142 | |||
143 | static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, | ||
144 | struct snd_ctl_elem_value *ucontrol) | ||
145 | { | ||
146 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
147 | struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); | ||
148 | int deemph = ucontrol->value.enumerated.item[0]; | ||
149 | |||
150 | if (deemph > 1) | ||
151 | return -EINVAL; | ||
152 | |||
153 | wm8960->deemph = deemph; | ||
154 | |||
155 | return wm8960_set_deemph(codec); | ||
156 | } | ||
157 | |||
103 | static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0); | 158 | static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0); |
104 | static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1); | 159 | static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1); |
105 | static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0); | 160 | static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0); |
@@ -133,6 +188,8 @@ SOC_ENUM("ADC Polarity", wm8960_enum[0]), | |||
133 | SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0), | 188 | SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0), |
134 | 189 | ||
135 | SOC_ENUM("DAC Polarity", wm8960_enum[2]), | 190 | SOC_ENUM("DAC Polarity", wm8960_enum[2]), |
191 | SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0, | ||
192 | wm8960_get_deemph, wm8960_put_deemph), | ||
136 | 193 | ||
137 | SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[2]), | 194 | SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[2]), |
138 | SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[3]), | 195 | SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[3]), |
@@ -437,6 +494,7 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, | |||
437 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 494 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
438 | struct snd_soc_device *socdev = rtd->socdev; | 495 | struct snd_soc_device *socdev = rtd->socdev; |
439 | struct snd_soc_codec *codec = socdev->card->codec; | 496 | struct snd_soc_codec *codec = socdev->card->codec; |
497 | struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); | ||
440 | u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; | 498 | u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; |
441 | 499 | ||
442 | /* bit size */ | 500 | /* bit size */ |
@@ -451,6 +509,12 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, | |||
451 | break; | 509 | break; |
452 | } | 510 | } |
453 | 511 | ||
512 | /* Update filters for the new rate */ | ||
513 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
514 | wm8960->playback_fs = params_rate(params); | ||
515 | wm8960_set_deemph(codec); | ||
516 | } | ||
517 | |||
454 | /* set iface */ | 518 | /* set iface */ |
455 | snd_soc_write(codec, WM8960_IFACE1, iface); | 519 | snd_soc_write(codec, WM8960_IFACE1, iface); |
456 | return 0; | 520 | return 0; |