18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Mixer controls for the Xonar DG/DGX
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
68c2ecf20Sopenharmony_ci * Copyright (c) Roman Volkov <v1ron@mail.ru>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/pci.h>
108c2ecf20Sopenharmony_ci#include <linux/delay.h>
118c2ecf20Sopenharmony_ci#include <sound/control.h>
128c2ecf20Sopenharmony_ci#include <sound/core.h>
138c2ecf20Sopenharmony_ci#include <sound/info.h>
148c2ecf20Sopenharmony_ci#include <sound/pcm.h>
158c2ecf20Sopenharmony_ci#include <sound/tlv.h>
168c2ecf20Sopenharmony_ci#include "oxygen.h"
178c2ecf20Sopenharmony_ci#include "xonar_dg.h"
188c2ecf20Sopenharmony_ci#include "cs4245.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* analog output select */
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic int output_select_apply(struct oxygen *chip)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	struct dg *data = chip->model_data;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	data->cs4245_shadow[CS4245_SIGNAL_SEL] &= ~CS4245_A_OUT_SEL_MASK;
278c2ecf20Sopenharmony_ci	if (data->output_sel == PLAYBACK_DST_HP) {
288c2ecf20Sopenharmony_ci		/* mute FP (aux output) amplifier, switch rear jack to CS4245 */
298c2ecf20Sopenharmony_ci		oxygen_set_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
308c2ecf20Sopenharmony_ci	} else if (data->output_sel == PLAYBACK_DST_HP_FP) {
318c2ecf20Sopenharmony_ci		/*
328c2ecf20Sopenharmony_ci		 * Unmute FP amplifier, switch rear jack to CS4361;
338c2ecf20Sopenharmony_ci		 * I2S channels 2,3,4 should be inactive.
348c2ecf20Sopenharmony_ci		 */
358c2ecf20Sopenharmony_ci		oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
368c2ecf20Sopenharmony_ci		data->cs4245_shadow[CS4245_SIGNAL_SEL] |= CS4245_A_OUT_SEL_DAC;
378c2ecf20Sopenharmony_ci	} else {
388c2ecf20Sopenharmony_ci		/*
398c2ecf20Sopenharmony_ci		 * 2.0, 4.0, 5.1: switch to CS4361, mute FP amp.,
408c2ecf20Sopenharmony_ci		 * and change playback routing.
418c2ecf20Sopenharmony_ci		 */
428c2ecf20Sopenharmony_ci		oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
438c2ecf20Sopenharmony_ci	}
448c2ecf20Sopenharmony_ci	return cs4245_write_spi(chip, CS4245_SIGNAL_SEL);
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic int output_select_info(struct snd_kcontrol *ctl,
488c2ecf20Sopenharmony_ci			      struct snd_ctl_elem_info *info)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	static const char *const names[3] = {
518c2ecf20Sopenharmony_ci		"Stereo Headphones",
528c2ecf20Sopenharmony_ci		"Stereo Headphones FP",
538c2ecf20Sopenharmony_ci		"Multichannel",
548c2ecf20Sopenharmony_ci	};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(info, 1, 3, names);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int output_select_get(struct snd_kcontrol *ctl,
608c2ecf20Sopenharmony_ci			     struct snd_ctl_elem_value *value)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
638c2ecf20Sopenharmony_ci	struct dg *data = chip->model_data;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
668c2ecf20Sopenharmony_ci	value->value.enumerated.item[0] = data->output_sel;
678c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
688c2ecf20Sopenharmony_ci	return 0;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int output_select_put(struct snd_kcontrol *ctl,
728c2ecf20Sopenharmony_ci			     struct snd_ctl_elem_value *value)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
758c2ecf20Sopenharmony_ci	struct dg *data = chip->model_data;
768c2ecf20Sopenharmony_ci	unsigned int new = value->value.enumerated.item[0];
778c2ecf20Sopenharmony_ci	int changed = 0;
788c2ecf20Sopenharmony_ci	int ret;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
818c2ecf20Sopenharmony_ci	if (data->output_sel != new) {
828c2ecf20Sopenharmony_ci		data->output_sel = new;
838c2ecf20Sopenharmony_ci		ret = output_select_apply(chip);
848c2ecf20Sopenharmony_ci		changed = ret >= 0 ? 1 : ret;
858c2ecf20Sopenharmony_ci		oxygen_update_dac_routing(chip);
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return changed;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/* CS4245 Headphone Channels A&B Volume Control */
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic int hp_stereo_volume_info(struct snd_kcontrol *ctl,
958c2ecf20Sopenharmony_ci				struct snd_ctl_elem_info *info)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
988c2ecf20Sopenharmony_ci	info->count = 2;
998c2ecf20Sopenharmony_ci	info->value.integer.min = 0;
1008c2ecf20Sopenharmony_ci	info->value.integer.max = 255;
1018c2ecf20Sopenharmony_ci	return 0;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int hp_stereo_volume_get(struct snd_kcontrol *ctl,
1058c2ecf20Sopenharmony_ci				struct snd_ctl_elem_value *val)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
1088c2ecf20Sopenharmony_ci	struct dg *data = chip->model_data;
1098c2ecf20Sopenharmony_ci	unsigned int tmp;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
1128c2ecf20Sopenharmony_ci	tmp = (~data->cs4245_shadow[CS4245_DAC_A_CTRL]) & 255;
1138c2ecf20Sopenharmony_ci	val->value.integer.value[0] = tmp;
1148c2ecf20Sopenharmony_ci	tmp = (~data->cs4245_shadow[CS4245_DAC_B_CTRL]) & 255;
1158c2ecf20Sopenharmony_ci	val->value.integer.value[1] = tmp;
1168c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
1178c2ecf20Sopenharmony_ci	return 0;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int hp_stereo_volume_put(struct snd_kcontrol *ctl,
1218c2ecf20Sopenharmony_ci				struct snd_ctl_elem_value *val)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
1248c2ecf20Sopenharmony_ci	struct dg *data = chip->model_data;
1258c2ecf20Sopenharmony_ci	int ret;
1268c2ecf20Sopenharmony_ci	int changed = 0;
1278c2ecf20Sopenharmony_ci	long new1 = val->value.integer.value[0];
1288c2ecf20Sopenharmony_ci	long new2 = val->value.integer.value[1];
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	if ((new1 > 255) || (new1 < 0) || (new2 > 255) || (new2 < 0))
1318c2ecf20Sopenharmony_ci		return -EINVAL;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
1348c2ecf20Sopenharmony_ci	if ((data->cs4245_shadow[CS4245_DAC_A_CTRL] != ~new1) ||
1358c2ecf20Sopenharmony_ci	    (data->cs4245_shadow[CS4245_DAC_B_CTRL] != ~new2)) {
1368c2ecf20Sopenharmony_ci		data->cs4245_shadow[CS4245_DAC_A_CTRL] = ~new1;
1378c2ecf20Sopenharmony_ci		data->cs4245_shadow[CS4245_DAC_B_CTRL] = ~new2;
1388c2ecf20Sopenharmony_ci		ret = cs4245_write_spi(chip, CS4245_DAC_A_CTRL);
1398c2ecf20Sopenharmony_ci		if (ret >= 0)
1408c2ecf20Sopenharmony_ci			ret = cs4245_write_spi(chip, CS4245_DAC_B_CTRL);
1418c2ecf20Sopenharmony_ci		changed = ret >= 0 ? 1 : ret;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	return changed;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/* Headphone Mute */
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int hp_mute_get(struct snd_kcontrol *ctl,
1518c2ecf20Sopenharmony_ci			struct snd_ctl_elem_value *val)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
1548c2ecf20Sopenharmony_ci	struct dg *data = chip->model_data;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
1578c2ecf20Sopenharmony_ci	val->value.integer.value[0] =
1588c2ecf20Sopenharmony_ci		!(data->cs4245_shadow[CS4245_DAC_CTRL_1] & CS4245_MUTE_DAC);
1598c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
1608c2ecf20Sopenharmony_ci	return 0;
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic int hp_mute_put(struct snd_kcontrol *ctl,
1648c2ecf20Sopenharmony_ci			struct snd_ctl_elem_value *val)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
1678c2ecf20Sopenharmony_ci	struct dg *data = chip->model_data;
1688c2ecf20Sopenharmony_ci	int ret;
1698c2ecf20Sopenharmony_ci	int changed;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (val->value.integer.value[0] > 1)
1728c2ecf20Sopenharmony_ci		return -EINVAL;
1738c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
1748c2ecf20Sopenharmony_ci	data->cs4245_shadow[CS4245_DAC_CTRL_1] &= ~CS4245_MUTE_DAC;
1758c2ecf20Sopenharmony_ci	data->cs4245_shadow[CS4245_DAC_CTRL_1] |=
1768c2ecf20Sopenharmony_ci		(~val->value.integer.value[0] << 2) & CS4245_MUTE_DAC;
1778c2ecf20Sopenharmony_ci	ret = cs4245_write_spi(chip, CS4245_DAC_CTRL_1);
1788c2ecf20Sopenharmony_ci	changed = ret >= 0 ? 1 : ret;
1798c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
1808c2ecf20Sopenharmony_ci	return changed;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci/* capture volume for all sources */
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int input_volume_apply(struct oxygen *chip, char left, char right)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct dg *data = chip->model_data;
1888c2ecf20Sopenharmony_ci	int ret;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	data->cs4245_shadow[CS4245_PGA_A_CTRL] = left;
1918c2ecf20Sopenharmony_ci	data->cs4245_shadow[CS4245_PGA_B_CTRL] = right;
1928c2ecf20Sopenharmony_ci	ret = cs4245_write_spi(chip, CS4245_PGA_A_CTRL);
1938c2ecf20Sopenharmony_ci	if (ret < 0)
1948c2ecf20Sopenharmony_ci		return ret;
1958c2ecf20Sopenharmony_ci	return cs4245_write_spi(chip, CS4245_PGA_B_CTRL);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic int input_vol_info(struct snd_kcontrol *ctl,
1998c2ecf20Sopenharmony_ci			  struct snd_ctl_elem_info *info)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
2028c2ecf20Sopenharmony_ci	info->count = 2;
2038c2ecf20Sopenharmony_ci	info->value.integer.min = 2 * -12;
2048c2ecf20Sopenharmony_ci	info->value.integer.max = 2 * 12;
2058c2ecf20Sopenharmony_ci	return 0;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic int input_vol_get(struct snd_kcontrol *ctl,
2098c2ecf20Sopenharmony_ci			 struct snd_ctl_elem_value *value)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
2128c2ecf20Sopenharmony_ci	struct dg *data = chip->model_data;
2138c2ecf20Sopenharmony_ci	unsigned int idx = ctl->private_value;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
2168c2ecf20Sopenharmony_ci	value->value.integer.value[0] = data->input_vol[idx][0];
2178c2ecf20Sopenharmony_ci	value->value.integer.value[1] = data->input_vol[idx][1];
2188c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
2198c2ecf20Sopenharmony_ci	return 0;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic int input_vol_put(struct snd_kcontrol *ctl,
2238c2ecf20Sopenharmony_ci			 struct snd_ctl_elem_value *value)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
2268c2ecf20Sopenharmony_ci	struct dg *data = chip->model_data;
2278c2ecf20Sopenharmony_ci	unsigned int idx = ctl->private_value;
2288c2ecf20Sopenharmony_ci	int changed = 0;
2298c2ecf20Sopenharmony_ci	int ret = 0;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	if (value->value.integer.value[0] < 2 * -12 ||
2328c2ecf20Sopenharmony_ci	    value->value.integer.value[0] > 2 * 12 ||
2338c2ecf20Sopenharmony_ci	    value->value.integer.value[1] < 2 * -12 ||
2348c2ecf20Sopenharmony_ci	    value->value.integer.value[1] > 2 * 12)
2358c2ecf20Sopenharmony_ci		return -EINVAL;
2368c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
2378c2ecf20Sopenharmony_ci	changed = data->input_vol[idx][0] != value->value.integer.value[0] ||
2388c2ecf20Sopenharmony_ci		  data->input_vol[idx][1] != value->value.integer.value[1];
2398c2ecf20Sopenharmony_ci	if (changed) {
2408c2ecf20Sopenharmony_ci		data->input_vol[idx][0] = value->value.integer.value[0];
2418c2ecf20Sopenharmony_ci		data->input_vol[idx][1] = value->value.integer.value[1];
2428c2ecf20Sopenharmony_ci		if (idx == data->input_sel) {
2438c2ecf20Sopenharmony_ci			ret = input_volume_apply(chip,
2448c2ecf20Sopenharmony_ci				data->input_vol[idx][0],
2458c2ecf20Sopenharmony_ci				data->input_vol[idx][1]);
2468c2ecf20Sopenharmony_ci		}
2478c2ecf20Sopenharmony_ci		changed = ret >= 0 ? 1 : ret;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
2508c2ecf20Sopenharmony_ci	return changed;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci/* Capture Source */
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic int input_source_apply(struct oxygen *chip)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	struct dg *data = chip->model_data;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	data->cs4245_shadow[CS4245_ANALOG_IN] &= ~CS4245_SEL_MASK;
2608c2ecf20Sopenharmony_ci	if (data->input_sel == CAPTURE_SRC_FP_MIC)
2618c2ecf20Sopenharmony_ci		data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_2;
2628c2ecf20Sopenharmony_ci	else if (data->input_sel == CAPTURE_SRC_LINE)
2638c2ecf20Sopenharmony_ci		data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_4;
2648c2ecf20Sopenharmony_ci	else if (data->input_sel != CAPTURE_SRC_MIC)
2658c2ecf20Sopenharmony_ci		data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_1;
2668c2ecf20Sopenharmony_ci	return cs4245_write_spi(chip, CS4245_ANALOG_IN);
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic int input_sel_info(struct snd_kcontrol *ctl,
2708c2ecf20Sopenharmony_ci			  struct snd_ctl_elem_info *info)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	static const char *const names[4] = {
2738c2ecf20Sopenharmony_ci		"Mic", "Front Mic", "Line", "Aux"
2748c2ecf20Sopenharmony_ci	};
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(info, 1, 4, names);
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic int input_sel_get(struct snd_kcontrol *ctl,
2808c2ecf20Sopenharmony_ci			 struct snd_ctl_elem_value *value)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
2838c2ecf20Sopenharmony_ci	struct dg *data = chip->model_data;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
2868c2ecf20Sopenharmony_ci	value->value.enumerated.item[0] = data->input_sel;
2878c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
2888c2ecf20Sopenharmony_ci	return 0;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic int input_sel_put(struct snd_kcontrol *ctl,
2928c2ecf20Sopenharmony_ci			 struct snd_ctl_elem_value *value)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
2958c2ecf20Sopenharmony_ci	struct dg *data = chip->model_data;
2968c2ecf20Sopenharmony_ci	int changed;
2978c2ecf20Sopenharmony_ci	int ret;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (value->value.enumerated.item[0] > 3)
3008c2ecf20Sopenharmony_ci		return -EINVAL;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
3038c2ecf20Sopenharmony_ci	changed = value->value.enumerated.item[0] != data->input_sel;
3048c2ecf20Sopenharmony_ci	if (changed) {
3058c2ecf20Sopenharmony_ci		data->input_sel = value->value.enumerated.item[0];
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci		ret = input_source_apply(chip);
3088c2ecf20Sopenharmony_ci		if (ret >= 0)
3098c2ecf20Sopenharmony_ci			ret = input_volume_apply(chip,
3108c2ecf20Sopenharmony_ci				data->input_vol[data->input_sel][0],
3118c2ecf20Sopenharmony_ci				data->input_vol[data->input_sel][1]);
3128c2ecf20Sopenharmony_ci		changed = ret >= 0 ? 1 : ret;
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
3158c2ecf20Sopenharmony_ci	return changed;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci/* ADC high-pass filter */
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	static const char *const names[2] = { "Active", "Frozen" };
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(info, 1, 2, names);
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
3308c2ecf20Sopenharmony_ci	struct dg *data = chip->model_data;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	value->value.enumerated.item[0] =
3338c2ecf20Sopenharmony_ci		!!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE);
3348c2ecf20Sopenharmony_ci	return 0;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
3408c2ecf20Sopenharmony_ci	struct dg *data = chip->model_data;
3418c2ecf20Sopenharmony_ci	u8 reg;
3428c2ecf20Sopenharmony_ci	int changed;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
3458c2ecf20Sopenharmony_ci	reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE;
3468c2ecf20Sopenharmony_ci	if (value->value.enumerated.item[0])
3478c2ecf20Sopenharmony_ci		reg |= CS4245_HPF_FREEZE;
3488c2ecf20Sopenharmony_ci	changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL];
3498c2ecf20Sopenharmony_ci	if (changed) {
3508c2ecf20Sopenharmony_ci		data->cs4245_shadow[CS4245_ADC_CTRL] = reg;
3518c2ecf20Sopenharmony_ci		cs4245_write_spi(chip, CS4245_ADC_CTRL);
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
3548c2ecf20Sopenharmony_ci	return changed;
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci#define INPUT_VOLUME(xname, index) { \
3588c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3598c2ecf20Sopenharmony_ci	.name = xname, \
3608c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
3618c2ecf20Sopenharmony_ci		  SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
3628c2ecf20Sopenharmony_ci	.info = input_vol_info, \
3638c2ecf20Sopenharmony_ci	.get = input_vol_get, \
3648c2ecf20Sopenharmony_ci	.put = input_vol_put, \
3658c2ecf20Sopenharmony_ci	.tlv = { .p = pga_db_scale }, \
3668c2ecf20Sopenharmony_ci	.private_value = index, \
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0);
3698c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_MINMAX(pga_db_scale, -1200, 1200);
3708c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new dg_controls[] = {
3718c2ecf20Sopenharmony_ci	{
3728c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3738c2ecf20Sopenharmony_ci		.name = "Analog Output Playback Enum",
3748c2ecf20Sopenharmony_ci		.info = output_select_info,
3758c2ecf20Sopenharmony_ci		.get = output_select_get,
3768c2ecf20Sopenharmony_ci		.put = output_select_put,
3778c2ecf20Sopenharmony_ci	},
3788c2ecf20Sopenharmony_ci	{
3798c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3808c2ecf20Sopenharmony_ci		.name = "Headphone Playback Volume",
3818c2ecf20Sopenharmony_ci		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
3828c2ecf20Sopenharmony_ci			  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
3838c2ecf20Sopenharmony_ci		.info = hp_stereo_volume_info,
3848c2ecf20Sopenharmony_ci		.get = hp_stereo_volume_get,
3858c2ecf20Sopenharmony_ci		.put = hp_stereo_volume_put,
3868c2ecf20Sopenharmony_ci		.tlv = { .p = hp_db_scale, },
3878c2ecf20Sopenharmony_ci	},
3888c2ecf20Sopenharmony_ci	{
3898c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3908c2ecf20Sopenharmony_ci		.name = "Headphone Playback Switch",
3918c2ecf20Sopenharmony_ci		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
3928c2ecf20Sopenharmony_ci		.info = snd_ctl_boolean_mono_info,
3938c2ecf20Sopenharmony_ci		.get = hp_mute_get,
3948c2ecf20Sopenharmony_ci		.put = hp_mute_put,
3958c2ecf20Sopenharmony_ci	},
3968c2ecf20Sopenharmony_ci	INPUT_VOLUME("Mic Capture Volume", CAPTURE_SRC_MIC),
3978c2ecf20Sopenharmony_ci	INPUT_VOLUME("Front Mic Capture Volume", CAPTURE_SRC_FP_MIC),
3988c2ecf20Sopenharmony_ci	INPUT_VOLUME("Line Capture Volume", CAPTURE_SRC_LINE),
3998c2ecf20Sopenharmony_ci	INPUT_VOLUME("Aux Capture Volume", CAPTURE_SRC_AUX),
4008c2ecf20Sopenharmony_ci	{
4018c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4028c2ecf20Sopenharmony_ci		.name = "Capture Source",
4038c2ecf20Sopenharmony_ci		.info = input_sel_info,
4048c2ecf20Sopenharmony_ci		.get = input_sel_get,
4058c2ecf20Sopenharmony_ci		.put = input_sel_put,
4068c2ecf20Sopenharmony_ci	},
4078c2ecf20Sopenharmony_ci	{
4088c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4098c2ecf20Sopenharmony_ci		.name = "ADC High-pass Filter Capture Enum",
4108c2ecf20Sopenharmony_ci		.info = hpf_info,
4118c2ecf20Sopenharmony_ci		.get = hpf_get,
4128c2ecf20Sopenharmony_ci		.put = hpf_put,
4138c2ecf20Sopenharmony_ci	},
4148c2ecf20Sopenharmony_ci};
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic int dg_control_filter(struct snd_kcontrol_new *template)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	if (!strncmp(template->name, "Master Playback ", 16))
4198c2ecf20Sopenharmony_ci		return 1;
4208c2ecf20Sopenharmony_ci	return 0;
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic int dg_mixer_init(struct oxygen *chip)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	unsigned int i;
4268c2ecf20Sopenharmony_ci	int err;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	output_select_apply(chip);
4298c2ecf20Sopenharmony_ci	input_source_apply(chip);
4308c2ecf20Sopenharmony_ci	oxygen_update_dac_routing(chip);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) {
4338c2ecf20Sopenharmony_ci		err = snd_ctl_add(chip->card,
4348c2ecf20Sopenharmony_ci				  snd_ctl_new1(&dg_controls[i], chip));
4358c2ecf20Sopenharmony_ci		if (err < 0)
4368c2ecf20Sopenharmony_ci			return err;
4378c2ecf20Sopenharmony_ci	}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	return 0;
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ciconst struct oxygen_model model_xonar_dg = {
4438c2ecf20Sopenharmony_ci	.longname = "C-Media Oxygen HD Audio",
4448c2ecf20Sopenharmony_ci	.chip = "CMI8786",
4458c2ecf20Sopenharmony_ci	.init = dg_init,
4468c2ecf20Sopenharmony_ci	.control_filter = dg_control_filter,
4478c2ecf20Sopenharmony_ci	.mixer_init = dg_mixer_init,
4488c2ecf20Sopenharmony_ci	.cleanup = dg_cleanup,
4498c2ecf20Sopenharmony_ci	.suspend = dg_suspend,
4508c2ecf20Sopenharmony_ci	.resume = dg_resume,
4518c2ecf20Sopenharmony_ci	.set_dac_params = set_cs4245_dac_params,
4528c2ecf20Sopenharmony_ci	.set_adc_params = set_cs4245_adc_params,
4538c2ecf20Sopenharmony_ci	.adjust_dac_routing = adjust_dg_dac_routing,
4548c2ecf20Sopenharmony_ci	.dump_registers = dump_cs4245_registers,
4558c2ecf20Sopenharmony_ci	.model_data_size = sizeof(struct dg),
4568c2ecf20Sopenharmony_ci	.device_config = PLAYBACK_0_TO_I2S |
4578c2ecf20Sopenharmony_ci			 PLAYBACK_1_TO_SPDIF |
4588c2ecf20Sopenharmony_ci			 CAPTURE_0_FROM_I2S_1 |
4598c2ecf20Sopenharmony_ci			 CAPTURE_1_FROM_SPDIF,
4608c2ecf20Sopenharmony_ci	.dac_channels_pcm = 6,
4618c2ecf20Sopenharmony_ci	.dac_channels_mixer = 0,
4628c2ecf20Sopenharmony_ci	.function_flags = OXYGEN_FUNCTION_SPI,
4638c2ecf20Sopenharmony_ci	.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
4648c2ecf20Sopenharmony_ci	.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
4658c2ecf20Sopenharmony_ci	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
4668c2ecf20Sopenharmony_ci	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
4678c2ecf20Sopenharmony_ci};
468