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>

authored by Roman Volkov and committed by Clemens Ladisch c754639a 2809cb84

+84 -33
+84 -33
sound/pci/oxygen/xonar_dg_mixer.c
··· 99 return changed; 100 } 101 102 - static int hp_volume_offset_info(struct snd_kcontrol *ctl, 103 - struct snd_ctl_elem_info *info) 104 - { 105 - static const char *const names[3] = { 106 - "< 64 ohms", "64-150 ohms", "150-300 ohms" 107 - }; 108 109 - return snd_ctl_enum_info(info, 1, 3, names); 110 } 111 112 - static int hp_volume_offset_get(struct snd_kcontrol *ctl, 113 - struct snd_ctl_elem_value *value) 114 { 115 struct oxygen *chip = ctl->private_data; 116 struct dg *data = chip->model_data; 117 118 mutex_lock(&chip->mutex); 119 - if (data->hp_vol_att > 2 * 7) 120 - value->value.enumerated.item[0] = 0; 121 - else if (data->hp_vol_att > 0) 122 - value->value.enumerated.item[0] = 1; 123 - else 124 - value->value.enumerated.item[0] = 2; 125 mutex_unlock(&chip->mutex); 126 return 0; 127 } 128 129 - static int hp_volume_offset_put(struct snd_kcontrol *ctl, 130 - struct snd_ctl_elem_value *value) 131 { 132 - static const s8 atts[3] = { 2 * 16, 2 * 7, 0 }; 133 struct oxygen *chip = ctl->private_data; 134 struct dg *data = chip->model_data; 135 - s8 att; 136 int changed; 137 138 - if (value->value.enumerated.item[0] > 2) 139 return -EINVAL; 140 - att = atts[value->value.enumerated.item[0]]; 141 mutex_lock(&chip->mutex); 142 - changed = att != data->hp_vol_att; 143 - if (changed) { 144 - data->hp_vol_att = att; 145 - if (data->output_sel) { 146 - cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att); 147 - cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att); 148 - } 149 - } 150 mutex_unlock(&chip->mutex); 151 return changed; 152 } ··· 351 .tlv = { .p = cs4245_pga_db_scale }, \ 352 .private_value = index, \ 353 } 354 static const struct snd_kcontrol_new dg_controls[] = { 355 { 356 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ··· 362 }, 363 { 364 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 365 - .name = "Headphones Impedance Playback Enum", 366 - .info = hp_volume_offset_info, 367 - .get = hp_volume_offset_get, 368 - .put = hp_volume_offset_put, 369 }, 370 INPUT_VOLUME("Mic Capture Volume", 0), 371 INPUT_VOLUME("Aux Capture Volume", 1),
··· 99 return changed; 100 } 101 102 + /* CS4245 Headphone Channels A&B Volume Control */ 103 104 + static int hp_stereo_volume_info(struct snd_kcontrol *ctl, 105 + struct snd_ctl_elem_info *info) 106 + { 107 + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 108 + info->count = 2; 109 + info->value.integer.min = 0; 110 + info->value.integer.max = 255; 111 + return 0; 112 } 113 114 + static 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; 128 } 129 130 + static int hp_stereo_volume_put(struct snd_kcontrol *ctl, 131 + struct snd_ctl_elem_value *val) 132 { 133 struct oxygen *chip = ctl->private_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; 142 + 143 + mutex_lock(&chip->mutex); 144 + if ((data->cs4245_shadow[CS4245_DAC_A_CTRL] != ~new1) || 145 + (data->cs4245_shadow[CS4245_DAC_B_CTRL] != ~new2)) { 146 + data->cs4245_shadow[CS4245_DAC_A_CTRL] = ~new1; 147 + data->cs4245_shadow[CS4245_DAC_B_CTRL] = ~new2; 148 + ret = cs4245_write_spi(chip, CS4245_DAC_A_CTRL); 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 + 160 + static 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); 169 + mutex_unlock(&chip->mutex); 170 + return 0; 171 + } 172 + 173 + static int hp_mute_put(struct snd_kcontrol *ctl, 174 + struct snd_ctl_elem_value *val) 175 + { 176 + struct oxygen *chip = ctl->private_data; 177 + struct dg *data = chip->model_data; 178 + int ret; 179 int changed; 180 181 + if (val->value.integer.value[0] > 1) 182 return -EINVAL; 183 mutex_lock(&chip->mutex); 184 + data->cs4245_shadow[CS4245_DAC_CTRL_1] &= ~CS4245_MUTE_DAC; 185 + data->cs4245_shadow[CS4245_DAC_CTRL_1] |= 186 + (~val->value.integer.value[0] << 2) & CS4245_MUTE_DAC; 187 + ret = cs4245_write_spi(chip, CS4245_DAC_CTRL_1); 188 + changed = ret >= 0 ? 1 : ret; 189 mutex_unlock(&chip->mutex); 190 return changed; 191 } ··· 312 .tlv = { .p = cs4245_pga_db_scale }, \ 313 .private_value = index, \ 314 } 315 + static const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0); 316 static const struct snd_kcontrol_new dg_controls[] = { 317 { 318 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ··· 322 }, 323 { 324 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 325 + .name = "Headphone Playback Volume", 326 + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 327 + SNDRV_CTL_ELEM_ACCESS_TLV_READ, 328 + .info = hp_stereo_volume_info, 329 + .get = hp_stereo_volume_get, 330 + .put = hp_stereo_volume_put, 331 + .tlv = { .p = hp_db_scale, }, 332 + }, 333 + { 334 + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 335 + .name = "Headphone Playback Switch", 336 + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 337 + .info = snd_ctl_boolean_mono_info, 338 + .get = hp_mute_get, 339 + .put = hp_mute_put, 340 }, 341 INPUT_VOLUME("Mic Capture Volume", 0), 342 INPUT_VOLUME("Aux Capture Volume", 1),