cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

xonar_dg_mixer.c (12631B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Mixer controls for the Xonar DG/DGX
      4 *
      5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
      6 * Copyright (c) Roman Volkov <v1ron@mail.ru>
      7 */
      8
      9#include <linux/pci.h>
     10#include <linux/delay.h>
     11#include <sound/control.h>
     12#include <sound/core.h>
     13#include <sound/info.h>
     14#include <sound/pcm.h>
     15#include <sound/tlv.h>
     16#include "oxygen.h"
     17#include "xonar_dg.h"
     18#include "cs4245.h"
     19
     20/* analog output select */
     21
     22static int output_select_apply(struct oxygen *chip)
     23{
     24	struct dg *data = chip->model_data;
     25
     26	data->cs4245_shadow[CS4245_SIGNAL_SEL] &= ~CS4245_A_OUT_SEL_MASK;
     27	if (data->output_sel == PLAYBACK_DST_HP) {
     28		/* mute FP (aux output) amplifier, switch rear jack to CS4245 */
     29		oxygen_set_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
     30	} else if (data->output_sel == PLAYBACK_DST_HP_FP) {
     31		/*
     32		 * Unmute FP amplifier, switch rear jack to CS4361;
     33		 * I2S channels 2,3,4 should be inactive.
     34		 */
     35		oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
     36		data->cs4245_shadow[CS4245_SIGNAL_SEL] |= CS4245_A_OUT_SEL_DAC;
     37	} else {
     38		/*
     39		 * 2.0, 4.0, 5.1: switch to CS4361, mute FP amp.,
     40		 * and change playback routing.
     41		 */
     42		oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
     43	}
     44	return cs4245_write_spi(chip, CS4245_SIGNAL_SEL);
     45}
     46
     47static int output_select_info(struct snd_kcontrol *ctl,
     48			      struct snd_ctl_elem_info *info)
     49{
     50	static const char *const names[3] = {
     51		"Stereo Headphones",
     52		"Stereo Headphones FP",
     53		"Multichannel",
     54	};
     55
     56	return snd_ctl_enum_info(info, 1, 3, names);
     57}
     58
     59static int output_select_get(struct snd_kcontrol *ctl,
     60			     struct snd_ctl_elem_value *value)
     61{
     62	struct oxygen *chip = ctl->private_data;
     63	struct dg *data = chip->model_data;
     64
     65	mutex_lock(&chip->mutex);
     66	value->value.enumerated.item[0] = data->output_sel;
     67	mutex_unlock(&chip->mutex);
     68	return 0;
     69}
     70
     71static int output_select_put(struct snd_kcontrol *ctl,
     72			     struct snd_ctl_elem_value *value)
     73{
     74	struct oxygen *chip = ctl->private_data;
     75	struct dg *data = chip->model_data;
     76	unsigned int new = value->value.enumerated.item[0];
     77	int changed = 0;
     78	int ret;
     79
     80	mutex_lock(&chip->mutex);
     81	if (data->output_sel != new) {
     82		data->output_sel = new;
     83		ret = output_select_apply(chip);
     84		changed = ret >= 0 ? 1 : ret;
     85		oxygen_update_dac_routing(chip);
     86	}
     87	mutex_unlock(&chip->mutex);
     88
     89	return changed;
     90}
     91
     92/* CS4245 Headphone Channels A&B Volume Control */
     93
     94static int hp_stereo_volume_info(struct snd_kcontrol *ctl,
     95				struct snd_ctl_elem_info *info)
     96{
     97	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
     98	info->count = 2;
     99	info->value.integer.min = 0;
    100	info->value.integer.max = 255;
    101	return 0;
    102}
    103
    104static int hp_stereo_volume_get(struct snd_kcontrol *ctl,
    105				struct snd_ctl_elem_value *val)
    106{
    107	struct oxygen *chip = ctl->private_data;
    108	struct dg *data = chip->model_data;
    109	unsigned int tmp;
    110
    111	mutex_lock(&chip->mutex);
    112	tmp = (~data->cs4245_shadow[CS4245_DAC_A_CTRL]) & 255;
    113	val->value.integer.value[0] = tmp;
    114	tmp = (~data->cs4245_shadow[CS4245_DAC_B_CTRL]) & 255;
    115	val->value.integer.value[1] = tmp;
    116	mutex_unlock(&chip->mutex);
    117	return 0;
    118}
    119
    120static int hp_stereo_volume_put(struct snd_kcontrol *ctl,
    121				struct snd_ctl_elem_value *val)
    122{
    123	struct oxygen *chip = ctl->private_data;
    124	struct dg *data = chip->model_data;
    125	int ret;
    126	int changed = 0;
    127	long new1 = val->value.integer.value[0];
    128	long new2 = val->value.integer.value[1];
    129
    130	if ((new1 > 255) || (new1 < 0) || (new2 > 255) || (new2 < 0))
    131		return -EINVAL;
    132
    133	mutex_lock(&chip->mutex);
    134	if ((data->cs4245_shadow[CS4245_DAC_A_CTRL] != ~new1) ||
    135	    (data->cs4245_shadow[CS4245_DAC_B_CTRL] != ~new2)) {
    136		data->cs4245_shadow[CS4245_DAC_A_CTRL] = ~new1;
    137		data->cs4245_shadow[CS4245_DAC_B_CTRL] = ~new2;
    138		ret = cs4245_write_spi(chip, CS4245_DAC_A_CTRL);
    139		if (ret >= 0)
    140			ret = cs4245_write_spi(chip, CS4245_DAC_B_CTRL);
    141		changed = ret >= 0 ? 1 : ret;
    142	}
    143	mutex_unlock(&chip->mutex);
    144
    145	return changed;
    146}
    147
    148/* Headphone Mute */
    149
    150static int hp_mute_get(struct snd_kcontrol *ctl,
    151			struct snd_ctl_elem_value *val)
    152{
    153	struct oxygen *chip = ctl->private_data;
    154	struct dg *data = chip->model_data;
    155
    156	mutex_lock(&chip->mutex);
    157	val->value.integer.value[0] =
    158		!(data->cs4245_shadow[CS4245_DAC_CTRL_1] & CS4245_MUTE_DAC);
    159	mutex_unlock(&chip->mutex);
    160	return 0;
    161}
    162
    163static int hp_mute_put(struct snd_kcontrol *ctl,
    164			struct snd_ctl_elem_value *val)
    165{
    166	struct oxygen *chip = ctl->private_data;
    167	struct dg *data = chip->model_data;
    168	int ret;
    169	int changed;
    170
    171	if (val->value.integer.value[0] > 1)
    172		return -EINVAL;
    173	mutex_lock(&chip->mutex);
    174	data->cs4245_shadow[CS4245_DAC_CTRL_1] &= ~CS4245_MUTE_DAC;
    175	data->cs4245_shadow[CS4245_DAC_CTRL_1] |=
    176		(~val->value.integer.value[0] << 2) & CS4245_MUTE_DAC;
    177	ret = cs4245_write_spi(chip, CS4245_DAC_CTRL_1);
    178	changed = ret >= 0 ? 1 : ret;
    179	mutex_unlock(&chip->mutex);
    180	return changed;
    181}
    182
    183/* capture volume for all sources */
    184
    185static int input_volume_apply(struct oxygen *chip, char left, char right)
    186{
    187	struct dg *data = chip->model_data;
    188	int ret;
    189
    190	data->cs4245_shadow[CS4245_PGA_A_CTRL] = left;
    191	data->cs4245_shadow[CS4245_PGA_B_CTRL] = right;
    192	ret = cs4245_write_spi(chip, CS4245_PGA_A_CTRL);
    193	if (ret < 0)
    194		return ret;
    195	return cs4245_write_spi(chip, CS4245_PGA_B_CTRL);
    196}
    197
    198static int input_vol_info(struct snd_kcontrol *ctl,
    199			  struct snd_ctl_elem_info *info)
    200{
    201	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
    202	info->count = 2;
    203	info->value.integer.min = 2 * -12;
    204	info->value.integer.max = 2 * 12;
    205	return 0;
    206}
    207
    208static int input_vol_get(struct snd_kcontrol *ctl,
    209			 struct snd_ctl_elem_value *value)
    210{
    211	struct oxygen *chip = ctl->private_data;
    212	struct dg *data = chip->model_data;
    213	unsigned int idx = ctl->private_value;
    214
    215	mutex_lock(&chip->mutex);
    216	value->value.integer.value[0] = data->input_vol[idx][0];
    217	value->value.integer.value[1] = data->input_vol[idx][1];
    218	mutex_unlock(&chip->mutex);
    219	return 0;
    220}
    221
    222static int input_vol_put(struct snd_kcontrol *ctl,
    223			 struct snd_ctl_elem_value *value)
    224{
    225	struct oxygen *chip = ctl->private_data;
    226	struct dg *data = chip->model_data;
    227	unsigned int idx = ctl->private_value;
    228	int changed = 0;
    229	int ret = 0;
    230
    231	if (value->value.integer.value[0] < 2 * -12 ||
    232	    value->value.integer.value[0] > 2 * 12 ||
    233	    value->value.integer.value[1] < 2 * -12 ||
    234	    value->value.integer.value[1] > 2 * 12)
    235		return -EINVAL;
    236	mutex_lock(&chip->mutex);
    237	changed = data->input_vol[idx][0] != value->value.integer.value[0] ||
    238		  data->input_vol[idx][1] != value->value.integer.value[1];
    239	if (changed) {
    240		data->input_vol[idx][0] = value->value.integer.value[0];
    241		data->input_vol[idx][1] = value->value.integer.value[1];
    242		if (idx == data->input_sel) {
    243			ret = input_volume_apply(chip,
    244				data->input_vol[idx][0],
    245				data->input_vol[idx][1]);
    246		}
    247		changed = ret >= 0 ? 1 : ret;
    248	}
    249	mutex_unlock(&chip->mutex);
    250	return changed;
    251}
    252
    253/* Capture Source */
    254
    255static int input_source_apply(struct oxygen *chip)
    256{
    257	struct dg *data = chip->model_data;
    258
    259	data->cs4245_shadow[CS4245_ANALOG_IN] &= ~CS4245_SEL_MASK;
    260	if (data->input_sel == CAPTURE_SRC_FP_MIC)
    261		data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_2;
    262	else if (data->input_sel == CAPTURE_SRC_LINE)
    263		data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_4;
    264	else if (data->input_sel != CAPTURE_SRC_MIC)
    265		data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_1;
    266	return cs4245_write_spi(chip, CS4245_ANALOG_IN);
    267}
    268
    269static int input_sel_info(struct snd_kcontrol *ctl,
    270			  struct snd_ctl_elem_info *info)
    271{
    272	static const char *const names[4] = {
    273		"Mic", "Front Mic", "Line", "Aux"
    274	};
    275
    276	return snd_ctl_enum_info(info, 1, 4, names);
    277}
    278
    279static int input_sel_get(struct snd_kcontrol *ctl,
    280			 struct snd_ctl_elem_value *value)
    281{
    282	struct oxygen *chip = ctl->private_data;
    283	struct dg *data = chip->model_data;
    284
    285	mutex_lock(&chip->mutex);
    286	value->value.enumerated.item[0] = data->input_sel;
    287	mutex_unlock(&chip->mutex);
    288	return 0;
    289}
    290
    291static int input_sel_put(struct snd_kcontrol *ctl,
    292			 struct snd_ctl_elem_value *value)
    293{
    294	struct oxygen *chip = ctl->private_data;
    295	struct dg *data = chip->model_data;
    296	int changed;
    297	int ret;
    298
    299	if (value->value.enumerated.item[0] > 3)
    300		return -EINVAL;
    301
    302	mutex_lock(&chip->mutex);
    303	changed = value->value.enumerated.item[0] != data->input_sel;
    304	if (changed) {
    305		data->input_sel = value->value.enumerated.item[0];
    306
    307		ret = input_source_apply(chip);
    308		if (ret >= 0)
    309			ret = input_volume_apply(chip,
    310				data->input_vol[data->input_sel][0],
    311				data->input_vol[data->input_sel][1]);
    312		changed = ret >= 0 ? 1 : ret;
    313	}
    314	mutex_unlock(&chip->mutex);
    315	return changed;
    316}
    317
    318/* ADC high-pass filter */
    319
    320static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
    321{
    322	static const char *const names[2] = { "Active", "Frozen" };
    323
    324	return snd_ctl_enum_info(info, 1, 2, names);
    325}
    326
    327static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
    328{
    329	struct oxygen *chip = ctl->private_data;
    330	struct dg *data = chip->model_data;
    331
    332	value->value.enumerated.item[0] =
    333		!!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE);
    334	return 0;
    335}
    336
    337static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
    338{
    339	struct oxygen *chip = ctl->private_data;
    340	struct dg *data = chip->model_data;
    341	u8 reg;
    342	int changed;
    343
    344	mutex_lock(&chip->mutex);
    345	reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE;
    346	if (value->value.enumerated.item[0])
    347		reg |= CS4245_HPF_FREEZE;
    348	changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL];
    349	if (changed) {
    350		data->cs4245_shadow[CS4245_ADC_CTRL] = reg;
    351		cs4245_write_spi(chip, CS4245_ADC_CTRL);
    352	}
    353	mutex_unlock(&chip->mutex);
    354	return changed;
    355}
    356
    357#define INPUT_VOLUME(xname, index) { \
    358	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
    359	.name = xname, \
    360	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
    361		  SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
    362	.info = input_vol_info, \
    363	.get = input_vol_get, \
    364	.put = input_vol_put, \
    365	.tlv = { .p = pga_db_scale }, \
    366	.private_value = index, \
    367}
    368static const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0);
    369static const DECLARE_TLV_DB_MINMAX(pga_db_scale, -1200, 1200);
    370static const struct snd_kcontrol_new dg_controls[] = {
    371	{
    372		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
    373		.name = "Analog Output Playback Enum",
    374		.info = output_select_info,
    375		.get = output_select_get,
    376		.put = output_select_put,
    377	},
    378	{
    379		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
    380		.name = "Headphone Playback Volume",
    381		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
    382			  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
    383		.info = hp_stereo_volume_info,
    384		.get = hp_stereo_volume_get,
    385		.put = hp_stereo_volume_put,
    386		.tlv = { .p = hp_db_scale, },
    387	},
    388	{
    389		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
    390		.name = "Headphone Playback Switch",
    391		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
    392		.info = snd_ctl_boolean_mono_info,
    393		.get = hp_mute_get,
    394		.put = hp_mute_put,
    395	},
    396	INPUT_VOLUME("Mic Capture Volume", CAPTURE_SRC_MIC),
    397	INPUT_VOLUME("Front Mic Capture Volume", CAPTURE_SRC_FP_MIC),
    398	INPUT_VOLUME("Line Capture Volume", CAPTURE_SRC_LINE),
    399	INPUT_VOLUME("Aux Capture Volume", CAPTURE_SRC_AUX),
    400	{
    401		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
    402		.name = "Capture Source",
    403		.info = input_sel_info,
    404		.get = input_sel_get,
    405		.put = input_sel_put,
    406	},
    407	{
    408		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
    409		.name = "ADC High-pass Filter Capture Enum",
    410		.info = hpf_info,
    411		.get = hpf_get,
    412		.put = hpf_put,
    413	},
    414};
    415
    416static int dg_control_filter(struct snd_kcontrol_new *template)
    417{
    418	if (!strncmp(template->name, "Master Playback ", 16))
    419		return 1;
    420	return 0;
    421}
    422
    423static int dg_mixer_init(struct oxygen *chip)
    424{
    425	unsigned int i;
    426	int err;
    427
    428	output_select_apply(chip);
    429	input_source_apply(chip);
    430	oxygen_update_dac_routing(chip);
    431
    432	for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) {
    433		err = snd_ctl_add(chip->card,
    434				  snd_ctl_new1(&dg_controls[i], chip));
    435		if (err < 0)
    436			return err;
    437	}
    438
    439	return 0;
    440}
    441
    442const struct oxygen_model model_xonar_dg = {
    443	.longname = "C-Media Oxygen HD Audio",
    444	.chip = "CMI8786",
    445	.init = dg_init,
    446	.control_filter = dg_control_filter,
    447	.mixer_init = dg_mixer_init,
    448	.cleanup = dg_cleanup,
    449	.suspend = dg_suspend,
    450	.resume = dg_resume,
    451	.set_dac_params = set_cs4245_dac_params,
    452	.set_adc_params = set_cs4245_adc_params,
    453	.adjust_dac_routing = adjust_dg_dac_routing,
    454	.dump_registers = dump_cs4245_registers,
    455	.model_data_size = sizeof(struct dg),
    456	.device_config = PLAYBACK_0_TO_I2S |
    457			 PLAYBACK_1_TO_SPDIF |
    458			 CAPTURE_0_FROM_I2S_1 |
    459			 CAPTURE_1_FROM_SPDIF,
    460	.dac_channels_pcm = 6,
    461	.dac_channels_mixer = 0,
    462	.function_flags = OXYGEN_FUNCTION_SPI,
    463	.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
    464	.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
    465	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
    466	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
    467};