diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-12-02 06:44:00 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-12-02 07:00:20 -0500 |
commit | dd31b310b9104327fb6bf7d2fe3b0f0f6fde4dd7 (patch) | |
tree | e1d0bc618d30bbac3efddb9883e07ae05b119eeb | |
parent | 59f7297014b7e96779493f132eed04a3d44565df (diff) |
ASoC: Automatically manage WM8731 deemphasis
The deemphasis filter should be selected based on sample rate for
optimal performance.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
-rw-r--r-- | sound/soc/codecs/wm8731.c | 76 |
1 files changed, 71 insertions, 5 deletions
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 8003761a8e02..71122dc36826 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c | |||
@@ -46,6 +46,8 @@ struct wm8731_priv { | |||
46 | u16 reg_cache[WM8731_CACHEREGNUM]; | 46 | u16 reg_cache[WM8731_CACHEREGNUM]; |
47 | unsigned int sysclk; | 47 | unsigned int sysclk; |
48 | int sysclk_type; | 48 | int sysclk_type; |
49 | int playback_fs; | ||
50 | bool deemph; | ||
49 | }; | 51 | }; |
50 | 52 | ||
51 | 53 | ||
@@ -65,14 +67,73 @@ static const u16 wm8731_reg[WM8731_CACHEREGNUM] = { | |||
65 | 67 | ||
66 | static const char *wm8731_input_select[] = {"Line In", "Mic"}; | 68 | static const char *wm8731_input_select[] = {"Line In", "Mic"}; |
67 | 69 | ||
68 | static const char *wm8731_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; | ||
69 | |||
70 | static const struct soc_enum wm8731_insel_enum = | 70 | static const struct soc_enum wm8731_insel_enum = |
71 | SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select); | 71 | SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select); |
72 | 72 | ||
73 | static const struct soc_enum wm8731_deemph_enum = | 73 | static int wm8731_deemph[] = { 0, 32000, 44100, 48000 }; |
74 | SOC_ENUM_SINGLE(WM8731_APDIGI, 1, 4, wm8731_deemph); | 74 | |
75 | static int wm8731_set_deemph(struct snd_soc_codec *codec) | ||
76 | { | ||
77 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); | ||
78 | int val, i, best; | ||
79 | |||
80 | /* If we're using deemphasis select the nearest available sample | ||
81 | * rate. | ||
82 | */ | ||
83 | if (wm8731->deemph) { | ||
84 | best = 1; | ||
85 | for (i = 2; i < ARRAY_SIZE(wm8731_deemph); i++) { | ||
86 | if (abs(wm8731_deemph[i] - wm8731->playback_fs) < | ||
87 | abs(wm8731_deemph[best] - wm8731->playback_fs)) | ||
88 | best = i; | ||
89 | } | ||
90 | |||
91 | val = best << 1; | ||
92 | } else { | ||
93 | best = 0; | ||
94 | val = 0; | ||
95 | } | ||
96 | |||
97 | dev_dbg(codec->dev, "Set deemphasis %d (%dHz)\n", | ||
98 | best, wm8731_deemph[best]); | ||
75 | 99 | ||
100 | return snd_soc_update_bits(codec, WM8731_APDIGI, 0x6, val); | ||
101 | } | ||
102 | |||
103 | static int wm8731_get_deemph(struct snd_kcontrol *kcontrol, | ||
104 | struct snd_ctl_elem_value *ucontrol) | ||
105 | { | ||
106 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
107 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); | ||
108 | |||
109 | ucontrol->value.enumerated.item[0] = wm8731->deemph; | ||
110 | |||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, | ||
115 | struct snd_ctl_elem_value *ucontrol) | ||
116 | { | ||
117 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
118 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); | ||
119 | int deemph = ucontrol->value.enumerated.item[0]; | ||
120 | int ret = 0; | ||
121 | |||
122 | if (deemph > 1) | ||
123 | return -EINVAL; | ||
124 | |||
125 | mutex_lock(&codec->mutex); | ||
126 | if (wm8731->deemph != deemph) { | ||
127 | wm8731->deemph = deemph; | ||
128 | |||
129 | wm8731_set_deemph(codec); | ||
130 | |||
131 | ret = 1; | ||
132 | } | ||
133 | mutex_unlock(&codec->mutex); | ||
134 | |||
135 | return ret; | ||
136 | } | ||
76 | 137 | ||
77 | static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0); | 138 | static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0); |
78 | static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -1500, 300, 0); | 139 | static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -1500, 300, 0); |
@@ -99,7 +160,8 @@ SOC_SINGLE_TLV("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1, | |||
99 | SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1), | 160 | SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1), |
100 | SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0), | 161 | SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0), |
101 | 162 | ||
102 | SOC_ENUM("Playback De-emphasis", wm8731_deemph_enum), | 163 | SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0, |
164 | wm8731_get_deemph, wm8731_put_deemph), | ||
103 | }; | 165 | }; |
104 | 166 | ||
105 | /* Output Mixer */ | 167 | /* Output Mixer */ |
@@ -243,6 +305,8 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream, | |||
243 | u16 srate = (coeff_div[i].sr << 2) | | 305 | u16 srate = (coeff_div[i].sr << 2) | |
244 | (coeff_div[i].bosr << 1) | coeff_div[i].usb; | 306 | (coeff_div[i].bosr << 1) | coeff_div[i].usb; |
245 | 307 | ||
308 | wm8731->playback_fs = params_rate(params); | ||
309 | |||
246 | snd_soc_write(codec, WM8731_SRATE, srate); | 310 | snd_soc_write(codec, WM8731_SRATE, srate); |
247 | 311 | ||
248 | /* bit size */ | 312 | /* bit size */ |
@@ -257,6 +321,8 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream, | |||
257 | break; | 321 | break; |
258 | } | 322 | } |
259 | 323 | ||
324 | wm8731_set_deemph(codec); | ||
325 | |||
260 | snd_soc_write(codec, WM8731_IFACE, iface); | 326 | snd_soc_write(codec, WM8731_IFACE, iface); |
261 | return 0; | 327 | return 0; |
262 | } | 328 | } |