Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fa5d8106 authored by Clemens Ladisch's avatar Clemens Ladisch Committed by Takashi Iwai
Browse files

[ALSA] oxygen: add monitor controls



Add controls to enable monitoring of the analog and digital inputs.

To allow monitoring after loading the driver when nothing has been
played back or recorded yet, the I2S input and outputs are initialized
to a valid configuration.

Signed-off-by: default avatarClemens Ladisch <clemens@ladisch.de>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 33fa724e
Loading
Loading
Loading
Loading
+8 −8
Original line number Original line Diff line number Diff line
@@ -267,20 +267,20 @@ static void oxygen_init(struct oxygen *chip)
		      (OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT));
		      (OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT));
	oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2);
	oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2);
	oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT,
	oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT,
		       OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
		       OXYGEN_RATE_48000 | chip->model->dac_i2s_format |
		       OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
		       OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
		       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
		       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
	oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
	oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
		       OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
		       OXYGEN_RATE_48000 | chip->model->adc_i2s_format |
		       OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
		       OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
		       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
		       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
	oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
	oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
		       OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
		       OXYGEN_RATE_48000 | chip->model->adc_i2s_format |
		       OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
		       OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
		       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
		       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
	oxygen_write16(chip, OXYGEN_I2S_C_FORMAT,
	oxygen_write16(chip, OXYGEN_I2S_C_FORMAT,
		       OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
		       OXYGEN_RATE_48000 | chip->model->adc_i2s_format |
		       OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
		       OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
		       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
		       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
	oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
	oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
			      OXYGEN_SPDIF_SENSE_MASK |
			      OXYGEN_SPDIF_SENSE_MASK |
+156 −0
Original line number Original line Diff line number Diff line
@@ -446,6 +446,50 @@ static int spdif_loopback_put(struct snd_kcontrol *ctl,
	return changed;
	return changed;
}
}


static int monitor_volume_info(struct snd_kcontrol *ctl,
			       struct snd_ctl_elem_info *info)
{
	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
	info->count = 1;
	info->value.integer.min = 0;
	info->value.integer.max = 1;
	return 0;
}

static int monitor_get(struct snd_kcontrol *ctl,
		       struct snd_ctl_elem_value *value)
{
	struct oxygen *chip = ctl->private_data;
	u8 bit = ctl->private_value;
	int invert = ctl->private_value & (1 << 8);

	value->value.integer.value[0] =
		!!invert ^ !!(oxygen_read8(chip, OXYGEN_ADC_MONITOR) & bit);
	return 0;
}

static int monitor_put(struct snd_kcontrol *ctl,
		       struct snd_ctl_elem_value *value)
{
	struct oxygen *chip = ctl->private_data;
	u8 bit = ctl->private_value;
	int invert = ctl->private_value & (1 << 8);
	u8 oldreg, newreg;
	int changed;

	spin_lock_irq(&chip->reg_lock);
	oldreg = oxygen_read8(chip, OXYGEN_ADC_MONITOR);
	if ((!!value->value.integer.value[0] ^ !!invert) != 0)
		newreg = oldreg | bit;
	else
		newreg = oldreg & ~bit;
	changed = newreg != oldreg;
	if (changed)
		oxygen_write8(chip, OXYGEN_ADC_MONITOR, newreg);
	spin_unlock_irq(&chip->reg_lock);
	return changed;
}

static int ac97_switch_get(struct snd_kcontrol *ctl,
static int ac97_switch_get(struct snd_kcontrol *ctl,
			   struct snd_ctl_elem_value *value)
			   struct snd_ctl_elem_value *value)
{
{
@@ -608,6 +652,7 @@ static int ac97_fp_rec_volume_put(struct snd_kcontrol *ctl,
		.private_value = ((codec) << 24) | (index), \
		.private_value = ((codec) << 24) | (index), \
	}
	}


static DECLARE_TLV_DB_SCALE(monitor_db_scale, -1000, 1000, 0);
static DECLARE_TLV_DB_SCALE(ac97_db_scale, -3450, 150, 0);
static DECLARE_TLV_DB_SCALE(ac97_db_scale, -3450, 150, 0);
static DECLARE_TLV_DB_SCALE(ac97_rec_db_scale, 0, 150, 0);
static DECLARE_TLV_DB_SCALE(ac97_rec_db_scale, 0, 150, 0);


@@ -692,6 +737,93 @@ static const struct snd_kcontrol_new controls[] = {
	},
	},
};
};


static const struct snd_kcontrol_new monitor_a_controls[] = {
	{
		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
		.name = "Analog Input Monitor Switch",
		.info = snd_ctl_boolean_mono_info,
		.get = monitor_get,
		.put = monitor_put,
		.private_value = OXYGEN_ADC_MONITOR_A,
	},
	{
		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
		.name = "Analog Input Monitor Volume",
		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
			  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
		.info = monitor_volume_info,
		.get = monitor_get,
		.put = monitor_put,
		.private_value = OXYGEN_ADC_MONITOR_A_HALF_VOL | (1 << 8),
		.tlv = { .p = monitor_db_scale, },
	},
};
static const struct snd_kcontrol_new monitor_b_controls[] = {
	{
		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
		.name = "Analog Input Monitor Switch",
		.info = snd_ctl_boolean_mono_info,
		.get = monitor_get,
		.put = monitor_put,
		.private_value = OXYGEN_ADC_MONITOR_B,
	},
	{
		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
		.name = "Analog Input Monitor Volume",
		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
			  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
		.info = monitor_volume_info,
		.get = monitor_get,
		.put = monitor_put,
		.private_value = OXYGEN_ADC_MONITOR_B_HALF_VOL | (1 << 8),
		.tlv = { .p = monitor_db_scale, },
	},
};
static const struct snd_kcontrol_new monitor_2nd_b_controls[] = {
	{
		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
		.name = "Analog Input Monitor Switch",
		.index = 1,
		.info = snd_ctl_boolean_mono_info,
		.get = monitor_get,
		.put = monitor_put,
		.private_value = OXYGEN_ADC_MONITOR_B,
	},
	{
		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
		.name = "Analog Input Monitor Volume",
		.index = 1,
		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
			  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
		.info = monitor_volume_info,
		.get = monitor_get,
		.put = monitor_put,
		.private_value = OXYGEN_ADC_MONITOR_B_HALF_VOL | (1 << 8),
		.tlv = { .p = monitor_db_scale, },
	},
};
static const struct snd_kcontrol_new monitor_c_controls[] = {
	{
		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
		.name = "Digital Input Monitor Switch",
		.info = snd_ctl_boolean_mono_info,
		.get = monitor_get,
		.put = monitor_put,
		.private_value = OXYGEN_ADC_MONITOR_C,
	},
	{
		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
		.name = "Digital Input Monitor Volume",
		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
			  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
		.info = monitor_volume_info,
		.get = monitor_get,
		.put = monitor_put,
		.private_value = OXYGEN_ADC_MONITOR_C_HALF_VOL | (1 << 8),
		.tlv = { .p = monitor_db_scale, },
	},
};

static const struct snd_kcontrol_new ac97_controls[] = {
static const struct snd_kcontrol_new ac97_controls[] = {
	AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC),
	AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC),
	AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1),
	AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1),
@@ -778,6 +910,30 @@ int oxygen_mixer_init(struct oxygen *chip)
	err = add_controls(chip, controls, ARRAY_SIZE(controls));
	err = add_controls(chip, controls, ARRAY_SIZE(controls));
	if (err < 0)
	if (err < 0)
		return err;
		return err;
	if (chip->model->used_channels & OXYGEN_CHANNEL_A) {
		err = add_controls(chip, monitor_a_controls,
				   ARRAY_SIZE(monitor_a_controls));
		if (err < 0)
			return err;
	} else if (chip->model->used_channels & OXYGEN_CHANNEL_B) {
		err = add_controls(chip, monitor_b_controls,
				   ARRAY_SIZE(monitor_b_controls));
		if (err < 0)
			return err;
	}
	if ((chip->model->used_channels & (OXYGEN_CHANNEL_A | OXYGEN_CHANNEL_B))
	    == (OXYGEN_CHANNEL_A | OXYGEN_CHANNEL_B)) {
		err = add_controls(chip, monitor_2nd_b_controls,
				   ARRAY_SIZE(monitor_2nd_b_controls));
		if (err < 0)
			return err;
	}
	if (chip->model->used_channels & OXYGEN_CHANNEL_C) {
		err = add_controls(chip, monitor_c_controls,
				   ARRAY_SIZE(monitor_c_controls));
		if (err < 0)
			return err;
	}
	if (chip->has_ac97_0) {
	if (chip->has_ac97_0) {
		err = add_controls(chip, ac97_controls,
		err = add_controls(chip, ac97_controls,
				   ARRAY_SIZE(ac97_controls));
				   ARRAY_SIZE(ac97_controls));