339 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2 3/*4 card-als100.c - driver for Avance Logic ALS100 based soundcards.5 Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>6 Copyright (C) 1999-2002 by Massimo Piccioni <dafastidio@libero.it>7 8 Thanks to Pierfrancesco 'qM2' Passerini.9 10 Generalised for soundcards based on DT-0196 and ALS-007 chips11 by Jonathan Woithe <jwoithe@just42.net>: June 2002.12 13*/14 15#include <linux/init.h>16#include <linux/wait.h>17#include <linux/time.h>18#include <linux/pnp.h>19#include <linux/module.h>20#include <sound/core.h>21#include <sound/initval.h>22#include <sound/mpu401.h>23#include <sound/opl3.h>24#include <sound/sb.h>25 26MODULE_DESCRIPTION("Avance Logic ALS007/ALS1X0");27MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");28MODULE_LICENSE("GPL");29 30static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */31static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */32static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */33static long port[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 int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */37static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */38static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */39static int dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */40 41module_param_array(index, int, NULL, 0444);42MODULE_PARM_DESC(index, "Index value for Avance Logic based soundcard.");43module_param_array(id, charp, NULL, 0444);44MODULE_PARM_DESC(id, "ID string for Avance Logic based soundcard.");45module_param_array(enable, bool, NULL, 0444);46MODULE_PARM_DESC(enable, "Enable Avance Logic based soundcard.");47 48MODULE_ALIAS("snd-dt019x");49 50struct snd_card_als100 {51 struct pnp_dev *dev;52 struct pnp_dev *devmpu;53 struct pnp_dev *devopl;54 struct snd_sb *chip;55};56 57static const struct pnp_card_device_id snd_als100_pnpids[] = {58 /* DT197A30 */59 { .id = "RWB1688",60 .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } },61 .driver_data = SB_HW_DT019X },62 /* DT0196 / ALS-007 */63 { .id = "ALS0007",64 .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } },65 .driver_data = SB_HW_DT019X },66 /* ALS100 - PRO16PNP */67 { .id = "ALS0001",68 .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } },69 .driver_data = SB_HW_ALS100 },70 /* ALS110 - MF1000 - Digimate 3D Sound */71 { .id = "ALS0110",72 .devs = { { "@@@1001" }, { "@X@1001" }, { "@H@1001" } },73 .driver_data = SB_HW_ALS100 },74 /* ALS120 */75 { .id = "ALS0120",76 .devs = { { "@@@2001" }, { "@X@2001" }, { "@H@2001" } },77 .driver_data = SB_HW_ALS100 },78 /* ALS200 */79 { .id = "ALS0200",80 .devs = { { "@@@0020" }, { "@X@0020" }, { "@H@0001" } },81 .driver_data = SB_HW_ALS100 },82 /* ALS200 OEM */83 { .id = "ALS0200",84 .devs = { { "@@@0020" }, { "@X@0020" }, { "@H@0020" } },85 .driver_data = SB_HW_ALS100 },86 /* RTL3000 */87 { .id = "RTL3000",88 .devs = { { "@@@2001" }, { "@X@2001" }, { "@H@2001" } },89 .driver_data = SB_HW_ALS100 },90 { .id = "" } /* end */91};92 93MODULE_DEVICE_TABLE(pnp_card, snd_als100_pnpids);94 95static int snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,96 struct pnp_card_link *card,97 const struct pnp_card_device_id *id)98{99 struct pnp_dev *pdev;100 int err;101 102 acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);103 if (acard->dev == NULL)104 return -ENODEV;105 106 acard->devmpu = pnp_request_card_device(card, id->devs[1].id, acard->dev);107 acard->devopl = pnp_request_card_device(card, id->devs[2].id, acard->dev);108 109 pdev = acard->dev;110 111 err = pnp_activate_dev(pdev);112 if (err < 0) {113 dev_err(&pdev->dev, "AUDIO pnp configure failure\n");114 return err;115 }116 port[dev] = pnp_port_start(pdev, 0);117 if (id->driver_data == SB_HW_DT019X)118 dma8[dev] = pnp_dma(pdev, 0);119 else {120 dma8[dev] = pnp_dma(pdev, 1);121 dma16[dev] = pnp_dma(pdev, 0);122 }123 irq[dev] = pnp_irq(pdev, 0);124 125 pdev = acard->devmpu;126 if (pdev != NULL) {127 err = pnp_activate_dev(pdev);128 if (err < 0)129 goto __mpu_error;130 mpu_port[dev] = pnp_port_start(pdev, 0);131 mpu_irq[dev] = pnp_irq(pdev, 0);132 } else {133 __mpu_error:134 if (pdev) {135 pnp_release_card_device(pdev);136 dev_err(&pdev->dev, "MPU401 pnp configure failure, skipping\n");137 }138 acard->devmpu = NULL;139 mpu_port[dev] = -1;140 }141 142 pdev = acard->devopl;143 if (pdev != NULL) {144 err = pnp_activate_dev(pdev);145 if (err < 0)146 goto __fm_error;147 fm_port[dev] = pnp_port_start(pdev, 0);148 } else {149 __fm_error:150 if (pdev) {151 pnp_release_card_device(pdev);152 dev_err(&pdev->dev, "OPL3 pnp configure failure, skipping\n");153 }154 acard->devopl = NULL;155 fm_port[dev] = -1;156 }157 158 return 0;159}160 161static int snd_card_als100_probe(int dev,162 struct pnp_card_link *pcard,163 const struct pnp_card_device_id *pid)164{165 int error;166 struct snd_sb *chip;167 struct snd_card *card;168 struct snd_card_als100 *acard;169 struct snd_opl3 *opl3;170 171 error = snd_devm_card_new(&pcard->card->dev,172 index[dev], id[dev], THIS_MODULE,173 sizeof(struct snd_card_als100), &card);174 if (error < 0)175 return error;176 acard = card->private_data;177 178 error = snd_card_als100_pnp(dev, acard, pcard, pid);179 if (error)180 return error;181 182 if (pid->driver_data == SB_HW_DT019X)183 dma16[dev] = -1;184 185 error = snd_sbdsp_create(card, port[dev], irq[dev],186 snd_sb16dsp_interrupt,187 dma8[dev], dma16[dev],188 pid->driver_data,189 &chip);190 if (error < 0)191 return error;192 acard->chip = chip;193 194 if (pid->driver_data == SB_HW_DT019X) {195 strcpy(card->driver, "DT-019X");196 strcpy(card->shortname, "Diamond Tech. DT-019X");197 snprintf(card->longname, sizeof(card->longname),198 "Diamond Tech. DT-019X, %s at 0x%lx, irq %d, dma %d",199 chip->name, chip->port, irq[dev], dma8[dev]);200 } else {201 strcpy(card->driver, "ALS100");202 strcpy(card->shortname, "Avance Logic ALS100");203 snprintf(card->longname, sizeof(card->longname),204 "Avance Logic ALS100, %s at 0x%lx, irq %d, dma %d&%d",205 chip->name, chip->port, irq[dev], dma8[dev],206 dma16[dev]);207 }208 209 error = snd_sb16dsp_pcm(chip, 0);210 if (error < 0)211 return error;212 213 error = snd_sbmixer_new(chip);214 if (error < 0)215 return error;216 217 if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {218 int mpu_type = MPU401_HW_ALS100;219 220 if (mpu_irq[dev] == SNDRV_AUTO_IRQ)221 mpu_irq[dev] = -1;222 223 if (pid->driver_data == SB_HW_DT019X)224 mpu_type = MPU401_HW_MPU401;225 226 if (snd_mpu401_uart_new(card, 0,227 mpu_type,228 mpu_port[dev], 0, 229 mpu_irq[dev],230 NULL) < 0)231 dev_err(card->dev, "no MPU-401 device at 0x%lx\n", mpu_port[dev]);232 }233 234 if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {235 if (snd_opl3_create(card,236 fm_port[dev], fm_port[dev] + 2,237 OPL3_HW_AUTO, 0, &opl3) < 0) {238 dev_err(card->dev, "no OPL device at 0x%lx-0x%lx\n",239 fm_port[dev], fm_port[dev] + 2);240 } else {241 error = snd_opl3_timer_new(opl3, 0, 1);242 if (error < 0)243 return error;244 error = snd_opl3_hwdep_new(opl3, 0, 1, NULL);245 if (error < 0)246 return error;247 }248 }249 250 error = snd_card_register(card);251 if (error < 0)252 return error;253 pnp_set_card_drvdata(pcard, card);254 return 0;255}256 257static unsigned int als100_devices;258 259static int snd_als100_pnp_detect(struct pnp_card_link *card,260 const struct pnp_card_device_id *id)261{262 static int dev;263 int res;264 265 for ( ; dev < SNDRV_CARDS; dev++) {266 if (!enable[dev])267 continue;268 res = snd_card_als100_probe(dev, card, id);269 if (res < 0)270 return res;271 dev++;272 als100_devices++;273 return 0;274 }275 return -ENODEV;276}277 278#ifdef CONFIG_PM279static int snd_als100_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)280{281 struct snd_card *card = pnp_get_card_drvdata(pcard);282 struct snd_card_als100 *acard = card->private_data;283 struct snd_sb *chip = acard->chip;284 285 snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);286 snd_sbmixer_suspend(chip);287 return 0;288}289 290static int snd_als100_pnp_resume(struct pnp_card_link *pcard)291{292 struct snd_card *card = pnp_get_card_drvdata(pcard);293 struct snd_card_als100 *acard = card->private_data;294 struct snd_sb *chip = acard->chip;295 296 snd_sbdsp_reset(chip);297 snd_sbmixer_resume(chip);298 snd_power_change_state(card, SNDRV_CTL_POWER_D0);299 return 0;300}301#endif302 303static struct pnp_card_driver als100_pnpc_driver = {304 .flags = PNP_DRIVER_RES_DISABLE,305 .name = "als100",306 .id_table = snd_als100_pnpids,307 .probe = snd_als100_pnp_detect,308#ifdef CONFIG_PM309 .suspend = snd_als100_pnp_suspend,310 .resume = snd_als100_pnp_resume,311#endif312};313 314static int __init alsa_card_als100_init(void)315{316 int err;317 318 err = pnp_register_card_driver(&als100_pnpc_driver);319 if (err)320 return err;321 322 if (!als100_devices) {323 pnp_unregister_card_driver(&als100_pnpc_driver);324#ifdef MODULE325 pr_err("no Avance Logic based soundcards found\n");326#endif327 return -ENODEV;328 }329 return 0;330}331 332static void __exit alsa_card_als100_exit(void)333{334 pnp_unregister_card_driver(&als100_pnpc_driver);335}336 337module_init(alsa_card_als100_init)338module_exit(alsa_card_als100_exit)339