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