mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-17 09:43:59 +08:00
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>
This commit is contained in:
parent
2809cb84d1
commit
c754639a29
@ -99,54 +99,93 @@ static int output_select_put(struct snd_kcontrol *ctl,
|
|||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hp_volume_offset_info(struct snd_kcontrol *ctl,
|
/* CS4245 Headphone Channels A&B Volume Control */
|
||||||
struct snd_ctl_elem_info *info)
|
|
||||||
{
|
|
||||||
static const char *const names[3] = {
|
|
||||||
"< 64 ohms", "64-150 ohms", "150-300 ohms"
|
|
||||||
};
|
|
||||||
|
|
||||||
return snd_ctl_enum_info(info, 1, 3, names);
|
static int hp_stereo_volume_info(struct snd_kcontrol *ctl,
|
||||||
|
struct snd_ctl_elem_info *info)
|
||||||
|
{
|
||||||
|
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||||
|
info->count = 2;
|
||||||
|
info->value.integer.min = 0;
|
||||||
|
info->value.integer.max = 255;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hp_volume_offset_get(struct snd_kcontrol *ctl,
|
static int hp_stereo_volume_get(struct snd_kcontrol *ctl,
|
||||||
struct snd_ctl_elem_value *value)
|
struct snd_ctl_elem_value *val)
|
||||||
{
|
{
|
||||||
struct oxygen *chip = ctl->private_data;
|
struct oxygen *chip = ctl->private_data;
|
||||||
struct dg *data = chip->model_data;
|
struct dg *data = chip->model_data;
|
||||||
|
unsigned int tmp;
|
||||||
|
|
||||||
mutex_lock(&chip->mutex);
|
mutex_lock(&chip->mutex);
|
||||||
if (data->hp_vol_att > 2 * 7)
|
tmp = (~data->cs4245_shadow[CS4245_DAC_A_CTRL]) & 255;
|
||||||
value->value.enumerated.item[0] = 0;
|
val->value.integer.value[0] = tmp;
|
||||||
else if (data->hp_vol_att > 0)
|
tmp = (~data->cs4245_shadow[CS4245_DAC_B_CTRL]) & 255;
|
||||||
value->value.enumerated.item[0] = 1;
|
val->value.integer.value[1] = tmp;
|
||||||
else
|
|
||||||
value->value.enumerated.item[0] = 2;
|
|
||||||
mutex_unlock(&chip->mutex);
|
mutex_unlock(&chip->mutex);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hp_volume_offset_put(struct snd_kcontrol *ctl,
|
static int hp_stereo_volume_put(struct snd_kcontrol *ctl,
|
||||||
struct snd_ctl_elem_value *value)
|
struct snd_ctl_elem_value *val)
|
||||||
{
|
{
|
||||||
static const s8 atts[3] = { 2 * 16, 2 * 7, 0 };
|
|
||||||
struct oxygen *chip = ctl->private_data;
|
struct oxygen *chip = ctl->private_data;
|
||||||
struct dg *data = chip->model_data;
|
struct dg *data = chip->model_data;
|
||||||
s8 att;
|
int ret;
|
||||||
|
int changed = 0;
|
||||||
|
long new1 = val->value.integer.value[0];
|
||||||
|
long new2 = val->value.integer.value[1];
|
||||||
|
|
||||||
|
if ((new1 > 255) || (new1 < 0) || (new2 > 255) || (new2 < 0))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
mutex_lock(&chip->mutex);
|
||||||
|
if ((data->cs4245_shadow[CS4245_DAC_A_CTRL] != ~new1) ||
|
||||||
|
(data->cs4245_shadow[CS4245_DAC_B_CTRL] != ~new2)) {
|
||||||
|
data->cs4245_shadow[CS4245_DAC_A_CTRL] = ~new1;
|
||||||
|
data->cs4245_shadow[CS4245_DAC_B_CTRL] = ~new2;
|
||||||
|
ret = cs4245_write_spi(chip, CS4245_DAC_A_CTRL);
|
||||||
|
if (ret >= 0)
|
||||||
|
ret = cs4245_write_spi(chip, CS4245_DAC_B_CTRL);
|
||||||
|
changed = ret >= 0 ? 1 : ret;
|
||||||
|
}
|
||||||
|
mutex_unlock(&chip->mutex);
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Headphone Mute */
|
||||||
|
|
||||||
|
static int hp_mute_get(struct snd_kcontrol *ctl,
|
||||||
|
struct snd_ctl_elem_value *val)
|
||||||
|
{
|
||||||
|
struct oxygen *chip = ctl->private_data;
|
||||||
|
struct dg *data = chip->model_data;
|
||||||
|
|
||||||
|
mutex_lock(&chip->mutex);
|
||||||
|
val->value.integer.value[0] =
|
||||||
|
!(data->cs4245_shadow[CS4245_DAC_CTRL_1] & CS4245_MUTE_DAC);
|
||||||
|
mutex_unlock(&chip->mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hp_mute_put(struct snd_kcontrol *ctl,
|
||||||
|
struct snd_ctl_elem_value *val)
|
||||||
|
{
|
||||||
|
struct oxygen *chip = ctl->private_data;
|
||||||
|
struct dg *data = chip->model_data;
|
||||||
|
int ret;
|
||||||
int changed;
|
int changed;
|
||||||
|
|
||||||
if (value->value.enumerated.item[0] > 2)
|
if (val->value.integer.value[0] > 1)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
att = atts[value->value.enumerated.item[0]];
|
|
||||||
mutex_lock(&chip->mutex);
|
mutex_lock(&chip->mutex);
|
||||||
changed = att != data->hp_vol_att;
|
data->cs4245_shadow[CS4245_DAC_CTRL_1] &= ~CS4245_MUTE_DAC;
|
||||||
if (changed) {
|
data->cs4245_shadow[CS4245_DAC_CTRL_1] |=
|
||||||
data->hp_vol_att = att;
|
(~val->value.integer.value[0] << 2) & CS4245_MUTE_DAC;
|
||||||
if (data->output_sel) {
|
ret = cs4245_write_spi(chip, CS4245_DAC_CTRL_1);
|
||||||
cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att);
|
changed = ret >= 0 ? 1 : ret;
|
||||||
cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mutex_unlock(&chip->mutex);
|
mutex_unlock(&chip->mutex);
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
@ -312,6 +351,7 @@ static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
|
|||||||
.tlv = { .p = cs4245_pga_db_scale }, \
|
.tlv = { .p = cs4245_pga_db_scale }, \
|
||||||
.private_value = index, \
|
.private_value = index, \
|
||||||
}
|
}
|
||||||
|
static const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0);
|
||||||
static const struct snd_kcontrol_new dg_controls[] = {
|
static const struct snd_kcontrol_new dg_controls[] = {
|
||||||
{
|
{
|
||||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||||
@ -322,10 +362,21 @@ static const struct snd_kcontrol_new dg_controls[] = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||||
.name = "Headphones Impedance Playback Enum",
|
.name = "Headphone Playback Volume",
|
||||||
.info = hp_volume_offset_info,
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||||
.get = hp_volume_offset_get,
|
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||||
.put = hp_volume_offset_put,
|
.info = hp_stereo_volume_info,
|
||||||
|
.get = hp_stereo_volume_get,
|
||||||
|
.put = hp_stereo_volume_put,
|
||||||
|
.tlv = { .p = hp_db_scale, },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||||
|
.name = "Headphone Playback Switch",
|
||||||
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||||
|
.info = snd_ctl_boolean_mono_info,
|
||||||
|
.get = hp_mute_get,
|
||||||
|
.put = hp_mute_put,
|
||||||
},
|
},
|
||||||
INPUT_VOLUME("Mic Capture Volume", 0),
|
INPUT_VOLUME("Mic Capture Volume", 0),
|
||||||
INPUT_VOLUME("Aux Capture Volume", 1),
|
INPUT_VOLUME("Aux Capture Volume", 1),
|
||||||
|
Loading…
Reference in New Issue
Block a user