162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for Digigram VX soundcards
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Common mixer part
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <sound/core.h>
1162306a36Sopenharmony_ci#include <sound/control.h>
1262306a36Sopenharmony_ci#include <sound/tlv.h>
1362306a36Sopenharmony_ci#include <sound/vx_core.h>
1462306a36Sopenharmony_ci#include "vx_cmd.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*
1862306a36Sopenharmony_ci * write a codec data (24bit)
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_cistatic void vx_write_codec_reg(struct vx_core *chip, int codec, unsigned int data)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	if (snd_BUG_ON(!chip->ops->write_codec))
2362306a36Sopenharmony_ci		return;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	if (chip->chip_status & VX_STAT_IS_STALE)
2662306a36Sopenharmony_ci		return;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	mutex_lock(&chip->lock);
2962306a36Sopenharmony_ci	chip->ops->write_codec(chip, codec, data);
3062306a36Sopenharmony_ci	mutex_unlock(&chip->lock);
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/*
3462306a36Sopenharmony_ci * Data type used to access the Codec
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ciunion vx_codec_data {
3762306a36Sopenharmony_ci	u32 l;
3862306a36Sopenharmony_ci#ifdef SNDRV_BIG_ENDIAN
3962306a36Sopenharmony_ci	struct w {
4062306a36Sopenharmony_ci		u16 h;
4162306a36Sopenharmony_ci		u16 l;
4262306a36Sopenharmony_ci	} w;
4362306a36Sopenharmony_ci	struct b {
4462306a36Sopenharmony_ci		u8 hh;
4562306a36Sopenharmony_ci		u8 mh;
4662306a36Sopenharmony_ci		u8 ml;
4762306a36Sopenharmony_ci		u8 ll;
4862306a36Sopenharmony_ci	} b;
4962306a36Sopenharmony_ci#else /* LITTLE_ENDIAN */
5062306a36Sopenharmony_ci	struct w {
5162306a36Sopenharmony_ci		u16 l;
5262306a36Sopenharmony_ci		u16 h;
5362306a36Sopenharmony_ci	} w;
5462306a36Sopenharmony_ci	struct b {
5562306a36Sopenharmony_ci		u8 ll;
5662306a36Sopenharmony_ci		u8 ml;
5762306a36Sopenharmony_ci		u8 mh;
5862306a36Sopenharmony_ci		u8 hh;
5962306a36Sopenharmony_ci	} b;
6062306a36Sopenharmony_ci#endif
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define SET_CDC_DATA_SEL(di,s)          ((di).b.mh = (u8) (s))
6462306a36Sopenharmony_ci#define SET_CDC_DATA_REG(di,r)          ((di).b.ml = (u8) (r))
6562306a36Sopenharmony_ci#define SET_CDC_DATA_VAL(di,d)          ((di).b.ll = (u8) (d))
6662306a36Sopenharmony_ci#define SET_CDC_DATA_INIT(di)           ((di).l = 0L, SET_CDC_DATA_SEL(di,XX_CODEC_SELECTOR))
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/*
6962306a36Sopenharmony_ci * set up codec register and write the value
7062306a36Sopenharmony_ci * @codec: the codec id, 0 or 1
7162306a36Sopenharmony_ci * @reg: register index
7262306a36Sopenharmony_ci * @val: data value
7362306a36Sopenharmony_ci */
7462306a36Sopenharmony_cistatic void vx_set_codec_reg(struct vx_core *chip, int codec, int reg, int val)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	union vx_codec_data data;
7762306a36Sopenharmony_ci	/* DAC control register */
7862306a36Sopenharmony_ci	SET_CDC_DATA_INIT(data);
7962306a36Sopenharmony_ci	SET_CDC_DATA_REG(data, reg);
8062306a36Sopenharmony_ci	SET_CDC_DATA_VAL(data, val);
8162306a36Sopenharmony_ci	vx_write_codec_reg(chip, codec, data.l);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/*
8662306a36Sopenharmony_ci * vx_set_analog_output_level - set the output attenuation level
8762306a36Sopenharmony_ci * @codec: the output codec, 0 or 1.  (1 for VXP440 only)
8862306a36Sopenharmony_ci * @left: left output level, 0 = mute
8962306a36Sopenharmony_ci * @right: right output level
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_cistatic void vx_set_analog_output_level(struct vx_core *chip, int codec, int left, int right)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	left  = chip->hw->output_level_max - left;
9462306a36Sopenharmony_ci	right = chip->hw->output_level_max - right;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (chip->ops->akm_write) {
9762306a36Sopenharmony_ci		chip->ops->akm_write(chip, XX_CODEC_LEVEL_LEFT_REGISTER, left);
9862306a36Sopenharmony_ci		chip->ops->akm_write(chip, XX_CODEC_LEVEL_RIGHT_REGISTER, right);
9962306a36Sopenharmony_ci	} else {
10062306a36Sopenharmony_ci		/* convert to attenuation level: 0 = 0dB (max), 0xe3 = -113.5 dB (min) */
10162306a36Sopenharmony_ci		vx_set_codec_reg(chip, codec, XX_CODEC_LEVEL_LEFT_REGISTER, left);
10262306a36Sopenharmony_ci		vx_set_codec_reg(chip, codec, XX_CODEC_LEVEL_RIGHT_REGISTER, right);
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/*
10862306a36Sopenharmony_ci * vx_toggle_dac_mute -  mute/unmute DAC
10962306a36Sopenharmony_ci * @mute: 0 = unmute, 1 = mute
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci#define DAC_ATTEN_MIN	0x08
11362306a36Sopenharmony_ci#define DAC_ATTEN_MAX	0x38
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_civoid vx_toggle_dac_mute(struct vx_core *chip, int mute)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	unsigned int i;
11862306a36Sopenharmony_ci	for (i = 0; i < chip->hw->num_codecs; i++) {
11962306a36Sopenharmony_ci		if (chip->ops->akm_write)
12062306a36Sopenharmony_ci			chip->ops->akm_write(chip, XX_CODEC_DAC_CONTROL_REGISTER, mute); /* XXX */
12162306a36Sopenharmony_ci		else
12262306a36Sopenharmony_ci			vx_set_codec_reg(chip, i, XX_CODEC_DAC_CONTROL_REGISTER,
12362306a36Sopenharmony_ci					 mute ? DAC_ATTEN_MAX : DAC_ATTEN_MIN);
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci/*
12862306a36Sopenharmony_ci * vx_reset_codec - reset and initialize the codecs
12962306a36Sopenharmony_ci */
13062306a36Sopenharmony_civoid vx_reset_codec(struct vx_core *chip, int cold_reset)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	unsigned int i;
13362306a36Sopenharmony_ci	int port = chip->type >= VX_TYPE_VXPOCKET ? 0x75 : 0x65;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	chip->ops->reset_codec(chip);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* AKM codecs should be initialized in reset_codec callback */
13862306a36Sopenharmony_ci	if (! chip->ops->akm_write) {
13962306a36Sopenharmony_ci		/* initialize old codecs */
14062306a36Sopenharmony_ci		for (i = 0; i < chip->hw->num_codecs; i++) {
14162306a36Sopenharmony_ci			/* DAC control register (change level when zero crossing + mute) */
14262306a36Sopenharmony_ci			vx_set_codec_reg(chip, i, XX_CODEC_DAC_CONTROL_REGISTER, DAC_ATTEN_MAX);
14362306a36Sopenharmony_ci			/* ADC control register */
14462306a36Sopenharmony_ci			vx_set_codec_reg(chip, i, XX_CODEC_ADC_CONTROL_REGISTER, 0x00);
14562306a36Sopenharmony_ci			/* Port mode register */
14662306a36Sopenharmony_ci			vx_set_codec_reg(chip, i, XX_CODEC_PORT_MODE_REGISTER, port);
14762306a36Sopenharmony_ci			/* Clock control register */
14862306a36Sopenharmony_ci			vx_set_codec_reg(chip, i, XX_CODEC_CLOCK_CONTROL_REGISTER, 0x00);
14962306a36Sopenharmony_ci		}
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	/* mute analog output */
15362306a36Sopenharmony_ci	for (i = 0; i < chip->hw->num_codecs; i++) {
15462306a36Sopenharmony_ci		chip->output_level[i][0] = 0;
15562306a36Sopenharmony_ci		chip->output_level[i][1] = 0;
15662306a36Sopenharmony_ci		vx_set_analog_output_level(chip, i, 0, 0);
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci/*
16162306a36Sopenharmony_ci * change the audio input source
16262306a36Sopenharmony_ci * @src: the target source (VX_AUDIO_SRC_XXX)
16362306a36Sopenharmony_ci */
16462306a36Sopenharmony_cistatic void vx_change_audio_source(struct vx_core *chip, int src)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	if (chip->chip_status & VX_STAT_IS_STALE)
16762306a36Sopenharmony_ci		return;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	mutex_lock(&chip->lock);
17062306a36Sopenharmony_ci	chip->ops->change_audio_source(chip, src);
17162306a36Sopenharmony_ci	mutex_unlock(&chip->lock);
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/*
17662306a36Sopenharmony_ci * change the audio source if necessary and possible
17762306a36Sopenharmony_ci * returns 1 if the source is actually changed.
17862306a36Sopenharmony_ci */
17962306a36Sopenharmony_ciint vx_sync_audio_source(struct vx_core *chip)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	if (chip->audio_source_target == chip->audio_source ||
18262306a36Sopenharmony_ci	    chip->pcm_running)
18362306a36Sopenharmony_ci		return 0;
18462306a36Sopenharmony_ci	vx_change_audio_source(chip, chip->audio_source_target);
18562306a36Sopenharmony_ci	chip->audio_source = chip->audio_source_target;
18662306a36Sopenharmony_ci	return 1;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci/*
19162306a36Sopenharmony_ci * audio level, mute, monitoring
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_cistruct vx_audio_level {
19462306a36Sopenharmony_ci	unsigned int has_level: 1;
19562306a36Sopenharmony_ci	unsigned int has_monitor_level: 1;
19662306a36Sopenharmony_ci	unsigned int has_mute: 1;
19762306a36Sopenharmony_ci	unsigned int has_monitor_mute: 1;
19862306a36Sopenharmony_ci	unsigned int mute;
19962306a36Sopenharmony_ci	unsigned int monitor_mute;
20062306a36Sopenharmony_ci	short level;
20162306a36Sopenharmony_ci	short monitor_level;
20262306a36Sopenharmony_ci};
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic int vx_adjust_audio_level(struct vx_core *chip, int audio, int capture,
20562306a36Sopenharmony_ci				 struct vx_audio_level *info)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct vx_rmh rmh;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (chip->chip_status & VX_STAT_IS_STALE)
21062306a36Sopenharmony_ci		return -EBUSY;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci        vx_init_rmh(&rmh, CMD_AUDIO_LEVEL_ADJUST);
21362306a36Sopenharmony_ci	if (capture)
21462306a36Sopenharmony_ci		rmh.Cmd[0] |= COMMAND_RECORD_MASK;
21562306a36Sopenharmony_ci	/* Add Audio IO mask */
21662306a36Sopenharmony_ci	rmh.Cmd[1] = 1 << audio;
21762306a36Sopenharmony_ci	rmh.Cmd[2] = 0;
21862306a36Sopenharmony_ci	if (info->has_level) {
21962306a36Sopenharmony_ci		rmh.Cmd[0] |=  VALID_AUDIO_IO_DIGITAL_LEVEL;
22062306a36Sopenharmony_ci		rmh.Cmd[2] |= info->level;
22162306a36Sopenharmony_ci        }
22262306a36Sopenharmony_ci	if (info->has_monitor_level) {
22362306a36Sopenharmony_ci		rmh.Cmd[0] |=  VALID_AUDIO_IO_MONITORING_LEVEL;
22462306a36Sopenharmony_ci		rmh.Cmd[2] |= ((unsigned int)info->monitor_level << 10);
22562306a36Sopenharmony_ci        }
22662306a36Sopenharmony_ci	if (info->has_mute) {
22762306a36Sopenharmony_ci		rmh.Cmd[0] |= VALID_AUDIO_IO_MUTE_LEVEL;
22862306a36Sopenharmony_ci		if (info->mute)
22962306a36Sopenharmony_ci			rmh.Cmd[2] |= AUDIO_IO_HAS_MUTE_LEVEL;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci	if (info->has_monitor_mute) {
23262306a36Sopenharmony_ci		/* validate flag for M2 at least to unmute it */
23362306a36Sopenharmony_ci		rmh.Cmd[0] |=  VALID_AUDIO_IO_MUTE_MONITORING_1 | VALID_AUDIO_IO_MUTE_MONITORING_2;
23462306a36Sopenharmony_ci		if (info->monitor_mute)
23562306a36Sopenharmony_ci			rmh.Cmd[2] |= AUDIO_IO_HAS_MUTE_MONITORING_1;
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return vx_send_msg(chip, &rmh);
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci#if 0 // not used
24362306a36Sopenharmony_cistatic int vx_read_audio_level(struct vx_core *chip, int audio, int capture,
24462306a36Sopenharmony_ci			       struct vx_audio_level *info)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	int err;
24762306a36Sopenharmony_ci	struct vx_rmh rmh;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	memset(info, 0, sizeof(*info));
25062306a36Sopenharmony_ci        vx_init_rmh(&rmh, CMD_GET_AUDIO_LEVELS);
25162306a36Sopenharmony_ci	if (capture)
25262306a36Sopenharmony_ci		rmh.Cmd[0] |= COMMAND_RECORD_MASK;
25362306a36Sopenharmony_ci	/* Add Audio IO mask */
25462306a36Sopenharmony_ci	rmh.Cmd[1] = 1 << audio;
25562306a36Sopenharmony_ci	err = vx_send_msg(chip, &rmh);
25662306a36Sopenharmony_ci	if (err < 0)
25762306a36Sopenharmony_ci		return err;
25862306a36Sopenharmony_ci	info.level = rmh.Stat[0] & MASK_DSP_WORD_LEVEL;
25962306a36Sopenharmony_ci	info.monitor_level = (rmh.Stat[0] >> 10) & MASK_DSP_WORD_LEVEL;
26062306a36Sopenharmony_ci	info.mute = (rmh.Stat[i] & AUDIO_IO_HAS_MUTE_LEVEL) ? 1 : 0;
26162306a36Sopenharmony_ci	info.monitor_mute = (rmh.Stat[i] & AUDIO_IO_HAS_MUTE_MONITORING_1) ? 1 : 0;
26262306a36Sopenharmony_ci	return 0;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci#endif // not used
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci/*
26762306a36Sopenharmony_ci * set the monitoring level and mute state of the given audio
26862306a36Sopenharmony_ci * no more static, because must be called from vx_pcm to demute monitoring
26962306a36Sopenharmony_ci */
27062306a36Sopenharmony_ciint vx_set_monitor_level(struct vx_core *chip, int audio, int level, int active)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct vx_audio_level info;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	memset(&info, 0, sizeof(info));
27562306a36Sopenharmony_ci	info.has_monitor_level = 1;
27662306a36Sopenharmony_ci	info.monitor_level = level;
27762306a36Sopenharmony_ci	info.has_monitor_mute = 1;
27862306a36Sopenharmony_ci	info.monitor_mute = !active;
27962306a36Sopenharmony_ci	chip->audio_monitor[audio] = level;
28062306a36Sopenharmony_ci	chip->audio_monitor_active[audio] = active;
28162306a36Sopenharmony_ci	return vx_adjust_audio_level(chip, audio, 0, &info); /* playback only */
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci/*
28662306a36Sopenharmony_ci * set the mute status of the given audio
28762306a36Sopenharmony_ci */
28862306a36Sopenharmony_cistatic int vx_set_audio_switch(struct vx_core *chip, int audio, int active)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct vx_audio_level info;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	memset(&info, 0, sizeof(info));
29362306a36Sopenharmony_ci	info.has_mute = 1;
29462306a36Sopenharmony_ci	info.mute = !active;
29562306a36Sopenharmony_ci	chip->audio_active[audio] = active;
29662306a36Sopenharmony_ci	return vx_adjust_audio_level(chip, audio, 0, &info); /* playback only */
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci/*
30062306a36Sopenharmony_ci * set the mute status of the given audio
30162306a36Sopenharmony_ci */
30262306a36Sopenharmony_cistatic int vx_set_audio_gain(struct vx_core *chip, int audio, int capture, int level)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct vx_audio_level info;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	memset(&info, 0, sizeof(info));
30762306a36Sopenharmony_ci	info.has_level = 1;
30862306a36Sopenharmony_ci	info.level = level;
30962306a36Sopenharmony_ci	chip->audio_gain[capture][audio] = level;
31062306a36Sopenharmony_ci	return vx_adjust_audio_level(chip, audio, capture, &info);
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci/*
31462306a36Sopenharmony_ci * reset all audio levels
31562306a36Sopenharmony_ci */
31662306a36Sopenharmony_cistatic void vx_reset_audio_levels(struct vx_core *chip)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	unsigned int i, c;
31962306a36Sopenharmony_ci	struct vx_audio_level info;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	memset(chip->audio_gain, 0, sizeof(chip->audio_gain));
32262306a36Sopenharmony_ci	memset(chip->audio_active, 0, sizeof(chip->audio_active));
32362306a36Sopenharmony_ci	memset(chip->audio_monitor, 0, sizeof(chip->audio_monitor));
32462306a36Sopenharmony_ci	memset(chip->audio_monitor_active, 0, sizeof(chip->audio_monitor_active));
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	for (c = 0; c < 2; c++) {
32762306a36Sopenharmony_ci		for (i = 0; i < chip->hw->num_ins * 2; i++) {
32862306a36Sopenharmony_ci			memset(&info, 0, sizeof(info));
32962306a36Sopenharmony_ci			if (c == 0) {
33062306a36Sopenharmony_ci				info.has_monitor_level = 1;
33162306a36Sopenharmony_ci				info.has_mute = 1;
33262306a36Sopenharmony_ci				info.has_monitor_mute = 1;
33362306a36Sopenharmony_ci			}
33462306a36Sopenharmony_ci			info.has_level = 1;
33562306a36Sopenharmony_ci			info.level = CVAL_0DB; /* default: 0dB */
33662306a36Sopenharmony_ci			vx_adjust_audio_level(chip, i, c, &info);
33762306a36Sopenharmony_ci			chip->audio_gain[c][i] = CVAL_0DB;
33862306a36Sopenharmony_ci			chip->audio_monitor[i] = CVAL_0DB;
33962306a36Sopenharmony_ci		}
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci/*
34562306a36Sopenharmony_ci * VU, peak meter record
34662306a36Sopenharmony_ci */
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci#define VU_METER_CHANNELS	2
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistruct vx_vu_meter {
35162306a36Sopenharmony_ci	int saturated;
35262306a36Sopenharmony_ci	int vu_level;
35362306a36Sopenharmony_ci	int peak_level;
35462306a36Sopenharmony_ci};
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci/*
35762306a36Sopenharmony_ci * get the VU and peak meter values
35862306a36Sopenharmony_ci * @audio: the audio index
35962306a36Sopenharmony_ci * @capture: 0 = playback, 1 = capture operation
36062306a36Sopenharmony_ci * @info: the array of vx_vu_meter records (size = 2).
36162306a36Sopenharmony_ci */
36262306a36Sopenharmony_cistatic int vx_get_audio_vu_meter(struct vx_core *chip, int audio, int capture, struct vx_vu_meter *info)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	struct vx_rmh rmh;
36562306a36Sopenharmony_ci	int i, err;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	if (chip->chip_status & VX_STAT_IS_STALE)
36862306a36Sopenharmony_ci		return -EBUSY;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	vx_init_rmh(&rmh, CMD_AUDIO_VU_PIC_METER);
37162306a36Sopenharmony_ci	rmh.LgStat += 2 * VU_METER_CHANNELS;
37262306a36Sopenharmony_ci	if (capture)
37362306a36Sopenharmony_ci		rmh.Cmd[0] |= COMMAND_RECORD_MASK;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci        /* Add Audio IO mask */
37662306a36Sopenharmony_ci	rmh.Cmd[1] = 0;
37762306a36Sopenharmony_ci	for (i = 0; i < VU_METER_CHANNELS; i++)
37862306a36Sopenharmony_ci		rmh.Cmd[1] |= 1 << (audio + i);
37962306a36Sopenharmony_ci	err = vx_send_msg(chip, &rmh);
38062306a36Sopenharmony_ci	if (err < 0)
38162306a36Sopenharmony_ci		return err;
38262306a36Sopenharmony_ci	/* Read response */
38362306a36Sopenharmony_ci	for (i = 0; i < 2 * VU_METER_CHANNELS; i +=2) {
38462306a36Sopenharmony_ci		info->saturated = (rmh.Stat[0] & (1 << (audio + i))) ? 1 : 0;
38562306a36Sopenharmony_ci		info->vu_level = rmh.Stat[i + 1];
38662306a36Sopenharmony_ci		info->peak_level = rmh.Stat[i + 2];
38762306a36Sopenharmony_ci		info++;
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci	return 0;
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci/*
39462306a36Sopenharmony_ci * control API entries
39562306a36Sopenharmony_ci */
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci/*
39862306a36Sopenharmony_ci * output level control
39962306a36Sopenharmony_ci */
40062306a36Sopenharmony_cistatic int vx_output_level_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
40362306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
40462306a36Sopenharmony_ci	uinfo->count = 2;
40562306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
40662306a36Sopenharmony_ci	uinfo->value.integer.max = chip->hw->output_level_max;
40762306a36Sopenharmony_ci	return 0;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic int vx_output_level_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
41362306a36Sopenharmony_ci	int codec = kcontrol->id.index;
41462306a36Sopenharmony_ci	mutex_lock(&chip->mixer_mutex);
41562306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = chip->output_level[codec][0];
41662306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = chip->output_level[codec][1];
41762306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_mutex);
41862306a36Sopenharmony_ci	return 0;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic int vx_output_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
42462306a36Sopenharmony_ci	int codec = kcontrol->id.index;
42562306a36Sopenharmony_ci	unsigned int val[2], vmax;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	vmax = chip->hw->output_level_max;
42862306a36Sopenharmony_ci	val[0] = ucontrol->value.integer.value[0];
42962306a36Sopenharmony_ci	val[1] = ucontrol->value.integer.value[1];
43062306a36Sopenharmony_ci	if (val[0] > vmax || val[1] > vmax)
43162306a36Sopenharmony_ci		return -EINVAL;
43262306a36Sopenharmony_ci	mutex_lock(&chip->mixer_mutex);
43362306a36Sopenharmony_ci	if (val[0] != chip->output_level[codec][0] ||
43462306a36Sopenharmony_ci	    val[1] != chip->output_level[codec][1]) {
43562306a36Sopenharmony_ci		vx_set_analog_output_level(chip, codec, val[0], val[1]);
43662306a36Sopenharmony_ci		chip->output_level[codec][0] = val[0];
43762306a36Sopenharmony_ci		chip->output_level[codec][1] = val[1];
43862306a36Sopenharmony_ci		mutex_unlock(&chip->mixer_mutex);
43962306a36Sopenharmony_ci		return 1;
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_mutex);
44262306a36Sopenharmony_ci	return 0;
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_cistatic const struct snd_kcontrol_new vx_control_output_level = {
44662306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
44762306a36Sopenharmony_ci	.access =	(SNDRV_CTL_ELEM_ACCESS_READWRITE |
44862306a36Sopenharmony_ci			 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
44962306a36Sopenharmony_ci	.name =		"Master Playback Volume",
45062306a36Sopenharmony_ci	.info =		vx_output_level_info,
45162306a36Sopenharmony_ci	.get =		vx_output_level_get,
45262306a36Sopenharmony_ci	.put =		vx_output_level_put,
45362306a36Sopenharmony_ci	/* tlv will be filled later */
45462306a36Sopenharmony_ci};
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci/*
45762306a36Sopenharmony_ci * audio source select
45862306a36Sopenharmony_ci */
45962306a36Sopenharmony_cistatic int vx_audio_src_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	static const char * const texts_mic[3] = {
46262306a36Sopenharmony_ci		"Digital", "Line", "Mic"
46362306a36Sopenharmony_ci	};
46462306a36Sopenharmony_ci	static const char * const texts_vx2[2] = {
46562306a36Sopenharmony_ci		"Digital", "Analog"
46662306a36Sopenharmony_ci	};
46762306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	if (chip->type >= VX_TYPE_VXPOCKET)
47062306a36Sopenharmony_ci		return snd_ctl_enum_info(uinfo, 1, 3, texts_mic);
47162306a36Sopenharmony_ci	else
47262306a36Sopenharmony_ci		return snd_ctl_enum_info(uinfo, 1, 2, texts_vx2);
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic int vx_audio_src_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
47862306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = chip->audio_source_target;
47962306a36Sopenharmony_ci	return 0;
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic int vx_audio_src_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	if (chip->type >= VX_TYPE_VXPOCKET) {
48762306a36Sopenharmony_ci		if (ucontrol->value.enumerated.item[0] > 2)
48862306a36Sopenharmony_ci			return -EINVAL;
48962306a36Sopenharmony_ci	} else {
49062306a36Sopenharmony_ci		if (ucontrol->value.enumerated.item[0] > 1)
49162306a36Sopenharmony_ci			return -EINVAL;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci	mutex_lock(&chip->mixer_mutex);
49462306a36Sopenharmony_ci	if (chip->audio_source_target != ucontrol->value.enumerated.item[0]) {
49562306a36Sopenharmony_ci		chip->audio_source_target = ucontrol->value.enumerated.item[0];
49662306a36Sopenharmony_ci		vx_sync_audio_source(chip);
49762306a36Sopenharmony_ci		mutex_unlock(&chip->mixer_mutex);
49862306a36Sopenharmony_ci		return 1;
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_mutex);
50162306a36Sopenharmony_ci	return 0;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic const struct snd_kcontrol_new vx_control_audio_src = {
50562306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
50662306a36Sopenharmony_ci	.name =		"Capture Source",
50762306a36Sopenharmony_ci	.info =		vx_audio_src_info,
50862306a36Sopenharmony_ci	.get =		vx_audio_src_get,
50962306a36Sopenharmony_ci	.put =		vx_audio_src_put,
51062306a36Sopenharmony_ci};
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci/*
51362306a36Sopenharmony_ci * clock mode selection
51462306a36Sopenharmony_ci */
51562306a36Sopenharmony_cistatic int vx_clock_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	static const char * const texts[3] = {
51862306a36Sopenharmony_ci		"Auto", "Internal", "External"
51962306a36Sopenharmony_ci	};
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 3, texts);
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic int vx_clock_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
52762306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = chip->clock_mode;
52862306a36Sopenharmony_ci	return 0;
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic int vx_clock_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] > 2)
53662306a36Sopenharmony_ci		return -EINVAL;
53762306a36Sopenharmony_ci	mutex_lock(&chip->mixer_mutex);
53862306a36Sopenharmony_ci	if (chip->clock_mode != ucontrol->value.enumerated.item[0]) {
53962306a36Sopenharmony_ci		chip->clock_mode = ucontrol->value.enumerated.item[0];
54062306a36Sopenharmony_ci		vx_set_clock(chip, chip->freq);
54162306a36Sopenharmony_ci		mutex_unlock(&chip->mixer_mutex);
54262306a36Sopenharmony_ci		return 1;
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_mutex);
54562306a36Sopenharmony_ci	return 0;
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic const struct snd_kcontrol_new vx_control_clock_mode = {
54962306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
55062306a36Sopenharmony_ci	.name =		"Clock Mode",
55162306a36Sopenharmony_ci	.info =		vx_clock_mode_info,
55262306a36Sopenharmony_ci	.get =		vx_clock_mode_get,
55362306a36Sopenharmony_ci	.put =		vx_clock_mode_put,
55462306a36Sopenharmony_ci};
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci/*
55762306a36Sopenharmony_ci * Audio Gain
55862306a36Sopenharmony_ci */
55962306a36Sopenharmony_cistatic int vx_audio_gain_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
56262306a36Sopenharmony_ci	uinfo->count = 2;
56362306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
56462306a36Sopenharmony_ci	uinfo->value.integer.max = CVAL_MAX;
56562306a36Sopenharmony_ci	return 0;
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic int vx_audio_gain_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
57162306a36Sopenharmony_ci	int audio = kcontrol->private_value & 0xff;
57262306a36Sopenharmony_ci	int capture = (kcontrol->private_value >> 8) & 1;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	mutex_lock(&chip->mixer_mutex);
57562306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = chip->audio_gain[capture][audio];
57662306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = chip->audio_gain[capture][audio+1];
57762306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_mutex);
57862306a36Sopenharmony_ci	return 0;
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic int vx_audio_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
58462306a36Sopenharmony_ci	int audio = kcontrol->private_value & 0xff;
58562306a36Sopenharmony_ci	int capture = (kcontrol->private_value >> 8) & 1;
58662306a36Sopenharmony_ci	unsigned int val[2];
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	val[0] = ucontrol->value.integer.value[0];
58962306a36Sopenharmony_ci	val[1] = ucontrol->value.integer.value[1];
59062306a36Sopenharmony_ci	if (val[0] > CVAL_MAX || val[1] > CVAL_MAX)
59162306a36Sopenharmony_ci		return -EINVAL;
59262306a36Sopenharmony_ci	mutex_lock(&chip->mixer_mutex);
59362306a36Sopenharmony_ci	if (val[0] != chip->audio_gain[capture][audio] ||
59462306a36Sopenharmony_ci	    val[1] != chip->audio_gain[capture][audio+1]) {
59562306a36Sopenharmony_ci		vx_set_audio_gain(chip, audio, capture, val[0]);
59662306a36Sopenharmony_ci		vx_set_audio_gain(chip, audio+1, capture, val[1]);
59762306a36Sopenharmony_ci		mutex_unlock(&chip->mixer_mutex);
59862306a36Sopenharmony_ci		return 1;
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_mutex);
60162306a36Sopenharmony_ci	return 0;
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic int vx_audio_monitor_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
60762306a36Sopenharmony_ci	int audio = kcontrol->private_value & 0xff;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	mutex_lock(&chip->mixer_mutex);
61062306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = chip->audio_monitor[audio];
61162306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = chip->audio_monitor[audio+1];
61262306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_mutex);
61362306a36Sopenharmony_ci	return 0;
61462306a36Sopenharmony_ci}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_cistatic int vx_audio_monitor_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
61762306a36Sopenharmony_ci{
61862306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
61962306a36Sopenharmony_ci	int audio = kcontrol->private_value & 0xff;
62062306a36Sopenharmony_ci	unsigned int val[2];
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	val[0] = ucontrol->value.integer.value[0];
62362306a36Sopenharmony_ci	val[1] = ucontrol->value.integer.value[1];
62462306a36Sopenharmony_ci	if (val[0] > CVAL_MAX || val[1] > CVAL_MAX)
62562306a36Sopenharmony_ci		return -EINVAL;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	mutex_lock(&chip->mixer_mutex);
62862306a36Sopenharmony_ci	if (val[0] != chip->audio_monitor[audio] ||
62962306a36Sopenharmony_ci	    val[1] != chip->audio_monitor[audio+1]) {
63062306a36Sopenharmony_ci		vx_set_monitor_level(chip, audio, val[0],
63162306a36Sopenharmony_ci				     chip->audio_monitor_active[audio]);
63262306a36Sopenharmony_ci		vx_set_monitor_level(chip, audio+1, val[1],
63362306a36Sopenharmony_ci				     chip->audio_monitor_active[audio+1]);
63462306a36Sopenharmony_ci		mutex_unlock(&chip->mixer_mutex);
63562306a36Sopenharmony_ci		return 1;
63662306a36Sopenharmony_ci	}
63762306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_mutex);
63862306a36Sopenharmony_ci	return 0;
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci#define vx_audio_sw_info	snd_ctl_boolean_stereo_info
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic int vx_audio_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
64662306a36Sopenharmony_ci	int audio = kcontrol->private_value & 0xff;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	mutex_lock(&chip->mixer_mutex);
64962306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = chip->audio_active[audio];
65062306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = chip->audio_active[audio+1];
65162306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_mutex);
65262306a36Sopenharmony_ci	return 0;
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic int vx_audio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
65862306a36Sopenharmony_ci	int audio = kcontrol->private_value & 0xff;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	mutex_lock(&chip->mixer_mutex);
66162306a36Sopenharmony_ci	if (ucontrol->value.integer.value[0] != chip->audio_active[audio] ||
66262306a36Sopenharmony_ci	    ucontrol->value.integer.value[1] != chip->audio_active[audio+1]) {
66362306a36Sopenharmony_ci		vx_set_audio_switch(chip, audio,
66462306a36Sopenharmony_ci				    !!ucontrol->value.integer.value[0]);
66562306a36Sopenharmony_ci		vx_set_audio_switch(chip, audio+1,
66662306a36Sopenharmony_ci				    !!ucontrol->value.integer.value[1]);
66762306a36Sopenharmony_ci		mutex_unlock(&chip->mixer_mutex);
66862306a36Sopenharmony_ci		return 1;
66962306a36Sopenharmony_ci	}
67062306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_mutex);
67162306a36Sopenharmony_ci	return 0;
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistatic int vx_monitor_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
67762306a36Sopenharmony_ci	int audio = kcontrol->private_value & 0xff;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	mutex_lock(&chip->mixer_mutex);
68062306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = chip->audio_monitor_active[audio];
68162306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = chip->audio_monitor_active[audio+1];
68262306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_mutex);
68362306a36Sopenharmony_ci	return 0;
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic int vx_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
68962306a36Sopenharmony_ci	int audio = kcontrol->private_value & 0xff;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	mutex_lock(&chip->mixer_mutex);
69262306a36Sopenharmony_ci	if (ucontrol->value.integer.value[0] != chip->audio_monitor_active[audio] ||
69362306a36Sopenharmony_ci	    ucontrol->value.integer.value[1] != chip->audio_monitor_active[audio+1]) {
69462306a36Sopenharmony_ci		vx_set_monitor_level(chip, audio, chip->audio_monitor[audio],
69562306a36Sopenharmony_ci				     !!ucontrol->value.integer.value[0]);
69662306a36Sopenharmony_ci		vx_set_monitor_level(chip, audio+1, chip->audio_monitor[audio+1],
69762306a36Sopenharmony_ci				     !!ucontrol->value.integer.value[1]);
69862306a36Sopenharmony_ci		mutex_unlock(&chip->mixer_mutex);
69962306a36Sopenharmony_ci		return 1;
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_mutex);
70262306a36Sopenharmony_ci	return 0;
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_audio_gain, -10975, 25, 0);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic const struct snd_kcontrol_new vx_control_audio_gain = {
70862306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
70962306a36Sopenharmony_ci	.access =	(SNDRV_CTL_ELEM_ACCESS_READWRITE |
71062306a36Sopenharmony_ci			 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
71162306a36Sopenharmony_ci	/* name will be filled later */
71262306a36Sopenharmony_ci	.info =         vx_audio_gain_info,
71362306a36Sopenharmony_ci	.get =          vx_audio_gain_get,
71462306a36Sopenharmony_ci	.put =          vx_audio_gain_put,
71562306a36Sopenharmony_ci	.tlv = { .p = db_scale_audio_gain },
71662306a36Sopenharmony_ci};
71762306a36Sopenharmony_cistatic const struct snd_kcontrol_new vx_control_output_switch = {
71862306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
71962306a36Sopenharmony_ci	.name =         "PCM Playback Switch",
72062306a36Sopenharmony_ci	.info =         vx_audio_sw_info,
72162306a36Sopenharmony_ci	.get =          vx_audio_sw_get,
72262306a36Sopenharmony_ci	.put =          vx_audio_sw_put
72362306a36Sopenharmony_ci};
72462306a36Sopenharmony_cistatic const struct snd_kcontrol_new vx_control_monitor_gain = {
72562306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
72662306a36Sopenharmony_ci	.name =         "Monitoring Volume",
72762306a36Sopenharmony_ci	.access =	(SNDRV_CTL_ELEM_ACCESS_READWRITE |
72862306a36Sopenharmony_ci			 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
72962306a36Sopenharmony_ci	.info =         vx_audio_gain_info,	/* shared */
73062306a36Sopenharmony_ci	.get =          vx_audio_monitor_get,
73162306a36Sopenharmony_ci	.put =          vx_audio_monitor_put,
73262306a36Sopenharmony_ci	.tlv = { .p = db_scale_audio_gain },
73362306a36Sopenharmony_ci};
73462306a36Sopenharmony_cistatic const struct snd_kcontrol_new vx_control_monitor_switch = {
73562306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
73662306a36Sopenharmony_ci	.name =         "Monitoring Switch",
73762306a36Sopenharmony_ci	.info =         vx_audio_sw_info,	/* shared */
73862306a36Sopenharmony_ci	.get =          vx_monitor_sw_get,
73962306a36Sopenharmony_ci	.put =          vx_monitor_sw_put
74062306a36Sopenharmony_ci};
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci/*
74462306a36Sopenharmony_ci * IEC958 status bits
74562306a36Sopenharmony_ci */
74662306a36Sopenharmony_cistatic int vx_iec958_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
74962306a36Sopenharmony_ci	uinfo->count = 1;
75062306a36Sopenharmony_ci	return 0;
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_cistatic int vx_iec958_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	mutex_lock(&chip->mixer_mutex);
75862306a36Sopenharmony_ci	ucontrol->value.iec958.status[0] = (chip->uer_bits >> 0) & 0xff;
75962306a36Sopenharmony_ci	ucontrol->value.iec958.status[1] = (chip->uer_bits >> 8) & 0xff;
76062306a36Sopenharmony_ci	ucontrol->value.iec958.status[2] = (chip->uer_bits >> 16) & 0xff;
76162306a36Sopenharmony_ci	ucontrol->value.iec958.status[3] = (chip->uer_bits >> 24) & 0xff;
76262306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_mutex);
76362306a36Sopenharmony_ci        return 0;
76462306a36Sopenharmony_ci}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_cistatic int vx_iec958_mask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
76762306a36Sopenharmony_ci{
76862306a36Sopenharmony_ci	ucontrol->value.iec958.status[0] = 0xff;
76962306a36Sopenharmony_ci	ucontrol->value.iec958.status[1] = 0xff;
77062306a36Sopenharmony_ci	ucontrol->value.iec958.status[2] = 0xff;
77162306a36Sopenharmony_ci	ucontrol->value.iec958.status[3] = 0xff;
77262306a36Sopenharmony_ci        return 0;
77362306a36Sopenharmony_ci}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_cistatic int vx_iec958_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
77662306a36Sopenharmony_ci{
77762306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
77862306a36Sopenharmony_ci	unsigned int val;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	val = (ucontrol->value.iec958.status[0] << 0) |
78162306a36Sopenharmony_ci	      (ucontrol->value.iec958.status[1] << 8) |
78262306a36Sopenharmony_ci	      (ucontrol->value.iec958.status[2] << 16) |
78362306a36Sopenharmony_ci	      (ucontrol->value.iec958.status[3] << 24);
78462306a36Sopenharmony_ci	mutex_lock(&chip->mixer_mutex);
78562306a36Sopenharmony_ci	if (chip->uer_bits != val) {
78662306a36Sopenharmony_ci		chip->uer_bits = val;
78762306a36Sopenharmony_ci		vx_set_iec958_status(chip, val);
78862306a36Sopenharmony_ci		mutex_unlock(&chip->mixer_mutex);
78962306a36Sopenharmony_ci		return 1;
79062306a36Sopenharmony_ci	}
79162306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_mutex);
79262306a36Sopenharmony_ci	return 0;
79362306a36Sopenharmony_ci}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_cistatic const struct snd_kcontrol_new vx_control_iec958_mask = {
79662306a36Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
79762306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
79862306a36Sopenharmony_ci	.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
79962306a36Sopenharmony_ci	.info =		vx_iec958_info,	/* shared */
80062306a36Sopenharmony_ci	.get =		vx_iec958_mask_get,
80162306a36Sopenharmony_ci};
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic const struct snd_kcontrol_new vx_control_iec958 = {
80462306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
80562306a36Sopenharmony_ci	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
80662306a36Sopenharmony_ci	.info =         vx_iec958_info,
80762306a36Sopenharmony_ci	.get =          vx_iec958_get,
80862306a36Sopenharmony_ci	.put =          vx_iec958_put
80962306a36Sopenharmony_ci};
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci/*
81362306a36Sopenharmony_ci * VU meter
81462306a36Sopenharmony_ci */
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci#define METER_MAX	0xff
81762306a36Sopenharmony_ci#define METER_SHIFT	16
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_cistatic int vx_vu_meter_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
82062306a36Sopenharmony_ci{
82162306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
82262306a36Sopenharmony_ci	uinfo->count = 2;
82362306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
82462306a36Sopenharmony_ci	uinfo->value.integer.max = METER_MAX;
82562306a36Sopenharmony_ci	return 0;
82662306a36Sopenharmony_ci}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_cistatic int vx_vu_meter_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
83162306a36Sopenharmony_ci	struct vx_vu_meter meter[2];
83262306a36Sopenharmony_ci	int audio = kcontrol->private_value & 0xff;
83362306a36Sopenharmony_ci	int capture = (kcontrol->private_value >> 8) & 1;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	vx_get_audio_vu_meter(chip, audio, capture, meter);
83662306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = meter[0].vu_level >> METER_SHIFT;
83762306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = meter[1].vu_level >> METER_SHIFT;
83862306a36Sopenharmony_ci	return 0;
83962306a36Sopenharmony_ci}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_cistatic int vx_peak_meter_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
84462306a36Sopenharmony_ci	struct vx_vu_meter meter[2];
84562306a36Sopenharmony_ci	int audio = kcontrol->private_value & 0xff;
84662306a36Sopenharmony_ci	int capture = (kcontrol->private_value >> 8) & 1;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	vx_get_audio_vu_meter(chip, audio, capture, meter);
84962306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = meter[0].peak_level >> METER_SHIFT;
85062306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = meter[1].peak_level >> METER_SHIFT;
85162306a36Sopenharmony_ci	return 0;
85262306a36Sopenharmony_ci}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci#define vx_saturation_info	snd_ctl_boolean_stereo_info
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_cistatic int vx_saturation_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
85762306a36Sopenharmony_ci{
85862306a36Sopenharmony_ci	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
85962306a36Sopenharmony_ci	struct vx_vu_meter meter[2];
86062306a36Sopenharmony_ci	int audio = kcontrol->private_value & 0xff;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	vx_get_audio_vu_meter(chip, audio, 1, meter); /* capture only */
86362306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = meter[0].saturated;
86462306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = meter[1].saturated;
86562306a36Sopenharmony_ci	return 0;
86662306a36Sopenharmony_ci}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_cistatic const struct snd_kcontrol_new vx_control_vu_meter = {
86962306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
87062306a36Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
87162306a36Sopenharmony_ci	/* name will be filled later */
87262306a36Sopenharmony_ci	.info =		vx_vu_meter_info,
87362306a36Sopenharmony_ci	.get =		vx_vu_meter_get,
87462306a36Sopenharmony_ci};
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_cistatic const struct snd_kcontrol_new vx_control_peak_meter = {
87762306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
87862306a36Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
87962306a36Sopenharmony_ci	/* name will be filled later */
88062306a36Sopenharmony_ci	.info =		vx_vu_meter_info,	/* shared */
88162306a36Sopenharmony_ci	.get =		vx_peak_meter_get,
88262306a36Sopenharmony_ci};
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_cistatic const struct snd_kcontrol_new vx_control_saturation = {
88562306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
88662306a36Sopenharmony_ci	.name =		"Input Saturation",
88762306a36Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
88862306a36Sopenharmony_ci	.info =		vx_saturation_info,
88962306a36Sopenharmony_ci	.get =		vx_saturation_get,
89062306a36Sopenharmony_ci};
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci/*
89562306a36Sopenharmony_ci *
89662306a36Sopenharmony_ci */
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ciint snd_vx_mixer_new(struct vx_core *chip)
89962306a36Sopenharmony_ci{
90062306a36Sopenharmony_ci	unsigned int i, c;
90162306a36Sopenharmony_ci	int err;
90262306a36Sopenharmony_ci	struct snd_kcontrol_new temp;
90362306a36Sopenharmony_ci	struct snd_card *card = chip->card;
90462306a36Sopenharmony_ci	char name[32];
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	strcpy(card->mixername, card->driver);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	/* output level controls */
90962306a36Sopenharmony_ci	for (i = 0; i < chip->hw->num_outs; i++) {
91062306a36Sopenharmony_ci		temp = vx_control_output_level;
91162306a36Sopenharmony_ci		temp.index = i;
91262306a36Sopenharmony_ci		temp.tlv.p = chip->hw->output_level_db_scale;
91362306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
91462306a36Sopenharmony_ci		if (err < 0)
91562306a36Sopenharmony_ci			return err;
91662306a36Sopenharmony_ci	}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	/* PCM volumes, switches, monitoring */
91962306a36Sopenharmony_ci	for (i = 0; i < chip->hw->num_outs; i++) {
92062306a36Sopenharmony_ci		int val = i * 2;
92162306a36Sopenharmony_ci		temp = vx_control_audio_gain;
92262306a36Sopenharmony_ci		temp.index = i;
92362306a36Sopenharmony_ci		temp.name = "PCM Playback Volume";
92462306a36Sopenharmony_ci		temp.private_value = val;
92562306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
92662306a36Sopenharmony_ci		if (err < 0)
92762306a36Sopenharmony_ci			return err;
92862306a36Sopenharmony_ci		temp = vx_control_output_switch;
92962306a36Sopenharmony_ci		temp.index = i;
93062306a36Sopenharmony_ci		temp.private_value = val;
93162306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
93262306a36Sopenharmony_ci		if (err < 0)
93362306a36Sopenharmony_ci			return err;
93462306a36Sopenharmony_ci		temp = vx_control_monitor_gain;
93562306a36Sopenharmony_ci		temp.index = i;
93662306a36Sopenharmony_ci		temp.private_value = val;
93762306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
93862306a36Sopenharmony_ci		if (err < 0)
93962306a36Sopenharmony_ci			return err;
94062306a36Sopenharmony_ci		temp = vx_control_monitor_switch;
94162306a36Sopenharmony_ci		temp.index = i;
94262306a36Sopenharmony_ci		temp.private_value = val;
94362306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
94462306a36Sopenharmony_ci		if (err < 0)
94562306a36Sopenharmony_ci			return err;
94662306a36Sopenharmony_ci	}
94762306a36Sopenharmony_ci	for (i = 0; i < chip->hw->num_outs; i++) {
94862306a36Sopenharmony_ci		temp = vx_control_audio_gain;
94962306a36Sopenharmony_ci		temp.index = i;
95062306a36Sopenharmony_ci		temp.name = "PCM Capture Volume";
95162306a36Sopenharmony_ci		temp.private_value = (i * 2) | (1 << 8);
95262306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
95362306a36Sopenharmony_ci		if (err < 0)
95462306a36Sopenharmony_ci			return err;
95562306a36Sopenharmony_ci	}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	/* Audio source */
95862306a36Sopenharmony_ci	err = snd_ctl_add(card, snd_ctl_new1(&vx_control_audio_src, chip));
95962306a36Sopenharmony_ci	if (err < 0)
96062306a36Sopenharmony_ci		return err;
96162306a36Sopenharmony_ci	/* clock mode */
96262306a36Sopenharmony_ci	err = snd_ctl_add(card, snd_ctl_new1(&vx_control_clock_mode, chip));
96362306a36Sopenharmony_ci	if (err < 0)
96462306a36Sopenharmony_ci		return err;
96562306a36Sopenharmony_ci	/* IEC958 controls */
96662306a36Sopenharmony_ci	err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958_mask, chip));
96762306a36Sopenharmony_ci	if (err < 0)
96862306a36Sopenharmony_ci		return err;
96962306a36Sopenharmony_ci	err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958, chip));
97062306a36Sopenharmony_ci	if (err < 0)
97162306a36Sopenharmony_ci		return err;
97262306a36Sopenharmony_ci	/* VU, peak, saturation meters */
97362306a36Sopenharmony_ci	for (c = 0; c < 2; c++) {
97462306a36Sopenharmony_ci		static const char * const dir[2] = { "Output", "Input" };
97562306a36Sopenharmony_ci		for (i = 0; i < chip->hw->num_ins; i++) {
97662306a36Sopenharmony_ci			int val = (i * 2) | (c << 8);
97762306a36Sopenharmony_ci			if (c == 1) {
97862306a36Sopenharmony_ci				temp = vx_control_saturation;
97962306a36Sopenharmony_ci				temp.index = i;
98062306a36Sopenharmony_ci				temp.private_value = val;
98162306a36Sopenharmony_ci				err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
98262306a36Sopenharmony_ci				if (err < 0)
98362306a36Sopenharmony_ci					return err;
98462306a36Sopenharmony_ci			}
98562306a36Sopenharmony_ci			sprintf(name, "%s VU Meter", dir[c]);
98662306a36Sopenharmony_ci			temp = vx_control_vu_meter;
98762306a36Sopenharmony_ci			temp.index = i;
98862306a36Sopenharmony_ci			temp.name = name;
98962306a36Sopenharmony_ci			temp.private_value = val;
99062306a36Sopenharmony_ci			err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
99162306a36Sopenharmony_ci			if (err < 0)
99262306a36Sopenharmony_ci				return err;
99362306a36Sopenharmony_ci			sprintf(name, "%s Peak Meter", dir[c]);
99462306a36Sopenharmony_ci			temp = vx_control_peak_meter;
99562306a36Sopenharmony_ci			temp.index = i;
99662306a36Sopenharmony_ci			temp.name = name;
99762306a36Sopenharmony_ci			temp.private_value = val;
99862306a36Sopenharmony_ci			err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
99962306a36Sopenharmony_ci			if (err < 0)
100062306a36Sopenharmony_ci				return err;
100162306a36Sopenharmony_ci		}
100262306a36Sopenharmony_ci	}
100362306a36Sopenharmony_ci	vx_reset_audio_levels(chip);
100462306a36Sopenharmony_ci	return 0;
100562306a36Sopenharmony_ci}
1006