diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/isa/ad1816a |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'sound/isa/ad1816a')
-rw-r--r-- | sound/isa/ad1816a/Makefile | 10 | ||||
-rw-r--r-- | sound/isa/ad1816a/ad1816a.c | 312 | ||||
-rw-r--r-- | sound/isa/ad1816a/ad1816a_lib.c | 974 |
3 files changed, 1296 insertions, 0 deletions
diff --git a/sound/isa/ad1816a/Makefile b/sound/isa/ad1816a/Makefile new file mode 100644 index 000000000000..a42b29cf8549 --- /dev/null +++ b/sound/isa/ad1816a/Makefile | |||
@@ -0,0 +1,10 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-ad1816a-lib-objs := ad1816a_lib.o | ||
7 | snd-ad1816a-objs := ad1816a.o | ||
8 | |||
9 | # Toplevel Module Dependency | ||
10 | obj-$(CONFIG_SND_AD1816A) += snd-ad1816a.o snd-ad1816a-lib.o | ||
diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c new file mode 100644 index 000000000000..9fa7a78da6c3 --- /dev/null +++ b/sound/isa/ad1816a/ad1816a.c | |||
@@ -0,0 +1,312 @@ | |||
1 | |||
2 | /* | ||
3 | card-ad1816a.c - driver for ADI SoundPort AD1816A based soundcards. | ||
4 | Copyright (C) 2000 by Massimo Piccioni <dafastidio@libero.it> | ||
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 | #include <sound/driver.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/time.h> | ||
24 | #include <linux/wait.h> | ||
25 | #include <linux/pnp.h> | ||
26 | #include <linux/moduleparam.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/initval.h> | ||
29 | #include <sound/ad1816a.h> | ||
30 | #include <sound/mpu401.h> | ||
31 | #include <sound/opl3.h> | ||
32 | |||
33 | #define PFX "ad1816a: " | ||
34 | |||
35 | MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>"); | ||
36 | MODULE_DESCRIPTION("AD1816A, AD1815"); | ||
37 | MODULE_LICENSE("GPL"); | ||
38 | MODULE_SUPPORTED_DEVICE("{{Highscreen,Sound-Boostar 16 3D}," | ||
39 | "{Analog Devices,AD1815}," | ||
40 | "{Analog Devices,AD1816A}," | ||
41 | "{TerraTec,Base 64}," | ||
42 | "{TerraTec,AudioSystem EWS64S}," | ||
43 | "{Aztech/Newcom SC-16 3D}," | ||
44 | "{Shark Predator ISA}}"); | ||
45 | |||
46 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 1-MAX */ | ||
47 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
48 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ | ||
49 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
50 | static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
51 | static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
52 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ | ||
53 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ | ||
54 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ | ||
55 | static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ | ||
56 | |||
57 | module_param_array(index, int, NULL, 0444); | ||
58 | MODULE_PARM_DESC(index, "Index value for ad1816a based soundcard."); | ||
59 | module_param_array(id, charp, NULL, 0444); | ||
60 | MODULE_PARM_DESC(id, "ID string for ad1816a based soundcard."); | ||
61 | module_param_array(enable, bool, NULL, 0444); | ||
62 | MODULE_PARM_DESC(enable, "Enable ad1816a based soundcard."); | ||
63 | module_param_array(port, long, NULL, 0444); | ||
64 | MODULE_PARM_DESC(port, "Port # for ad1816a driver."); | ||
65 | module_param_array(mpu_port, long, NULL, 0444); | ||
66 | MODULE_PARM_DESC(mpu_port, "MPU-401 port # for ad1816a driver."); | ||
67 | module_param_array(fm_port, long, NULL, 0444); | ||
68 | MODULE_PARM_DESC(fm_port, "FM port # for ad1816a driver."); | ||
69 | module_param_array(irq, int, NULL, 0444); | ||
70 | MODULE_PARM_DESC(irq, "IRQ # for ad1816a driver."); | ||
71 | module_param_array(mpu_irq, int, NULL, 0444); | ||
72 | MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for ad1816a driver."); | ||
73 | module_param_array(dma1, int, NULL, 0444); | ||
74 | MODULE_PARM_DESC(dma1, "1st DMA # for ad1816a driver."); | ||
75 | module_param_array(dma2, int, NULL, 0444); | ||
76 | MODULE_PARM_DESC(dma2, "2nd DMA # for ad1816a driver."); | ||
77 | |||
78 | struct snd_card_ad1816a { | ||
79 | struct pnp_dev *dev; | ||
80 | struct pnp_dev *devmpu; | ||
81 | }; | ||
82 | |||
83 | static struct pnp_card_device_id snd_ad1816a_pnpids[] = { | ||
84 | /* Analog Devices AD1815 */ | ||
85 | { .id = "ADS7150", .devs = { { .id = "ADS7150" }, { .id = "ADS7151" } } }, | ||
86 | /* Analog Devices AD1816A - added by Kenneth Platz <kxp@atl.hp.com> */ | ||
87 | { .id = "ADS7181", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, | ||
88 | /* Analog Devices AD1816A - Aztech/Newcom SC-16 3D */ | ||
89 | { .id = "AZT1022", .devs = { { .id = "AZT1018" }, { .id = "AZT2002" } } }, | ||
90 | /* Highscreen Sound-Boostar 16 3D - added by Stefan Behnel */ | ||
91 | { .id = "LWC1061", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, | ||
92 | /* Highscreen Sound-Boostar 16 3D */ | ||
93 | { .id = "MDK1605", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, | ||
94 | /* Shark Predator ISA - added by Ken Arromdee */ | ||
95 | { .id = "SMM7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, | ||
96 | /* Analog Devices AD1816A - Terratec AudioSystem EWS64S */ | ||
97 | { .id = "TER1112", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, | ||
98 | /* Analog Devices AD1816A - Terratec Base 64 */ | ||
99 | { .id = "TER1411", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, | ||
100 | /* end */ | ||
101 | { .id = "" } | ||
102 | }; | ||
103 | |||
104 | MODULE_DEVICE_TABLE(pnp_card, snd_ad1816a_pnpids); | ||
105 | |||
106 | |||
107 | #define DRIVER_NAME "snd-card-ad1816a" | ||
108 | |||
109 | |||
110 | static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acard, | ||
111 | struct pnp_card_link *card, | ||
112 | const struct pnp_card_device_id *id) | ||
113 | { | ||
114 | struct pnp_dev *pdev; | ||
115 | struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); | ||
116 | int err; | ||
117 | |||
118 | acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); | ||
119 | if (acard->dev == NULL) { | ||
120 | kfree(cfg); | ||
121 | return -EBUSY; | ||
122 | } | ||
123 | acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL); | ||
124 | if (acard->devmpu == NULL) { | ||
125 | kfree(cfg); | ||
126 | return -EBUSY; | ||
127 | } | ||
128 | |||
129 | pdev = acard->dev; | ||
130 | pnp_init_resource_table(cfg); | ||
131 | |||
132 | if (port[dev] != SNDRV_AUTO_PORT) | ||
133 | pnp_resource_change(&cfg->port_resource[2], port[dev], 16); | ||
134 | if (fm_port[dev] != SNDRV_AUTO_PORT) | ||
135 | pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4); | ||
136 | if (dma1[dev] != SNDRV_AUTO_DMA) | ||
137 | pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); | ||
138 | if (dma2[dev] != SNDRV_AUTO_DMA) | ||
139 | pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1); | ||
140 | if (irq[dev] != SNDRV_AUTO_IRQ) | ||
141 | pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); | ||
142 | |||
143 | if (pnp_manual_config_dev(pdev, cfg, 0) < 0) | ||
144 | snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n"); | ||
145 | err = pnp_activate_dev(pdev); | ||
146 | if (err < 0) { | ||
147 | printk(KERN_ERR PFX "AUDIO PnP configure failure\n"); | ||
148 | kfree(cfg); | ||
149 | return -EBUSY; | ||
150 | } | ||
151 | |||
152 | port[dev] = pnp_port_start(pdev, 2); | ||
153 | fm_port[dev] = pnp_port_start(pdev, 1); | ||
154 | dma1[dev] = pnp_dma(pdev, 0); | ||
155 | dma2[dev] = pnp_dma(pdev, 1); | ||
156 | irq[dev] = pnp_irq(pdev, 0); | ||
157 | |||
158 | pdev = acard->devmpu; | ||
159 | pnp_init_resource_table(cfg); | ||
160 | |||
161 | if (mpu_port[dev] != SNDRV_AUTO_PORT) | ||
162 | pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2); | ||
163 | if (mpu_irq[dev] != SNDRV_AUTO_IRQ) | ||
164 | pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1); | ||
165 | |||
166 | if (pnp_manual_config_dev(pdev, cfg, 0) < 0) | ||
167 | snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n"); | ||
168 | err = pnp_activate_dev(pdev); | ||
169 | if (err < 0) { | ||
170 | printk(KERN_ERR PFX "MPU401 PnP configure failure\n"); | ||
171 | mpu_port[dev] = -1; | ||
172 | acard->devmpu = NULL; | ||
173 | } else { | ||
174 | mpu_port[dev] = pnp_port_start(pdev, 0); | ||
175 | mpu_irq[dev] = pnp_irq(pdev, 0); | ||
176 | } | ||
177 | |||
178 | kfree(cfg); | ||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | static int __devinit snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard, | ||
183 | const struct pnp_card_device_id *pid) | ||
184 | { | ||
185 | int error; | ||
186 | snd_card_t *card; | ||
187 | struct snd_card_ad1816a *acard; | ||
188 | ad1816a_t *chip; | ||
189 | opl3_t *opl3; | ||
190 | |||
191 | if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE, | ||
192 | sizeof(struct snd_card_ad1816a))) == NULL) | ||
193 | return -ENOMEM; | ||
194 | acard = (struct snd_card_ad1816a *)card->private_data; | ||
195 | |||
196 | if ((error = snd_card_ad1816a_pnp(dev, acard, pcard, pid))) { | ||
197 | snd_card_free(card); | ||
198 | return error; | ||
199 | } | ||
200 | snd_card_set_dev(card, &pcard->card->dev); | ||
201 | |||
202 | if ((error = snd_ad1816a_create(card, port[dev], | ||
203 | irq[dev], | ||
204 | dma1[dev], | ||
205 | dma2[dev], | ||
206 | &chip)) < 0) { | ||
207 | snd_card_free(card); | ||
208 | return error; | ||
209 | } | ||
210 | |||
211 | strcpy(card->driver, "AD1816A"); | ||
212 | strcpy(card->shortname, "ADI SoundPort AD1816A"); | ||
213 | sprintf(card->longname, "%s, SS at 0x%lx, irq %d, dma %d&%d", | ||
214 | card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]); | ||
215 | |||
216 | if ((error = snd_ad1816a_pcm(chip, 0, NULL)) < 0) { | ||
217 | snd_card_free(card); | ||
218 | return error; | ||
219 | } | ||
220 | |||
221 | if ((error = snd_ad1816a_mixer(chip)) < 0) { | ||
222 | snd_card_free(card); | ||
223 | return error; | ||
224 | } | ||
225 | |||
226 | if (mpu_port[dev] > 0) { | ||
227 | if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, | ||
228 | mpu_port[dev], 0, mpu_irq[dev], SA_INTERRUPT, | ||
229 | NULL) < 0) | ||
230 | printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n", mpu_port[dev]); | ||
231 | } | ||
232 | |||
233 | if (fm_port[dev] > 0) { | ||
234 | if (snd_opl3_create(card, | ||
235 | fm_port[dev], fm_port[dev] + 2, | ||
236 | OPL3_HW_AUTO, 0, &opl3) < 0) { | ||
237 | printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx.\n", fm_port[dev], fm_port[dev] + 2); | ||
238 | } else { | ||
239 | if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) { | ||
240 | snd_card_free(card); | ||
241 | return error; | ||
242 | } | ||
243 | if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { | ||
244 | snd_card_free(card); | ||
245 | return error; | ||
246 | } | ||
247 | } | ||
248 | } | ||
249 | |||
250 | if ((error = snd_card_register(card)) < 0) { | ||
251 | snd_card_free(card); | ||
252 | return error; | ||
253 | } | ||
254 | pnp_set_card_drvdata(pcard, card); | ||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | static int __devinit snd_ad1816a_pnp_detect(struct pnp_card_link *card, | ||
259 | const struct pnp_card_device_id *id) | ||
260 | { | ||
261 | static int dev; | ||
262 | int res; | ||
263 | |||
264 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
265 | if (!enable[dev]) | ||
266 | continue; | ||
267 | res = snd_card_ad1816a_probe(dev, card, id); | ||
268 | if (res < 0) | ||
269 | return res; | ||
270 | dev++; | ||
271 | return 0; | ||
272 | } | ||
273 | return -ENODEV; | ||
274 | } | ||
275 | |||
276 | static void __devexit snd_ad1816a_pnp_remove(struct pnp_card_link * pcard) | ||
277 | { | ||
278 | snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); | ||
279 | |||
280 | snd_card_disconnect(card); | ||
281 | snd_card_free_in_thread(card); | ||
282 | } | ||
283 | |||
284 | static struct pnp_card_driver ad1816a_pnpc_driver = { | ||
285 | .flags = PNP_DRIVER_RES_DISABLE, | ||
286 | .name = "ad1816a", | ||
287 | .id_table = snd_ad1816a_pnpids, | ||
288 | .probe = snd_ad1816a_pnp_detect, | ||
289 | .remove = __devexit_p(snd_ad1816a_pnp_remove), | ||
290 | }; | ||
291 | |||
292 | static int __init alsa_card_ad1816a_init(void) | ||
293 | { | ||
294 | int cards = 0; | ||
295 | |||
296 | cards += pnp_register_card_driver(&ad1816a_pnpc_driver); | ||
297 | #ifdef MODULE | ||
298 | if (!cards) { | ||
299 | pnp_unregister_card_driver(&ad1816a_pnpc_driver); | ||
300 | printk(KERN_ERR "no AD1816A based soundcards found.\n"); | ||
301 | } | ||
302 | #endif /* MODULE */ | ||
303 | return cards ? 0 : -ENODEV; | ||
304 | } | ||
305 | |||
306 | static void __exit alsa_card_ad1816a_exit(void) | ||
307 | { | ||
308 | pnp_unregister_card_driver(&ad1816a_pnpc_driver); | ||
309 | } | ||
310 | |||
311 | module_init(alsa_card_ad1816a_init) | ||
312 | module_exit(alsa_card_ad1816a_exit) | ||
diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c new file mode 100644 index 000000000000..625b2eff14a1 --- /dev/null +++ b/sound/isa/ad1816a/ad1816a_lib.c | |||
@@ -0,0 +1,974 @@ | |||
1 | |||
2 | /* | ||
3 | ad1816a.c - lowlevel code for Analog Devices AD1816A chip. | ||
4 | Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it> | ||
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 | #include <sound/driver.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/ioport.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/ad1816a.h> | ||
29 | |||
30 | #include <asm/io.h> | ||
31 | #include <asm/dma.h> | ||
32 | |||
33 | MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>"); | ||
34 | MODULE_DESCRIPTION("lowlevel code for Analog Devices AD1816A chip"); | ||
35 | MODULE_LICENSE("GPL"); | ||
36 | |||
37 | static inline int snd_ad1816a_busy_wait(ad1816a_t *chip) | ||
38 | { | ||
39 | int timeout; | ||
40 | |||
41 | for (timeout = 1000; timeout-- > 0; udelay(10)) | ||
42 | if (inb(AD1816A_REG(AD1816A_CHIP_STATUS)) & AD1816A_READY) | ||
43 | return 0; | ||
44 | |||
45 | snd_printk("chip busy.\n"); | ||
46 | return -EBUSY; | ||
47 | } | ||
48 | |||
49 | static inline unsigned char snd_ad1816a_in(ad1816a_t *chip, unsigned char reg) | ||
50 | { | ||
51 | snd_ad1816a_busy_wait(chip); | ||
52 | return inb(AD1816A_REG(reg)); | ||
53 | } | ||
54 | |||
55 | static inline void snd_ad1816a_out(ad1816a_t *chip, unsigned char reg, | ||
56 | unsigned char value) | ||
57 | { | ||
58 | snd_ad1816a_busy_wait(chip); | ||
59 | outb(value, AD1816A_REG(reg)); | ||
60 | } | ||
61 | |||
62 | static inline void snd_ad1816a_out_mask(ad1816a_t *chip, unsigned char reg, | ||
63 | unsigned char mask, unsigned char value) | ||
64 | { | ||
65 | snd_ad1816a_out(chip, reg, | ||
66 | (value & mask) | (snd_ad1816a_in(chip, reg) & ~mask)); | ||
67 | } | ||
68 | |||
69 | static unsigned short snd_ad1816a_read(ad1816a_t *chip, unsigned char reg) | ||
70 | { | ||
71 | snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f); | ||
72 | return snd_ad1816a_in(chip, AD1816A_INDIR_DATA_LOW) | | ||
73 | (snd_ad1816a_in(chip, AD1816A_INDIR_DATA_HIGH) << 8); | ||
74 | } | ||
75 | |||
76 | static void snd_ad1816a_write(ad1816a_t *chip, unsigned char reg, | ||
77 | unsigned short value) | ||
78 | { | ||
79 | snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f); | ||
80 | snd_ad1816a_out(chip, AD1816A_INDIR_DATA_LOW, value & 0xff); | ||
81 | snd_ad1816a_out(chip, AD1816A_INDIR_DATA_HIGH, (value >> 8) & 0xff); | ||
82 | } | ||
83 | |||
84 | static void snd_ad1816a_write_mask(ad1816a_t *chip, unsigned char reg, | ||
85 | unsigned short mask, unsigned short value) | ||
86 | { | ||
87 | snd_ad1816a_write(chip, reg, | ||
88 | (value & mask) | (snd_ad1816a_read(chip, reg) & ~mask)); | ||
89 | } | ||
90 | |||
91 | |||
92 | static unsigned char snd_ad1816a_get_format(ad1816a_t *chip, | ||
93 | unsigned int format, int channels) | ||
94 | { | ||
95 | unsigned char retval = AD1816A_FMT_LINEAR_8; | ||
96 | |||
97 | switch (format) { | ||
98 | case SNDRV_PCM_FORMAT_MU_LAW: | ||
99 | retval = AD1816A_FMT_ULAW_8; | ||
100 | break; | ||
101 | case SNDRV_PCM_FORMAT_A_LAW: | ||
102 | retval = AD1816A_FMT_ALAW_8; | ||
103 | break; | ||
104 | case SNDRV_PCM_FORMAT_S16_LE: | ||
105 | retval = AD1816A_FMT_LINEAR_16_LIT; | ||
106 | break; | ||
107 | case SNDRV_PCM_FORMAT_S16_BE: | ||
108 | retval = AD1816A_FMT_LINEAR_16_BIG; | ||
109 | } | ||
110 | return (channels > 1) ? (retval | AD1816A_FMT_STEREO) : retval; | ||
111 | } | ||
112 | |||
113 | static int snd_ad1816a_open(ad1816a_t *chip, unsigned int mode) | ||
114 | { | ||
115 | unsigned long flags; | ||
116 | |||
117 | spin_lock_irqsave(&chip->lock, flags); | ||
118 | |||
119 | if (chip->mode & mode) { | ||
120 | spin_unlock_irqrestore(&chip->lock, flags); | ||
121 | return -EAGAIN; | ||
122 | } | ||
123 | |||
124 | switch ((mode &= AD1816A_MODE_OPEN)) { | ||
125 | case AD1816A_MODE_PLAYBACK: | ||
126 | snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, | ||
127 | AD1816A_PLAYBACK_IRQ_PENDING, 0x00); | ||
128 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
129 | AD1816A_PLAYBACK_IRQ_ENABLE, 0xffff); | ||
130 | break; | ||
131 | case AD1816A_MODE_CAPTURE: | ||
132 | snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, | ||
133 | AD1816A_CAPTURE_IRQ_PENDING, 0x00); | ||
134 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
135 | AD1816A_CAPTURE_IRQ_ENABLE, 0xffff); | ||
136 | break; | ||
137 | case AD1816A_MODE_TIMER: | ||
138 | snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, | ||
139 | AD1816A_TIMER_IRQ_PENDING, 0x00); | ||
140 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
141 | AD1816A_TIMER_IRQ_ENABLE, 0xffff); | ||
142 | } | ||
143 | chip->mode |= mode; | ||
144 | |||
145 | spin_unlock_irqrestore(&chip->lock, flags); | ||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | static void snd_ad1816a_close(ad1816a_t *chip, unsigned int mode) | ||
150 | { | ||
151 | unsigned long flags; | ||
152 | |||
153 | spin_lock_irqsave(&chip->lock, flags); | ||
154 | |||
155 | switch ((mode &= AD1816A_MODE_OPEN)) { | ||
156 | case AD1816A_MODE_PLAYBACK: | ||
157 | snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, | ||
158 | AD1816A_PLAYBACK_IRQ_PENDING, 0x00); | ||
159 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
160 | AD1816A_PLAYBACK_IRQ_ENABLE, 0x0000); | ||
161 | break; | ||
162 | case AD1816A_MODE_CAPTURE: | ||
163 | snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, | ||
164 | AD1816A_CAPTURE_IRQ_PENDING, 0x00); | ||
165 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
166 | AD1816A_CAPTURE_IRQ_ENABLE, 0x0000); | ||
167 | break; | ||
168 | case AD1816A_MODE_TIMER: | ||
169 | snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, | ||
170 | AD1816A_TIMER_IRQ_PENDING, 0x00); | ||
171 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
172 | AD1816A_TIMER_IRQ_ENABLE, 0x0000); | ||
173 | } | ||
174 | if (!((chip->mode &= ~mode) & AD1816A_MODE_OPEN)) | ||
175 | chip->mode = 0; | ||
176 | |||
177 | spin_unlock_irqrestore(&chip->lock, flags); | ||
178 | } | ||
179 | |||
180 | |||
181 | static int snd_ad1816a_trigger(ad1816a_t *chip, unsigned char what, | ||
182 | int channel, int cmd) | ||
183 | { | ||
184 | int error = 0; | ||
185 | |||
186 | switch (cmd) { | ||
187 | case SNDRV_PCM_TRIGGER_START: | ||
188 | case SNDRV_PCM_TRIGGER_STOP: | ||
189 | spin_lock(&chip->lock); | ||
190 | cmd = (cmd == SNDRV_PCM_TRIGGER_START) ? 0xff: 0x00; | ||
191 | if (what & AD1816A_PLAYBACK_ENABLE) | ||
192 | snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, | ||
193 | AD1816A_PLAYBACK_ENABLE, cmd); | ||
194 | if (what & AD1816A_CAPTURE_ENABLE) | ||
195 | snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, | ||
196 | AD1816A_CAPTURE_ENABLE, cmd); | ||
197 | spin_unlock(&chip->lock); | ||
198 | break; | ||
199 | default: | ||
200 | snd_printk("invalid trigger mode 0x%x.\n", what); | ||
201 | error = -EINVAL; | ||
202 | } | ||
203 | |||
204 | return error; | ||
205 | } | ||
206 | |||
207 | static int snd_ad1816a_playback_trigger(snd_pcm_substream_t *substream, int cmd) | ||
208 | { | ||
209 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
210 | return snd_ad1816a_trigger(chip, AD1816A_PLAYBACK_ENABLE, | ||
211 | SNDRV_PCM_STREAM_PLAYBACK, cmd); | ||
212 | } | ||
213 | |||
214 | static int snd_ad1816a_capture_trigger(snd_pcm_substream_t *substream, int cmd) | ||
215 | { | ||
216 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
217 | return snd_ad1816a_trigger(chip, AD1816A_CAPTURE_ENABLE, | ||
218 | SNDRV_PCM_STREAM_CAPTURE, cmd); | ||
219 | } | ||
220 | |||
221 | static int snd_ad1816a_hw_params(snd_pcm_substream_t * substream, | ||
222 | snd_pcm_hw_params_t * hw_params) | ||
223 | { | ||
224 | return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); | ||
225 | } | ||
226 | |||
227 | static int snd_ad1816a_hw_free(snd_pcm_substream_t * substream) | ||
228 | { | ||
229 | return snd_pcm_lib_free_pages(substream); | ||
230 | } | ||
231 | |||
232 | static int snd_ad1816a_playback_prepare(snd_pcm_substream_t *substream) | ||
233 | { | ||
234 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
235 | unsigned long flags; | ||
236 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
237 | unsigned int size; | ||
238 | |||
239 | spin_lock_irqsave(&chip->lock, flags); | ||
240 | |||
241 | chip->p_dma_size = size = snd_pcm_lib_buffer_bytes(substream); | ||
242 | snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, | ||
243 | AD1816A_PLAYBACK_ENABLE | AD1816A_PLAYBACK_PIO, 0x00); | ||
244 | |||
245 | snd_dma_program(chip->dma1, runtime->dma_addr, size, | ||
246 | DMA_MODE_WRITE | DMA_AUTOINIT); | ||
247 | |||
248 | snd_ad1816a_write(chip, AD1816A_PLAYBACK_SAMPLE_RATE, runtime->rate); | ||
249 | snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, | ||
250 | AD1816A_FMT_ALL | AD1816A_FMT_STEREO, | ||
251 | snd_ad1816a_get_format(chip, runtime->format, | ||
252 | runtime->channels)); | ||
253 | |||
254 | snd_ad1816a_write(chip, AD1816A_PLAYBACK_BASE_COUNT, | ||
255 | snd_pcm_lib_period_bytes(substream) / 4 - 1); | ||
256 | |||
257 | spin_unlock_irqrestore(&chip->lock, flags); | ||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | static int snd_ad1816a_capture_prepare(snd_pcm_substream_t *substream) | ||
262 | { | ||
263 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
264 | unsigned long flags; | ||
265 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
266 | unsigned int size; | ||
267 | |||
268 | spin_lock_irqsave(&chip->lock, flags); | ||
269 | |||
270 | chip->c_dma_size = size = snd_pcm_lib_buffer_bytes(substream); | ||
271 | snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, | ||
272 | AD1816A_CAPTURE_ENABLE | AD1816A_CAPTURE_PIO, 0x00); | ||
273 | |||
274 | snd_dma_program(chip->dma2, runtime->dma_addr, size, | ||
275 | DMA_MODE_READ | DMA_AUTOINIT); | ||
276 | |||
277 | snd_ad1816a_write(chip, AD1816A_CAPTURE_SAMPLE_RATE, runtime->rate); | ||
278 | snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, | ||
279 | AD1816A_FMT_ALL | AD1816A_FMT_STEREO, | ||
280 | snd_ad1816a_get_format(chip, runtime->format, | ||
281 | runtime->channels)); | ||
282 | |||
283 | snd_ad1816a_write(chip, AD1816A_CAPTURE_BASE_COUNT, | ||
284 | snd_pcm_lib_period_bytes(substream) / 4 - 1); | ||
285 | |||
286 | spin_unlock_irqrestore(&chip->lock, flags); | ||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | |||
291 | static snd_pcm_uframes_t snd_ad1816a_playback_pointer(snd_pcm_substream_t *substream) | ||
292 | { | ||
293 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
294 | size_t ptr; | ||
295 | if (!(chip->mode & AD1816A_MODE_PLAYBACK)) | ||
296 | return 0; | ||
297 | ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size); | ||
298 | return bytes_to_frames(substream->runtime, ptr); | ||
299 | } | ||
300 | |||
301 | static snd_pcm_uframes_t snd_ad1816a_capture_pointer(snd_pcm_substream_t *substream) | ||
302 | { | ||
303 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
304 | size_t ptr; | ||
305 | if (!(chip->mode & AD1816A_MODE_CAPTURE)) | ||
306 | return 0; | ||
307 | ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size); | ||
308 | return bytes_to_frames(substream->runtime, ptr); | ||
309 | } | ||
310 | |||
311 | |||
312 | static irqreturn_t snd_ad1816a_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
313 | { | ||
314 | ad1816a_t *chip = dev_id; | ||
315 | unsigned char status; | ||
316 | |||
317 | spin_lock(&chip->lock); | ||
318 | status = snd_ad1816a_in(chip, AD1816A_INTERRUPT_STATUS); | ||
319 | spin_unlock(&chip->lock); | ||
320 | |||
321 | if ((status & AD1816A_PLAYBACK_IRQ_PENDING) && chip->playback_substream) | ||
322 | snd_pcm_period_elapsed(chip->playback_substream); | ||
323 | |||
324 | if ((status & AD1816A_CAPTURE_IRQ_PENDING) && chip->capture_substream) | ||
325 | snd_pcm_period_elapsed(chip->capture_substream); | ||
326 | |||
327 | if ((status & AD1816A_TIMER_IRQ_PENDING) && chip->timer) | ||
328 | snd_timer_interrupt(chip->timer, chip->timer->sticks); | ||
329 | |||
330 | spin_lock(&chip->lock); | ||
331 | snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00); | ||
332 | spin_unlock(&chip->lock); | ||
333 | return IRQ_HANDLED; | ||
334 | } | ||
335 | |||
336 | |||
337 | static snd_pcm_hardware_t snd_ad1816a_playback = { | ||
338 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
339 | SNDRV_PCM_INFO_MMAP_VALID), | ||
340 | .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | | ||
341 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | | ||
342 | SNDRV_PCM_FMTBIT_S16_BE), | ||
343 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
344 | .rate_min = 4000, | ||
345 | .rate_max = 55200, | ||
346 | .channels_min = 1, | ||
347 | .channels_max = 2, | ||
348 | .buffer_bytes_max = (128*1024), | ||
349 | .period_bytes_min = 64, | ||
350 | .period_bytes_max = (128*1024), | ||
351 | .periods_min = 1, | ||
352 | .periods_max = 1024, | ||
353 | .fifo_size = 0, | ||
354 | }; | ||
355 | |||
356 | static snd_pcm_hardware_t snd_ad1816a_capture = { | ||
357 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
358 | SNDRV_PCM_INFO_MMAP_VALID), | ||
359 | .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | | ||
360 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | | ||
361 | SNDRV_PCM_FMTBIT_S16_BE), | ||
362 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
363 | .rate_min = 4000, | ||
364 | .rate_max = 55200, | ||
365 | .channels_min = 1, | ||
366 | .channels_max = 2, | ||
367 | .buffer_bytes_max = (128*1024), | ||
368 | .period_bytes_min = 64, | ||
369 | .period_bytes_max = (128*1024), | ||
370 | .periods_min = 1, | ||
371 | .periods_max = 1024, | ||
372 | .fifo_size = 0, | ||
373 | }; | ||
374 | |||
375 | #if 0 /* not used now */ | ||
376 | static int snd_ad1816a_timer_close(snd_timer_t *timer) | ||
377 | { | ||
378 | ad1816a_t *chip = snd_timer_chip(timer); | ||
379 | snd_ad1816a_close(chip, AD1816A_MODE_TIMER); | ||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | static int snd_ad1816a_timer_open(snd_timer_t *timer) | ||
384 | { | ||
385 | ad1816a_t *chip = snd_timer_chip(timer); | ||
386 | snd_ad1816a_open(chip, AD1816A_MODE_TIMER); | ||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | static unsigned long snd_ad1816a_timer_resolution(snd_timer_t *timer) | ||
391 | { | ||
392 | snd_assert(timer != NULL, return 0); | ||
393 | |||
394 | return 10000; | ||
395 | } | ||
396 | |||
397 | static int snd_ad1816a_timer_start(snd_timer_t *timer) | ||
398 | { | ||
399 | unsigned short bits; | ||
400 | unsigned long flags; | ||
401 | ad1816a_t *chip = snd_timer_chip(timer); | ||
402 | spin_lock_irqsave(&chip->lock, flags); | ||
403 | bits = snd_ad1816a_read(chip, AD1816A_INTERRUPT_ENABLE); | ||
404 | |||
405 | if (!(bits & AD1816A_TIMER_ENABLE)) { | ||
406 | snd_ad1816a_write(chip, AD1816A_TIMER_BASE_COUNT, | ||
407 | timer->sticks & 0xffff); | ||
408 | |||
409 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
410 | AD1816A_TIMER_ENABLE, 0xffff); | ||
411 | } | ||
412 | spin_unlock_irqrestore(&chip->lock, flags); | ||
413 | return 0; | ||
414 | } | ||
415 | |||
416 | static int snd_ad1816a_timer_stop(snd_timer_t *timer) | ||
417 | { | ||
418 | unsigned long flags; | ||
419 | ad1816a_t *chip = snd_timer_chip(timer); | ||
420 | spin_lock_irqsave(&chip->lock, flags); | ||
421 | |||
422 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
423 | AD1816A_TIMER_ENABLE, 0x0000); | ||
424 | |||
425 | spin_unlock_irqrestore(&chip->lock, flags); | ||
426 | return 0; | ||
427 | } | ||
428 | |||
429 | static struct _snd_timer_hardware snd_ad1816a_timer_table = { | ||
430 | .flags = SNDRV_TIMER_HW_AUTO, | ||
431 | .resolution = 10000, | ||
432 | .ticks = 65535, | ||
433 | .open = snd_ad1816a_timer_open, | ||
434 | .close = snd_ad1816a_timer_close, | ||
435 | .c_resolution = snd_ad1816a_timer_resolution, | ||
436 | .start = snd_ad1816a_timer_start, | ||
437 | .stop = snd_ad1816a_timer_stop, | ||
438 | }; | ||
439 | #endif /* not used now */ | ||
440 | |||
441 | |||
442 | static int snd_ad1816a_playback_open(snd_pcm_substream_t *substream) | ||
443 | { | ||
444 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
445 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
446 | int error; | ||
447 | |||
448 | if ((error = snd_ad1816a_open(chip, AD1816A_MODE_PLAYBACK)) < 0) | ||
449 | return error; | ||
450 | snd_pcm_set_sync(substream); | ||
451 | runtime->hw = snd_ad1816a_playback; | ||
452 | snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); | ||
453 | snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max); | ||
454 | chip->playback_substream = substream; | ||
455 | return 0; | ||
456 | } | ||
457 | |||
458 | static int snd_ad1816a_capture_open(snd_pcm_substream_t *substream) | ||
459 | { | ||
460 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
461 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
462 | int error; | ||
463 | |||
464 | if ((error = snd_ad1816a_open(chip, AD1816A_MODE_CAPTURE)) < 0) | ||
465 | return error; | ||
466 | snd_pcm_set_sync(substream); | ||
467 | runtime->hw = snd_ad1816a_capture; | ||
468 | snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); | ||
469 | snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max); | ||
470 | chip->capture_substream = substream; | ||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | static int snd_ad1816a_playback_close(snd_pcm_substream_t *substream) | ||
475 | { | ||
476 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
477 | |||
478 | chip->playback_substream = NULL; | ||
479 | snd_ad1816a_close(chip, AD1816A_MODE_PLAYBACK); | ||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | static int snd_ad1816a_capture_close(snd_pcm_substream_t *substream) | ||
484 | { | ||
485 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
486 | |||
487 | chip->capture_substream = NULL; | ||
488 | snd_ad1816a_close(chip, AD1816A_MODE_CAPTURE); | ||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | |||
493 | static void snd_ad1816a_init(ad1816a_t *chip) | ||
494 | { | ||
495 | unsigned long flags; | ||
496 | |||
497 | spin_lock_irqsave(&chip->lock, flags); | ||
498 | |||
499 | snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00); | ||
500 | snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, | ||
501 | AD1816A_PLAYBACK_ENABLE | AD1816A_PLAYBACK_PIO, 0x00); | ||
502 | snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, | ||
503 | AD1816A_CAPTURE_ENABLE | AD1816A_CAPTURE_PIO, 0x00); | ||
504 | snd_ad1816a_write(chip, AD1816A_INTERRUPT_ENABLE, 0x0000); | ||
505 | snd_ad1816a_write_mask(chip, AD1816A_CHIP_CONFIG, | ||
506 | AD1816A_CAPTURE_NOT_EQUAL | AD1816A_WSS_ENABLE, 0xffff); | ||
507 | snd_ad1816a_write(chip, AD1816A_DSP_CONFIG, 0x0000); | ||
508 | snd_ad1816a_write(chip, AD1816A_POWERDOWN_CTRL, 0x0000); | ||
509 | |||
510 | spin_unlock_irqrestore(&chip->lock, flags); | ||
511 | } | ||
512 | |||
513 | static int snd_ad1816a_probe(ad1816a_t *chip) | ||
514 | { | ||
515 | unsigned long flags; | ||
516 | |||
517 | spin_lock_irqsave(&chip->lock, flags); | ||
518 | |||
519 | switch (chip->version = snd_ad1816a_read(chip, AD1816A_VERSION_ID)) { | ||
520 | case 0: | ||
521 | chip->hardware = AD1816A_HW_AD1815; | ||
522 | break; | ||
523 | case 1: | ||
524 | chip->hardware = AD1816A_HW_AD18MAX10; | ||
525 | break; | ||
526 | case 3: | ||
527 | chip->hardware = AD1816A_HW_AD1816A; | ||
528 | break; | ||
529 | default: | ||
530 | chip->hardware = AD1816A_HW_AUTO; | ||
531 | } | ||
532 | |||
533 | spin_unlock_irqrestore(&chip->lock, flags); | ||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | static int snd_ad1816a_free(ad1816a_t *chip) | ||
538 | { | ||
539 | if (chip->res_port) { | ||
540 | release_resource(chip->res_port); | ||
541 | kfree_nocheck(chip->res_port); | ||
542 | } | ||
543 | if (chip->irq >= 0) | ||
544 | free_irq(chip->irq, (void *) chip); | ||
545 | if (chip->dma1 >= 0) { | ||
546 | snd_dma_disable(chip->dma1); | ||
547 | free_dma(chip->dma1); | ||
548 | } | ||
549 | if (chip->dma2 >= 0) { | ||
550 | snd_dma_disable(chip->dma2); | ||
551 | free_dma(chip->dma2); | ||
552 | } | ||
553 | kfree(chip); | ||
554 | return 0; | ||
555 | } | ||
556 | |||
557 | static int snd_ad1816a_dev_free(snd_device_t *device) | ||
558 | { | ||
559 | ad1816a_t *chip = device->device_data; | ||
560 | return snd_ad1816a_free(chip); | ||
561 | } | ||
562 | |||
563 | static const char *snd_ad1816a_chip_id(ad1816a_t *chip) | ||
564 | { | ||
565 | switch (chip->hardware) { | ||
566 | case AD1816A_HW_AD1816A: return "AD1816A"; | ||
567 | case AD1816A_HW_AD1815: return "AD1815"; | ||
568 | case AD1816A_HW_AD18MAX10: return "AD18max10"; | ||
569 | default: | ||
570 | snd_printk("Unknown chip version %d:%d.\n", | ||
571 | chip->version, chip->hardware); | ||
572 | return "AD1816A - unknown"; | ||
573 | } | ||
574 | } | ||
575 | |||
576 | int snd_ad1816a_create(snd_card_t *card, | ||
577 | unsigned long port, int irq, int dma1, int dma2, | ||
578 | ad1816a_t **rchip) | ||
579 | { | ||
580 | static snd_device_ops_t ops = { | ||
581 | .dev_free = snd_ad1816a_dev_free, | ||
582 | }; | ||
583 | int error; | ||
584 | ad1816a_t *chip; | ||
585 | |||
586 | *rchip = NULL; | ||
587 | |||
588 | chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); | ||
589 | if (chip == NULL) | ||
590 | return -ENOMEM; | ||
591 | chip->irq = -1; | ||
592 | chip->dma1 = -1; | ||
593 | chip->dma2 = -1; | ||
594 | |||
595 | if ((chip->res_port = request_region(port, 16, "AD1816A")) == NULL) { | ||
596 | snd_printk(KERN_ERR "ad1816a: can't grab port 0x%lx\n", port); | ||
597 | snd_ad1816a_free(chip); | ||
598 | return -EBUSY; | ||
599 | } | ||
600 | if (request_irq(irq, snd_ad1816a_interrupt, SA_INTERRUPT, "AD1816A", (void *) chip)) { | ||
601 | snd_printk(KERN_ERR "ad1816a: can't grab IRQ %d\n", irq); | ||
602 | snd_ad1816a_free(chip); | ||
603 | return -EBUSY; | ||
604 | } | ||
605 | chip->irq = irq; | ||
606 | if (request_dma(dma1, "AD1816A - 1")) { | ||
607 | snd_printk(KERN_ERR "ad1816a: can't grab DMA1 %d\n", dma1); | ||
608 | snd_ad1816a_free(chip); | ||
609 | return -EBUSY; | ||
610 | } | ||
611 | chip->dma1 = dma1; | ||
612 | if (request_dma(dma2, "AD1816A - 2")) { | ||
613 | snd_printk(KERN_ERR "ad1816a: can't grab DMA2 %d\n", dma2); | ||
614 | snd_ad1816a_free(chip); | ||
615 | return -EBUSY; | ||
616 | } | ||
617 | chip->dma2 = dma2; | ||
618 | |||
619 | chip->card = card; | ||
620 | chip->port = port; | ||
621 | spin_lock_init(&chip->lock); | ||
622 | |||
623 | if ((error = snd_ad1816a_probe(chip))) { | ||
624 | snd_ad1816a_free(chip); | ||
625 | return error; | ||
626 | } | ||
627 | |||
628 | snd_ad1816a_init(chip); | ||
629 | |||
630 | /* Register device */ | ||
631 | if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { | ||
632 | snd_ad1816a_free(chip); | ||
633 | return error; | ||
634 | } | ||
635 | |||
636 | *rchip = chip; | ||
637 | return 0; | ||
638 | } | ||
639 | |||
640 | static snd_pcm_ops_t snd_ad1816a_playback_ops = { | ||
641 | .open = snd_ad1816a_playback_open, | ||
642 | .close = snd_ad1816a_playback_close, | ||
643 | .ioctl = snd_pcm_lib_ioctl, | ||
644 | .hw_params = snd_ad1816a_hw_params, | ||
645 | .hw_free = snd_ad1816a_hw_free, | ||
646 | .prepare = snd_ad1816a_playback_prepare, | ||
647 | .trigger = snd_ad1816a_playback_trigger, | ||
648 | .pointer = snd_ad1816a_playback_pointer, | ||
649 | }; | ||
650 | |||
651 | static snd_pcm_ops_t snd_ad1816a_capture_ops = { | ||
652 | .open = snd_ad1816a_capture_open, | ||
653 | .close = snd_ad1816a_capture_close, | ||
654 | .ioctl = snd_pcm_lib_ioctl, | ||
655 | .hw_params = snd_ad1816a_hw_params, | ||
656 | .hw_free = snd_ad1816a_hw_free, | ||
657 | .prepare = snd_ad1816a_capture_prepare, | ||
658 | .trigger = snd_ad1816a_capture_trigger, | ||
659 | .pointer = snd_ad1816a_capture_pointer, | ||
660 | }; | ||
661 | |||
662 | static void snd_ad1816a_pcm_free(snd_pcm_t *pcm) | ||
663 | { | ||
664 | ad1816a_t *chip = pcm->private_data; | ||
665 | chip->pcm = NULL; | ||
666 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
667 | } | ||
668 | |||
669 | int snd_ad1816a_pcm(ad1816a_t *chip, int device, snd_pcm_t **rpcm) | ||
670 | { | ||
671 | int error; | ||
672 | snd_pcm_t *pcm; | ||
673 | |||
674 | if ((error = snd_pcm_new(chip->card, "AD1816A", device, 1, 1, &pcm))) | ||
675 | return error; | ||
676 | |||
677 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1816a_playback_ops); | ||
678 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1816a_capture_ops); | ||
679 | |||
680 | pcm->private_data = chip; | ||
681 | pcm->private_free = snd_ad1816a_pcm_free; | ||
682 | pcm->info_flags = (chip->dma1 == chip->dma2 ) ? SNDRV_PCM_INFO_JOINT_DUPLEX : 0; | ||
683 | |||
684 | strcpy(pcm->name, snd_ad1816a_chip_id(chip)); | ||
685 | snd_ad1816a_init(chip); | ||
686 | |||
687 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
688 | snd_dma_isa_data(), | ||
689 | 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); | ||
690 | |||
691 | chip->pcm = pcm; | ||
692 | if (rpcm) | ||
693 | *rpcm = pcm; | ||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | #if 0 /* not used now */ | ||
698 | static void snd_ad1816a_timer_free(snd_timer_t *timer) | ||
699 | { | ||
700 | ad1816a_t *chip = timer->private_data; | ||
701 | chip->timer = NULL; | ||
702 | } | ||
703 | |||
704 | int snd_ad1816a_timer(ad1816a_t *chip, int device, snd_timer_t **rtimer) | ||
705 | { | ||
706 | snd_timer_t *timer; | ||
707 | snd_timer_id_t tid; | ||
708 | int error; | ||
709 | |||
710 | tid.dev_class = SNDRV_TIMER_CLASS_CARD; | ||
711 | tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; | ||
712 | tid.card = chip->card->number; | ||
713 | tid.device = device; | ||
714 | tid.subdevice = 0; | ||
715 | if ((error = snd_timer_new(chip->card, "AD1816A", &tid, &timer)) < 0) | ||
716 | return error; | ||
717 | strcpy(timer->name, snd_ad1816a_chip_id(chip)); | ||
718 | timer->private_data = chip; | ||
719 | timer->private_free = snd_ad1816a_timer_free; | ||
720 | chip->timer = timer; | ||
721 | timer->hw = snd_ad1816a_timer_table; | ||
722 | if (rtimer) | ||
723 | *rtimer = timer; | ||
724 | return 0; | ||
725 | } | ||
726 | #endif /* not used now */ | ||
727 | |||
728 | /* | ||
729 | * | ||
730 | */ | ||
731 | |||
732 | static int snd_ad1816a_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
733 | { | ||
734 | static char *texts[8] = { | ||
735 | "Line", "Mix", "CD", "Synth", "Video", | ||
736 | "Mic", "Phone", | ||
737 | }; | ||
738 | |||
739 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
740 | uinfo->count = 2; | ||
741 | uinfo->value.enumerated.items = 7; | ||
742 | if (uinfo->value.enumerated.item > 6) | ||
743 | uinfo->value.enumerated.item = 6; | ||
744 | strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); | ||
745 | return 0; | ||
746 | } | ||
747 | |||
748 | static int snd_ad1816a_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
749 | { | ||
750 | ad1816a_t *chip = snd_kcontrol_chip(kcontrol); | ||
751 | unsigned long flags; | ||
752 | unsigned short val; | ||
753 | |||
754 | spin_lock_irqsave(&chip->lock, flags); | ||
755 | val = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL); | ||
756 | spin_unlock_irqrestore(&chip->lock, flags); | ||
757 | ucontrol->value.enumerated.item[0] = (val >> 12) & 7; | ||
758 | ucontrol->value.enumerated.item[1] = (val >> 4) & 7; | ||
759 | return 0; | ||
760 | } | ||
761 | |||
762 | static int snd_ad1816a_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
763 | { | ||
764 | ad1816a_t *chip = snd_kcontrol_chip(kcontrol); | ||
765 | unsigned long flags; | ||
766 | unsigned short val; | ||
767 | int change; | ||
768 | |||
769 | if (ucontrol->value.enumerated.item[0] > 6 || | ||
770 | ucontrol->value.enumerated.item[1] > 6) | ||
771 | return -EINVAL; | ||
772 | val = (ucontrol->value.enumerated.item[0] << 12) | | ||
773 | (ucontrol->value.enumerated.item[1] << 4); | ||
774 | spin_lock_irqsave(&chip->lock, flags); | ||
775 | change = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL) != val; | ||
776 | snd_ad1816a_write(chip, AD1816A_ADC_SOURCE_SEL, val); | ||
777 | spin_unlock_irqrestore(&chip->lock, flags); | ||
778 | return change; | ||
779 | } | ||
780 | |||
781 | #define AD1816A_SINGLE(xname, reg, shift, mask, invert) \ | ||
782 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_single, \ | ||
783 | .get = snd_ad1816a_get_single, .put = snd_ad1816a_put_single, \ | ||
784 | .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } | ||
785 | |||
786 | static int snd_ad1816a_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
787 | { | ||
788 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
789 | |||
790 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
791 | uinfo->count = 1; | ||
792 | uinfo->value.integer.min = 0; | ||
793 | uinfo->value.integer.max = mask; | ||
794 | return 0; | ||
795 | } | ||
796 | |||
797 | static int snd_ad1816a_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
798 | { | ||
799 | ad1816a_t *chip = snd_kcontrol_chip(kcontrol); | ||
800 | unsigned long flags; | ||
801 | int reg = kcontrol->private_value & 0xff; | ||
802 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
803 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
804 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
805 | |||
806 | spin_lock_irqsave(&chip->lock, flags); | ||
807 | ucontrol->value.integer.value[0] = (snd_ad1816a_read(chip, reg) >> shift) & mask; | ||
808 | spin_unlock_irqrestore(&chip->lock, flags); | ||
809 | if (invert) | ||
810 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
811 | return 0; | ||
812 | } | ||
813 | |||
814 | static int snd_ad1816a_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
815 | { | ||
816 | ad1816a_t *chip = snd_kcontrol_chip(kcontrol); | ||
817 | unsigned long flags; | ||
818 | int reg = kcontrol->private_value & 0xff; | ||
819 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
820 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
821 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
822 | int change; | ||
823 | unsigned short old_val, val; | ||
824 | |||
825 | val = (ucontrol->value.integer.value[0] & mask); | ||
826 | if (invert) | ||
827 | val = mask - val; | ||
828 | val <<= shift; | ||
829 | spin_lock_irqsave(&chip->lock, flags); | ||
830 | old_val = snd_ad1816a_read(chip, reg); | ||
831 | val = (old_val & ~(mask << shift)) | val; | ||
832 | change = val != old_val; | ||
833 | snd_ad1816a_write(chip, reg, val); | ||
834 | spin_unlock_irqrestore(&chip->lock, flags); | ||
835 | return change; | ||
836 | } | ||
837 | |||
838 | #define AD1816A_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ | ||
839 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_double, \ | ||
840 | .get = snd_ad1816a_get_double, .put = snd_ad1816a_put_double, \ | ||
841 | .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) } | ||
842 | |||
843 | static int snd_ad1816a_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
844 | { | ||
845 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
846 | |||
847 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
848 | uinfo->count = 2; | ||
849 | uinfo->value.integer.min = 0; | ||
850 | uinfo->value.integer.max = mask; | ||
851 | return 0; | ||
852 | } | ||
853 | |||
854 | static int snd_ad1816a_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
855 | { | ||
856 | ad1816a_t *chip = snd_kcontrol_chip(kcontrol); | ||
857 | unsigned long flags; | ||
858 | int reg = kcontrol->private_value & 0xff; | ||
859 | int shift_left = (kcontrol->private_value >> 8) & 0x0f; | ||
860 | int shift_right = (kcontrol->private_value >> 12) & 0x0f; | ||
861 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
862 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
863 | unsigned short val; | ||
864 | |||
865 | spin_lock_irqsave(&chip->lock, flags); | ||
866 | val = snd_ad1816a_read(chip, reg); | ||
867 | ucontrol->value.integer.value[0] = (val >> shift_left) & mask; | ||
868 | ucontrol->value.integer.value[1] = (val >> shift_right) & mask; | ||
869 | spin_unlock_irqrestore(&chip->lock, flags); | ||
870 | if (invert) { | ||
871 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
872 | ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; | ||
873 | } | ||
874 | return 0; | ||
875 | } | ||
876 | |||
877 | static int snd_ad1816a_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
878 | { | ||
879 | ad1816a_t *chip = snd_kcontrol_chip(kcontrol); | ||
880 | unsigned long flags; | ||
881 | int reg = kcontrol->private_value & 0xff; | ||
882 | int shift_left = (kcontrol->private_value >> 8) & 0x0f; | ||
883 | int shift_right = (kcontrol->private_value >> 12) & 0x0f; | ||
884 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
885 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
886 | int change; | ||
887 | unsigned short old_val, val1, val2; | ||
888 | |||
889 | val1 = ucontrol->value.integer.value[0] & mask; | ||
890 | val2 = ucontrol->value.integer.value[1] & mask; | ||
891 | if (invert) { | ||
892 | val1 = mask - val1; | ||
893 | val2 = mask - val2; | ||
894 | } | ||
895 | val1 <<= shift_left; | ||
896 | val2 <<= shift_right; | ||
897 | spin_lock_irqsave(&chip->lock, flags); | ||
898 | old_val = snd_ad1816a_read(chip, reg); | ||
899 | val1 = (old_val & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; | ||
900 | change = val1 != old_val; | ||
901 | snd_ad1816a_write(chip, reg, val1); | ||
902 | spin_unlock_irqrestore(&chip->lock, flags); | ||
903 | return change; | ||
904 | } | ||
905 | |||
906 | static snd_kcontrol_new_t snd_ad1816a_controls[] = { | ||
907 | AD1816A_DOUBLE("Master Playback Switch", AD1816A_MASTER_ATT, 15, 7, 1, 1), | ||
908 | AD1816A_DOUBLE("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 1), | ||
909 | AD1816A_DOUBLE("PCM Playback Switch", AD1816A_VOICE_ATT, 15, 7, 1, 1), | ||
910 | AD1816A_DOUBLE("PCM Playback Volume", AD1816A_VOICE_ATT, 8, 0, 63, 1), | ||
911 | AD1816A_DOUBLE("Line Playback Switch", AD1816A_LINE_GAIN_ATT, 15, 7, 1, 1), | ||
912 | AD1816A_DOUBLE("Line Playback Volume", AD1816A_LINE_GAIN_ATT, 8, 0, 31, 1), | ||
913 | AD1816A_DOUBLE("CD Playback Switch", AD1816A_CD_GAIN_ATT, 15, 7, 1, 1), | ||
914 | AD1816A_DOUBLE("CD Playback Volume", AD1816A_CD_GAIN_ATT, 8, 0, 31, 1), | ||
915 | AD1816A_DOUBLE("Synth Playback Switch", AD1816A_SYNTH_GAIN_ATT, 15, 7, 1, 1), | ||
916 | AD1816A_DOUBLE("Synth Playback Volume", AD1816A_SYNTH_GAIN_ATT, 8, 0, 31, 1), | ||
917 | AD1816A_DOUBLE("FM Playback Switch", AD1816A_FM_ATT, 15, 7, 1, 1), | ||
918 | AD1816A_DOUBLE("FM Playback Volume", AD1816A_FM_ATT, 8, 0, 63, 1), | ||
919 | AD1816A_SINGLE("Mic Playback Switch", AD1816A_MIC_GAIN_ATT, 15, 1, 1), | ||
920 | AD1816A_SINGLE("Mic Playback Volume", AD1816A_MIC_GAIN_ATT, 8, 31, 1), | ||
921 | AD1816A_SINGLE("Mic Boost", AD1816A_MIC_GAIN_ATT, 14, 1, 0), | ||
922 | AD1816A_DOUBLE("Video Playback Switch", AD1816A_VID_GAIN_ATT, 15, 7, 1, 1), | ||
923 | AD1816A_DOUBLE("Video Playback Volume", AD1816A_VID_GAIN_ATT, 8, 0, 31, 1), | ||
924 | AD1816A_SINGLE("Phone Capture Switch", AD1816A_PHONE_IN_GAIN_ATT, 15, 1, 1), | ||
925 | AD1816A_SINGLE("Phone Capture Volume", AD1816A_PHONE_IN_GAIN_ATT, 0, 15, 1), | ||
926 | AD1816A_SINGLE("Phone Playback Switch", AD1816A_PHONE_OUT_ATT, 7, 1, 1), | ||
927 | AD1816A_SINGLE("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1), | ||
928 | { | ||
929 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
930 | .name = "Capture Source", | ||
931 | .info = snd_ad1816a_info_mux, | ||
932 | .get = snd_ad1816a_get_mux, | ||
933 | .put = snd_ad1816a_put_mux, | ||
934 | }, | ||
935 | AD1816A_DOUBLE("Capture Switch", AD1816A_ADC_PGA, 15, 7, 1, 1), | ||
936 | AD1816A_DOUBLE("Capture Volume", AD1816A_ADC_PGA, 8, 0, 15, 0), | ||
937 | AD1816A_SINGLE("3D Control - Switch", AD1816A_3D_PHAT_CTRL, 15, 1, 1), | ||
938 | AD1816A_SINGLE("3D Control - Level", AD1816A_3D_PHAT_CTRL, 0, 15, 0), | ||
939 | }; | ||
940 | |||
941 | int snd_ad1816a_mixer(ad1816a_t *chip) | ||
942 | { | ||
943 | snd_card_t *card; | ||
944 | unsigned int idx; | ||
945 | int err; | ||
946 | |||
947 | snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); | ||
948 | |||
949 | card = chip->card; | ||
950 | |||
951 | strcpy(card->mixername, snd_ad1816a_chip_id(chip)); | ||
952 | |||
953 | for (idx = 0; idx < ARRAY_SIZE(snd_ad1816a_controls); idx++) { | ||
954 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1816a_controls[idx], chip))) < 0) | ||
955 | return err; | ||
956 | } | ||
957 | return 0; | ||
958 | } | ||
959 | |||
960 | EXPORT_SYMBOL(snd_ad1816a_create); | ||
961 | EXPORT_SYMBOL(snd_ad1816a_pcm); | ||
962 | EXPORT_SYMBOL(snd_ad1816a_mixer); | ||
963 | |||
964 | static int __init alsa_ad1816a_init(void) | ||
965 | { | ||
966 | return 0; | ||
967 | } | ||
968 | |||
969 | static void __exit alsa_ad1816a_exit(void) | ||
970 | { | ||
971 | } | ||
972 | |||
973 | module_init(alsa_ad1816a_init) | ||
974 | module_exit(alsa_ad1816a_exit) | ||