diff options
author | Clemens Ladisch <clemens@ladisch.de> | 2007-12-23 13:52:08 -0500 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2008-01-31 11:29:44 -0500 |
commit | 1b8ff22fa8d724e7f4367ec220c2c44ae38743fc (patch) | |
tree | 81810c7414f1af02b58952f19ee7bed4c4c17abe /sound/pci/oxygen/virtuoso.c | |
parent | d0ce9946c52e7bdf95afb09553775cf28b752254 (diff) |
[ALSA] add Asus Xonar driver
Add the snd-virtuoso driver for the Asus Virtuoso 200 chip used on the
PCI and PCI-E models of the Xonar sound card.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Diffstat (limited to 'sound/pci/oxygen/virtuoso.c')
-rw-r--r-- | sound/pci/oxygen/virtuoso.c | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c new file mode 100644 index 000000000000..5678dc36f9e5 --- /dev/null +++ b/sound/pci/oxygen/virtuoso.c | |||
@@ -0,0 +1,269 @@ | |||
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 | /* | ||
21 | * SPI 0 -> 1st PCM1796 (front) | ||
22 | * SPI 1 -> 2nd PCM1796 (side) | ||
23 | * SPI 2 -> 3rd PCM1796 (center/LFE) | ||
24 | * SPI 4 -> 4th PCM1796 (rear) | ||
25 | * | ||
26 | * GPIO 2 -> M0 of CS5381 | ||
27 | * GPIO 3 -> M1 of CS5381 | ||
28 | * GPIO 5 <- ? (D2X only) | ||
29 | * GPIO 7 -> ALT | ||
30 | * GPIO 8 -> ? (amps enable?) | ||
31 | */ | ||
32 | |||
33 | #include <sound/driver.h> | ||
34 | #include <linux/pci.h> | ||
35 | #include <linux/delay.h> | ||
36 | #include <sound/control.h> | ||
37 | #include <sound/core.h> | ||
38 | #include <sound/initval.h> | ||
39 | #include <sound/pcm.h> | ||
40 | #include <sound/tlv.h> | ||
41 | #include "oxygen.h" | ||
42 | |||
43 | MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); | ||
44 | MODULE_DESCRIPTION("Asus AV200 driver"); | ||
45 | MODULE_LICENSE("GPL"); | ||
46 | MODULE_SUPPORTED_DEVICE("{{Asus,AV200}}"); | ||
47 | |||
48 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; | ||
49 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; | ||
50 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; | ||
51 | |||
52 | module_param_array(index, int, NULL, 0444); | ||
53 | MODULE_PARM_DESC(index, "card index"); | ||
54 | module_param_array(id, charp, NULL, 0444); | ||
55 | MODULE_PARM_DESC(id, "ID string"); | ||
56 | module_param_array(enable, bool, NULL, 0444); | ||
57 | MODULE_PARM_DESC(enable, "enable card"); | ||
58 | |||
59 | static struct pci_device_id xonar_ids[] __devinitdata = { | ||
60 | { OXYGEN_PCI_SUBID(0x1043, 0x8269) }, /* Asus Xonar D2 */ | ||
61 | { OXYGEN_PCI_SUBID(0x1043, 0x82b7) }, /* Asus Xonar D2X */ | ||
62 | { } | ||
63 | }; | ||
64 | MODULE_DEVICE_TABLE(pci, xonar_ids); | ||
65 | |||
66 | /* register 0x12 */ | ||
67 | #define PCM1796_MUTE 0x01 | ||
68 | #define PCM1796_FMT_24_MSB 0x30 | ||
69 | #define PCM1796_ATLD 0x80 | ||
70 | /* register 0x14 */ | ||
71 | #define PCM1796_OS_64 0x00 | ||
72 | #define PCM1796_OS_32 0x01 | ||
73 | #define PCM1796_OS_128 0x02 | ||
74 | |||
75 | static void pcm1796_write(struct oxygen *chip, unsigned int codec, | ||
76 | u8 reg, u8 value) | ||
77 | { | ||
78 | /* maps ALSA channel pair number to SPI output */ | ||
79 | static const u8 codec_map[4] = { | ||
80 | 0, 4, 2, 1 | ||
81 | }; | ||
82 | oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER_WRITE | | ||
83 | OXYGEN_SPI_DATA_LENGTH_2 | | ||
84 | (codec_map[codec] << OXYGEN_SPI_CODEC_SHIFT) | | ||
85 | OXYGEN_SPI_MAGIC, | ||
86 | (reg << 8) | value); | ||
87 | } | ||
88 | |||
89 | static void xonar_init(struct oxygen *chip) | ||
90 | { | ||
91 | unsigned int i; | ||
92 | |||
93 | for (i = 0; i < 4; ++i) { | ||
94 | pcm1796_write(chip, i, 0x12, PCM1796_FMT_24_MSB | PCM1796_ATLD); | ||
95 | pcm1796_write(chip, i, 0x13, 0); | ||
96 | pcm1796_write(chip, i, 0x14, PCM1796_OS_64); | ||
97 | pcm1796_write(chip, i, 0x15, 0); | ||
98 | pcm1796_write(chip, i, 0x10, 0xff); | ||
99 | pcm1796_write(chip, i, 0x11, 0xff); | ||
100 | } | ||
101 | |||
102 | oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, 0x8c); | ||
103 | oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, 0x00, 0x8c); | ||
104 | #if 0 | ||
105 | oxygen_clear_bits16(chip, OXYGEN_I2S_MULTICH_FORMAT, | ||
106 | OXYGEN_I2S_MAGIC1_MASK); | ||
107 | #endif | ||
108 | oxygen_ac97_set_bits(chip, 0, 0x62, 0x0080); | ||
109 | msleep(300); | ||
110 | oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, 0x100); | ||
111 | oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, 0x100); | ||
112 | |||
113 | snd_component_add(chip->card, "PCM1796"); | ||
114 | snd_component_add(chip->card, "CS5381"); | ||
115 | } | ||
116 | |||
117 | static void xonar_cleanup(struct oxygen *chip) | ||
118 | { | ||
119 | oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, 0x100); | ||
120 | } | ||
121 | |||
122 | static void set_pcm1796_params(struct oxygen *chip, | ||
123 | struct snd_pcm_hw_params *params) | ||
124 | { | ||
125 | #if 0 | ||
126 | unsigned int i; | ||
127 | u8 value; | ||
128 | |||
129 | value = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64; | ||
130 | for (i = 0; i < 4; ++i) | ||
131 | pcm1796_write(chip, i, 0x14, value); | ||
132 | #endif | ||
133 | } | ||
134 | |||
135 | static void update_pcm1796_volume(struct oxygen *chip) | ||
136 | { | ||
137 | unsigned int i; | ||
138 | |||
139 | for (i = 0; i < 4; ++i) { | ||
140 | pcm1796_write(chip, i, 0x10, chip->dac_volume[i * 2]); | ||
141 | pcm1796_write(chip, i, 0x11, chip->dac_volume[i * 2 + 1]); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | static void update_pcm1796_mute(struct oxygen *chip) | ||
146 | { | ||
147 | unsigned int i; | ||
148 | u8 value; | ||
149 | |||
150 | value = PCM1796_FMT_24_MSB | PCM1796_ATLD; | ||
151 | if (chip->dac_mute) | ||
152 | value |= PCM1796_MUTE; | ||
153 | for (i = 0; i < 4; ++i) | ||
154 | pcm1796_write(chip, i, 0x12, value); | ||
155 | } | ||
156 | |||
157 | static void set_cs5381_params(struct oxygen *chip, | ||
158 | struct snd_pcm_hw_params *params) | ||
159 | { | ||
160 | unsigned int value; | ||
161 | |||
162 | if (params_rate(params) <= 54000) | ||
163 | value = 0; | ||
164 | else if (params_rate(params) <= 108000) | ||
165 | value = 4; | ||
166 | else | ||
167 | value = 8; | ||
168 | oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, value, 0x000c); | ||
169 | } | ||
170 | |||
171 | static int alt_switch_get(struct snd_kcontrol *ctl, | ||
172 | struct snd_ctl_elem_value *value) | ||
173 | { | ||
174 | struct oxygen *chip = ctl->private_data; | ||
175 | |||
176 | value->value.integer.value[0] = | ||
177 | !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & 0x80); | ||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | static int alt_switch_put(struct snd_kcontrol *ctl, | ||
182 | struct snd_ctl_elem_value *value) | ||
183 | { | ||
184 | struct oxygen *chip = ctl->private_data; | ||
185 | u16 old_bits, new_bits; | ||
186 | int changed; | ||
187 | |||
188 | spin_lock_irq(&chip->reg_lock); | ||
189 | old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA); | ||
190 | if (value->value.integer.value[0]) | ||
191 | new_bits = old_bits | 0x80; | ||
192 | else | ||
193 | new_bits = old_bits & ~0x80; | ||
194 | changed = new_bits != old_bits; | ||
195 | if (changed) | ||
196 | oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits); | ||
197 | spin_unlock_irq(&chip->reg_lock); | ||
198 | return changed; | ||
199 | } | ||
200 | |||
201 | static const struct snd_kcontrol_new alt_switch = { | ||
202 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
203 | .name = "Analog Loopback Switch", | ||
204 | .info = snd_ctl_boolean_mono_info, | ||
205 | .get = alt_switch_get, | ||
206 | .put = alt_switch_put, | ||
207 | }; | ||
208 | |||
209 | static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -12000, 50, 0); | ||
210 | |||
211 | static int xonar_mixer_init(struct oxygen *chip) | ||
212 | { | ||
213 | return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip)); | ||
214 | } | ||
215 | |||
216 | static const struct oxygen_model model_xonar = { | ||
217 | .shortname = "Asus AV200", | ||
218 | .longname = "Asus Virtuoso 200", | ||
219 | .chip = "AV200", | ||
220 | .init = xonar_init, | ||
221 | .mixer_init = xonar_mixer_init, | ||
222 | .cleanup = xonar_cleanup, | ||
223 | .set_dac_params = set_pcm1796_params, | ||
224 | .set_adc_params = set_cs5381_params, | ||
225 | .update_dac_volume = update_pcm1796_volume, | ||
226 | .update_dac_mute = update_pcm1796_mute, | ||
227 | .dac_tlv = pcm1796_db_scale, | ||
228 | .record_from_dma_b = 1, | ||
229 | .cd_in_from_video_in = 1, | ||
230 | .dac_minimum_volume = 15, | ||
231 | }; | ||
232 | |||
233 | static int __devinit xonar_probe(struct pci_dev *pci, | ||
234 | const struct pci_device_id *pci_id) | ||
235 | { | ||
236 | static int dev; | ||
237 | int err; | ||
238 | |||
239 | if (dev >= SNDRV_CARDS) | ||
240 | return -ENODEV; | ||
241 | if (!enable[dev]) { | ||
242 | ++dev; | ||
243 | return -ENOENT; | ||
244 | } | ||
245 | err = oxygen_pci_probe(pci, index[dev], id[dev], &model_xonar); | ||
246 | if (err >= 0) | ||
247 | ++dev; | ||
248 | return err; | ||
249 | } | ||
250 | |||
251 | static struct pci_driver xonar_driver = { | ||
252 | .name = "AV200", | ||
253 | .id_table = xonar_ids, | ||
254 | .probe = xonar_probe, | ||
255 | .remove = __devexit_p(oxygen_pci_remove), | ||
256 | }; | ||
257 | |||
258 | static int __init alsa_card_xonar_init(void) | ||
259 | { | ||
260 | return pci_register_driver(&xonar_driver); | ||
261 | } | ||
262 | |||
263 | static void __exit alsa_card_xonar_exit(void) | ||
264 | { | ||
265 | pci_unregister_driver(&xonar_driver); | ||
266 | } | ||
267 | |||
268 | module_init(alsa_card_xonar_init) | ||
269 | module_exit(alsa_card_xonar_exit) | ||