diff options
Diffstat (limited to 'sound/pci/oxygen/xonar_dg.c')
-rw-r--r-- | sound/pci/oxygen/xonar_dg.c | 572 |
1 files changed, 572 insertions, 0 deletions
diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c new file mode 100644 index 000000000000..e4de0b8d087a --- /dev/null +++ b/sound/pci/oxygen/xonar_dg.c | |||
@@ -0,0 +1,572 @@ | |||
1 | /* | ||
2 | * card driver for the Xonar DG | ||
3 | * | ||
4 | * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | ||
5 | * | ||
6 | * | ||
7 | * This driver is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License, version 2. | ||
9 | * | ||
10 | * This driver is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this driver; if not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | |||
19 | /* | ||
20 | * Xonar DG | ||
21 | * -------- | ||
22 | * | ||
23 | * CMI8788: | ||
24 | * | ||
25 | * SPI 0 -> CS4245 | ||
26 | * | ||
27 | * GPIO 3 <- ? | ||
28 | * GPIO 4 <- headphone detect | ||
29 | * GPIO 5 -> route input jack to line-in (0) or mic-in (1) | ||
30 | * GPIO 6 -> route input jack to line-in (0) or mic-in (1) | ||
31 | * GPIO 7 -> enable rear headphone amp | ||
32 | * GPIO 8 -> enable output to speakers | ||
33 | * | ||
34 | * CS4245: | ||
35 | * | ||
36 | * input 1 <- aux | ||
37 | * input 2 <- front mic | ||
38 | * input 4 <- line/mic | ||
39 | * aux out -> front panel headphones | ||
40 | */ | ||
41 | |||
42 | #include <linux/pci.h> | ||
43 | #include <linux/delay.h> | ||
44 | #include <sound/control.h> | ||
45 | #include <sound/core.h> | ||
46 | #include <sound/info.h> | ||
47 | #include <sound/pcm.h> | ||
48 | #include <sound/tlv.h> | ||
49 | #include "oxygen.h" | ||
50 | #include "xonar_dg.h" | ||
51 | #include "cs4245.h" | ||
52 | |||
53 | #define GPIO_MAGIC 0x0008 | ||
54 | #define GPIO_HP_DETECT 0x0010 | ||
55 | #define GPIO_INPUT_ROUTE 0x0060 | ||
56 | #define GPIO_HP_REAR 0x0080 | ||
57 | #define GPIO_OUTPUT_ENABLE 0x0100 | ||
58 | |||
59 | struct dg { | ||
60 | unsigned int output_sel; | ||
61 | s8 input_vol[4][2]; | ||
62 | unsigned int input_sel; | ||
63 | u8 hp_vol_att; | ||
64 | u8 cs4245_regs[0x11]; | ||
65 | }; | ||
66 | |||
67 | static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value) | ||
68 | { | ||
69 | struct dg *data = chip->model_data; | ||
70 | |||
71 | oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | | ||
72 | OXYGEN_SPI_DATA_LENGTH_3 | | ||
73 | OXYGEN_SPI_CLOCK_1280 | | ||
74 | (0 << OXYGEN_SPI_CODEC_SHIFT) | | ||
75 | OXYGEN_SPI_CEN_LATCH_CLOCK_HI, | ||
76 | CS4245_SPI_ADDRESS | | ||
77 | CS4245_SPI_WRITE | | ||
78 | (value << 8) | reg); | ||
79 | data->cs4245_regs[reg] = value; | ||
80 | } | ||
81 | |||
82 | static void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value) | ||
83 | { | ||
84 | struct dg *data = chip->model_data; | ||
85 | |||
86 | if (value != data->cs4245_regs[reg]) | ||
87 | cs4245_write(chip, reg, value); | ||
88 | } | ||
89 | |||
90 | static void cs4245_registers_init(struct oxygen *chip) | ||
91 | { | ||
92 | struct dg *data = chip->model_data; | ||
93 | |||
94 | cs4245_write(chip, CS4245_POWER_CTRL, CS4245_PDN); | ||
95 | cs4245_write(chip, CS4245_DAC_CTRL_1, | ||
96 | data->cs4245_regs[CS4245_DAC_CTRL_1]); | ||
97 | cs4245_write(chip, CS4245_ADC_CTRL, | ||
98 | data->cs4245_regs[CS4245_ADC_CTRL]); | ||
99 | cs4245_write(chip, CS4245_SIGNAL_SEL, | ||
100 | data->cs4245_regs[CS4245_SIGNAL_SEL]); | ||
101 | cs4245_write(chip, CS4245_PGA_B_CTRL, | ||
102 | data->cs4245_regs[CS4245_PGA_B_CTRL]); | ||
103 | cs4245_write(chip, CS4245_PGA_A_CTRL, | ||
104 | data->cs4245_regs[CS4245_PGA_A_CTRL]); | ||
105 | cs4245_write(chip, CS4245_ANALOG_IN, | ||
106 | data->cs4245_regs[CS4245_ANALOG_IN]); | ||
107 | cs4245_write(chip, CS4245_DAC_A_CTRL, | ||
108 | data->cs4245_regs[CS4245_DAC_A_CTRL]); | ||
109 | cs4245_write(chip, CS4245_DAC_B_CTRL, | ||
110 | data->cs4245_regs[CS4245_DAC_B_CTRL]); | ||
111 | cs4245_write(chip, CS4245_DAC_CTRL_2, | ||
112 | CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC); | ||
113 | cs4245_write(chip, CS4245_INT_MASK, 0); | ||
114 | cs4245_write(chip, CS4245_POWER_CTRL, 0); | ||
115 | } | ||
116 | |||
117 | static void cs4245_init(struct oxygen *chip) | ||
118 | { | ||
119 | struct dg *data = chip->model_data; | ||
120 | |||
121 | data->cs4245_regs[CS4245_DAC_CTRL_1] = | ||
122 | CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST; | ||
123 | data->cs4245_regs[CS4245_ADC_CTRL] = | ||
124 | CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST; | ||
125 | data->cs4245_regs[CS4245_SIGNAL_SEL] = | ||
126 | CS4245_A_OUT_SEL_HIZ | CS4245_ASYNCH; | ||
127 | data->cs4245_regs[CS4245_PGA_B_CTRL] = 0; | ||
128 | data->cs4245_regs[CS4245_PGA_A_CTRL] = 0; | ||
129 | data->cs4245_regs[CS4245_ANALOG_IN] = | ||
130 | CS4245_PGA_SOFT | CS4245_PGA_ZERO | CS4245_SEL_INPUT_4; | ||
131 | data->cs4245_regs[CS4245_DAC_A_CTRL] = 0; | ||
132 | data->cs4245_regs[CS4245_DAC_B_CTRL] = 0; | ||
133 | cs4245_registers_init(chip); | ||
134 | snd_component_add(chip->card, "CS4245"); | ||
135 | } | ||
136 | |||
137 | static void dg_output_enable(struct oxygen *chip) | ||
138 | { | ||
139 | msleep(2500); | ||
140 | oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); | ||
141 | } | ||
142 | |||
143 | static void dg_init(struct oxygen *chip) | ||
144 | { | ||
145 | struct dg *data = chip->model_data; | ||
146 | |||
147 | data->output_sel = 0; | ||
148 | data->input_sel = 3; | ||
149 | data->hp_vol_att = 2 * 16; | ||
150 | |||
151 | cs4245_init(chip); | ||
152 | |||
153 | oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, | ||
154 | GPIO_MAGIC | GPIO_HP_DETECT); | ||
155 | oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, | ||
156 | GPIO_INPUT_ROUTE | GPIO_HP_REAR | GPIO_OUTPUT_ENABLE); | ||
157 | oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, | ||
158 | GPIO_INPUT_ROUTE | GPIO_HP_REAR); | ||
159 | dg_output_enable(chip); | ||
160 | } | ||
161 | |||
162 | static void dg_cleanup(struct oxygen *chip) | ||
163 | { | ||
164 | oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); | ||
165 | } | ||
166 | |||
167 | static void dg_suspend(struct oxygen *chip) | ||
168 | { | ||
169 | dg_cleanup(chip); | ||
170 | } | ||
171 | |||
172 | static void dg_resume(struct oxygen *chip) | ||
173 | { | ||
174 | cs4245_registers_init(chip); | ||
175 | dg_output_enable(chip); | ||
176 | } | ||
177 | |||
178 | static void set_cs4245_dac_params(struct oxygen *chip, | ||
179 | struct snd_pcm_hw_params *params) | ||
180 | { | ||
181 | struct dg *data = chip->model_data; | ||
182 | u8 value; | ||
183 | |||
184 | value = data->cs4245_regs[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK; | ||
185 | if (params_rate(params) <= 50000) | ||
186 | value |= CS4245_DAC_FM_SINGLE; | ||
187 | else if (params_rate(params) <= 100000) | ||
188 | value |= CS4245_DAC_FM_DOUBLE; | ||
189 | else | ||
190 | value |= CS4245_DAC_FM_QUAD; | ||
191 | cs4245_write_cached(chip, CS4245_DAC_CTRL_1, value); | ||
192 | } | ||
193 | |||
194 | static void set_cs4245_adc_params(struct oxygen *chip, | ||
195 | struct snd_pcm_hw_params *params) | ||
196 | { | ||
197 | struct dg *data = chip->model_data; | ||
198 | u8 value; | ||
199 | |||
200 | value = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK; | ||
201 | if (params_rate(params) <= 50000) | ||
202 | value |= CS4245_ADC_FM_SINGLE; | ||
203 | else if (params_rate(params) <= 100000) | ||
204 | value |= CS4245_ADC_FM_DOUBLE; | ||
205 | else | ||
206 | value |= CS4245_ADC_FM_QUAD; | ||
207 | cs4245_write_cached(chip, CS4245_ADC_CTRL, value); | ||
208 | } | ||
209 | |||
210 | static int output_switch_info(struct snd_kcontrol *ctl, | ||
211 | struct snd_ctl_elem_info *info) | ||
212 | { | ||
213 | static const char *const names[3] = { | ||
214 | "Speakers", "Headphones", "FP Headphones" | ||
215 | }; | ||
216 | |||
217 | return snd_ctl_enum_info(info, 1, 3, names); | ||
218 | } | ||
219 | |||
220 | static int output_switch_get(struct snd_kcontrol *ctl, | ||
221 | struct snd_ctl_elem_value *value) | ||
222 | { | ||
223 | struct oxygen *chip = ctl->private_data; | ||
224 | struct dg *data = chip->model_data; | ||
225 | |||
226 | mutex_lock(&chip->mutex); | ||
227 | value->value.enumerated.item[0] = data->output_sel; | ||
228 | mutex_unlock(&chip->mutex); | ||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | static int output_switch_put(struct snd_kcontrol *ctl, | ||
233 | struct snd_ctl_elem_value *value) | ||
234 | { | ||
235 | struct oxygen *chip = ctl->private_data; | ||
236 | struct dg *data = chip->model_data; | ||
237 | u8 reg; | ||
238 | int changed; | ||
239 | |||
240 | if (value->value.enumerated.item[0] > 2) | ||
241 | return -EINVAL; | ||
242 | |||
243 | mutex_lock(&chip->mutex); | ||
244 | changed = value->value.enumerated.item[0] != data->output_sel; | ||
245 | if (changed) { | ||
246 | data->output_sel = value->value.enumerated.item[0]; | ||
247 | |||
248 | reg = data->cs4245_regs[CS4245_SIGNAL_SEL] & | ||
249 | ~CS4245_A_OUT_SEL_MASK; | ||
250 | reg |= data->output_sel == 2 ? | ||
251 | CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ; | ||
252 | cs4245_write_cached(chip, CS4245_SIGNAL_SEL, reg); | ||
253 | |||
254 | cs4245_write_cached(chip, CS4245_DAC_A_CTRL, | ||
255 | data->output_sel ? data->hp_vol_att : 0); | ||
256 | cs4245_write_cached(chip, CS4245_DAC_B_CTRL, | ||
257 | data->output_sel ? data->hp_vol_att : 0); | ||
258 | |||
259 | oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, | ||
260 | data->output_sel == 1 ? GPIO_HP_REAR : 0, | ||
261 | GPIO_HP_REAR); | ||
262 | } | ||
263 | mutex_unlock(&chip->mutex); | ||
264 | return changed; | ||
265 | } | ||
266 | |||
267 | static int hp_volume_offset_info(struct snd_kcontrol *ctl, | ||
268 | struct snd_ctl_elem_info *info) | ||
269 | { | ||
270 | static const char *const names[3] = { | ||
271 | "< 64 ohms", "64-150 ohms", "150-300 ohms" | ||
272 | }; | ||
273 | |||
274 | return snd_ctl_enum_info(info, 1, 3, names); | ||
275 | } | ||
276 | |||
277 | static int hp_volume_offset_get(struct snd_kcontrol *ctl, | ||
278 | struct snd_ctl_elem_value *value) | ||
279 | { | ||
280 | struct oxygen *chip = ctl->private_data; | ||
281 | struct dg *data = chip->model_data; | ||
282 | |||
283 | mutex_lock(&chip->mutex); | ||
284 | if (data->hp_vol_att > 2 * 7) | ||
285 | value->value.enumerated.item[0] = 0; | ||
286 | else if (data->hp_vol_att > 0) | ||
287 | value->value.enumerated.item[0] = 1; | ||
288 | else | ||
289 | value->value.enumerated.item[0] = 2; | ||
290 | mutex_unlock(&chip->mutex); | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | static int hp_volume_offset_put(struct snd_kcontrol *ctl, | ||
295 | struct snd_ctl_elem_value *value) | ||
296 | { | ||
297 | static const s8 atts[3] = { 2 * 16, 2 * 7, 0 }; | ||
298 | struct oxygen *chip = ctl->private_data; | ||
299 | struct dg *data = chip->model_data; | ||
300 | s8 att; | ||
301 | int changed; | ||
302 | |||
303 | if (value->value.enumerated.item[0] > 2) | ||
304 | return -EINVAL; | ||
305 | att = atts[value->value.enumerated.item[0]]; | ||
306 | mutex_lock(&chip->mutex); | ||
307 | changed = att != data->hp_vol_att; | ||
308 | if (changed) { | ||
309 | data->hp_vol_att = att; | ||
310 | if (data->output_sel) { | ||
311 | cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att); | ||
312 | cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att); | ||
313 | } | ||
314 | } | ||
315 | mutex_unlock(&chip->mutex); | ||
316 | return changed; | ||
317 | } | ||
318 | |||
319 | static int input_vol_info(struct snd_kcontrol *ctl, | ||
320 | struct snd_ctl_elem_info *info) | ||
321 | { | ||
322 | info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
323 | info->count = 2; | ||
324 | info->value.integer.min = 2 * -12; | ||
325 | info->value.integer.max = 2 * 12; | ||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | static int input_vol_get(struct snd_kcontrol *ctl, | ||
330 | struct snd_ctl_elem_value *value) | ||
331 | { | ||
332 | struct oxygen *chip = ctl->private_data; | ||
333 | struct dg *data = chip->model_data; | ||
334 | unsigned int idx = ctl->private_value; | ||
335 | |||
336 | mutex_lock(&chip->mutex); | ||
337 | value->value.integer.value[0] = data->input_vol[idx][0]; | ||
338 | value->value.integer.value[1] = data->input_vol[idx][1]; | ||
339 | mutex_unlock(&chip->mutex); | ||
340 | return 0; | ||
341 | } | ||
342 | |||
343 | static int input_vol_put(struct snd_kcontrol *ctl, | ||
344 | struct snd_ctl_elem_value *value) | ||
345 | { | ||
346 | struct oxygen *chip = ctl->private_data; | ||
347 | struct dg *data = chip->model_data; | ||
348 | unsigned int idx = ctl->private_value; | ||
349 | int changed = 0; | ||
350 | |||
351 | if (value->value.integer.value[0] < 2 * -12 || | ||
352 | value->value.integer.value[0] > 2 * 12 || | ||
353 | value->value.integer.value[1] < 2 * -12 || | ||
354 | value->value.integer.value[1] > 2 * 12) | ||
355 | return -EINVAL; | ||
356 | mutex_lock(&chip->mutex); | ||
357 | changed = data->input_vol[idx][0] != value->value.integer.value[0] || | ||
358 | data->input_vol[idx][1] != value->value.integer.value[1]; | ||
359 | if (changed) { | ||
360 | data->input_vol[idx][0] = value->value.integer.value[0]; | ||
361 | data->input_vol[idx][1] = value->value.integer.value[1]; | ||
362 | if (idx == data->input_sel) { | ||
363 | cs4245_write_cached(chip, CS4245_PGA_A_CTRL, | ||
364 | data->input_vol[idx][0]); | ||
365 | cs4245_write_cached(chip, CS4245_PGA_B_CTRL, | ||
366 | data->input_vol[idx][1]); | ||
367 | } | ||
368 | } | ||
369 | mutex_unlock(&chip->mutex); | ||
370 | return changed; | ||
371 | } | ||
372 | |||
373 | static DECLARE_TLV_DB_SCALE(cs4245_pga_db_scale, -1200, 50, 0); | ||
374 | |||
375 | static int input_sel_info(struct snd_kcontrol *ctl, | ||
376 | struct snd_ctl_elem_info *info) | ||
377 | { | ||
378 | static const char *const names[4] = { | ||
379 | "Mic", "Aux", "Front Mic", "Line" | ||
380 | }; | ||
381 | |||
382 | return snd_ctl_enum_info(info, 1, 4, names); | ||
383 | } | ||
384 | |||
385 | static int input_sel_get(struct snd_kcontrol *ctl, | ||
386 | struct snd_ctl_elem_value *value) | ||
387 | { | ||
388 | struct oxygen *chip = ctl->private_data; | ||
389 | struct dg *data = chip->model_data; | ||
390 | |||
391 | mutex_lock(&chip->mutex); | ||
392 | value->value.enumerated.item[0] = data->input_sel; | ||
393 | mutex_unlock(&chip->mutex); | ||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | static int input_sel_put(struct snd_kcontrol *ctl, | ||
398 | struct snd_ctl_elem_value *value) | ||
399 | { | ||
400 | static const u8 sel_values[4] = { | ||
401 | CS4245_SEL_MIC, | ||
402 | CS4245_SEL_INPUT_1, | ||
403 | CS4245_SEL_INPUT_2, | ||
404 | CS4245_SEL_INPUT_4 | ||
405 | }; | ||
406 | struct oxygen *chip = ctl->private_data; | ||
407 | struct dg *data = chip->model_data; | ||
408 | int changed; | ||
409 | |||
410 | if (value->value.enumerated.item[0] > 3) | ||
411 | return -EINVAL; | ||
412 | |||
413 | mutex_lock(&chip->mutex); | ||
414 | changed = value->value.enumerated.item[0] != data->input_sel; | ||
415 | if (changed) { | ||
416 | data->input_sel = value->value.enumerated.item[0]; | ||
417 | |||
418 | cs4245_write(chip, CS4245_ANALOG_IN, | ||
419 | (data->cs4245_regs[CS4245_ANALOG_IN] & | ||
420 | ~CS4245_SEL_MASK) | | ||
421 | sel_values[data->input_sel]); | ||
422 | |||
423 | cs4245_write_cached(chip, CS4245_PGA_A_CTRL, | ||
424 | data->input_vol[data->input_sel][0]); | ||
425 | cs4245_write_cached(chip, CS4245_PGA_B_CTRL, | ||
426 | data->input_vol[data->input_sel][1]); | ||
427 | |||
428 | oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, | ||
429 | data->input_sel ? 0 : GPIO_INPUT_ROUTE, | ||
430 | GPIO_INPUT_ROUTE); | ||
431 | } | ||
432 | mutex_unlock(&chip->mutex); | ||
433 | return changed; | ||
434 | } | ||
435 | |||
436 | static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) | ||
437 | { | ||
438 | static const char *const names[2] = { "Active", "Frozen" }; | ||
439 | |||
440 | return snd_ctl_enum_info(info, 1, 2, names); | ||
441 | } | ||
442 | |||
443 | static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) | ||
444 | { | ||
445 | struct oxygen *chip = ctl->private_data; | ||
446 | struct dg *data = chip->model_data; | ||
447 | |||
448 | value->value.enumerated.item[0] = | ||
449 | !!(data->cs4245_regs[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE); | ||
450 | return 0; | ||
451 | } | ||
452 | |||
453 | static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) | ||
454 | { | ||
455 | struct oxygen *chip = ctl->private_data; | ||
456 | struct dg *data = chip->model_data; | ||
457 | u8 reg; | ||
458 | int changed; | ||
459 | |||
460 | mutex_lock(&chip->mutex); | ||
461 | reg = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE; | ||
462 | if (value->value.enumerated.item[0]) | ||
463 | reg |= CS4245_HPF_FREEZE; | ||
464 | changed = reg != data->cs4245_regs[CS4245_ADC_CTRL]; | ||
465 | if (changed) | ||
466 | cs4245_write(chip, CS4245_ADC_CTRL, reg); | ||
467 | mutex_unlock(&chip->mutex); | ||
468 | return changed; | ||
469 | } | ||
470 | |||
471 | #define INPUT_VOLUME(xname, index) { \ | ||
472 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ | ||
473 | .name = xname, \ | ||
474 | .info = input_vol_info, \ | ||
475 | .get = input_vol_get, \ | ||
476 | .put = input_vol_put, \ | ||
477 | .tlv = { .p = cs4245_pga_db_scale }, \ | ||
478 | .private_value = index, \ | ||
479 | } | ||
480 | static const struct snd_kcontrol_new dg_controls[] = { | ||
481 | { | ||
482 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
483 | .name = "Analog Output Playback Enum", | ||
484 | .info = output_switch_info, | ||
485 | .get = output_switch_get, | ||
486 | .put = output_switch_put, | ||
487 | }, | ||
488 | { | ||
489 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
490 | .name = "Headphones Impedance Playback Enum", | ||
491 | .info = hp_volume_offset_info, | ||
492 | .get = hp_volume_offset_get, | ||
493 | .put = hp_volume_offset_put, | ||
494 | }, | ||
495 | INPUT_VOLUME("Mic Capture Volume", 0), | ||
496 | INPUT_VOLUME("Aux Capture Volume", 1), | ||
497 | INPUT_VOLUME("Front Mic Capture Volume", 2), | ||
498 | INPUT_VOLUME("Line Capture Volume", 3), | ||
499 | { | ||
500 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
501 | .name = "Capture Source", | ||
502 | .info = input_sel_info, | ||
503 | .get = input_sel_get, | ||
504 | .put = input_sel_put, | ||
505 | }, | ||
506 | { | ||
507 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
508 | .name = "ADC High-pass Filter Capture Enum", | ||
509 | .info = hpf_info, | ||
510 | .get = hpf_get, | ||
511 | .put = hpf_put, | ||
512 | }, | ||
513 | }; | ||
514 | |||
515 | static int dg_control_filter(struct snd_kcontrol_new *template) | ||
516 | { | ||
517 | if (!strncmp(template->name, "Master Playback ", 16)) | ||
518 | return 1; | ||
519 | return 0; | ||
520 | } | ||
521 | |||
522 | static int dg_mixer_init(struct oxygen *chip) | ||
523 | { | ||
524 | unsigned int i; | ||
525 | int err; | ||
526 | |||
527 | for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) { | ||
528 | err = snd_ctl_add(chip->card, | ||
529 | snd_ctl_new1(&dg_controls[i], chip)); | ||
530 | if (err < 0) | ||
531 | return err; | ||
532 | } | ||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | static void dump_cs4245_registers(struct oxygen *chip, | ||
537 | struct snd_info_buffer *buffer) | ||
538 | { | ||
539 | struct dg *data = chip->model_data; | ||
540 | unsigned int i; | ||
541 | |||
542 | snd_iprintf(buffer, "\nCS4245:"); | ||
543 | for (i = 1; i <= 0x10; ++i) | ||
544 | snd_iprintf(buffer, " %02x", data->cs4245_regs[i]); | ||
545 | snd_iprintf(buffer, "\n"); | ||
546 | } | ||
547 | |||
548 | struct oxygen_model model_xonar_dg = { | ||
549 | .shortname = "Xonar DG", | ||
550 | .longname = "C-Media Oxygen HD Audio", | ||
551 | .chip = "CMI8786", | ||
552 | .init = dg_init, | ||
553 | .control_filter = dg_control_filter, | ||
554 | .mixer_init = dg_mixer_init, | ||
555 | .cleanup = dg_cleanup, | ||
556 | .suspend = dg_suspend, | ||
557 | .resume = dg_resume, | ||
558 | .set_dac_params = set_cs4245_dac_params, | ||
559 | .set_adc_params = set_cs4245_adc_params, | ||
560 | .dump_registers = dump_cs4245_registers, | ||
561 | .model_data_size = sizeof(struct dg), | ||
562 | .device_config = PLAYBACK_0_TO_I2S | | ||
563 | PLAYBACK_1_TO_SPDIF | | ||
564 | CAPTURE_0_FROM_I2S_2, | ||
565 | .dac_channels_pcm = 6, | ||
566 | .dac_channels_mixer = 0, | ||
567 | .function_flags = OXYGEN_FUNCTION_SPI, | ||
568 | .dac_mclks = OXYGEN_MCLKS(256, 128, 128), | ||
569 | .adc_mclks = OXYGEN_MCLKS(256, 128, 128), | ||
570 | .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | ||
571 | .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | ||
572 | }; | ||