cs4236.c (20011B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Driver for generic CS4232/CS4235/CS4236/CS4236B/CS4237B/CS4238B/CS4239 chips 4 * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 5 */ 6 7#include <linux/init.h> 8#include <linux/err.h> 9#include <linux/isa.h> 10#include <linux/pnp.h> 11#include <linux/module.h> 12#include <sound/core.h> 13#include <sound/wss.h> 14#include <sound/mpu401.h> 15#include <sound/opl3.h> 16#include <sound/initval.h> 17 18MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 19MODULE_LICENSE("GPL"); 20MODULE_DESCRIPTION("Cirrus Logic CS4232-9"); 21MODULE_ALIAS("snd_cs4232"); 22 23#define IDENT "CS4232+" 24#define DEV_NAME "cs4232+" 25 26static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 27static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 28static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ 29#ifdef CONFIG_PNP 30static bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; 31#endif 32static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 33static long cport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 34static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* PnP setup */ 35static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 36static long sb_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 37static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */ 38static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 9,11,12,15 */ 39static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ 40static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ 41 42module_param_array(index, int, NULL, 0444); 43MODULE_PARM_DESC(index, "Index value for " IDENT " soundcard."); 44module_param_array(id, charp, NULL, 0444); 45MODULE_PARM_DESC(id, "ID string for " IDENT " soundcard."); 46module_param_array(enable, bool, NULL, 0444); 47MODULE_PARM_DESC(enable, "Enable " IDENT " soundcard."); 48#ifdef CONFIG_PNP 49module_param_array(isapnp, bool, NULL, 0444); 50MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard."); 51#endif 52module_param_hw_array(port, long, ioport, NULL, 0444); 53MODULE_PARM_DESC(port, "Port # for " IDENT " driver."); 54module_param_hw_array(cport, long, ioport, NULL, 0444); 55MODULE_PARM_DESC(cport, "Control port # for " IDENT " driver."); 56module_param_hw_array(mpu_port, long, ioport, NULL, 0444); 57MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " IDENT " driver."); 58module_param_hw_array(fm_port, long, ioport, NULL, 0444); 59MODULE_PARM_DESC(fm_port, "FM port # for " IDENT " driver."); 60module_param_hw_array(sb_port, long, ioport, NULL, 0444); 61MODULE_PARM_DESC(sb_port, "SB port # for " IDENT " driver (optional)."); 62module_param_hw_array(irq, int, irq, NULL, 0444); 63MODULE_PARM_DESC(irq, "IRQ # for " IDENT " driver."); 64module_param_hw_array(mpu_irq, int, irq, NULL, 0444); 65MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " IDENT " driver."); 66module_param_hw_array(dma1, int, dma, NULL, 0444); 67MODULE_PARM_DESC(dma1, "DMA1 # for " IDENT " driver."); 68module_param_hw_array(dma2, int, dma, NULL, 0444); 69MODULE_PARM_DESC(dma2, "DMA2 # for " IDENT " driver."); 70 71#ifdef CONFIG_PNP 72static int isa_registered; 73static int pnpc_registered; 74static int pnp_registered; 75#endif /* CONFIG_PNP */ 76 77struct snd_card_cs4236 { 78 struct snd_wss *chip; 79#ifdef CONFIG_PNP 80 struct pnp_dev *wss; 81 struct pnp_dev *ctrl; 82 struct pnp_dev *mpu; 83#endif 84}; 85 86#ifdef CONFIG_PNP 87 88/* 89 * PNP BIOS 90 */ 91static const struct pnp_device_id snd_cs423x_pnpbiosids[] = { 92 { .id = "CSC0100" }, 93 { .id = "CSC0000" }, 94 /* Guillemot Turtlebeach something appears to be cs4232 compatible 95 * (untested) */ 96 { .id = "GIM0100" }, 97 { .id = "" } 98}; 99MODULE_DEVICE_TABLE(pnp, snd_cs423x_pnpbiosids); 100 101#define CS423X_ISAPNP_DRIVER "cs4232_isapnp" 102static const struct pnp_card_device_id snd_cs423x_pnpids[] = { 103 /* Philips PCA70PS */ 104 { .id = "CSC0d32", .devs = { { "CSC0000" }, { "CSC0010" }, { "PNPb006" } } }, 105 /* TerraTec Maestro 32/96 (CS4232) */ 106 { .id = "CSC1a32", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 107 /* HP Omnibook 5500 onboard */ 108 { .id = "CSC4232", .devs = { { "CSC0000" }, { "CSC0002" }, { "CSC0003" } } }, 109 /* Unnamed CS4236 card (Made in Taiwan) */ 110 { .id = "CSC4236", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 111 /* Turtle Beach TBS-2000 (CS4232) */ 112 { .id = "CSC7532", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSCb006" } } }, 113 /* Turtle Beach Tropez Plus (CS4232) */ 114 { .id = "CSC7632", .devs = { { "CSC0000" }, { "CSC0010" }, { "PNPb006" } } }, 115 /* SIC CrystalWave 32 (CS4232) */ 116 { .id = "CSCf032", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 117 /* Netfinity 3000 on-board soundcard */ 118 { .id = "CSCe825", .devs = { { "CSC0100" }, { "CSC0110" }, { "CSC010f" } } }, 119 /* Intel Marlin Spike Motherboard - CS4235 */ 120 { .id = "CSC0225", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 121 /* Intel Marlin Spike Motherboard (#2) - CS4235 */ 122 { .id = "CSC0225", .devs = { { "CSC0100" }, { "CSC0110" }, { "CSC0103" } } }, 123 /* Unknown Intel mainboard - CS4235 */ 124 { .id = "CSC0225", .devs = { { "CSC0100" }, { "CSC0110" } } }, 125 /* Genius Sound Maker 3DJ - CS4237B */ 126 { .id = "CSC0437", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 127 /* Digital PC 5000 Onboard - CS4236B */ 128 { .id = "CSC0735", .devs = { { "CSC0000" }, { "CSC0010" } } }, 129 /* some unknown CS4236B */ 130 { .id = "CSC0b35", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 131 /* Intel PR440FX Onboard sound */ 132 { .id = "CSC0b36", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 133 /* CS4235 on mainboard without MPU */ 134 { .id = "CSC1425", .devs = { { "CSC0100" }, { "CSC0110" } } }, 135 /* Gateway E1000 Onboard CS4236B */ 136 { .id = "CSC1335", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 137 /* HP 6330 Onboard sound */ 138 { .id = "CSC1525", .devs = { { "CSC0100" }, { "CSC0110" }, { "CSC0103" } } }, 139 /* Crystal Computer TidalWave128 */ 140 { .id = "CSC1e37", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 141 /* ACER AW37 - CS4235 */ 142 { .id = "CSC4236", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 143 /* build-in soundcard in EliteGroup P5TX-LA motherboard - CS4237B */ 144 { .id = "CSC4237", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 145 /* Crystal 3D - CS4237B */ 146 { .id = "CSC4336", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 147 /* Typhoon Soundsystem PnP - CS4236B */ 148 { .id = "CSC4536", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 149 /* Crystal CX4235-XQ3 EP - CS4235 */ 150 { .id = "CSC4625", .devs = { { "CSC0100" }, { "CSC0110" }, { "CSC0103" } } }, 151 /* Crystal Semiconductors CS4237B */ 152 { .id = "CSC4637", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 153 /* NewClear 3D - CX4237B-XQ3 */ 154 { .id = "CSC4837", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 155 /* Dell Optiplex GX1 - CS4236B */ 156 { .id = "CSC6835", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 157 /* Dell P410 motherboard - CS4236B */ 158 { .id = "CSC6835", .devs = { { "CSC0000" }, { "CSC0010" } } }, 159 /* Dell Workstation 400 Onboard - CS4236B */ 160 { .id = "CSC6836", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 161 /* Turtle Beach Malibu - CS4237B */ 162 { .id = "CSC7537", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 163 /* CS4235 - onboard */ 164 { .id = "CSC8025", .devs = { { "CSC0100" }, { "CSC0110" }, { "CSC0103" } } }, 165 /* IBM Aptiva 2137 E24 Onboard - CS4237B */ 166 { .id = "CSC8037", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 167 /* IBM IntelliStation M Pro motherboard */ 168 { .id = "CSCc835", .devs = { { "CSC0000" }, { "CSC0010" } } }, 169 /* Guillemot MaxiSound 16 PnP - CS4236B */ 170 { .id = "CSC9836", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 171 /* Gallant SC-70P */ 172 { .id = "CSC9837", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 173 /* Techmakers MF-4236PW */ 174 { .id = "CSCa736", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 175 /* TerraTec AudioSystem EWS64XL - CS4236B */ 176 { .id = "CSCa836", .devs = { { "CSCa800" }, { "CSCa810" }, { "CSCa803" } } }, 177 /* TerraTec AudioSystem EWS64XL - CS4236B */ 178 { .id = "CSCa836", .devs = { { "CSCa800" }, { "CSCa810" } } }, 179 /* ACER AW37/Pro - CS4235 */ 180 { .id = "CSCd925", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 181 /* ACER AW35/Pro - CS4237B */ 182 { .id = "CSCd937", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 183 /* CS4235 without MPU401 */ 184 { .id = "CSCe825", .devs = { { "CSC0100" }, { "CSC0110" } } }, 185 /* Unknown SiS530 - CS4235 */ 186 { .id = "CSC4825", .devs = { { "CSC0100" }, { "CSC0110" } } }, 187 /* IBM IntelliStation M Pro 6898 11U - CS4236B */ 188 { .id = "CSCe835", .devs = { { "CSC0000" }, { "CSC0010" } } }, 189 /* IBM PC 300PL Onboard - CS4236B */ 190 { .id = "CSCe836", .devs = { { "CSC0000" }, { "CSC0010" } } }, 191 /* Some noname CS4236 based card */ 192 { .id = "CSCe936", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 193 /* CS4236B */ 194 { .id = "CSCf235", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 195 /* CS4236B */ 196 { .id = "CSCf238", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, 197 /* --- */ 198 { .id = "" } /* end */ 199}; 200 201MODULE_DEVICE_TABLE(pnp_card, snd_cs423x_pnpids); 202 203/* WSS initialization */ 204static int snd_cs423x_pnp_init_wss(int dev, struct pnp_dev *pdev) 205{ 206 if (pnp_activate_dev(pdev) < 0) { 207 printk(KERN_ERR IDENT " WSS PnP configure failed for WSS (out of resources?)\n"); 208 return -EBUSY; 209 } 210 port[dev] = pnp_port_start(pdev, 0); 211 if (fm_port[dev] > 0) 212 fm_port[dev] = pnp_port_start(pdev, 1); 213 sb_port[dev] = pnp_port_start(pdev, 2); 214 irq[dev] = pnp_irq(pdev, 0); 215 dma1[dev] = pnp_dma(pdev, 0); 216 dma2[dev] = pnp_dma(pdev, 1) == 4 ? -1 : (int)pnp_dma(pdev, 1); 217 snd_printdd("isapnp WSS: wss port=0x%lx, fm port=0x%lx, sb port=0x%lx\n", 218 port[dev], fm_port[dev], sb_port[dev]); 219 snd_printdd("isapnp WSS: irq=%i, dma1=%i, dma2=%i\n", 220 irq[dev], dma1[dev], dma2[dev]); 221 return 0; 222} 223 224/* CTRL initialization */ 225static int snd_cs423x_pnp_init_ctrl(int dev, struct pnp_dev *pdev) 226{ 227 if (pnp_activate_dev(pdev) < 0) { 228 printk(KERN_ERR IDENT " CTRL PnP configure failed for WSS (out of resources?)\n"); 229 return -EBUSY; 230 } 231 cport[dev] = pnp_port_start(pdev, 0); 232 snd_printdd("isapnp CTRL: control port=0x%lx\n", cport[dev]); 233 return 0; 234} 235 236/* MPU initialization */ 237static int snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev) 238{ 239 if (pnp_activate_dev(pdev) < 0) { 240 printk(KERN_ERR IDENT " MPU401 PnP configure failed for WSS (out of resources?)\n"); 241 mpu_port[dev] = SNDRV_AUTO_PORT; 242 mpu_irq[dev] = SNDRV_AUTO_IRQ; 243 } else { 244 mpu_port[dev] = pnp_port_start(pdev, 0); 245 if (mpu_irq[dev] >= 0 && 246 pnp_irq_valid(pdev, 0) && 247 pnp_irq(pdev, 0) != (resource_size_t)-1) { 248 mpu_irq[dev] = pnp_irq(pdev, 0); 249 } else { 250 mpu_irq[dev] = -1; /* disable interrupt */ 251 } 252 } 253 snd_printdd("isapnp MPU: port=0x%lx, irq=%i\n", mpu_port[dev], mpu_irq[dev]); 254 return 0; 255} 256 257static int snd_card_cs423x_pnp(int dev, struct snd_card_cs4236 *acard, 258 struct pnp_dev *pdev, 259 struct pnp_dev *cdev) 260{ 261 acard->wss = pdev; 262 if (snd_cs423x_pnp_init_wss(dev, acard->wss) < 0) 263 return -EBUSY; 264 if (cdev) 265 cport[dev] = pnp_port_start(cdev, 0); 266 else 267 cport[dev] = -1; 268 return 0; 269} 270 271static int snd_card_cs423x_pnpc(int dev, struct snd_card_cs4236 *acard, 272 struct pnp_card_link *card, 273 const struct pnp_card_device_id *id) 274{ 275 acard->wss = pnp_request_card_device(card, id->devs[0].id, NULL); 276 if (acard->wss == NULL) 277 return -EBUSY; 278 acard->ctrl = pnp_request_card_device(card, id->devs[1].id, NULL); 279 if (acard->ctrl == NULL) 280 return -EBUSY; 281 if (id->devs[2].id[0]) { 282 acard->mpu = pnp_request_card_device(card, id->devs[2].id, NULL); 283 if (acard->mpu == NULL) 284 return -EBUSY; 285 } 286 287 /* WSS initialization */ 288 if (snd_cs423x_pnp_init_wss(dev, acard->wss) < 0) 289 return -EBUSY; 290 291 /* CTRL initialization */ 292 if (acard->ctrl && cport[dev] > 0) { 293 if (snd_cs423x_pnp_init_ctrl(dev, acard->ctrl) < 0) 294 return -EBUSY; 295 } 296 /* MPU initialization */ 297 if (acard->mpu && mpu_port[dev] > 0) { 298 if (snd_cs423x_pnp_init_mpu(dev, acard->mpu) < 0) 299 return -EBUSY; 300 } 301 return 0; 302} 303#endif /* CONFIG_PNP */ 304 305#ifdef CONFIG_PNP 306#define is_isapnp_selected(dev) isapnp[dev] 307#else 308#define is_isapnp_selected(dev) 0 309#endif 310 311static int snd_cs423x_card_new(struct device *pdev, int dev, 312 struct snd_card **cardp) 313{ 314 struct snd_card *card; 315 int err; 316 317 err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE, 318 sizeof(struct snd_card_cs4236), &card); 319 if (err < 0) 320 return err; 321 *cardp = card; 322 return 0; 323} 324 325static int snd_cs423x_probe(struct snd_card *card, int dev) 326{ 327 struct snd_card_cs4236 *acard; 328 struct snd_wss *chip; 329 struct snd_opl3 *opl3; 330 int err; 331 332 acard = card->private_data; 333 if (sb_port[dev] > 0 && sb_port[dev] != SNDRV_AUTO_PORT) { 334 if (!devm_request_region(card->dev, sb_port[dev], 16, 335 IDENT " SB")) { 336 printk(KERN_ERR IDENT ": unable to register SB port at 0x%lx\n", sb_port[dev]); 337 return -EBUSY; 338 } 339 } 340 341 err = snd_cs4236_create(card, port[dev], cport[dev], 342 irq[dev], 343 dma1[dev], dma2[dev], 344 WSS_HW_DETECT3, 0, &chip); 345 if (err < 0) 346 return err; 347 348 acard->chip = chip; 349 if (chip->hardware & WSS_HW_CS4236B_MASK) { 350 351 err = snd_cs4236_pcm(chip, 0); 352 if (err < 0) 353 return err; 354 355 err = snd_cs4236_mixer(chip); 356 if (err < 0) 357 return err; 358 } else { 359 err = snd_wss_pcm(chip, 0); 360 if (err < 0) 361 return err; 362 363 err = snd_wss_mixer(chip); 364 if (err < 0) 365 return err; 366 } 367 strscpy(card->driver, chip->pcm->name, sizeof(card->driver)); 368 strscpy(card->shortname, chip->pcm->name, sizeof(card->shortname)); 369 if (dma2[dev] < 0) 370 snprintf(card->longname, sizeof(card->longname), 371 "%s at 0x%lx, irq %i, dma %i", 372 chip->pcm->name, chip->port, irq[dev], dma1[dev]); 373 else 374 snprintf(card->longname, sizeof(card->longname), 375 "%s at 0x%lx, irq %i, dma %i&%d", 376 chip->pcm->name, chip->port, irq[dev], dma1[dev], 377 dma2[dev]); 378 379 err = snd_wss_timer(chip, 0); 380 if (err < 0) 381 return err; 382 383 if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) { 384 if (snd_opl3_create(card, 385 fm_port[dev], fm_port[dev] + 2, 386 OPL3_HW_OPL3_CS, 0, &opl3) < 0) { 387 printk(KERN_WARNING IDENT ": OPL3 not detected\n"); 388 } else { 389 err = snd_opl3_hwdep_new(opl3, 0, 1, NULL); 390 if (err < 0) 391 return err; 392 } 393 } 394 395 if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) { 396 if (mpu_irq[dev] == SNDRV_AUTO_IRQ) 397 mpu_irq[dev] = -1; 398 if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232, 399 mpu_port[dev], 0, 400 mpu_irq[dev], NULL) < 0) 401 printk(KERN_WARNING IDENT ": MPU401 not detected\n"); 402 } 403 404 return snd_card_register(card); 405} 406 407static int snd_cs423x_isa_match(struct device *pdev, 408 unsigned int dev) 409{ 410 if (!enable[dev] || is_isapnp_selected(dev)) 411 return 0; 412 413 if (port[dev] == SNDRV_AUTO_PORT) { 414 dev_err(pdev, "please specify port\n"); 415 return 0; 416 } 417 if (cport[dev] == SNDRV_AUTO_PORT) { 418 dev_err(pdev, "please specify cport\n"); 419 return 0; 420 } 421 if (irq[dev] == SNDRV_AUTO_IRQ) { 422 dev_err(pdev, "please specify irq\n"); 423 return 0; 424 } 425 if (dma1[dev] == SNDRV_AUTO_DMA) { 426 dev_err(pdev, "please specify dma1\n"); 427 return 0; 428 } 429 return 1; 430} 431 432static int snd_cs423x_isa_probe(struct device *pdev, 433 unsigned int dev) 434{ 435 struct snd_card *card; 436 int err; 437 438 err = snd_cs423x_card_new(pdev, dev, &card); 439 if (err < 0) 440 return err; 441 err = snd_cs423x_probe(card, dev); 442 if (err < 0) 443 return err; 444 dev_set_drvdata(pdev, card); 445 return 0; 446} 447 448#ifdef CONFIG_PM 449static int snd_cs423x_suspend(struct snd_card *card) 450{ 451 struct snd_card_cs4236 *acard = card->private_data; 452 snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 453 acard->chip->suspend(acard->chip); 454 return 0; 455} 456 457static int snd_cs423x_resume(struct snd_card *card) 458{ 459 struct snd_card_cs4236 *acard = card->private_data; 460 acard->chip->resume(acard->chip); 461 snd_power_change_state(card, SNDRV_CTL_POWER_D0); 462 return 0; 463} 464 465static int snd_cs423x_isa_suspend(struct device *dev, unsigned int n, 466 pm_message_t state) 467{ 468 return snd_cs423x_suspend(dev_get_drvdata(dev)); 469} 470 471static int snd_cs423x_isa_resume(struct device *dev, unsigned int n) 472{ 473 return snd_cs423x_resume(dev_get_drvdata(dev)); 474} 475#endif 476 477static struct isa_driver cs423x_isa_driver = { 478 .match = snd_cs423x_isa_match, 479 .probe = snd_cs423x_isa_probe, 480#ifdef CONFIG_PM 481 .suspend = snd_cs423x_isa_suspend, 482 .resume = snd_cs423x_isa_resume, 483#endif 484 .driver = { 485 .name = DEV_NAME 486 }, 487}; 488 489 490#ifdef CONFIG_PNP 491static int snd_cs423x_pnpbios_detect(struct pnp_dev *pdev, 492 const struct pnp_device_id *id) 493{ 494 static int dev; 495 int err; 496 struct snd_card *card; 497 struct pnp_dev *cdev, *iter; 498 char cid[PNP_ID_LEN]; 499 500 if (pnp_device_is_isapnp(pdev)) 501 return -ENOENT; /* we have another procedure - card */ 502 for (; dev < SNDRV_CARDS; dev++) { 503 if (enable[dev] && isapnp[dev]) 504 break; 505 } 506 if (dev >= SNDRV_CARDS) 507 return -ENODEV; 508 509 /* prepare second id */ 510 strcpy(cid, pdev->id[0].id); 511 cid[5] = '1'; 512 cdev = NULL; 513 list_for_each_entry(iter, &(pdev->protocol->devices), protocol_list) { 514 if (!strcmp(iter->id[0].id, cid)) { 515 cdev = iter; 516 break; 517 } 518 } 519 err = snd_cs423x_card_new(&pdev->dev, dev, &card); 520 if (err < 0) 521 return err; 522 err = snd_card_cs423x_pnp(dev, card->private_data, pdev, cdev); 523 if (err < 0) { 524 printk(KERN_ERR "PnP BIOS detection failed for " IDENT "\n"); 525 return err; 526 } 527 err = snd_cs423x_probe(card, dev); 528 if (err < 0) 529 return err; 530 pnp_set_drvdata(pdev, card); 531 dev++; 532 return 0; 533} 534 535#ifdef CONFIG_PM 536static int snd_cs423x_pnp_suspend(struct pnp_dev *pdev, pm_message_t state) 537{ 538 return snd_cs423x_suspend(pnp_get_drvdata(pdev)); 539} 540 541static int snd_cs423x_pnp_resume(struct pnp_dev *pdev) 542{ 543 return snd_cs423x_resume(pnp_get_drvdata(pdev)); 544} 545#endif 546 547static struct pnp_driver cs423x_pnp_driver = { 548 .name = "cs423x-pnpbios", 549 .id_table = snd_cs423x_pnpbiosids, 550 .probe = snd_cs423x_pnpbios_detect, 551#ifdef CONFIG_PM 552 .suspend = snd_cs423x_pnp_suspend, 553 .resume = snd_cs423x_pnp_resume, 554#endif 555}; 556 557static int snd_cs423x_pnpc_detect(struct pnp_card_link *pcard, 558 const struct pnp_card_device_id *pid) 559{ 560 static int dev; 561 struct snd_card *card; 562 int res; 563 564 for ( ; dev < SNDRV_CARDS; dev++) { 565 if (enable[dev] && isapnp[dev]) 566 break; 567 } 568 if (dev >= SNDRV_CARDS) 569 return -ENODEV; 570 571 res = snd_cs423x_card_new(&pcard->card->dev, dev, &card); 572 if (res < 0) 573 return res; 574 res = snd_card_cs423x_pnpc(dev, card->private_data, pcard, pid); 575 if (res < 0) { 576 printk(KERN_ERR "isapnp detection failed and probing for " IDENT 577 " is not supported\n"); 578 return res; 579 } 580 res = snd_cs423x_probe(card, dev); 581 if (res < 0) 582 return res; 583 pnp_set_card_drvdata(pcard, card); 584 dev++; 585 return 0; 586} 587 588#ifdef CONFIG_PM 589static int snd_cs423x_pnpc_suspend(struct pnp_card_link *pcard, pm_message_t state) 590{ 591 return snd_cs423x_suspend(pnp_get_card_drvdata(pcard)); 592} 593 594static int snd_cs423x_pnpc_resume(struct pnp_card_link *pcard) 595{ 596 return snd_cs423x_resume(pnp_get_card_drvdata(pcard)); 597} 598#endif 599 600static struct pnp_card_driver cs423x_pnpc_driver = { 601 .flags = PNP_DRIVER_RES_DISABLE, 602 .name = CS423X_ISAPNP_DRIVER, 603 .id_table = snd_cs423x_pnpids, 604 .probe = snd_cs423x_pnpc_detect, 605#ifdef CONFIG_PM 606 .suspend = snd_cs423x_pnpc_suspend, 607 .resume = snd_cs423x_pnpc_resume, 608#endif 609}; 610#endif /* CONFIG_PNP */ 611 612static int __init alsa_card_cs423x_init(void) 613{ 614 int err; 615 616 err = isa_register_driver(&cs423x_isa_driver, SNDRV_CARDS); 617#ifdef CONFIG_PNP 618 if (!err) 619 isa_registered = 1; 620 err = pnp_register_driver(&cs423x_pnp_driver); 621 if (!err) 622 pnp_registered = 1; 623 err = pnp_register_card_driver(&cs423x_pnpc_driver); 624 if (!err) 625 pnpc_registered = 1; 626 if (pnp_registered) 627 err = 0; 628 if (isa_registered) 629 err = 0; 630#endif 631 return err; 632} 633 634static void __exit alsa_card_cs423x_exit(void) 635{ 636#ifdef CONFIG_PNP 637 if (pnpc_registered) 638 pnp_unregister_card_driver(&cs423x_pnpc_driver); 639 if (pnp_registered) 640 pnp_unregister_driver(&cs423x_pnp_driver); 641 if (isa_registered) 642#endif 643 isa_unregister_driver(&cs423x_isa_driver); 644} 645 646module_init(alsa_card_cs423x_init) 647module_exit(alsa_card_cs423x_exit)