xref: /openbmc/linux/sound/pci/oxygen/xonar_dg_mixer.c (revision 041f26b6257d15449e1634ba8dea426dd06514c7)
1*041f26b6SRoman Volkov /*
2*041f26b6SRoman Volkov  * Mixer controls for the Xonar DG/DGX
3*041f26b6SRoman Volkov  *
4*041f26b6SRoman Volkov  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
5*041f26b6SRoman Volkov  * Copyright (c) Roman Volkov <v1ron@mail.ru>
6*041f26b6SRoman Volkov  *
7*041f26b6SRoman Volkov  *  This driver is free software; you can redistribute it and/or modify
8*041f26b6SRoman Volkov  *  it under the terms of the GNU General Public License, version 2.
9*041f26b6SRoman Volkov  *
10*041f26b6SRoman Volkov  *  This driver is distributed in the hope that it will be useful,
11*041f26b6SRoman Volkov  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12*041f26b6SRoman Volkov  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13*041f26b6SRoman Volkov  *  GNU General Public License for more details.
14*041f26b6SRoman Volkov  *
15*041f26b6SRoman Volkov  *  You should have received a copy of the GNU General Public License
16*041f26b6SRoman Volkov  *  along with this driver; if not, see <http://www.gnu.org/licenses/>.
17*041f26b6SRoman Volkov  */
18*041f26b6SRoman Volkov 
19*041f26b6SRoman Volkov #include <linux/pci.h>
20*041f26b6SRoman Volkov #include <linux/delay.h>
21*041f26b6SRoman Volkov #include <sound/control.h>
22*041f26b6SRoman Volkov #include <sound/core.h>
23*041f26b6SRoman Volkov #include <sound/info.h>
24*041f26b6SRoman Volkov #include <sound/pcm.h>
25*041f26b6SRoman Volkov #include <sound/tlv.h>
26*041f26b6SRoman Volkov #include "oxygen.h"
27*041f26b6SRoman Volkov #include "xonar_dg.h"
28*041f26b6SRoman Volkov #include "cs4245.h"
29*041f26b6SRoman Volkov 
30*041f26b6SRoman Volkov static int output_switch_info(struct snd_kcontrol *ctl,
31*041f26b6SRoman Volkov 			      struct snd_ctl_elem_info *info)
32*041f26b6SRoman Volkov {
33*041f26b6SRoman Volkov 	static const char *const names[3] = {
34*041f26b6SRoman Volkov 		"Speakers", "Headphones", "FP Headphones"
35*041f26b6SRoman Volkov 	};
36*041f26b6SRoman Volkov 
37*041f26b6SRoman Volkov 	return snd_ctl_enum_info(info, 1, 3, names);
38*041f26b6SRoman Volkov }
39*041f26b6SRoman Volkov 
40*041f26b6SRoman Volkov static int output_switch_get(struct snd_kcontrol *ctl,
41*041f26b6SRoman Volkov 			     struct snd_ctl_elem_value *value)
42*041f26b6SRoman Volkov {
43*041f26b6SRoman Volkov 	struct oxygen *chip = ctl->private_data;
44*041f26b6SRoman Volkov 	struct dg *data = chip->model_data;
45*041f26b6SRoman Volkov 
46*041f26b6SRoman Volkov 	mutex_lock(&chip->mutex);
47*041f26b6SRoman Volkov 	value->value.enumerated.item[0] = data->output_sel;
48*041f26b6SRoman Volkov 	mutex_unlock(&chip->mutex);
49*041f26b6SRoman Volkov 	return 0;
50*041f26b6SRoman Volkov }
51*041f26b6SRoman Volkov 
52*041f26b6SRoman Volkov static int output_switch_put(struct snd_kcontrol *ctl,
53*041f26b6SRoman Volkov 			     struct snd_ctl_elem_value *value)
54*041f26b6SRoman Volkov {
55*041f26b6SRoman Volkov 	struct oxygen *chip = ctl->private_data;
56*041f26b6SRoman Volkov 	struct dg *data = chip->model_data;
57*041f26b6SRoman Volkov 	u8 reg;
58*041f26b6SRoman Volkov 	int changed;
59*041f26b6SRoman Volkov 
60*041f26b6SRoman Volkov 	if (value->value.enumerated.item[0] > 2)
61*041f26b6SRoman Volkov 		return -EINVAL;
62*041f26b6SRoman Volkov 
63*041f26b6SRoman Volkov 	mutex_lock(&chip->mutex);
64*041f26b6SRoman Volkov 	changed = value->value.enumerated.item[0] != data->output_sel;
65*041f26b6SRoman Volkov 	if (changed) {
66*041f26b6SRoman Volkov 		data->output_sel = value->value.enumerated.item[0];
67*041f26b6SRoman Volkov 
68*041f26b6SRoman Volkov 		reg = data->cs4245_shadow[CS4245_SIGNAL_SEL] &
69*041f26b6SRoman Volkov 						~CS4245_A_OUT_SEL_MASK;
70*041f26b6SRoman Volkov 		reg |= data->output_sel == 2 ?
71*041f26b6SRoman Volkov 				CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ;
72*041f26b6SRoman Volkov 		cs4245_write_cached(chip, CS4245_SIGNAL_SEL, reg);
73*041f26b6SRoman Volkov 
74*041f26b6SRoman Volkov 		cs4245_write_cached(chip, CS4245_DAC_A_CTRL,
75*041f26b6SRoman Volkov 				    data->output_sel ? data->hp_vol_att : 0);
76*041f26b6SRoman Volkov 		cs4245_write_cached(chip, CS4245_DAC_B_CTRL,
77*041f26b6SRoman Volkov 				    data->output_sel ? data->hp_vol_att : 0);
78*041f26b6SRoman Volkov 
79*041f26b6SRoman Volkov 		oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
80*041f26b6SRoman Volkov 				      data->output_sel == 1 ? GPIO_HP_REAR : 0,
81*041f26b6SRoman Volkov 				      GPIO_HP_REAR);
82*041f26b6SRoman Volkov 	}
83*041f26b6SRoman Volkov 	mutex_unlock(&chip->mutex);
84*041f26b6SRoman Volkov 	return changed;
85*041f26b6SRoman Volkov }
86*041f26b6SRoman Volkov 
87*041f26b6SRoman Volkov static int hp_volume_offset_info(struct snd_kcontrol *ctl,
88*041f26b6SRoman Volkov 				 struct snd_ctl_elem_info *info)
89*041f26b6SRoman Volkov {
90*041f26b6SRoman Volkov 	static const char *const names[3] = {
91*041f26b6SRoman Volkov 		"< 64 ohms", "64-150 ohms", "150-300 ohms"
92*041f26b6SRoman Volkov 	};
93*041f26b6SRoman Volkov 
94*041f26b6SRoman Volkov 	return snd_ctl_enum_info(info, 1, 3, names);
95*041f26b6SRoman Volkov }
96*041f26b6SRoman Volkov 
97*041f26b6SRoman Volkov static int hp_volume_offset_get(struct snd_kcontrol *ctl,
98*041f26b6SRoman Volkov 				struct snd_ctl_elem_value *value)
99*041f26b6SRoman Volkov {
100*041f26b6SRoman Volkov 	struct oxygen *chip = ctl->private_data;
101*041f26b6SRoman Volkov 	struct dg *data = chip->model_data;
102*041f26b6SRoman Volkov 
103*041f26b6SRoman Volkov 	mutex_lock(&chip->mutex);
104*041f26b6SRoman Volkov 	if (data->hp_vol_att > 2 * 7)
105*041f26b6SRoman Volkov 		value->value.enumerated.item[0] = 0;
106*041f26b6SRoman Volkov 	else if (data->hp_vol_att > 0)
107*041f26b6SRoman Volkov 		value->value.enumerated.item[0] = 1;
108*041f26b6SRoman Volkov 	else
109*041f26b6SRoman Volkov 		value->value.enumerated.item[0] = 2;
110*041f26b6SRoman Volkov 	mutex_unlock(&chip->mutex);
111*041f26b6SRoman Volkov 	return 0;
112*041f26b6SRoman Volkov }
113*041f26b6SRoman Volkov 
114*041f26b6SRoman Volkov static int hp_volume_offset_put(struct snd_kcontrol *ctl,
115*041f26b6SRoman Volkov 				struct snd_ctl_elem_value *value)
116*041f26b6SRoman Volkov {
117*041f26b6SRoman Volkov 	static const s8 atts[3] = { 2 * 16, 2 * 7, 0 };
118*041f26b6SRoman Volkov 	struct oxygen *chip = ctl->private_data;
119*041f26b6SRoman Volkov 	struct dg *data = chip->model_data;
120*041f26b6SRoman Volkov 	s8 att;
121*041f26b6SRoman Volkov 	int changed;
122*041f26b6SRoman Volkov 
123*041f26b6SRoman Volkov 	if (value->value.enumerated.item[0] > 2)
124*041f26b6SRoman Volkov 		return -EINVAL;
125*041f26b6SRoman Volkov 	att = atts[value->value.enumerated.item[0]];
126*041f26b6SRoman Volkov 	mutex_lock(&chip->mutex);
127*041f26b6SRoman Volkov 	changed = att != data->hp_vol_att;
128*041f26b6SRoman Volkov 	if (changed) {
129*041f26b6SRoman Volkov 		data->hp_vol_att = att;
130*041f26b6SRoman Volkov 		if (data->output_sel) {
131*041f26b6SRoman Volkov 			cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att);
132*041f26b6SRoman Volkov 			cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att);
133*041f26b6SRoman Volkov 		}
134*041f26b6SRoman Volkov 	}
135*041f26b6SRoman Volkov 	mutex_unlock(&chip->mutex);
136*041f26b6SRoman Volkov 	return changed;
137*041f26b6SRoman Volkov }
138*041f26b6SRoman Volkov 
139*041f26b6SRoman Volkov static int input_vol_info(struct snd_kcontrol *ctl,
140*041f26b6SRoman Volkov 			  struct snd_ctl_elem_info *info)
141*041f26b6SRoman Volkov {
142*041f26b6SRoman Volkov 	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
143*041f26b6SRoman Volkov 	info->count = 2;
144*041f26b6SRoman Volkov 	info->value.integer.min = 2 * -12;
145*041f26b6SRoman Volkov 	info->value.integer.max = 2 * 12;
146*041f26b6SRoman Volkov 	return 0;
147*041f26b6SRoman Volkov }
148*041f26b6SRoman Volkov 
149*041f26b6SRoman Volkov static int input_vol_get(struct snd_kcontrol *ctl,
150*041f26b6SRoman Volkov 			 struct snd_ctl_elem_value *value)
151*041f26b6SRoman Volkov {
152*041f26b6SRoman Volkov 	struct oxygen *chip = ctl->private_data;
153*041f26b6SRoman Volkov 	struct dg *data = chip->model_data;
154*041f26b6SRoman Volkov 	unsigned int idx = ctl->private_value;
155*041f26b6SRoman Volkov 
156*041f26b6SRoman Volkov 	mutex_lock(&chip->mutex);
157*041f26b6SRoman Volkov 	value->value.integer.value[0] = data->input_vol[idx][0];
158*041f26b6SRoman Volkov 	value->value.integer.value[1] = data->input_vol[idx][1];
159*041f26b6SRoman Volkov 	mutex_unlock(&chip->mutex);
160*041f26b6SRoman Volkov 	return 0;
161*041f26b6SRoman Volkov }
162*041f26b6SRoman Volkov 
163*041f26b6SRoman Volkov static int input_vol_put(struct snd_kcontrol *ctl,
164*041f26b6SRoman Volkov 			 struct snd_ctl_elem_value *value)
165*041f26b6SRoman Volkov {
166*041f26b6SRoman Volkov 	struct oxygen *chip = ctl->private_data;
167*041f26b6SRoman Volkov 	struct dg *data = chip->model_data;
168*041f26b6SRoman Volkov 	unsigned int idx = ctl->private_value;
169*041f26b6SRoman Volkov 	int changed = 0;
170*041f26b6SRoman Volkov 
171*041f26b6SRoman Volkov 	if (value->value.integer.value[0] < 2 * -12 ||
172*041f26b6SRoman Volkov 	    value->value.integer.value[0] > 2 * 12 ||
173*041f26b6SRoman Volkov 	    value->value.integer.value[1] < 2 * -12 ||
174*041f26b6SRoman Volkov 	    value->value.integer.value[1] > 2 * 12)
175*041f26b6SRoman Volkov 		return -EINVAL;
176*041f26b6SRoman Volkov 	mutex_lock(&chip->mutex);
177*041f26b6SRoman Volkov 	changed = data->input_vol[idx][0] != value->value.integer.value[0] ||
178*041f26b6SRoman Volkov 		  data->input_vol[idx][1] != value->value.integer.value[1];
179*041f26b6SRoman Volkov 	if (changed) {
180*041f26b6SRoman Volkov 		data->input_vol[idx][0] = value->value.integer.value[0];
181*041f26b6SRoman Volkov 		data->input_vol[idx][1] = value->value.integer.value[1];
182*041f26b6SRoman Volkov 		if (idx == data->input_sel) {
183*041f26b6SRoman Volkov 			cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
184*041f26b6SRoman Volkov 					    data->input_vol[idx][0]);
185*041f26b6SRoman Volkov 			cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
186*041f26b6SRoman Volkov 					    data->input_vol[idx][1]);
187*041f26b6SRoman Volkov 		}
188*041f26b6SRoman Volkov 	}
189*041f26b6SRoman Volkov 	mutex_unlock(&chip->mutex);
190*041f26b6SRoman Volkov 	return changed;
191*041f26b6SRoman Volkov }
192*041f26b6SRoman Volkov 
193*041f26b6SRoman Volkov static DECLARE_TLV_DB_SCALE(cs4245_pga_db_scale, -1200, 50, 0);
194*041f26b6SRoman Volkov 
195*041f26b6SRoman Volkov static int input_sel_info(struct snd_kcontrol *ctl,
196*041f26b6SRoman Volkov 			  struct snd_ctl_elem_info *info)
197*041f26b6SRoman Volkov {
198*041f26b6SRoman Volkov 	static const char *const names[4] = {
199*041f26b6SRoman Volkov 		"Mic", "Aux", "Front Mic", "Line"
200*041f26b6SRoman Volkov 	};
201*041f26b6SRoman Volkov 
202*041f26b6SRoman Volkov 	return snd_ctl_enum_info(info, 1, 4, names);
203*041f26b6SRoman Volkov }
204*041f26b6SRoman Volkov 
205*041f26b6SRoman Volkov static int input_sel_get(struct snd_kcontrol *ctl,
206*041f26b6SRoman Volkov 			 struct snd_ctl_elem_value *value)
207*041f26b6SRoman Volkov {
208*041f26b6SRoman Volkov 	struct oxygen *chip = ctl->private_data;
209*041f26b6SRoman Volkov 	struct dg *data = chip->model_data;
210*041f26b6SRoman Volkov 
211*041f26b6SRoman Volkov 	mutex_lock(&chip->mutex);
212*041f26b6SRoman Volkov 	value->value.enumerated.item[0] = data->input_sel;
213*041f26b6SRoman Volkov 	mutex_unlock(&chip->mutex);
214*041f26b6SRoman Volkov 	return 0;
215*041f26b6SRoman Volkov }
216*041f26b6SRoman Volkov 
217*041f26b6SRoman Volkov static int input_sel_put(struct snd_kcontrol *ctl,
218*041f26b6SRoman Volkov 			 struct snd_ctl_elem_value *value)
219*041f26b6SRoman Volkov {
220*041f26b6SRoman Volkov 	static const u8 sel_values[4] = {
221*041f26b6SRoman Volkov 		CS4245_SEL_MIC,
222*041f26b6SRoman Volkov 		CS4245_SEL_INPUT_1,
223*041f26b6SRoman Volkov 		CS4245_SEL_INPUT_2,
224*041f26b6SRoman Volkov 		CS4245_SEL_INPUT_4
225*041f26b6SRoman Volkov 	};
226*041f26b6SRoman Volkov 	struct oxygen *chip = ctl->private_data;
227*041f26b6SRoman Volkov 	struct dg *data = chip->model_data;
228*041f26b6SRoman Volkov 	int changed;
229*041f26b6SRoman Volkov 
230*041f26b6SRoman Volkov 	if (value->value.enumerated.item[0] > 3)
231*041f26b6SRoman Volkov 		return -EINVAL;
232*041f26b6SRoman Volkov 
233*041f26b6SRoman Volkov 	mutex_lock(&chip->mutex);
234*041f26b6SRoman Volkov 	changed = value->value.enumerated.item[0] != data->input_sel;
235*041f26b6SRoman Volkov 	if (changed) {
236*041f26b6SRoman Volkov 		data->input_sel = value->value.enumerated.item[0];
237*041f26b6SRoman Volkov 
238*041f26b6SRoman Volkov 		cs4245_write(chip, CS4245_ANALOG_IN,
239*041f26b6SRoman Volkov 			     (data->cs4245_shadow[CS4245_ANALOG_IN] &
240*041f26b6SRoman Volkov 							~CS4245_SEL_MASK) |
241*041f26b6SRoman Volkov 			     sel_values[data->input_sel]);
242*041f26b6SRoman Volkov 
243*041f26b6SRoman Volkov 		cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
244*041f26b6SRoman Volkov 				    data->input_vol[data->input_sel][0]);
245*041f26b6SRoman Volkov 		cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
246*041f26b6SRoman Volkov 				    data->input_vol[data->input_sel][1]);
247*041f26b6SRoman Volkov 
248*041f26b6SRoman Volkov 		oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
249*041f26b6SRoman Volkov 				      data->input_sel ? 0 : GPIO_INPUT_ROUTE,
250*041f26b6SRoman Volkov 				      GPIO_INPUT_ROUTE);
251*041f26b6SRoman Volkov 	}
252*041f26b6SRoman Volkov 	mutex_unlock(&chip->mutex);
253*041f26b6SRoman Volkov 	return changed;
254*041f26b6SRoman Volkov }
255*041f26b6SRoman Volkov 
256*041f26b6SRoman Volkov static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
257*041f26b6SRoman Volkov {
258*041f26b6SRoman Volkov 	static const char *const names[2] = { "Active", "Frozen" };
259*041f26b6SRoman Volkov 
260*041f26b6SRoman Volkov 	return snd_ctl_enum_info(info, 1, 2, names);
261*041f26b6SRoman Volkov }
262*041f26b6SRoman Volkov 
263*041f26b6SRoman Volkov static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
264*041f26b6SRoman Volkov {
265*041f26b6SRoman Volkov 	struct oxygen *chip = ctl->private_data;
266*041f26b6SRoman Volkov 	struct dg *data = chip->model_data;
267*041f26b6SRoman Volkov 
268*041f26b6SRoman Volkov 	value->value.enumerated.item[0] =
269*041f26b6SRoman Volkov 		!!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE);
270*041f26b6SRoman Volkov 	return 0;
271*041f26b6SRoman Volkov }
272*041f26b6SRoman Volkov 
273*041f26b6SRoman Volkov static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
274*041f26b6SRoman Volkov {
275*041f26b6SRoman Volkov 	struct oxygen *chip = ctl->private_data;
276*041f26b6SRoman Volkov 	struct dg *data = chip->model_data;
277*041f26b6SRoman Volkov 	u8 reg;
278*041f26b6SRoman Volkov 	int changed;
279*041f26b6SRoman Volkov 
280*041f26b6SRoman Volkov 	mutex_lock(&chip->mutex);
281*041f26b6SRoman Volkov 	reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE;
282*041f26b6SRoman Volkov 	if (value->value.enumerated.item[0])
283*041f26b6SRoman Volkov 		reg |= CS4245_HPF_FREEZE;
284*041f26b6SRoman Volkov 	changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL];
285*041f26b6SRoman Volkov 	if (changed)
286*041f26b6SRoman Volkov 		cs4245_write(chip, CS4245_ADC_CTRL, reg);
287*041f26b6SRoman Volkov 	mutex_unlock(&chip->mutex);
288*041f26b6SRoman Volkov 	return changed;
289*041f26b6SRoman Volkov }
290*041f26b6SRoman Volkov 
291*041f26b6SRoman Volkov #define INPUT_VOLUME(xname, index) { \
292*041f26b6SRoman Volkov 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
293*041f26b6SRoman Volkov 	.name = xname, \
294*041f26b6SRoman Volkov 	.info = input_vol_info, \
295*041f26b6SRoman Volkov 	.get = input_vol_get, \
296*041f26b6SRoman Volkov 	.put = input_vol_put, \
297*041f26b6SRoman Volkov 	.tlv = { .p = cs4245_pga_db_scale }, \
298*041f26b6SRoman Volkov 	.private_value = index, \
299*041f26b6SRoman Volkov }
300*041f26b6SRoman Volkov static const struct snd_kcontrol_new dg_controls[] = {
301*041f26b6SRoman Volkov 	{
302*041f26b6SRoman Volkov 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
303*041f26b6SRoman Volkov 		.name = "Analog Output Playback Enum",
304*041f26b6SRoman Volkov 		.info = output_switch_info,
305*041f26b6SRoman Volkov 		.get = output_switch_get,
306*041f26b6SRoman Volkov 		.put = output_switch_put,
307*041f26b6SRoman Volkov 	},
308*041f26b6SRoman Volkov 	{
309*041f26b6SRoman Volkov 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
310*041f26b6SRoman Volkov 		.name = "Headphones Impedance Playback Enum",
311*041f26b6SRoman Volkov 		.info = hp_volume_offset_info,
312*041f26b6SRoman Volkov 		.get = hp_volume_offset_get,
313*041f26b6SRoman Volkov 		.put = hp_volume_offset_put,
314*041f26b6SRoman Volkov 	},
315*041f26b6SRoman Volkov 	INPUT_VOLUME("Mic Capture Volume", 0),
316*041f26b6SRoman Volkov 	INPUT_VOLUME("Aux Capture Volume", 1),
317*041f26b6SRoman Volkov 	INPUT_VOLUME("Front Mic Capture Volume", 2),
318*041f26b6SRoman Volkov 	INPUT_VOLUME("Line Capture Volume", 3),
319*041f26b6SRoman Volkov 	{
320*041f26b6SRoman Volkov 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
321*041f26b6SRoman Volkov 		.name = "Capture Source",
322*041f26b6SRoman Volkov 		.info = input_sel_info,
323*041f26b6SRoman Volkov 		.get = input_sel_get,
324*041f26b6SRoman Volkov 		.put = input_sel_put,
325*041f26b6SRoman Volkov 	},
326*041f26b6SRoman Volkov 	{
327*041f26b6SRoman Volkov 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
328*041f26b6SRoman Volkov 		.name = "ADC High-pass Filter Capture Enum",
329*041f26b6SRoman Volkov 		.info = hpf_info,
330*041f26b6SRoman Volkov 		.get = hpf_get,
331*041f26b6SRoman Volkov 		.put = hpf_put,
332*041f26b6SRoman Volkov 	},
333*041f26b6SRoman Volkov };
334*041f26b6SRoman Volkov 
335*041f26b6SRoman Volkov static int dg_control_filter(struct snd_kcontrol_new *template)
336*041f26b6SRoman Volkov {
337*041f26b6SRoman Volkov 	if (!strncmp(template->name, "Master Playback ", 16))
338*041f26b6SRoman Volkov 		return 1;
339*041f26b6SRoman Volkov 	return 0;
340*041f26b6SRoman Volkov }
341*041f26b6SRoman Volkov 
342*041f26b6SRoman Volkov static int dg_mixer_init(struct oxygen *chip)
343*041f26b6SRoman Volkov {
344*041f26b6SRoman Volkov 	unsigned int i;
345*041f26b6SRoman Volkov 	int err;
346*041f26b6SRoman Volkov 
347*041f26b6SRoman Volkov 	for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) {
348*041f26b6SRoman Volkov 		err = snd_ctl_add(chip->card,
349*041f26b6SRoman Volkov 				  snd_ctl_new1(&dg_controls[i], chip));
350*041f26b6SRoman Volkov 		if (err < 0)
351*041f26b6SRoman Volkov 			return err;
352*041f26b6SRoman Volkov 	}
353*041f26b6SRoman Volkov 	return 0;
354*041f26b6SRoman Volkov }
355*041f26b6SRoman Volkov 
356*041f26b6SRoman Volkov struct oxygen_model model_xonar_dg = {
357*041f26b6SRoman Volkov 	.longname = "C-Media Oxygen HD Audio",
358*041f26b6SRoman Volkov 	.chip = "CMI8786",
359*041f26b6SRoman Volkov 	.init = dg_init,
360*041f26b6SRoman Volkov 	.control_filter = dg_control_filter,
361*041f26b6SRoman Volkov 	.mixer_init = dg_mixer_init,
362*041f26b6SRoman Volkov 	.cleanup = dg_cleanup,
363*041f26b6SRoman Volkov 	.suspend = dg_suspend,
364*041f26b6SRoman Volkov 	.resume = dg_resume,
365*041f26b6SRoman Volkov 	.set_dac_params = set_cs4245_dac_params,
366*041f26b6SRoman Volkov 	.set_adc_params = set_cs4245_adc_params,
367*041f26b6SRoman Volkov 	.adjust_dac_routing = adjust_dg_dac_routing,
368*041f26b6SRoman Volkov 	.dump_registers = dump_cs4245_registers,
369*041f26b6SRoman Volkov 	.model_data_size = sizeof(struct dg),
370*041f26b6SRoman Volkov 	.device_config = PLAYBACK_0_TO_I2S |
371*041f26b6SRoman Volkov 			 PLAYBACK_1_TO_SPDIF |
372*041f26b6SRoman Volkov 			 CAPTURE_0_FROM_I2S_2 |
373*041f26b6SRoman Volkov 			 CAPTURE_1_FROM_SPDIF,
374*041f26b6SRoman Volkov 	.dac_channels_pcm = 6,
375*041f26b6SRoman Volkov 	.dac_channels_mixer = 0,
376*041f26b6SRoman Volkov 	.function_flags = OXYGEN_FUNCTION_SPI,
377*041f26b6SRoman Volkov 	.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
378*041f26b6SRoman Volkov 	.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
379*041f26b6SRoman Volkov 	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
380*041f26b6SRoman Volkov 	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
381*041f26b6SRoman Volkov };
382