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