aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/oxygen
diff options
context:
space:
mode:
authorRoman Volkov <v1ron@mail.ru>2014-01-24 07:18:16 -0500
committerClemens Ladisch <clemens@ladisch.de>2014-01-29 14:45:50 -0500
commitc754639a29e8d00933ccd2d7ec41505d0074de8b (patch)
treeb21dc2482cb929a2bec642e573197148336530c7 /sound/pci/oxygen
parent2809cb84d1672b639a4a41a0fa077fb554699072 (diff)
ALSA: oxygen: Xonar DG(X): use headphone volume control
I tried both variants: volume control and impedance selector. In the first case one minus is that we can't change the volume of multichannel output without additional software volume control. However, I am using this variant for the last three months and this seems good. All multichannel speaker systems have internal amplifier with the volume control included, but not all headphones have this regulator. In the second case, my software volume control does not save the value after reboot. Signed-off-by: Roman Volkov <v1ron@mail.ru> Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Diffstat (limited to 'sound/pci/oxygen')
-rw-r--r--sound/pci/oxygen/xonar_dg_mixer.c115
1 files changed, 83 insertions, 32 deletions
diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c
index 6dfe63554742..0c310e7c3a04 100644
--- a/sound/pci/oxygen/xonar_dg_mixer.c
+++ b/sound/pci/oxygen/xonar_dg_mixer.c
@@ -99,54 +99,93 @@ static int output_select_put(struct snd_kcontrol *ctl,
99 return changed; 99 return changed;
100} 100}
101 101
102static int hp_volume_offset_info(struct snd_kcontrol *ctl, 102/* CS4245 Headphone Channels A&B Volume Control */
103 struct snd_ctl_elem_info *info) 103
104static int hp_stereo_volume_info(struct snd_kcontrol *ctl,
105 struct snd_ctl_elem_info *info)
104{ 106{
105 static const char *const names[3] = { 107 info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
106 "< 64 ohms", "64-150 ohms", "150-300 ohms" 108 info->count = 2;
107 }; 109 info->value.integer.min = 0;
110 info->value.integer.max = 255;
111 return 0;
112}
108 113
109 return snd_ctl_enum_info(info, 1, 3, names); 114static int hp_stereo_volume_get(struct snd_kcontrol *ctl,
115 struct snd_ctl_elem_value *val)
116{
117 struct oxygen *chip = ctl->private_data;
118 struct dg *data = chip->model_data;
119 unsigned int tmp;
120
121 mutex_lock(&chip->mutex);
122 tmp = (~data->cs4245_shadow[CS4245_DAC_A_CTRL]) & 255;
123 val->value.integer.value[0] = tmp;
124 tmp = (~data->cs4245_shadow[CS4245_DAC_B_CTRL]) & 255;
125 val->value.integer.value[1] = tmp;
126 mutex_unlock(&chip->mutex);
127 return 0;
110} 128}
111 129
112static int hp_volume_offset_get(struct snd_kcontrol *ctl, 130static int hp_stereo_volume_put(struct snd_kcontrol *ctl,
113 struct snd_ctl_elem_value *value) 131 struct snd_ctl_elem_value *val)
114{ 132{
115 struct oxygen *chip = ctl->private_data; 133 struct oxygen *chip = ctl->private_data;
116 struct dg *data = chip->model_data; 134 struct dg *data = chip->model_data;
135 int ret;
136 int changed = 0;
137 long new1 = val->value.integer.value[0];
138 long new2 = val->value.integer.value[1];
139
140 if ((new1 > 255) || (new1 < 0) || (new2 > 255) || (new2 < 0))
141 return -EINVAL;
117 142
118 mutex_lock(&chip->mutex); 143 mutex_lock(&chip->mutex);
119 if (data->hp_vol_att > 2 * 7) 144 if ((data->cs4245_shadow[CS4245_DAC_A_CTRL] != ~new1) ||
120 value->value.enumerated.item[0] = 0; 145 (data->cs4245_shadow[CS4245_DAC_B_CTRL] != ~new2)) {
121 else if (data->hp_vol_att > 0) 146 data->cs4245_shadow[CS4245_DAC_A_CTRL] = ~new1;
122 value->value.enumerated.item[0] = 1; 147 data->cs4245_shadow[CS4245_DAC_B_CTRL] = ~new2;
123 else 148 ret = cs4245_write_spi(chip, CS4245_DAC_A_CTRL);
124 value->value.enumerated.item[0] = 2; 149 if (ret >= 0)
150 ret = cs4245_write_spi(chip, CS4245_DAC_B_CTRL);
151 changed = ret >= 0 ? 1 : ret;
152 }
153 mutex_unlock(&chip->mutex);
154
155 return changed;
156}
157
158/* Headphone Mute */
159
160static int hp_mute_get(struct snd_kcontrol *ctl,
161 struct snd_ctl_elem_value *val)
162{
163 struct oxygen *chip = ctl->private_data;
164 struct dg *data = chip->model_data;
165
166 mutex_lock(&chip->mutex);
167 val->value.integer.value[0] =
168 !(data->cs4245_shadow[CS4245_DAC_CTRL_1] & CS4245_MUTE_DAC);
125 mutex_unlock(&chip->mutex); 169 mutex_unlock(&chip->mutex);
126 return 0; 170 return 0;
127} 171}
128 172
129static int hp_volume_offset_put(struct snd_kcontrol *ctl, 173static int hp_mute_put(struct snd_kcontrol *ctl,
130 struct snd_ctl_elem_value *value) 174 struct snd_ctl_elem_value *val)
131{ 175{
132 static const s8 atts[3] = { 2 * 16, 2 * 7, 0 };
133 struct oxygen *chip = ctl->private_data; 176 struct oxygen *chip = ctl->private_data;
134 struct dg *data = chip->model_data; 177 struct dg *data = chip->model_data;
135 s8 att; 178 int ret;
136 int changed; 179 int changed;
137 180
138 if (value->value.enumerated.item[0] > 2) 181 if (val->value.integer.value[0] > 1)
139 return -EINVAL; 182 return -EINVAL;
140 att = atts[value->value.enumerated.item[0]];
141 mutex_lock(&chip->mutex); 183 mutex_lock(&chip->mutex);
142 changed = att != data->hp_vol_att; 184 data->cs4245_shadow[CS4245_DAC_CTRL_1] &= ~CS4245_MUTE_DAC;
143 if (changed) { 185 data->cs4245_shadow[CS4245_DAC_CTRL_1] |=
144 data->hp_vol_att = att; 186 (~val->value.integer.value[0] << 2) & CS4245_MUTE_DAC;
145 if (data->output_sel) { 187 ret = cs4245_write_spi(chip, CS4245_DAC_CTRL_1);
146 cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att); 188 changed = ret >= 0 ? 1 : ret;
147 cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att);
148 }
149 }
150 mutex_unlock(&chip->mutex); 189 mutex_unlock(&chip->mutex);
151 return changed; 190 return changed;
152} 191}
@@ -312,6 +351,7 @@ static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
312 .tlv = { .p = cs4245_pga_db_scale }, \ 351 .tlv = { .p = cs4245_pga_db_scale }, \
313 .private_value = index, \ 352 .private_value = index, \
314} 353}
354static const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0);
315static const struct snd_kcontrol_new dg_controls[] = { 355static const struct snd_kcontrol_new dg_controls[] = {
316 { 356 {
317 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 357 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -322,10 +362,21 @@ static const struct snd_kcontrol_new dg_controls[] = {
322 }, 362 },
323 { 363 {
324 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 364 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
325 .name = "Headphones Impedance Playback Enum", 365 .name = "Headphone Playback Volume",
326 .info = hp_volume_offset_info, 366 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
327 .get = hp_volume_offset_get, 367 SNDRV_CTL_ELEM_ACCESS_TLV_READ,
328 .put = hp_volume_offset_put, 368 .info = hp_stereo_volume_info,
369 .get = hp_stereo_volume_get,
370 .put = hp_stereo_volume_put,
371 .tlv = { .p = hp_db_scale, },
372 },
373 {
374 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
375 .name = "Headphone Playback Switch",
376 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
377 .info = snd_ctl_boolean_mono_info,
378 .get = hp_mute_get,
379 .put = hp_mute_put,
329 }, 380 },
330 INPUT_VOLUME("Mic Capture Volume", 0), 381 INPUT_VOLUME("Mic Capture Volume", 0),
331 INPUT_VOLUME("Aux Capture Volume", 1), 382 INPUT_VOLUME("Aux Capture Volume", 1),