Commit | Line | Data |
---|---|---|
1b8ff22f CL |
1 | /* |
2 | * C-Media CMI8788 driver for Asus Xonar cards | |
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, write to the Free Software | |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | */ | |
19 | ||
20 | /* | |
a9d3cc48 CL |
21 | * Xonar D2/D2X |
22 | * ------------ | |
23 | * | |
878ac3ee CL |
24 | * CMI8788: |
25 | * | |
1b8ff22f | 26 | * SPI 0 -> 1st PCM1796 (front) |
7113e958 | 27 | * SPI 1 -> 2nd PCM1796 (surround) |
1b8ff22f | 28 | * SPI 2 -> 3rd PCM1796 (center/LFE) |
7113e958 | 29 | * SPI 4 -> 4th PCM1796 (back) |
1b8ff22f CL |
30 | * |
31 | * GPIO 2 -> M0 of CS5381 | |
32 | * GPIO 3 -> M1 of CS5381 | |
878ac3ee | 33 | * GPIO 5 <- external power present (D2X only) |
1b8ff22f | 34 | * GPIO 7 -> ALT |
878ac3ee | 35 | * GPIO 8 -> enable output to speakers |
1b8ff22f CL |
36 | */ |
37 | ||
a9d3cc48 CL |
38 | /* |
39 | * Xonar DX | |
40 | * -------- | |
41 | * | |
42 | * CMI8788: | |
43 | * | |
44 | * I²C <-> CS4398 (front) | |
45 | * <-> CS4362A (surround, center/LFE, back) | |
46 | * | |
47 | * GPI 0 <- external power present | |
48 | * | |
49 | * GPIO 0 -> enable output to speakers | |
11864b4b | 50 | * GPIO 1 -> ? |
a9d3cc48 CL |
51 | * GPIO 2 -> M0 of CS5361 |
52 | * GPIO 3 -> M1 of CS5361 | |
11864b4b | 53 | * GPIO 8 -> route input jack to line-in (0) or mic-in (1) |
a9d3cc48 CL |
54 | * |
55 | * CS4398: | |
56 | * | |
57 | * AD0 <- 1 | |
58 | * AD1 <- 1 | |
59 | * | |
60 | * CS4362A: | |
61 | * | |
62 | * AD0 <- 0 | |
63 | */ | |
64 | ||
1b8ff22f CL |
65 | #include <linux/pci.h> |
66 | #include <linux/delay.h> | |
ccc80fb4 CL |
67 | #include <linux/mutex.h> |
68 | #include <sound/ac97_codec.h> | |
1b8ff22f CL |
69 | #include <sound/control.h> |
70 | #include <sound/core.h> | |
71 | #include <sound/initval.h> | |
72 | #include <sound/pcm.h> | |
73 | #include <sound/tlv.h> | |
74 | #include "oxygen.h" | |
878ac3ee | 75 | #include "cm9780.h" |
33fa724e | 76 | #include "pcm1796.h" |
a9d3cc48 CL |
77 | #include "cs4398.h" |
78 | #include "cs4362a.h" | |
1b8ff22f CL |
79 | |
80 | MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); | |
a9d3cc48 | 81 | MODULE_DESCRIPTION("Asus AVx00 driver"); |
1b8ff22f | 82 | MODULE_LICENSE("GPL"); |
a9d3cc48 | 83 | MODULE_SUPPORTED_DEVICE("{{Asus,AV100},{Asus,AV200}}"); |
1b8ff22f CL |
84 | |
85 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; | |
86 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; | |
87 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; | |
88 | ||
89 | module_param_array(index, int, NULL, 0444); | |
90 | MODULE_PARM_DESC(index, "card index"); | |
91 | module_param_array(id, charp, NULL, 0444); | |
92 | MODULE_PARM_DESC(id, "ID string"); | |
93 | module_param_array(enable, bool, NULL, 0444); | |
94 | MODULE_PARM_DESC(enable, "enable card"); | |
95 | ||
271ebfca CL |
96 | enum { |
97 | MODEL_D2, | |
98 | MODEL_D2X, | |
a9d3cc48 | 99 | MODEL_DX, |
271ebfca CL |
100 | }; |
101 | ||
1b8ff22f | 102 | static struct pci_device_id xonar_ids[] __devinitdata = { |
271ebfca | 103 | { OXYGEN_PCI_SUBID(0x1043, 0x8269), .driver_data = MODEL_D2 }, |
a9d3cc48 | 104 | { OXYGEN_PCI_SUBID(0x1043, 0x8275), .driver_data = MODEL_DX }, |
271ebfca | 105 | { OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X }, |
1b8ff22f CL |
106 | { } |
107 | }; | |
108 | MODULE_DEVICE_TABLE(pci, xonar_ids); | |
109 | ||
878ac3ee | 110 | |
a694a6a0 CL |
111 | #define GPIO_CS53x1_M_MASK 0x000c |
112 | #define GPIO_CS53x1_M_SINGLE 0x0000 | |
113 | #define GPIO_CS53x1_M_DOUBLE 0x0004 | |
114 | #define GPIO_CS53x1_M_QUAD 0x0008 | |
115 | ||
af9af174 CL |
116 | #define GPIO_D2X_EXT_POWER 0x0020 |
117 | #define GPIO_D2_ALT 0x0080 | |
118 | #define GPIO_D2_OUTPUT_ENABLE 0x0100 | |
878ac3ee | 119 | |
a9d3cc48 CL |
120 | #define GPI_DX_EXT_POWER 0x01 |
121 | #define GPIO_DX_OUTPUT_ENABLE 0x0001 | |
122 | #define GPIO_DX_UNKNOWN1 0x0002 | |
11864b4b | 123 | #define GPIO_DX_INPUT_ROUTE 0x0100 |
a9d3cc48 CL |
124 | |
125 | #define I2C_DEVICE_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */ | |
126 | #define I2C_DEVICE_CS4362A 0x30 /* 001100, AD0=0, /W=0 */ | |
127 | ||
7c014159 | 128 | struct xonar_data { |
af9af174 CL |
129 | unsigned int anti_pop_delay; |
130 | u16 output_enable_bit; | |
131 | u8 ext_power_reg; | |
132 | u8 ext_power_int_reg; | |
133 | u8 ext_power_bit; | |
7c014159 CL |
134 | u8 has_power; |
135 | }; | |
136 | ||
1b8ff22f CL |
137 | static void pcm1796_write(struct oxygen *chip, unsigned int codec, |
138 | u8 reg, u8 value) | |
139 | { | |
140 | /* maps ALSA channel pair number to SPI output */ | |
141 | static const u8 codec_map[4] = { | |
7113e958 | 142 | 0, 1, 2, 4 |
1b8ff22f | 143 | }; |
c2353a08 | 144 | oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | |
1b8ff22f | 145 | OXYGEN_SPI_DATA_LENGTH_2 | |
c2353a08 | 146 | OXYGEN_SPI_CLOCK_160 | |
1b8ff22f | 147 | (codec_map[codec] << OXYGEN_SPI_CODEC_SHIFT) | |
c2353a08 | 148 | OXYGEN_SPI_CEN_LATCH_CLOCK_HI, |
1b8ff22f CL |
149 | (reg << 8) | value); |
150 | } | |
151 | ||
a9d3cc48 CL |
152 | static void cs4398_write(struct oxygen *chip, u8 reg, u8 value) |
153 | { | |
154 | oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value); | |
155 | } | |
156 | ||
157 | static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value) | |
158 | { | |
159 | oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value); | |
160 | } | |
161 | ||
af9af174 CL |
162 | static void xonar_common_init(struct oxygen *chip) |
163 | { | |
164 | struct xonar_data *data = chip->model_data; | |
165 | ||
166 | if (data->ext_power_reg) { | |
167 | oxygen_set_bits8(chip, data->ext_power_int_reg, | |
168 | data->ext_power_bit); | |
169 | chip->interrupt_mask |= OXYGEN_INT_GPIO; | |
170 | data->has_power = !!(oxygen_read8(chip, data->ext_power_reg) | |
171 | & data->ext_power_bit); | |
172 | } | |
173 | oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CS53x1_M_MASK); | |
174 | oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, | |
175 | GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK); | |
176 | oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC); | |
177 | msleep(data->anti_pop_delay); | |
178 | oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, data->output_enable_bit); | |
179 | oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit); | |
180 | } | |
181 | ||
271ebfca | 182 | static void xonar_d2_init(struct oxygen *chip) |
1b8ff22f | 183 | { |
af9af174 | 184 | struct xonar_data *data = chip->model_data; |
1b8ff22f CL |
185 | unsigned int i; |
186 | ||
af9af174 CL |
187 | data->anti_pop_delay = 300; |
188 | data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE; | |
189 | ||
1b8ff22f | 190 | for (i = 0; i < 4; ++i) { |
878ac3ee CL |
191 | pcm1796_write(chip, i, 18, PCM1796_FMT_24_LJUST | PCM1796_ATLD); |
192 | pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1); | |
193 | pcm1796_write(chip, i, 20, PCM1796_OS_64); | |
194 | pcm1796_write(chip, i, 21, 0); | |
195 | pcm1796_write(chip, i, 16, 0xff); /* set ATL/ATR after ATLD */ | |
196 | pcm1796_write(chip, i, 17, 0xff); | |
1b8ff22f CL |
197 | } |
198 | ||
af9af174 CL |
199 | oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2_ALT); |
200 | oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_D2_ALT); | |
201 | ||
202 | xonar_common_init(chip); | |
1b8ff22f CL |
203 | |
204 | snd_component_add(chip->card, "PCM1796"); | |
205 | snd_component_add(chip->card, "CS5381"); | |
206 | } | |
207 | ||
271ebfca CL |
208 | static void xonar_d2x_init(struct oxygen *chip) |
209 | { | |
210 | struct xonar_data *data = chip->model_data; | |
211 | ||
af9af174 CL |
212 | data->ext_power_reg = OXYGEN_GPIO_DATA; |
213 | data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK; | |
214 | data->ext_power_bit = GPIO_D2X_EXT_POWER; | |
215 | oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2X_EXT_POWER); | |
271ebfca | 216 | xonar_d2_init(chip); |
271ebfca CL |
217 | } |
218 | ||
a9d3cc48 CL |
219 | static void xonar_dx_init(struct oxygen *chip) |
220 | { | |
221 | struct xonar_data *data = chip->model_data; | |
222 | unsigned int i; | |
223 | ||
224 | for (i = 0; i < 8; ++i) | |
225 | chip->dac_volume[i] = 127; | |
226 | data->anti_pop_delay = 800; | |
227 | data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE; | |
228 | data->ext_power_reg = OXYGEN_GPI_DATA; | |
229 | data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; | |
230 | data->ext_power_bit = GPI_DX_EXT_POWER; | |
231 | ||
232 | /* XXX the DACs' datasheets say fast mode is not allowed */ | |
233 | oxygen_set_bits16(chip, OXYGEN_2WIRE_BUS_STATUS, | |
234 | OXYGEN_2WIRE_SPEED_FAST); | |
235 | ||
236 | /* set CPEN (control port mode) and power down */ | |
237 | cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN); | |
238 | cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN); | |
239 | /* configure */ | |
240 | cs4398_write(chip, 2, CS4398_FM_SINGLE | | |
241 | CS4398_DEM_NONE | CS4398_DIF_LJUST); | |
242 | cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L); | |
243 | cs4398_write(chip, 4, CS4398_MUTEP_LOW | CS4398_PAMUTE); | |
244 | cs4398_write(chip, 5, 0); | |
245 | cs4398_write(chip, 6, 0); | |
246 | cs4398_write(chip, 7, CS4398_RMP_DN | CS4398_RMP_UP | | |
247 | CS4398_ZERO_CROSS | CS4398_SOFT_RAMP); | |
248 | cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST); | |
249 | cs4362a_write(chip, 0x03, CS4362A_MUTEC_6 | CS4362A_AMUTE | | |
250 | CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP); | |
251 | cs4362a_write(chip, 0x04, CS4362A_RMP_DN | CS4362A_DEM_NONE); | |
252 | cs4362a_write(chip, 0x05, 0); | |
253 | cs4362a_write(chip, 0x06, CS4362A_FM_SINGLE | | |
254 | CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L); | |
255 | cs4362a_write(chip, 0x09, CS4362A_FM_SINGLE | | |
256 | CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L); | |
257 | cs4362a_write(chip, 0x0c, CS4362A_FM_SINGLE | | |
258 | CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L); | |
259 | cs4362a_write(chip, 0x07, 0); | |
260 | cs4362a_write(chip, 0x08, 0); | |
261 | cs4362a_write(chip, 0x0a, 0); | |
262 | cs4362a_write(chip, 0x0b, 0); | |
263 | cs4362a_write(chip, 0x0d, 0); | |
264 | cs4362a_write(chip, 0x0e, 0); | |
265 | /* clear power down */ | |
266 | cs4398_write(chip, 8, CS4398_CPEN); | |
267 | cs4362a_write(chip, 0x01, CS4362A_CPEN); | |
268 | ||
269 | oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, | |
11864b4b CL |
270 | GPIO_DX_UNKNOWN1 | GPIO_DX_INPUT_ROUTE); |
271 | oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DX_INPUT_ROUTE); | |
a9d3cc48 CL |
272 | |
273 | xonar_common_init(chip); | |
274 | ||
275 | snd_component_add(chip->card, "CS4398"); | |
276 | snd_component_add(chip->card, "CS4362A"); | |
277 | snd_component_add(chip->card, "CS5361"); | |
278 | } | |
279 | ||
1b8ff22f CL |
280 | static void xonar_cleanup(struct oxygen *chip) |
281 | { | |
af9af174 CL |
282 | struct xonar_data *data = chip->model_data; |
283 | ||
284 | oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit); | |
1b8ff22f CL |
285 | } |
286 | ||
a9d3cc48 CL |
287 | static void xonar_dx_cleanup(struct oxygen *chip) |
288 | { | |
289 | xonar_cleanup(chip); | |
290 | cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN); | |
291 | oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC); | |
292 | } | |
293 | ||
1b8ff22f CL |
294 | static void set_pcm1796_params(struct oxygen *chip, |
295 | struct snd_pcm_hw_params *params) | |
296 | { | |
1b8ff22f CL |
297 | unsigned int i; |
298 | u8 value; | |
299 | ||
300 | value = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64; | |
301 | for (i = 0; i < 4; ++i) | |
878ac3ee | 302 | pcm1796_write(chip, i, 20, value); |
1b8ff22f CL |
303 | } |
304 | ||
305 | static void update_pcm1796_volume(struct oxygen *chip) | |
306 | { | |
307 | unsigned int i; | |
308 | ||
309 | for (i = 0; i < 4; ++i) { | |
878ac3ee CL |
310 | pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]); |
311 | pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]); | |
1b8ff22f CL |
312 | } |
313 | } | |
314 | ||
315 | static void update_pcm1796_mute(struct oxygen *chip) | |
316 | { | |
317 | unsigned int i; | |
318 | u8 value; | |
319 | ||
878ac3ee | 320 | value = PCM1796_FMT_24_LJUST | PCM1796_ATLD; |
1b8ff22f CL |
321 | if (chip->dac_mute) |
322 | value |= PCM1796_MUTE; | |
323 | for (i = 0; i < 4; ++i) | |
878ac3ee | 324 | pcm1796_write(chip, i, 18, value); |
1b8ff22f CL |
325 | } |
326 | ||
a694a6a0 | 327 | static void set_cs53x1_params(struct oxygen *chip, |
1b8ff22f CL |
328 | struct snd_pcm_hw_params *params) |
329 | { | |
330 | unsigned int value; | |
331 | ||
332 | if (params_rate(params) <= 54000) | |
a694a6a0 | 333 | value = GPIO_CS53x1_M_SINGLE; |
1b8ff22f | 334 | else if (params_rate(params) <= 108000) |
a694a6a0 | 335 | value = GPIO_CS53x1_M_DOUBLE; |
1b8ff22f | 336 | else |
a694a6a0 | 337 | value = GPIO_CS53x1_M_QUAD; |
878ac3ee | 338 | oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, |
a694a6a0 | 339 | value, GPIO_CS53x1_M_MASK); |
1b8ff22f CL |
340 | } |
341 | ||
a9d3cc48 CL |
342 | static void set_cs43xx_params(struct oxygen *chip, |
343 | struct snd_pcm_hw_params *params) | |
344 | { | |
345 | u8 fm_cs4398, fm_cs4362a; | |
346 | ||
347 | fm_cs4398 = CS4398_DEM_NONE | CS4398_DIF_LJUST; | |
348 | fm_cs4362a = CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; | |
349 | if (params_rate(params) <= 50000) { | |
350 | fm_cs4398 |= CS4398_FM_SINGLE; | |
351 | fm_cs4362a |= CS4362A_FM_SINGLE; | |
352 | } else if (params_rate(params) <= 100000) { | |
353 | fm_cs4398 |= CS4398_FM_DOUBLE; | |
354 | fm_cs4362a |= CS4362A_FM_DOUBLE; | |
355 | } else { | |
356 | fm_cs4398 |= CS4398_FM_QUAD; | |
357 | fm_cs4362a |= CS4362A_FM_QUAD; | |
358 | } | |
359 | cs4398_write(chip, 2, fm_cs4398); | |
360 | cs4362a_write(chip, 0x06, fm_cs4362a); | |
361 | cs4362a_write(chip, 0x09, fm_cs4362a); | |
362 | cs4362a_write(chip, 0x0c, fm_cs4362a); | |
363 | } | |
364 | ||
365 | static void update_cs4362a_volumes(struct oxygen *chip) | |
366 | { | |
367 | u8 mute; | |
368 | ||
369 | mute = chip->dac_mute ? CS4362A_MUTE : 0; | |
370 | cs4362a_write(chip, 7, (127 - chip->dac_volume[2]) | mute); | |
371 | cs4362a_write(chip, 8, (127 - chip->dac_volume[3]) | mute); | |
372 | cs4362a_write(chip, 10, (127 - chip->dac_volume[4]) | mute); | |
373 | cs4362a_write(chip, 11, (127 - chip->dac_volume[5]) | mute); | |
374 | cs4362a_write(chip, 13, (127 - chip->dac_volume[6]) | mute); | |
375 | cs4362a_write(chip, 14, (127 - chip->dac_volume[7]) | mute); | |
376 | } | |
377 | ||
378 | static void update_cs43xx_volume(struct oxygen *chip) | |
379 | { | |
380 | cs4398_write(chip, 5, (127 - chip->dac_volume[0]) * 2); | |
381 | cs4398_write(chip, 6, (127 - chip->dac_volume[1]) * 2); | |
382 | update_cs4362a_volumes(chip); | |
383 | } | |
384 | ||
385 | static void update_cs43xx_mute(struct oxygen *chip) | |
386 | { | |
387 | u8 reg; | |
388 | ||
389 | reg = CS4398_MUTEP_LOW | CS4398_PAMUTE; | |
390 | if (chip->dac_mute) | |
391 | reg |= CS4398_MUTE_B | CS4398_MUTE_A; | |
392 | cs4398_write(chip, 4, reg); | |
393 | update_cs4362a_volumes(chip); | |
394 | } | |
395 | ||
7c014159 CL |
396 | static void xonar_gpio_changed(struct oxygen *chip) |
397 | { | |
398 | struct xonar_data *data = chip->model_data; | |
399 | u8 has_power; | |
400 | ||
af9af174 CL |
401 | has_power = !!(oxygen_read8(chip, data->ext_power_reg) |
402 | & data->ext_power_bit); | |
7c014159 CL |
403 | if (has_power != data->has_power) { |
404 | data->has_power = has_power; | |
405 | if (has_power) { | |
406 | snd_printk(KERN_NOTICE "power restored\n"); | |
407 | } else { | |
408 | snd_printk(KERN_CRIT | |
409 | "Hey! Don't unplug the power cable!\n"); | |
410 | /* TODO: stop PCMs */ | |
411 | } | |
412 | } | |
413 | } | |
414 | ||
ccc80fb4 CL |
415 | static int pcm1796_volume_info(struct snd_kcontrol *ctl, |
416 | struct snd_ctl_elem_info *info) | |
417 | { | |
418 | info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
419 | info->count = 8; | |
420 | info->value.integer.min = 0x0f; | |
421 | info->value.integer.max = 0xff; | |
422 | return 0; | |
423 | } | |
424 | ||
a9d3cc48 CL |
425 | static int cs4362a_volume_info(struct snd_kcontrol *ctl, |
426 | struct snd_ctl_elem_info *info) | |
427 | { | |
428 | info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
429 | info->count = 8; | |
430 | info->value.integer.min = 0; | |
431 | info->value.integer.max = 127; | |
432 | return 0; | |
433 | } | |
434 | ||
1b8ff22f CL |
435 | static int alt_switch_get(struct snd_kcontrol *ctl, |
436 | struct snd_ctl_elem_value *value) | |
437 | { | |
438 | struct oxygen *chip = ctl->private_data; | |
439 | ||
440 | value->value.integer.value[0] = | |
af9af174 | 441 | !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_D2_ALT); |
1b8ff22f CL |
442 | return 0; |
443 | } | |
444 | ||
445 | static int alt_switch_put(struct snd_kcontrol *ctl, | |
446 | struct snd_ctl_elem_value *value) | |
447 | { | |
448 | struct oxygen *chip = ctl->private_data; | |
449 | u16 old_bits, new_bits; | |
450 | int changed; | |
451 | ||
452 | spin_lock_irq(&chip->reg_lock); | |
453 | old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA); | |
454 | if (value->value.integer.value[0]) | |
af9af174 | 455 | new_bits = old_bits | GPIO_D2_ALT; |
1b8ff22f | 456 | else |
af9af174 | 457 | new_bits = old_bits & ~GPIO_D2_ALT; |
1b8ff22f CL |
458 | changed = new_bits != old_bits; |
459 | if (changed) | |
460 | oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits); | |
461 | spin_unlock_irq(&chip->reg_lock); | |
462 | return changed; | |
463 | } | |
464 | ||
465 | static const struct snd_kcontrol_new alt_switch = { | |
466 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
467 | .name = "Analog Loopback Switch", | |
468 | .info = snd_ctl_boolean_mono_info, | |
469 | .get = alt_switch_get, | |
470 | .put = alt_switch_put, | |
471 | }; | |
472 | ||
11864b4b CL |
473 | static void xonar_dx_ac97_switch(struct oxygen *chip, |
474 | unsigned int reg, unsigned int mute) | |
475 | { | |
476 | if (reg == AC97_LINE) { | |
477 | spin_lock_irq(&chip->reg_lock); | |
478 | oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, | |
479 | mute ? GPIO_DX_INPUT_ROUTE : 0, | |
480 | GPIO_DX_INPUT_ROUTE); | |
481 | spin_unlock_irq(&chip->reg_lock); | |
482 | } | |
483 | } | |
484 | ||
1b8ff22f | 485 | static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -12000, 50, 0); |
a9d3cc48 | 486 | static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -12700, 100, 0); |
1b8ff22f | 487 | |
af9af174 | 488 | static int xonar_d2_control_filter(struct snd_kcontrol_new *template) |
ccc80fb4 CL |
489 | { |
490 | if (!strcmp(template->name, "Master Playback Volume")) { | |
491 | template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; | |
80647ee2 | 492 | template->info = pcm1796_volume_info; |
ccc80fb4 CL |
493 | template->tlv.p = pcm1796_db_scale; |
494 | } else if (!strncmp(template->name, "CD Capture ", 11)) { | |
911b499a | 495 | /* CD in is actually connected to the video in pin */ |
ccc80fb4 CL |
496 | template->private_value ^= AC97_CD ^ AC97_VIDEO; |
497 | } | |
498 | return 0; | |
499 | } | |
500 | ||
a9d3cc48 CL |
501 | static int xonar_dx_control_filter(struct snd_kcontrol_new *template) |
502 | { | |
503 | if (!strcmp(template->name, "Master Playback Volume")) { | |
504 | template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; | |
505 | template->info = cs4362a_volume_info; | |
506 | template->tlv.p = cs4362a_db_scale; | |
507 | } else if (!strncmp(template->name, "CD Capture ", 11)) { | |
508 | return 1; /* no CD input */ | |
509 | } else if (!strcmp(template->name, | |
510 | SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK)) || | |
511 | !strcmp(template->name, | |
512 | SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT))) { | |
513 | return 1; /* no digital input */ | |
514 | } | |
515 | return 0; | |
516 | } | |
517 | ||
1b8ff22f CL |
518 | static int xonar_mixer_init(struct oxygen *chip) |
519 | { | |
520 | return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip)); | |
521 | } | |
522 | ||
271ebfca CL |
523 | static const struct oxygen_model xonar_models[] = { |
524 | [MODEL_D2] = { | |
aef1a535 | 525 | .shortname = "Xonar D2", |
271ebfca CL |
526 | .longname = "Asus Virtuoso 200", |
527 | .chip = "AV200", | |
528 | .owner = THIS_MODULE, | |
529 | .init = xonar_d2_init, | |
af9af174 | 530 | .control_filter = xonar_d2_control_filter, |
271ebfca CL |
531 | .mixer_init = xonar_mixer_init, |
532 | .cleanup = xonar_cleanup, | |
533 | .set_dac_params = set_pcm1796_params, | |
a694a6a0 | 534 | .set_adc_params = set_cs53x1_params, |
271ebfca CL |
535 | .update_dac_volume = update_pcm1796_volume, |
536 | .update_dac_mute = update_pcm1796_mute, | |
537 | .model_data_size = sizeof(struct xonar_data), | |
538 | .pcm_dev_cfg = PLAYBACK_0_TO_I2S | | |
539 | PLAYBACK_1_TO_SPDIF | | |
540 | CAPTURE_0_FROM_I2S_2 | | |
541 | CAPTURE_1_FROM_SPDIF, | |
542 | .dac_channels = 8, | |
543 | .misc_flags = OXYGEN_MISC_MIDI, | |
544 | .function_flags = OXYGEN_FUNCTION_SPI | | |
545 | OXYGEN_FUNCTION_ENABLE_SPI_4_5, | |
546 | .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | |
547 | .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | |
548 | }, | |
549 | [MODEL_D2X] = { | |
aef1a535 | 550 | .shortname = "Xonar D2X", |
271ebfca CL |
551 | .longname = "Asus Virtuoso 200", |
552 | .chip = "AV200", | |
553 | .owner = THIS_MODULE, | |
554 | .init = xonar_d2x_init, | |
af9af174 | 555 | .control_filter = xonar_d2_control_filter, |
271ebfca CL |
556 | .mixer_init = xonar_mixer_init, |
557 | .cleanup = xonar_cleanup, | |
558 | .set_dac_params = set_pcm1796_params, | |
a694a6a0 | 559 | .set_adc_params = set_cs53x1_params, |
271ebfca CL |
560 | .update_dac_volume = update_pcm1796_volume, |
561 | .update_dac_mute = update_pcm1796_mute, | |
562 | .gpio_changed = xonar_gpio_changed, | |
563 | .model_data_size = sizeof(struct xonar_data), | |
564 | .pcm_dev_cfg = PLAYBACK_0_TO_I2S | | |
565 | PLAYBACK_1_TO_SPDIF | | |
566 | CAPTURE_0_FROM_I2S_2 | | |
567 | CAPTURE_1_FROM_SPDIF, | |
568 | .dac_channels = 8, | |
569 | .misc_flags = OXYGEN_MISC_MIDI, | |
570 | .function_flags = OXYGEN_FUNCTION_SPI | | |
571 | OXYGEN_FUNCTION_ENABLE_SPI_4_5, | |
572 | .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | |
573 | .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | |
574 | }, | |
a9d3cc48 CL |
575 | [MODEL_DX] = { |
576 | .shortname = "Xonar DX", | |
577 | .longname = "Asus Virtuoso 100", | |
578 | .chip = "AV200", | |
579 | .owner = THIS_MODULE, | |
580 | .init = xonar_dx_init, | |
581 | .control_filter = xonar_dx_control_filter, | |
582 | .cleanup = xonar_dx_cleanup, | |
583 | .set_dac_params = set_cs43xx_params, | |
584 | .set_adc_params = set_cs53x1_params, | |
585 | .update_dac_volume = update_cs43xx_volume, | |
586 | .update_dac_mute = update_cs43xx_mute, | |
587 | .gpio_changed = xonar_gpio_changed, | |
11864b4b | 588 | .ac97_switch = xonar_dx_ac97_switch, |
a9d3cc48 CL |
589 | .model_data_size = sizeof(struct xonar_data), |
590 | .pcm_dev_cfg = PLAYBACK_0_TO_I2S | | |
591 | PLAYBACK_1_TO_SPDIF | | |
592 | CAPTURE_0_FROM_I2S_2, | |
593 | .dac_channels = 8, | |
594 | .function_flags = OXYGEN_FUNCTION_2WIRE, | |
595 | .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | |
596 | .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | |
597 | }, | |
1b8ff22f CL |
598 | }; |
599 | ||
600 | static int __devinit xonar_probe(struct pci_dev *pci, | |
601 | const struct pci_device_id *pci_id) | |
602 | { | |
603 | static int dev; | |
604 | int err; | |
605 | ||
606 | if (dev >= SNDRV_CARDS) | |
607 | return -ENODEV; | |
608 | if (!enable[dev]) { | |
609 | ++dev; | |
610 | return -ENOENT; | |
611 | } | |
271ebfca CL |
612 | err = oxygen_pci_probe(pci, index[dev], id[dev], |
613 | &xonar_models[pci_id->driver_data]); | |
1b8ff22f CL |
614 | if (err >= 0) |
615 | ++dev; | |
616 | return err; | |
617 | } | |
618 | ||
619 | static struct pci_driver xonar_driver = { | |
620 | .name = "AV200", | |
621 | .id_table = xonar_ids, | |
622 | .probe = xonar_probe, | |
623 | .remove = __devexit_p(oxygen_pci_remove), | |
624 | }; | |
625 | ||
626 | static int __init alsa_card_xonar_init(void) | |
627 | { | |
628 | return pci_register_driver(&xonar_driver); | |
629 | } | |
630 | ||
631 | static void __exit alsa_card_xonar_exit(void) | |
632 | { | |
633 | pci_unregister_driver(&xonar_driver); | |
634 | } | |
635 | ||
636 | module_init(alsa_card_xonar_init) | |
637 | module_exit(alsa_card_xonar_exit) |