diff options
Diffstat (limited to 'sound/isa/cs423x/cs4236.c')
-rw-r--r-- | sound/isa/cs423x/cs4236.c | 608 |
1 files changed, 608 insertions, 0 deletions
diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c new file mode 100644 index 000000000000..e745a54e00a1 --- /dev/null +++ b/sound/isa/cs423x/cs4236.c | |||
@@ -0,0 +1,608 @@ | |||
1 | /* | ||
2 | * Driver for generic CS4232/CS4235/CS4236/CS4236B/CS4237B/CS4238B/CS4239 chips | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/pnp.h> | ||
26 | #include <linux/moduleparam.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/cs4231.h> | ||
29 | #include <sound/mpu401.h> | ||
30 | #include <sound/opl3.h> | ||
31 | #include <sound/initval.h> | ||
32 | |||
33 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
34 | MODULE_LICENSE("GPL"); | ||
35 | #ifdef CS4232 | ||
36 | MODULE_DESCRIPTION("Cirrus Logic CS4232"); | ||
37 | MODULE_SUPPORTED_DEVICE("{{Turtle Beach,TBS-2000}," | ||
38 | "{Turtle Beach,Tropez Plus}," | ||
39 | "{SIC CrystalWave 32}," | ||
40 | "{Hewlett Packard,Omnibook 5500}," | ||
41 | "{TerraTec,Maestro 32/96}," | ||
42 | "{Philips,PCA70PS}}"); | ||
43 | #else | ||
44 | MODULE_DESCRIPTION("Cirrus Logic CS4235-9"); | ||
45 | MODULE_SUPPORTED_DEVICE("{{Crystal Semiconductors,CS4235}," | ||
46 | "{Crystal Semiconductors,CS4236}," | ||
47 | "{Crystal Semiconductors,CS4237}," | ||
48 | "{Crystal Semiconductors,CS4238}," | ||
49 | "{Crystal Semiconductors,CS4239}," | ||
50 | "{Acer,AW37}," | ||
51 | "{Acer,AW35/Pro}," | ||
52 | "{Crystal,3D}," | ||
53 | "{Crystal Computer,TidalWave128}," | ||
54 | "{Dell,Optiplex GX1}," | ||
55 | "{Dell,Workstation 400 sound}," | ||
56 | "{EliteGroup,P5TX-LA sound}," | ||
57 | "{Gallant,SC-70P}," | ||
58 | "{Gateway,E1000 Onboard CS4236B}," | ||
59 | "{Genius,Sound Maker 3DJ}," | ||
60 | "{Hewlett Packard,HP6330 sound}," | ||
61 | "{IBM,PC 300PL sound}," | ||
62 | "{IBM,Aptiva 2137 E24}," | ||
63 | "{IBM,IntelliStation M Pro}," | ||
64 | "{Intel,Marlin Spike Mobo CS4235}," | ||
65 | "{Intel PR440FX Onboard}," | ||
66 | "{Guillemot,MaxiSound 16 PnP}," | ||
67 | "{NewClear,3D}," | ||
68 | "{TerraTec,AudioSystem EWS64L/XL}," | ||
69 | "{Typhoon Soundsystem,CS4236B}," | ||
70 | "{Turtle Beach,Malibu}," | ||
71 | "{Unknown,Digital PC 5000 Onboard}}"); | ||
72 | #endif | ||
73 | |||
74 | #ifdef CS4232 | ||
75 | #define IDENT "CS4232" | ||
76 | #else | ||
77 | #define IDENT "CS4236+" | ||
78 | #endif | ||
79 | |||
80 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
81 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
82 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ | ||
83 | #ifdef CONFIG_PNP | ||
84 | static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; | ||
85 | #endif | ||
86 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
87 | static long cport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
88 | static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* PnP setup */ | ||
89 | static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
90 | static long sb_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
91 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */ | ||
92 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 9,11,12,15 */ | ||
93 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ | ||
94 | static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ | ||
95 | |||
96 | module_param_array(index, int, NULL, 0444); | ||
97 | MODULE_PARM_DESC(index, "Index value for " IDENT " soundcard."); | ||
98 | module_param_array(id, charp, NULL, 0444); | ||
99 | MODULE_PARM_DESC(id, "ID string for " IDENT " soundcard."); | ||
100 | module_param_array(enable, bool, NULL, 0444); | ||
101 | MODULE_PARM_DESC(enable, "Enable " IDENT " soundcard."); | ||
102 | #ifdef CONFIG_PNP | ||
103 | module_param_array(isapnp, bool, NULL, 0444); | ||
104 | MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard."); | ||
105 | #endif | ||
106 | module_param_array(port, long, NULL, 0444); | ||
107 | MODULE_PARM_DESC(port, "Port # for " IDENT " driver."); | ||
108 | module_param_array(cport, long, NULL, 0444); | ||
109 | MODULE_PARM_DESC(cport, "Control port # for " IDENT " driver."); | ||
110 | module_param_array(mpu_port, long, NULL, 0444); | ||
111 | MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " IDENT " driver."); | ||
112 | module_param_array(fm_port, long, NULL, 0444); | ||
113 | MODULE_PARM_DESC(fm_port, "FM port # for " IDENT " driver."); | ||
114 | module_param_array(sb_port, long, NULL, 0444); | ||
115 | MODULE_PARM_DESC(sb_port, "SB port # for " IDENT " driver (optional)."); | ||
116 | module_param_array(irq, int, NULL, 0444); | ||
117 | MODULE_PARM_DESC(irq, "IRQ # for " IDENT " driver."); | ||
118 | module_param_array(mpu_irq, int, NULL, 0444); | ||
119 | MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " IDENT " driver."); | ||
120 | module_param_array(dma1, int, NULL, 0444); | ||
121 | MODULE_PARM_DESC(dma1, "DMA1 # for " IDENT " driver."); | ||
122 | module_param_array(dma2, int, NULL, 0444); | ||
123 | MODULE_PARM_DESC(dma2, "DMA2 # for " IDENT " driver."); | ||
124 | |||
125 | struct snd_card_cs4236 { | ||
126 | struct resource *res_sb_port; | ||
127 | #ifdef CONFIG_PNP | ||
128 | struct pnp_dev *wss; | ||
129 | struct pnp_dev *ctrl; | ||
130 | struct pnp_dev *mpu; | ||
131 | #endif | ||
132 | }; | ||
133 | |||
134 | static snd_card_t *snd_cs4236_legacy[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
135 | |||
136 | #ifdef CONFIG_PNP | ||
137 | |||
138 | #define ISAPNP_CS4232(_va, _vb, _vc, _device, _wss, _ctrl, _mpu401) \ | ||
139 | { \ | ||
140 | ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ | ||
141 | .devs = { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \ | ||
142 | ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl), \ | ||
143 | ISAPNP_DEVICE_ID(_va, _vb, _vc, _mpu401) } \ | ||
144 | } | ||
145 | #define ISAPNP_CS4232_1(_va, _vb, _vc, _device, _wss, _ctrl, _mpu401) \ | ||
146 | { \ | ||
147 | ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ | ||
148 | .devs = { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \ | ||
149 | ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl), \ | ||
150 | ISAPNP_DEVICE_ID('P', 'N', 'P', _mpu401) } \ | ||
151 | } | ||
152 | #define ISAPNP_CS4232_WOMPU(_va, _vb, _vc, _device, _wss, _ctrl) \ | ||
153 | { \ | ||
154 | ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ | ||
155 | .devs = { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \ | ||
156 | ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl) } \ | ||
157 | } | ||
158 | |||
159 | |||
160 | #ifdef CS4232 | ||
161 | static struct pnp_card_device_id snd_cs423x_pnpids[] = { | ||
162 | /* Philips PCA70PS */ | ||
163 | { .id = "CSC0d32", .devs = { { "CSC0000" }, { "CSC0010" }, { "PNPb006" } } }, | ||
164 | /* TerraTec Maestro 32/96 (CS4232) */ | ||
165 | { .id = "CSC1a32", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
166 | /* HP Omnibook 5500 onboard */ | ||
167 | { .id = "CSC4232", .devs = { { "CSC0000" }, { "CSC0002" }, { "CSC0003" } } }, | ||
168 | /* Unnamed CS4236 card (Made in Taiwan) */ | ||
169 | { .id = "CSC4236", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
170 | /* Turtle Beach TBS-2000 (CS4232) */ | ||
171 | { .id = "CSC7532", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSCb006" } } }, | ||
172 | /* Turtle Beach Tropez Plus (CS4232) */ | ||
173 | { .id = "CSC7632", .devs = { { "CSC0000" }, { "CSC0010" }, { "PNPb006" } } }, | ||
174 | /* SIC CrystalWave 32 (CS4232) */ | ||
175 | { .id = "CSCf032", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
176 | /* --- */ | ||
177 | { .id = "" } /* end */ | ||
178 | }; | ||
179 | #else /* CS4236 */ | ||
180 | static struct pnp_card_device_id snd_cs423x_pnpids[] = { | ||
181 | /* Intel Marlin Spike Motherboard - CS4235 */ | ||
182 | { .id = "CSC0225", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
183 | /* Intel Marlin Spike Motherboard (#2) - CS4235 */ | ||
184 | { .id = "CSC0225", .devs = { { "CSC0100" }, { "CSC0110" }, { "CSC0103" } } }, | ||
185 | /* Unknown Intel mainboard - CS4235 */ | ||
186 | { .id = "CSC0225", .devs = { { "CSC0100" }, { "CSC0110" } } }, | ||
187 | /* Genius Sound Maker 3DJ - CS4237B */ | ||
188 | { .id = "CSC0437", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
189 | /* Digital PC 5000 Onboard - CS4236B */ | ||
190 | { .id = "CSC0735", .devs = { { "CSC0000" }, { "CSC0010" } } }, | ||
191 | /* some uknown CS4236B */ | ||
192 | { .id = "CSC0b35", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
193 | /* Intel PR440FX Onboard sound */ | ||
194 | { .id = "CSC0b36", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
195 | /* CS4235 on mainboard without MPU */ | ||
196 | { .id = "CSC1425", .devs = { { "CSC0100" }, { "CSC0110" } } }, | ||
197 | /* Gateway E1000 Onboard CS4236B */ | ||
198 | { .id = "CSC1335", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
199 | /* HP 6330 Onboard sound */ | ||
200 | { .id = "CSC1525", .devs = { { "CSC0100" }, { "CSC0110" }, { "CSC0103" } } }, | ||
201 | /* Crystal Computer TidalWave128 */ | ||
202 | { .id = "CSC1e37", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
203 | /* ACER AW37 - CS4235 */ | ||
204 | { .id = "CSC4236", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
205 | /* build-in soundcard in EliteGroup P5TX-LA motherboard - CS4237B */ | ||
206 | { .id = "CSC4237", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
207 | /* Crystal 3D - CS4237B */ | ||
208 | { .id = "CSC4336", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
209 | /* Typhoon Soundsystem PnP - CS4236B */ | ||
210 | { .id = "CSC4536", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
211 | /* Crystal CX4235-XQ3 EP - CS4235 */ | ||
212 | { .id = "CSC4625", .devs = { { "CSC0100" }, { "CSC0110" }, { "CSC0103" } } }, | ||
213 | /* Crystal Semiconductors CS4237B */ | ||
214 | { .id = "CSC4637", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
215 | /* NewClear 3D - CX4237B-XQ3 */ | ||
216 | { .id = "CSC4837", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
217 | /* Dell Optiplex GX1 - CS4236B */ | ||
218 | { .id = "CSC6835", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
219 | /* Dell P410 motherboard - CS4236B */ | ||
220 | { .id = "CSC6835", .devs = { { "CSC0000" }, { "CSC0010" } } }, | ||
221 | /* Dell Workstation 400 Onboard - CS4236B */ | ||
222 | { .id = "CSC6836", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
223 | /* Turtle Beach Malibu - CS4237B */ | ||
224 | { .id = "CSC7537", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
225 | /* CS4235 - onboard */ | ||
226 | { .id = "CSC8025", .devs = { { "CSC0100" }, { "CSC0110" }, { "CSC0103" } } }, | ||
227 | /* IBM Aptiva 2137 E24 Onboard - CS4237B */ | ||
228 | { .id = "CSC8037", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
229 | /* IBM IntelliStation M Pro motherboard */ | ||
230 | { .id = "CSCc835", .devs = { { "CSC0000" }, { "CSC0010" } } }, | ||
231 | /* Guillemot MaxiSound 16 PnP - CS4236B */ | ||
232 | { .id = "CSC9836", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
233 | /* Gallant SC-70P */ | ||
234 | { .id = "CSC9837", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
235 | /* TerraTec AudioSystem EWS64XL - CS4236B */ | ||
236 | { .id = "CSCa836", .devs = { { "CSCa800" }, { "CSCa810" }, { "CSCa803" } } }, | ||
237 | /* TerraTec AudioSystem EWS64XL - CS4236B */ | ||
238 | { .id = "CSCa836", .devs = { { "CSCa800" }, { "CSCa810" } } }, | ||
239 | /* ACER AW37/Pro - CS4235 */ | ||
240 | { .id = "CSCd925", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
241 | /* ACER AW35/Pro - CS4237B */ | ||
242 | { .id = "CSCd937", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
243 | /* CS4235 without MPU401 */ | ||
244 | { .id = "CSCe825", .devs = { { "CSC0100" }, { "CSC0110" } } }, | ||
245 | /* Unknown SiS530 - CS4235 */ | ||
246 | { .id = "CSC4825", .devs = { { "CSC0100" }, { "CSC0110" } } }, | ||
247 | /* IBM IntelliStation M Pro 6898 11U - CS4236B */ | ||
248 | { .id = "CSCe835", .devs = { { "CSC0000" }, { "CSC0010" } } }, | ||
249 | /* IBM PC 300PL Onboard - CS4236B */ | ||
250 | { .id = "CSCe836", .devs = { { "CSC0000" }, { "CSC0010" } } }, | ||
251 | /* Some noname CS4236 based card */ | ||
252 | { .id = "CSCe936", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
253 | /* CS4236B */ | ||
254 | { .id = "CSCf235", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
255 | /* CS4236B */ | ||
256 | { .id = "CSCf238", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
257 | /* --- */ | ||
258 | { .id = "" } /* end */ | ||
259 | }; | ||
260 | #endif | ||
261 | |||
262 | MODULE_DEVICE_TABLE(pnp_card, snd_cs423x_pnpids); | ||
263 | |||
264 | static int __devinit snd_card_cs4236_pnp(int dev, struct snd_card_cs4236 *acard, | ||
265 | struct pnp_card_link *card, | ||
266 | const struct pnp_card_device_id *id) | ||
267 | { | ||
268 | struct pnp_dev *pdev; | ||
269 | struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); | ||
270 | int err; | ||
271 | |||
272 | if (!cfg) | ||
273 | return -ENOMEM; | ||
274 | |||
275 | acard->wss = pnp_request_card_device(card, id->devs[0].id, NULL); | ||
276 | if (acard->wss == NULL) { | ||
277 | kfree(cfg); | ||
278 | return -EBUSY; | ||
279 | } | ||
280 | acard->ctrl = pnp_request_card_device(card, id->devs[1].id, NULL); | ||
281 | if (acard->ctrl == NULL) { | ||
282 | kfree(cfg); | ||
283 | return -EBUSY; | ||
284 | } | ||
285 | if (id->devs[2].id[0]) { | ||
286 | acard->mpu = pnp_request_card_device(card, id->devs[2].id, NULL); | ||
287 | if (acard->mpu == NULL) { | ||
288 | kfree(cfg); | ||
289 | return -EBUSY; | ||
290 | } | ||
291 | } | ||
292 | |||
293 | /* WSS initialization */ | ||
294 | pdev = acard->wss; | ||
295 | pnp_init_resource_table(cfg); | ||
296 | if (port[dev] != SNDRV_AUTO_PORT) | ||
297 | pnp_resource_change(&cfg->port_resource[0], port[dev], 4); | ||
298 | if (fm_port[dev] != SNDRV_AUTO_PORT && fm_port[dev] > 0) | ||
299 | pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4); | ||
300 | if (sb_port[dev] != SNDRV_AUTO_PORT) | ||
301 | pnp_resource_change(&cfg->port_resource[2], sb_port[dev], 16); | ||
302 | if (irq[dev] != SNDRV_AUTO_IRQ) | ||
303 | pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); | ||
304 | if (dma1[dev] != SNDRV_AUTO_DMA) | ||
305 | pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); | ||
306 | if (dma2[dev] != SNDRV_AUTO_DMA) | ||
307 | pnp_resource_change(&cfg->dma_resource[1], dma2[dev] < 0 ? 4 : dma2[dev], 1); | ||
308 | err = pnp_manual_config_dev(pdev, cfg, 0); | ||
309 | if (err < 0) | ||
310 | snd_printk(KERN_ERR IDENT " WSS PnP manual resources are invalid, using auto config\n"); | ||
311 | err = pnp_activate_dev(pdev); | ||
312 | if (err < 0) { | ||
313 | kfree(cfg); | ||
314 | printk(KERN_ERR IDENT " WSS PnP configure failed for WSS (out of resources?)\n"); | ||
315 | return -EBUSY; | ||
316 | } | ||
317 | port[dev] = pnp_port_start(pdev, 0); | ||
318 | if (fm_port[dev] > 0) | ||
319 | fm_port[dev] = pnp_port_start(pdev, 1); | ||
320 | sb_port[dev] = pnp_port_start(pdev, 2); | ||
321 | irq[dev] = pnp_irq(pdev, 0); | ||
322 | dma1[dev] = pnp_dma(pdev, 0); | ||
323 | dma2[dev] = pnp_dma(pdev, 1) == 4 ? -1 : (int)pnp_dma(pdev, 1); | ||
324 | snd_printdd("isapnp WSS: wss port=0x%lx, fm port=0x%lx, sb port=0x%lx\n", | ||
325 | port[dev], fm_port[dev], sb_port[dev]); | ||
326 | snd_printdd("isapnp WSS: irq=%i, dma1=%i, dma2=%i\n", | ||
327 | irq[dev], dma1[dev], dma2[dev]); | ||
328 | /* CTRL initialization */ | ||
329 | if (acard->ctrl && cport[dev] > 0) { | ||
330 | pdev = acard->ctrl; | ||
331 | pnp_init_resource_table(cfg); | ||
332 | if (cport[dev] != SNDRV_AUTO_PORT) | ||
333 | pnp_resource_change(&cfg->port_resource[0], cport[dev], 8); | ||
334 | err = pnp_manual_config_dev(pdev, cfg, 0); | ||
335 | if (err < 0) | ||
336 | snd_printk(KERN_ERR IDENT " CTRL PnP manual resources are invalid, using auto config\n"); | ||
337 | err = pnp_activate_dev(pdev); | ||
338 | if (err < 0) { | ||
339 | kfree(cfg); | ||
340 | printk(KERN_ERR IDENT " CTRL PnP configure failed for WSS (out of resources?)\n"); | ||
341 | return -EBUSY; | ||
342 | } | ||
343 | cport[dev] = pnp_port_start(pdev, 0); | ||
344 | snd_printdd("isapnp CTRL: control port=0x%lx\n", cport[dev]); | ||
345 | } | ||
346 | /* MPU initialization */ | ||
347 | if (acard->mpu && mpu_port[dev] > 0) { | ||
348 | pdev = acard->mpu; | ||
349 | pnp_init_resource_table(cfg); | ||
350 | if (mpu_port[dev] != SNDRV_AUTO_PORT) | ||
351 | pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2); | ||
352 | if (mpu_irq[dev] != SNDRV_AUTO_IRQ && mpu_irq[dev] >= 0 && | ||
353 | pnp_irq_valid(pdev, 0)) | ||
354 | pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1); | ||
355 | err = pnp_manual_config_dev(pdev, cfg, 0); | ||
356 | if (err < 0) | ||
357 | snd_printk(KERN_ERR IDENT " MPU401 PnP manual resources are invalid, using auto config\n"); | ||
358 | err = pnp_activate_dev(pdev); | ||
359 | if (err < 0) { | ||
360 | printk(KERN_ERR IDENT " MPU401 PnP configure failed for WSS (out of resources?)\n"); | ||
361 | mpu_port[dev] = SNDRV_AUTO_PORT; | ||
362 | mpu_irq[dev] = SNDRV_AUTO_IRQ; | ||
363 | } else { | ||
364 | mpu_port[dev] = pnp_port_start(pdev, 0); | ||
365 | if (mpu_irq[dev] >= 0 && | ||
366 | pnp_irq_valid(pdev, 0) && pnp_irq(pdev, 0) >= 0) { | ||
367 | mpu_irq[dev] = pnp_irq(pdev, 0); | ||
368 | } else { | ||
369 | mpu_irq[dev] = -1; /* disable interrupt */ | ||
370 | } | ||
371 | } | ||
372 | snd_printdd("isapnp MPU: port=0x%lx, irq=%i\n", mpu_port[dev], mpu_irq[dev]); | ||
373 | } | ||
374 | kfree(cfg); | ||
375 | return 0; | ||
376 | } | ||
377 | #endif /* CONFIG_PNP */ | ||
378 | |||
379 | static void snd_card_cs4236_free(snd_card_t *card) | ||
380 | { | ||
381 | struct snd_card_cs4236 *acard = (struct snd_card_cs4236 *)card->private_data; | ||
382 | |||
383 | if (acard) { | ||
384 | if (acard->res_sb_port) { | ||
385 | release_resource(acard->res_sb_port); | ||
386 | kfree_nocheck(acard->res_sb_port); | ||
387 | } | ||
388 | } | ||
389 | } | ||
390 | |||
391 | static int __devinit snd_card_cs423x_probe(int dev, struct pnp_card_link *pcard, | ||
392 | const struct pnp_card_device_id *pid) | ||
393 | { | ||
394 | snd_card_t *card; | ||
395 | struct snd_card_cs4236 *acard; | ||
396 | snd_pcm_t *pcm = NULL; | ||
397 | cs4231_t *chip; | ||
398 | opl3_t *opl3; | ||
399 | int err; | ||
400 | |||
401 | #ifdef CONFIG_PNP | ||
402 | if (!isapnp[dev]) { | ||
403 | #endif | ||
404 | if (port[dev] == SNDRV_AUTO_PORT) { | ||
405 | snd_printk("specify port\n"); | ||
406 | return -EINVAL; | ||
407 | } | ||
408 | if (cport[dev] == SNDRV_AUTO_PORT) { | ||
409 | snd_printk("specify cport\n"); | ||
410 | return -EINVAL; | ||
411 | } | ||
412 | #ifdef CONFIG_PNP | ||
413 | } | ||
414 | #endif | ||
415 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, | ||
416 | sizeof(struct snd_card_cs4236)); | ||
417 | if (card == NULL) | ||
418 | return -ENOMEM; | ||
419 | acard = (struct snd_card_cs4236 *)card->private_data; | ||
420 | card->private_free = snd_card_cs4236_free; | ||
421 | #ifdef CONFIG_PNP | ||
422 | if (isapnp[dev]) { | ||
423 | if ((err = snd_card_cs4236_pnp(dev, acard, pcard, pid))<0) { | ||
424 | printk(KERN_ERR "isapnp detection failed and probing for " IDENT " is not supported\n"); | ||
425 | snd_card_free(card); | ||
426 | return -ENXIO; | ||
427 | } | ||
428 | snd_card_set_dev(card, &pcard->card->dev); | ||
429 | } | ||
430 | #endif | ||
431 | if (sb_port[dev] > 0 && sb_port[dev] != SNDRV_AUTO_PORT) | ||
432 | if ((acard->res_sb_port = request_region(sb_port[dev], 16, IDENT " SB")) == NULL) { | ||
433 | printk(KERN_ERR IDENT ": unable to register SB port at 0x%lx\n", sb_port[dev]); | ||
434 | snd_card_free(card); | ||
435 | return -ENOMEM; | ||
436 | } | ||
437 | |||
438 | #ifdef CS4232 | ||
439 | if ((err = snd_cs4231_create(card, | ||
440 | port[dev], | ||
441 | cport[dev], | ||
442 | irq[dev], | ||
443 | dma1[dev], | ||
444 | dma2[dev], | ||
445 | CS4231_HW_DETECT, | ||
446 | 0, | ||
447 | &chip)) < 0) { | ||
448 | snd_card_free(card); | ||
449 | return err; | ||
450 | } | ||
451 | if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0) { | ||
452 | snd_card_free(card); | ||
453 | return err; | ||
454 | } | ||
455 | if ((err = snd_cs4231_mixer(chip)) < 0) { | ||
456 | snd_card_free(card); | ||
457 | return err; | ||
458 | } | ||
459 | |||
460 | #else /* CS4236 */ | ||
461 | if ((err = snd_cs4236_create(card, | ||
462 | port[dev], | ||
463 | cport[dev], | ||
464 | irq[dev], | ||
465 | dma1[dev], | ||
466 | dma2[dev], | ||
467 | CS4231_HW_DETECT, | ||
468 | 0, | ||
469 | &chip)) < 0) { | ||
470 | snd_card_free(card); | ||
471 | return err; | ||
472 | } | ||
473 | if ((err = snd_cs4236_pcm(chip, 0, &pcm)) < 0) { | ||
474 | snd_card_free(card); | ||
475 | return err; | ||
476 | } | ||
477 | if ((err = snd_cs4236_mixer(chip)) < 0) { | ||
478 | snd_card_free(card); | ||
479 | return err; | ||
480 | } | ||
481 | #endif | ||
482 | strcpy(card->driver, pcm->name); | ||
483 | strcpy(card->shortname, pcm->name); | ||
484 | sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i", | ||
485 | pcm->name, | ||
486 | chip->port, | ||
487 | irq[dev], | ||
488 | dma1[dev]); | ||
489 | if (dma2[dev] >= 0) | ||
490 | sprintf(card->longname + strlen(card->longname), "&%d", dma2[dev]); | ||
491 | |||
492 | if ((err = snd_cs4231_timer(chip, 0, NULL)) < 0) { | ||
493 | snd_card_free(card); | ||
494 | return err; | ||
495 | } | ||
496 | |||
497 | if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) { | ||
498 | if (snd_opl3_create(card, | ||
499 | fm_port[dev], fm_port[dev] + 2, | ||
500 | OPL3_HW_OPL3_CS, 0, &opl3) < 0) { | ||
501 | printk(KERN_ERR IDENT ": OPL3 not detected\n"); | ||
502 | } else { | ||
503 | if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { | ||
504 | snd_card_free(card); | ||
505 | return err; | ||
506 | } | ||
507 | } | ||
508 | } | ||
509 | |||
510 | if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) { | ||
511 | if (mpu_irq[dev] == SNDRV_AUTO_IRQ) | ||
512 | mpu_irq[dev] = -1; | ||
513 | if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232, | ||
514 | mpu_port[dev], 0, | ||
515 | mpu_irq[dev], | ||
516 | mpu_irq[dev] >= 0 ? SA_INTERRUPT : 0, NULL) < 0) | ||
517 | printk(KERN_ERR IDENT ": MPU401 not detected\n"); | ||
518 | } | ||
519 | if ((err = snd_card_register(card)) < 0) { | ||
520 | snd_card_free(card); | ||
521 | return err; | ||
522 | } | ||
523 | if (pcard) | ||
524 | pnp_set_card_drvdata(pcard, card); | ||
525 | else | ||
526 | snd_cs4236_legacy[dev] = card; | ||
527 | return 0; | ||
528 | } | ||
529 | |||
530 | #ifdef CONFIG_PNP | ||
531 | static int __devinit snd_cs423x_pnp_detect(struct pnp_card_link *card, | ||
532 | const struct pnp_card_device_id *id) | ||
533 | { | ||
534 | static int dev; | ||
535 | int res; | ||
536 | |||
537 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
538 | if (!enable[dev] || !isapnp[dev]) | ||
539 | continue; | ||
540 | res = snd_card_cs423x_probe(dev, card, id); | ||
541 | if (res < 0) | ||
542 | return res; | ||
543 | dev++; | ||
544 | return 0; | ||
545 | } | ||
546 | return -ENODEV; | ||
547 | } | ||
548 | |||
549 | static void __devexit snd_cs423x_pnp_remove(struct pnp_card_link * pcard) | ||
550 | { | ||
551 | snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); | ||
552 | |||
553 | snd_card_disconnect(card); | ||
554 | snd_card_free_in_thread(card); | ||
555 | } | ||
556 | |||
557 | static struct pnp_card_driver cs423x_pnpc_driver = { | ||
558 | .flags = PNP_DRIVER_RES_DISABLE, | ||
559 | .name = "cs423x", | ||
560 | .id_table = snd_cs423x_pnpids, | ||
561 | .probe = snd_cs423x_pnp_detect, | ||
562 | .remove = __devexit_p(snd_cs423x_pnp_remove), | ||
563 | }; | ||
564 | #endif /* CONFIG_PNP */ | ||
565 | |||
566 | static int __init alsa_card_cs423x_init(void) | ||
567 | { | ||
568 | int dev, cards = 0; | ||
569 | |||
570 | for (dev = 0; dev < SNDRV_CARDS; dev++) { | ||
571 | if (!enable[dev]) | ||
572 | continue; | ||
573 | #ifdef CONFIG_PNP | ||
574 | if (isapnp[dev]) | ||
575 | continue; | ||
576 | #endif | ||
577 | if (snd_card_cs423x_probe(dev, NULL, NULL) >= 0) | ||
578 | cards++; | ||
579 | } | ||
580 | #ifdef CONFIG_PNP | ||
581 | cards += pnp_register_card_driver(&cs423x_pnpc_driver); | ||
582 | #endif | ||
583 | if (!cards) { | ||
584 | #ifdef CONFIG_PNP | ||
585 | pnp_unregister_card_driver(&cs423x_pnpc_driver); | ||
586 | #endif | ||
587 | #ifdef MODULE | ||
588 | printk(KERN_ERR IDENT " soundcard not found or device busy\n"); | ||
589 | #endif | ||
590 | return -ENODEV; | ||
591 | } | ||
592 | return 0; | ||
593 | } | ||
594 | |||
595 | static void __exit alsa_card_cs423x_exit(void) | ||
596 | { | ||
597 | int idx; | ||
598 | |||
599 | #ifdef CONFIG_PNP | ||
600 | /* PnP cards first */ | ||
601 | pnp_unregister_card_driver(&cs423x_pnpc_driver); | ||
602 | #endif | ||
603 | for (idx = 0; idx < SNDRV_CARDS; idx++) | ||
604 | snd_card_free(snd_cs4236_legacy[idx]); | ||
605 | } | ||
606 | |||
607 | module_init(alsa_card_cs423x_init) | ||
608 | module_exit(alsa_card_cs423x_exit) | ||