651 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Driver for generic CS4232/CS4235/CS4236/CS4236B/CS4237B/CS4238B/CS4239 chips4 * 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_PNP30static bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};31#endif32static 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_PNP49module_param_array(isapnp, bool, NULL, 0444);50MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard.");51#endif52module_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_PNP72static 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_PNP80 struct pnp_dev *wss;81 struct pnp_dev *ctrl;82 struct pnp_dev *mpu;83#endif84};85 86#ifdef CONFIG_PNP87 88/*89 * PNP BIOS90 */91static const struct pnp_device_id snd_cs423x_pnpbiosids[] = {92 { .id = "CSC0100" },93 { .id = "CSC0000" },94 /* Guillemot Turtlebeach something appears to be cs4232 compatible95 * (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 dev_err(&pdev->dev, 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 dev_dbg(&pdev->dev,218 "isapnp WSS: wss port=0x%lx, fm port=0x%lx, sb port=0x%lx\n",219 port[dev], fm_port[dev], sb_port[dev]);220 dev_dbg(&pdev->dev,221 "isapnp WSS: irq=%i, dma1=%i, dma2=%i\n",222 irq[dev], dma1[dev], dma2[dev]);223 return 0;224}225 226/* CTRL initialization */227static int snd_cs423x_pnp_init_ctrl(int dev, struct pnp_dev *pdev)228{229 if (pnp_activate_dev(pdev) < 0) {230 dev_err(&pdev->dev, IDENT " CTRL PnP configure failed for WSS (out of resources?)\n");231 return -EBUSY;232 }233 cport[dev] = pnp_port_start(pdev, 0);234 dev_dbg(&pdev->dev, "isapnp CTRL: control port=0x%lx\n", cport[dev]);235 return 0;236}237 238/* MPU initialization */239static int snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev)240{241 if (pnp_activate_dev(pdev) < 0) {242 dev_err(&pdev->dev, IDENT " MPU401 PnP configure failed for WSS (out of resources?)\n");243 mpu_port[dev] = SNDRV_AUTO_PORT;244 mpu_irq[dev] = SNDRV_AUTO_IRQ;245 } else {246 mpu_port[dev] = pnp_port_start(pdev, 0);247 if (mpu_irq[dev] >= 0 &&248 pnp_irq_valid(pdev, 0) &&249 pnp_irq(pdev, 0) != (resource_size_t)-1) {250 mpu_irq[dev] = pnp_irq(pdev, 0);251 } else {252 mpu_irq[dev] = -1; /* disable interrupt */253 }254 }255 dev_dbg(&pdev->dev, "isapnp MPU: port=0x%lx, irq=%i\n", mpu_port[dev], mpu_irq[dev]);256 return 0;257}258 259static int snd_card_cs423x_pnp(int dev, struct snd_card_cs4236 *acard,260 struct pnp_dev *pdev,261 struct pnp_dev *cdev)262{263 acard->wss = pdev;264 if (snd_cs423x_pnp_init_wss(dev, acard->wss) < 0)265 return -EBUSY;266 if (cdev)267 cport[dev] = pnp_port_start(cdev, 0);268 else269 cport[dev] = -1;270 return 0;271}272 273static int snd_card_cs423x_pnpc(int dev, struct snd_card_cs4236 *acard,274 struct pnp_card_link *card,275 const struct pnp_card_device_id *id)276{277 acard->wss = pnp_request_card_device(card, id->devs[0].id, NULL);278 if (acard->wss == NULL)279 return -EBUSY;280 acard->ctrl = pnp_request_card_device(card, id->devs[1].id, NULL);281 if (acard->ctrl == NULL)282 return -EBUSY;283 if (id->devs[2].id[0]) {284 acard->mpu = pnp_request_card_device(card, id->devs[2].id, NULL);285 if (acard->mpu == NULL)286 return -EBUSY;287 }288 289 /* WSS initialization */290 if (snd_cs423x_pnp_init_wss(dev, acard->wss) < 0)291 return -EBUSY;292 293 /* CTRL initialization */294 if (acard->ctrl && cport[dev] > 0) {295 if (snd_cs423x_pnp_init_ctrl(dev, acard->ctrl) < 0)296 return -EBUSY;297 }298 /* MPU initialization */299 if (acard->mpu && mpu_port[dev] > 0) {300 if (snd_cs423x_pnp_init_mpu(dev, acard->mpu) < 0)301 return -EBUSY;302 }303 return 0;304}305#endif /* CONFIG_PNP */306 307#ifdef CONFIG_PNP308#define is_isapnp_selected(dev) isapnp[dev]309#else310#define is_isapnp_selected(dev) 0311#endif312 313static int snd_cs423x_card_new(struct device *pdev, int dev,314 struct snd_card **cardp)315{316 struct snd_card *card;317 int err;318 319 err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,320 sizeof(struct snd_card_cs4236), &card);321 if (err < 0)322 return err;323 *cardp = card;324 return 0;325}326 327static int snd_cs423x_probe(struct snd_card *card, int dev)328{329 struct snd_card_cs4236 *acard;330 struct snd_wss *chip;331 struct snd_opl3 *opl3;332 int err;333 334 acard = card->private_data;335 if (sb_port[dev] > 0 && sb_port[dev] != SNDRV_AUTO_PORT) {336 if (!devm_request_region(card->dev, sb_port[dev], 16,337 IDENT " SB")) {338 dev_err(card->dev, IDENT ": unable to register SB port at 0x%lx\n",339 sb_port[dev]);340 return -EBUSY;341 }342 }343 344 err = snd_cs4236_create(card, port[dev], cport[dev],345 irq[dev],346 dma1[dev], dma2[dev],347 WSS_HW_DETECT3, 0, &chip);348 if (err < 0)349 return err;350 351 acard->chip = chip;352 if (chip->hardware & WSS_HW_CS4236B_MASK) {353 354 err = snd_cs4236_pcm(chip, 0);355 if (err < 0)356 return err;357 358 err = snd_cs4236_mixer(chip);359 if (err < 0)360 return err;361 } else {362 err = snd_wss_pcm(chip, 0);363 if (err < 0)364 return err;365 366 err = snd_wss_mixer(chip);367 if (err < 0)368 return err;369 }370 strscpy(card->driver, chip->pcm->name, sizeof(card->driver));371 strscpy(card->shortname, chip->pcm->name, sizeof(card->shortname));372 if (dma2[dev] < 0)373 scnprintf(card->longname, sizeof(card->longname),374 "%s at 0x%lx, irq %i, dma %i",375 chip->pcm->name, chip->port, irq[dev], dma1[dev]);376 else377 scnprintf(card->longname, sizeof(card->longname),378 "%s at 0x%lx, irq %i, dma %i&%d",379 chip->pcm->name, chip->port, irq[dev], dma1[dev],380 dma2[dev]);381 382 err = snd_wss_timer(chip, 0);383 if (err < 0)384 return err;385 386 if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {387 if (snd_opl3_create(card,388 fm_port[dev], fm_port[dev] + 2,389 OPL3_HW_OPL3_CS, 0, &opl3) < 0) {390 dev_warn(card->dev, IDENT ": OPL3 not detected\n");391 } else {392 err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);393 if (err < 0)394 return err;395 }396 }397 398 if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {399 if (mpu_irq[dev] == SNDRV_AUTO_IRQ)400 mpu_irq[dev] = -1;401 if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232,402 mpu_port[dev], 0,403 mpu_irq[dev], NULL) < 0)404 dev_warn(card->dev, IDENT ": MPU401 not detected\n");405 }406 407 return snd_card_register(card);408}409 410static int snd_cs423x_isa_match(struct device *pdev,411 unsigned int dev)412{413 if (!enable[dev] || is_isapnp_selected(dev))414 return 0;415 416 if (port[dev] == SNDRV_AUTO_PORT) {417 dev_err(pdev, "please specify port\n");418 return 0;419 }420 if (cport[dev] == SNDRV_AUTO_PORT) {421 dev_err(pdev, "please specify cport\n");422 return 0;423 }424 if (irq[dev] == SNDRV_AUTO_IRQ) {425 dev_err(pdev, "please specify irq\n");426 return 0;427 }428 if (dma1[dev] == SNDRV_AUTO_DMA) {429 dev_err(pdev, "please specify dma1\n");430 return 0;431 }432 return 1;433}434 435static int snd_cs423x_isa_probe(struct device *pdev,436 unsigned int dev)437{438 struct snd_card *card;439 int err;440 441 err = snd_cs423x_card_new(pdev, dev, &card);442 if (err < 0)443 return err;444 err = snd_cs423x_probe(card, dev);445 if (err < 0)446 return err;447 dev_set_drvdata(pdev, card);448 return 0;449}450 451#ifdef CONFIG_PM452static int snd_cs423x_suspend(struct snd_card *card)453{454 struct snd_card_cs4236 *acard = card->private_data;455 snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);456 acard->chip->suspend(acard->chip);457 return 0;458}459 460static int snd_cs423x_resume(struct snd_card *card)461{462 struct snd_card_cs4236 *acard = card->private_data;463 acard->chip->resume(acard->chip);464 snd_power_change_state(card, SNDRV_CTL_POWER_D0);465 return 0;466}467 468static int snd_cs423x_isa_suspend(struct device *dev, unsigned int n,469 pm_message_t state)470{471 return snd_cs423x_suspend(dev_get_drvdata(dev));472}473 474static int snd_cs423x_isa_resume(struct device *dev, unsigned int n)475{476 return snd_cs423x_resume(dev_get_drvdata(dev));477}478#endif479 480static struct isa_driver cs423x_isa_driver = {481 .match = snd_cs423x_isa_match,482 .probe = snd_cs423x_isa_probe,483#ifdef CONFIG_PM484 .suspend = snd_cs423x_isa_suspend,485 .resume = snd_cs423x_isa_resume,486#endif487 .driver = {488 .name = DEV_NAME489 },490};491 492 493#ifdef CONFIG_PNP494static int snd_cs423x_pnpbios_detect(struct pnp_dev *pdev,495 const struct pnp_device_id *id)496{497 static int dev;498 int err;499 struct snd_card *card;500 struct pnp_dev *cdev, *iter;501 char cid[PNP_ID_LEN];502 503 if (pnp_device_is_isapnp(pdev))504 return -ENOENT; /* we have another procedure - card */505 for (; dev < SNDRV_CARDS; dev++) {506 if (enable[dev] && isapnp[dev])507 break;508 }509 if (dev >= SNDRV_CARDS)510 return -ENODEV;511 512 /* prepare second id */513 strcpy(cid, pdev->id[0].id);514 cid[5] = '1';515 cdev = NULL;516 list_for_each_entry(iter, &(pdev->protocol->devices), protocol_list) {517 if (!strcmp(iter->id[0].id, cid)) {518 cdev = iter;519 break;520 }521 }522 err = snd_cs423x_card_new(&pdev->dev, dev, &card);523 if (err < 0)524 return err;525 err = snd_card_cs423x_pnp(dev, card->private_data, pdev, cdev);526 if (err < 0) {527 dev_err(card->dev, "PnP BIOS detection failed for " IDENT "\n");528 return err;529 }530 err = snd_cs423x_probe(card, dev);531 if (err < 0)532 return err;533 pnp_set_drvdata(pdev, card);534 dev++;535 return 0;536}537 538#ifdef CONFIG_PM539static int snd_cs423x_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)540{541 return snd_cs423x_suspend(pnp_get_drvdata(pdev));542}543 544static int snd_cs423x_pnp_resume(struct pnp_dev *pdev)545{546 return snd_cs423x_resume(pnp_get_drvdata(pdev));547}548#endif549 550static struct pnp_driver cs423x_pnp_driver = {551 .name = "cs423x-pnpbios",552 .id_table = snd_cs423x_pnpbiosids,553 .probe = snd_cs423x_pnpbios_detect,554#ifdef CONFIG_PM555 .suspend = snd_cs423x_pnp_suspend,556 .resume = snd_cs423x_pnp_resume,557#endif558};559 560static int snd_cs423x_pnpc_detect(struct pnp_card_link *pcard,561 const struct pnp_card_device_id *pid)562{563 static int dev;564 struct snd_card *card;565 int res;566 567 for ( ; dev < SNDRV_CARDS; dev++) {568 if (enable[dev] && isapnp[dev])569 break;570 }571 if (dev >= SNDRV_CARDS)572 return -ENODEV;573 574 res = snd_cs423x_card_new(&pcard->card->dev, dev, &card);575 if (res < 0)576 return res;577 res = snd_card_cs423x_pnpc(dev, card->private_data, pcard, pid);578 if (res < 0) {579 dev_err(card->dev, "isapnp detection failed and probing for " IDENT580 " is not supported\n");581 return res;582 }583 res = snd_cs423x_probe(card, dev);584 if (res < 0)585 return res;586 pnp_set_card_drvdata(pcard, card);587 dev++;588 return 0;589}590 591#ifdef CONFIG_PM592static int snd_cs423x_pnpc_suspend(struct pnp_card_link *pcard, pm_message_t state)593{594 return snd_cs423x_suspend(pnp_get_card_drvdata(pcard));595}596 597static int snd_cs423x_pnpc_resume(struct pnp_card_link *pcard)598{599 return snd_cs423x_resume(pnp_get_card_drvdata(pcard));600}601#endif602 603static struct pnp_card_driver cs423x_pnpc_driver = {604 .flags = PNP_DRIVER_RES_DISABLE,605 .name = CS423X_ISAPNP_DRIVER,606 .id_table = snd_cs423x_pnpids,607 .probe = snd_cs423x_pnpc_detect,608#ifdef CONFIG_PM609 .suspend = snd_cs423x_pnpc_suspend,610 .resume = snd_cs423x_pnpc_resume,611#endif612};613#endif /* CONFIG_PNP */614 615static int __init alsa_card_cs423x_init(void)616{617 int err;618 619 err = isa_register_driver(&cs423x_isa_driver, SNDRV_CARDS);620#ifdef CONFIG_PNP621 if (!err)622 isa_registered = 1;623 err = pnp_register_driver(&cs423x_pnp_driver);624 if (!err)625 pnp_registered = 1;626 err = pnp_register_card_driver(&cs423x_pnpc_driver);627 if (!err)628 pnpc_registered = 1;629 if (pnp_registered)630 err = 0;631 if (isa_registered)632 err = 0;633#endif634 return err;635}636 637static void __exit alsa_card_cs423x_exit(void)638{639#ifdef CONFIG_PNP640 if (pnpc_registered)641 pnp_unregister_card_driver(&cs423x_pnpc_driver);642 if (pnp_registered)643 pnp_unregister_driver(&cs423x_pnp_driver);644 if (isa_registered)645#endif646 isa_unregister_driver(&cs423x_isa_driver);647}648 649module_init(alsa_card_cs423x_init)650module_exit(alsa_card_cs423x_exit)651