diff options
Diffstat (limited to 'sound/soc/codecs/wm8960.c')
-rw-r--r-- | sound/soc/codecs/wm8960.c | 99 |
1 files changed, 91 insertions, 8 deletions
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 7233cc68435a..3c6ee61f6c95 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c | |||
@@ -79,12 +79,13 @@ 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) |
85 | 87 | ||
86 | /* enumerated controls */ | 88 | /* enumerated controls */ |
87 | static const char *wm8960_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; | ||
88 | static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted", | 89 | static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted", |
89 | "Right Inverted", "Stereo Inversion"}; | 90 | "Right Inverted", "Stereo Inversion"}; |
90 | static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"}; | 91 | static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"}; |
@@ -93,7 +94,6 @@ static const char *wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"}; | |||
93 | static const char *wm8960_alcmode[] = {"ALC", "Limiter"}; | 94 | static const char *wm8960_alcmode[] = {"ALC", "Limiter"}; |
94 | 95 | ||
95 | static const struct soc_enum wm8960_enum[] = { | 96 | static const struct soc_enum wm8960_enum[] = { |
96 | SOC_ENUM_SINGLE(WM8960_DACCTL1, 1, 4, wm8960_deemph), | ||
97 | SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity), | 97 | SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity), |
98 | SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity), | 98 | SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity), |
99 | SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff), | 99 | SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff), |
@@ -102,6 +102,59 @@ static const struct soc_enum wm8960_enum[] = { | |||
102 | SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode), | 102 | SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode), |
103 | }; | 103 | }; |
104 | 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 | |||
105 | static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0); | 158 | static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0); |
106 | static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1); | 159 | static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1); |
107 | static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0); | 160 | static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0); |
@@ -131,23 +184,24 @@ SOC_SINGLE("Speaker DC Volume", WM8960_CLASSD3, 3, 5, 0), | |||
131 | SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0), | 184 | SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0), |
132 | 185 | ||
133 | SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0), | 186 | SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0), |
134 | SOC_ENUM("ADC Polarity", wm8960_enum[1]), | 187 | SOC_ENUM("ADC Polarity", wm8960_enum[0]), |
135 | SOC_ENUM("Playback De-emphasis", wm8960_enum[0]), | ||
136 | 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), |
137 | 189 | ||
138 | 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), | ||
139 | 193 | ||
140 | SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[3]), | 194 | SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[2]), |
141 | SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[4]), | 195 | SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[3]), |
142 | SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0), | 196 | SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0), |
143 | SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0), | 197 | SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0), |
144 | 198 | ||
145 | SOC_ENUM("ALC Function", wm8960_enum[5]), | 199 | SOC_ENUM("ALC Function", wm8960_enum[4]), |
146 | SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0), | 200 | SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0), |
147 | SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1), | 201 | SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1), |
148 | SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0), | 202 | SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0), |
149 | SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0), | 203 | SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0), |
150 | SOC_ENUM("ALC Mode", wm8960_enum[6]), | 204 | SOC_ENUM("ALC Mode", wm8960_enum[5]), |
151 | SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0), | 205 | SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0), |
152 | SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0), | 206 | SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0), |
153 | 207 | ||
@@ -433,6 +487,21 @@ static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
433 | return 0; | 487 | return 0; |
434 | } | 488 | } |
435 | 489 | ||
490 | static struct { | ||
491 | int rate; | ||
492 | unsigned int val; | ||
493 | } alc_rates[] = { | ||
494 | { 48000, 0 }, | ||
495 | { 44100, 0 }, | ||
496 | { 32000, 1 }, | ||
497 | { 22050, 2 }, | ||
498 | { 24000, 2 }, | ||
499 | { 16000, 3 }, | ||
500 | { 11250, 4 }, | ||
501 | { 12000, 4 }, | ||
502 | { 8000, 5 }, | ||
503 | }; | ||
504 | |||
436 | static int wm8960_hw_params(struct snd_pcm_substream *substream, | 505 | static int wm8960_hw_params(struct snd_pcm_substream *substream, |
437 | struct snd_pcm_hw_params *params, | 506 | struct snd_pcm_hw_params *params, |
438 | struct snd_soc_dai *dai) | 507 | struct snd_soc_dai *dai) |
@@ -440,7 +509,9 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, | |||
440 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 509 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
441 | struct snd_soc_device *socdev = rtd->socdev; | 510 | struct snd_soc_device *socdev = rtd->socdev; |
442 | struct snd_soc_codec *codec = socdev->card->codec; | 511 | struct snd_soc_codec *codec = socdev->card->codec; |
512 | struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); | ||
443 | u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; | 513 | u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; |
514 | int i; | ||
444 | 515 | ||
445 | /* bit size */ | 516 | /* bit size */ |
446 | switch (params_format(params)) { | 517 | switch (params_format(params)) { |
@@ -454,6 +525,18 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, | |||
454 | break; | 525 | break; |
455 | } | 526 | } |
456 | 527 | ||
528 | /* Update filters for the new rate */ | ||
529 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
530 | wm8960->playback_fs = params_rate(params); | ||
531 | wm8960_set_deemph(codec); | ||
532 | } else { | ||
533 | for (i = 0; i < ARRAY_SIZE(alc_rates); i++) | ||
534 | if (alc_rates[i].rate == params_rate(params)) | ||
535 | snd_soc_update_bits(codec, | ||
536 | WM8960_ADDCTL3, 0x7, | ||
537 | alc_rates[i].val); | ||
538 | } | ||
539 | |||
457 | /* set iface */ | 540 | /* set iface */ |
458 | snd_soc_write(codec, WM8960_IFACE1, iface); | 541 | snd_soc_write(codec, WM8960_IFACE1, iface); |
459 | return 0; | 542 | return 0; |