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 | |
a8bb1bad | 50 | * GPIO 1 -> enable front panel I/O |
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 | |
a8bb1bad | 122 | #define GPIO_DX_FRONT_PANEL 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) { |
e983532e CL |
191 | pcm1796_write(chip, i, 18, PCM1796_MUTE | PCM1796_DMF_DISABLED | |
192 | PCM1796_FMT_24_LJUST | PCM1796_ATLD); | |
878ac3ee CL |
193 | pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1); |
194 | pcm1796_write(chip, i, 20, PCM1796_OS_64); | |
195 | pcm1796_write(chip, i, 21, 0); | |
e983532e CL |
196 | pcm1796_write(chip, i, 16, 0x0f); /* set ATL/ATR after ATLD */ |
197 | pcm1796_write(chip, i, 17, 0x0f); | |
1b8ff22f CL |
198 | } |
199 | ||
af9af174 CL |
200 | oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2_ALT); |
201 | oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_D2_ALT); | |
202 | ||
203 | xonar_common_init(chip); | |
1b8ff22f CL |
204 | |
205 | snd_component_add(chip->card, "PCM1796"); | |
206 | snd_component_add(chip->card, "CS5381"); | |
207 | } | |
208 | ||
271ebfca CL |
209 | static void xonar_d2x_init(struct oxygen *chip) |
210 | { | |
211 | struct xonar_data *data = chip->model_data; | |
212 | ||
af9af174 CL |
213 | data->ext_power_reg = OXYGEN_GPIO_DATA; |
214 | data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK; | |
215 | data->ext_power_bit = GPIO_D2X_EXT_POWER; | |
216 | oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2X_EXT_POWER); | |
271ebfca | 217 | xonar_d2_init(chip); |
271ebfca CL |
218 | } |
219 | ||
a9d3cc48 CL |
220 | static void xonar_dx_init(struct oxygen *chip) |
221 | { | |
222 | struct xonar_data *data = chip->model_data; | |
a9d3cc48 | 223 | |
a9d3cc48 CL |
224 | data->anti_pop_delay = 800; |
225 | data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE; | |
226 | data->ext_power_reg = OXYGEN_GPI_DATA; | |
227 | data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; | |
228 | data->ext_power_bit = GPI_DX_EXT_POWER; | |
229 | ||
80060ecc CL |
230 | oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, |
231 | OXYGEN_2WIRE_LENGTH_8 | | |
232 | OXYGEN_2WIRE_INTERRUPT_MASK | | |
233 | OXYGEN_2WIRE_SPEED_FAST); | |
a9d3cc48 CL |
234 | |
235 | /* set CPEN (control port mode) and power down */ | |
236 | cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN); | |
237 | cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN); | |
238 | /* configure */ | |
239 | cs4398_write(chip, 2, CS4398_FM_SINGLE | | |
240 | CS4398_DEM_NONE | CS4398_DIF_LJUST); | |
241 | cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L); | |
242 | cs4398_write(chip, 4, CS4398_MUTEP_LOW | CS4398_PAMUTE); | |
e983532e CL |
243 | cs4398_write(chip, 5, 0xfe); |
244 | cs4398_write(chip, 6, 0xfe); | |
a9d3cc48 CL |
245 | cs4398_write(chip, 7, CS4398_RMP_DN | CS4398_RMP_UP | |
246 | CS4398_ZERO_CROSS | CS4398_SOFT_RAMP); | |
247 | cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST); | |
248 | cs4362a_write(chip, 0x03, CS4362A_MUTEC_6 | CS4362A_AMUTE | | |
249 | CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP); | |
250 | cs4362a_write(chip, 0x04, CS4362A_RMP_DN | CS4362A_DEM_NONE); | |
251 | cs4362a_write(chip, 0x05, 0); | |
252 | cs4362a_write(chip, 0x06, CS4362A_FM_SINGLE | | |
253 | CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L); | |
e983532e CL |
254 | cs4362a_write(chip, 0x07, 0x7f | CS4362A_MUTE); |
255 | cs4362a_write(chip, 0x08, 0x7f | CS4362A_MUTE); | |
a9d3cc48 CL |
256 | cs4362a_write(chip, 0x09, CS4362A_FM_SINGLE | |
257 | CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L); | |
e983532e CL |
258 | cs4362a_write(chip, 0x0a, 0x7f | CS4362A_MUTE); |
259 | cs4362a_write(chip, 0x0b, 0x7f | CS4362A_MUTE); | |
a9d3cc48 CL |
260 | cs4362a_write(chip, 0x0c, CS4362A_FM_SINGLE | |
261 | CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L); | |
e983532e CL |
262 | cs4362a_write(chip, 0x0d, 0x7f | CS4362A_MUTE); |
263 | cs4362a_write(chip, 0x0e, 0x7f | CS4362A_MUTE); | |
a9d3cc48 CL |
264 | /* clear power down */ |
265 | cs4398_write(chip, 8, CS4398_CPEN); | |
266 | cs4362a_write(chip, 0x01, CS4362A_CPEN); | |
267 | ||
268 | oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, | |
a8bb1bad CL |
269 | GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE); |
270 | oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, | |
271 | GPIO_DX_FRONT_PANEL | 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 | ||
1b8ff22f CL |
415 | static int alt_switch_get(struct snd_kcontrol *ctl, |
416 | struct snd_ctl_elem_value *value) | |
417 | { | |
418 | struct oxygen *chip = ctl->private_data; | |
419 | ||
420 | value->value.integer.value[0] = | |
af9af174 | 421 | !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_D2_ALT); |
1b8ff22f CL |
422 | return 0; |
423 | } | |
424 | ||
425 | static int alt_switch_put(struct snd_kcontrol *ctl, | |
426 | struct snd_ctl_elem_value *value) | |
427 | { | |
428 | struct oxygen *chip = ctl->private_data; | |
429 | u16 old_bits, new_bits; | |
430 | int changed; | |
431 | ||
432 | spin_lock_irq(&chip->reg_lock); | |
433 | old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA); | |
434 | if (value->value.integer.value[0]) | |
af9af174 | 435 | new_bits = old_bits | GPIO_D2_ALT; |
1b8ff22f | 436 | else |
af9af174 | 437 | new_bits = old_bits & ~GPIO_D2_ALT; |
1b8ff22f CL |
438 | changed = new_bits != old_bits; |
439 | if (changed) | |
440 | oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits); | |
441 | spin_unlock_irq(&chip->reg_lock); | |
442 | return changed; | |
443 | } | |
444 | ||
445 | static const struct snd_kcontrol_new alt_switch = { | |
446 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
447 | .name = "Analog Loopback Switch", | |
448 | .info = snd_ctl_boolean_mono_info, | |
449 | .get = alt_switch_get, | |
450 | .put = alt_switch_put, | |
451 | }; | |
452 | ||
a8bb1bad CL |
453 | static int front_panel_get(struct snd_kcontrol *ctl, |
454 | struct snd_ctl_elem_value *value) | |
387fb6a2 CL |
455 | { |
456 | struct oxygen *chip = ctl->private_data; | |
457 | ||
a8bb1bad CL |
458 | value->value.integer.value[0] = |
459 | !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DX_FRONT_PANEL); | |
387fb6a2 CL |
460 | return 0; |
461 | } | |
462 | ||
a8bb1bad CL |
463 | static int front_panel_put(struct snd_kcontrol *ctl, |
464 | struct snd_ctl_elem_value *value) | |
387fb6a2 CL |
465 | { |
466 | struct oxygen *chip = ctl->private_data; | |
467 | u16 old_reg, new_reg; | |
468 | ||
469 | spin_lock_irq(&chip->reg_lock); | |
470 | old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA); | |
a8bb1bad CL |
471 | if (value->value.integer.value[0]) |
472 | new_reg = old_reg | GPIO_DX_FRONT_PANEL; | |
387fb6a2 | 473 | else |
a8bb1bad | 474 | new_reg = old_reg & ~GPIO_DX_FRONT_PANEL; |
387fb6a2 CL |
475 | oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg); |
476 | spin_unlock_irq(&chip->reg_lock); | |
477 | return old_reg != new_reg; | |
478 | } | |
479 | ||
a8bb1bad | 480 | static const struct snd_kcontrol_new front_panel_switch = { |
387fb6a2 | 481 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
a8bb1bad CL |
482 | .name = "Front Panel Switch", |
483 | .info = snd_ctl_boolean_mono_info, | |
484 | .get = front_panel_get, | |
485 | .put = front_panel_put, | |
387fb6a2 CL |
486 | }; |
487 | ||
11864b4b CL |
488 | static void xonar_dx_ac97_switch(struct oxygen *chip, |
489 | unsigned int reg, unsigned int mute) | |
490 | { | |
491 | if (reg == AC97_LINE) { | |
492 | spin_lock_irq(&chip->reg_lock); | |
493 | oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, | |
494 | mute ? GPIO_DX_INPUT_ROUTE : 0, | |
495 | GPIO_DX_INPUT_ROUTE); | |
496 | spin_unlock_irq(&chip->reg_lock); | |
497 | } | |
498 | } | |
499 | ||
1b8ff22f | 500 | static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -12000, 50, 0); |
a9d3cc48 | 501 | static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -12700, 100, 0); |
1b8ff22f | 502 | |
af9af174 | 503 | static int xonar_d2_control_filter(struct snd_kcontrol_new *template) |
ccc80fb4 | 504 | { |
4972a177 | 505 | if (!strncmp(template->name, "CD Capture ", 11)) |
911b499a | 506 | /* CD in is actually connected to the video in pin */ |
ccc80fb4 | 507 | template->private_value ^= AC97_CD ^ AC97_VIDEO; |
ccc80fb4 CL |
508 | return 0; |
509 | } | |
510 | ||
a9d3cc48 CL |
511 | static int xonar_dx_control_filter(struct snd_kcontrol_new *template) |
512 | { | |
4972a177 | 513 | if (!strncmp(template->name, "CD Capture ", 11)) |
a9d3cc48 | 514 | return 1; /* no CD input */ |
a9d3cc48 CL |
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 | ||
387fb6a2 CL |
523 | static int xonar_dx_mixer_init(struct oxygen *chip) |
524 | { | |
a8bb1bad | 525 | return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip)); |
387fb6a2 CL |
526 | } |
527 | ||
271ebfca CL |
528 | static const struct oxygen_model xonar_models[] = { |
529 | [MODEL_D2] = { | |
aef1a535 | 530 | .shortname = "Xonar D2", |
271ebfca CL |
531 | .longname = "Asus Virtuoso 200", |
532 | .chip = "AV200", | |
533 | .owner = THIS_MODULE, | |
534 | .init = xonar_d2_init, | |
af9af174 | 535 | .control_filter = xonar_d2_control_filter, |
271ebfca CL |
536 | .mixer_init = xonar_mixer_init, |
537 | .cleanup = xonar_cleanup, | |
538 | .set_dac_params = set_pcm1796_params, | |
a694a6a0 | 539 | .set_adc_params = set_cs53x1_params, |
271ebfca CL |
540 | .update_dac_volume = update_pcm1796_volume, |
541 | .update_dac_mute = update_pcm1796_mute, | |
4972a177 | 542 | .dac_tlv = pcm1796_db_scale, |
271ebfca CL |
543 | .model_data_size = sizeof(struct xonar_data), |
544 | .pcm_dev_cfg = PLAYBACK_0_TO_I2S | | |
545 | PLAYBACK_1_TO_SPDIF | | |
546 | CAPTURE_0_FROM_I2S_2 | | |
547 | CAPTURE_1_FROM_SPDIF, | |
548 | .dac_channels = 8, | |
193e8138 CL |
549 | .dac_volume_min = 0x0f, |
550 | .dac_volume_max = 0xff, | |
271ebfca CL |
551 | .misc_flags = OXYGEN_MISC_MIDI, |
552 | .function_flags = OXYGEN_FUNCTION_SPI | | |
553 | OXYGEN_FUNCTION_ENABLE_SPI_4_5, | |
554 | .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | |
555 | .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | |
556 | }, | |
557 | [MODEL_D2X] = { | |
aef1a535 | 558 | .shortname = "Xonar D2X", |
271ebfca CL |
559 | .longname = "Asus Virtuoso 200", |
560 | .chip = "AV200", | |
561 | .owner = THIS_MODULE, | |
562 | .init = xonar_d2x_init, | |
af9af174 | 563 | .control_filter = xonar_d2_control_filter, |
271ebfca CL |
564 | .mixer_init = xonar_mixer_init, |
565 | .cleanup = xonar_cleanup, | |
566 | .set_dac_params = set_pcm1796_params, | |
a694a6a0 | 567 | .set_adc_params = set_cs53x1_params, |
271ebfca CL |
568 | .update_dac_volume = update_pcm1796_volume, |
569 | .update_dac_mute = update_pcm1796_mute, | |
570 | .gpio_changed = xonar_gpio_changed, | |
4972a177 | 571 | .dac_tlv = pcm1796_db_scale, |
271ebfca CL |
572 | .model_data_size = sizeof(struct xonar_data), |
573 | .pcm_dev_cfg = PLAYBACK_0_TO_I2S | | |
574 | PLAYBACK_1_TO_SPDIF | | |
575 | CAPTURE_0_FROM_I2S_2 | | |
576 | CAPTURE_1_FROM_SPDIF, | |
577 | .dac_channels = 8, | |
193e8138 CL |
578 | .dac_volume_min = 0x0f, |
579 | .dac_volume_max = 0xff, | |
271ebfca CL |
580 | .misc_flags = OXYGEN_MISC_MIDI, |
581 | .function_flags = OXYGEN_FUNCTION_SPI | | |
582 | OXYGEN_FUNCTION_ENABLE_SPI_4_5, | |
583 | .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | |
584 | .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | |
585 | }, | |
a9d3cc48 CL |
586 | [MODEL_DX] = { |
587 | .shortname = "Xonar DX", | |
588 | .longname = "Asus Virtuoso 100", | |
589 | .chip = "AV200", | |
590 | .owner = THIS_MODULE, | |
591 | .init = xonar_dx_init, | |
592 | .control_filter = xonar_dx_control_filter, | |
387fb6a2 | 593 | .mixer_init = xonar_dx_mixer_init, |
a9d3cc48 CL |
594 | .cleanup = xonar_dx_cleanup, |
595 | .set_dac_params = set_cs43xx_params, | |
596 | .set_adc_params = set_cs53x1_params, | |
597 | .update_dac_volume = update_cs43xx_volume, | |
598 | .update_dac_mute = update_cs43xx_mute, | |
599 | .gpio_changed = xonar_gpio_changed, | |
11864b4b | 600 | .ac97_switch = xonar_dx_ac97_switch, |
4972a177 | 601 | .dac_tlv = cs4362a_db_scale, |
a9d3cc48 CL |
602 | .model_data_size = sizeof(struct xonar_data), |
603 | .pcm_dev_cfg = PLAYBACK_0_TO_I2S | | |
604 | PLAYBACK_1_TO_SPDIF | | |
605 | CAPTURE_0_FROM_I2S_2, | |
606 | .dac_channels = 8, | |
193e8138 CL |
607 | .dac_volume_min = 0, |
608 | .dac_volume_max = 127, | |
a9d3cc48 CL |
609 | .function_flags = OXYGEN_FUNCTION_2WIRE, |
610 | .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | |
611 | .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | |
612 | }, | |
1b8ff22f CL |
613 | }; |
614 | ||
615 | static int __devinit xonar_probe(struct pci_dev *pci, | |
616 | const struct pci_device_id *pci_id) | |
617 | { | |
618 | static int dev; | |
619 | int err; | |
620 | ||
621 | if (dev >= SNDRV_CARDS) | |
622 | return -ENODEV; | |
623 | if (!enable[dev]) { | |
624 | ++dev; | |
625 | return -ENOENT; | |
626 | } | |
271ebfca CL |
627 | err = oxygen_pci_probe(pci, index[dev], id[dev], |
628 | &xonar_models[pci_id->driver_data]); | |
1b8ff22f CL |
629 | if (err >= 0) |
630 | ++dev; | |
631 | return err; | |
632 | } | |
633 | ||
634 | static struct pci_driver xonar_driver = { | |
635 | .name = "AV200", | |
636 | .id_table = xonar_ids, | |
637 | .probe = xonar_probe, | |
638 | .remove = __devexit_p(oxygen_pci_remove), | |
639 | }; | |
640 | ||
641 | static int __init alsa_card_xonar_init(void) | |
642 | { | |
643 | return pci_register_driver(&xonar_driver); | |
644 | } | |
645 | ||
646 | static void __exit alsa_card_xonar_exit(void) | |
647 | { | |
648 | pci_unregister_driver(&xonar_driver); | |
649 | } | |
650 | ||
651 | module_init(alsa_card_xonar_init) | |
652 | module_exit(alsa_card_xonar_exit) |