aboutsummaryrefslogtreecommitdiffstats
path: root/sound/isa/ad1848/ad1848_lib.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/isa/ad1848/ad1848_lib.c')
-rw-r--r--sound/isa/ad1848/ad1848_lib.c487
1 files changed, 0 insertions, 487 deletions
diff --git a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c
index aa803d38a8ad..e69de29bb2d1 100644
--- a/sound/isa/ad1848/ad1848_lib.c
+++ b/sound/isa/ad1848/ad1848_lib.c
@@ -1,487 +0,0 @@
1/*
2 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
3 * Routines for control of AD1848/AD1847/CS4248
4 *
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#define SNDRV_MAIN_OBJECT_FILE
23#include <linux/delay.h>
24#include <linux/init.h>
25#include <linux/interrupt.h>
26#include <linux/slab.h>
27#include <linux/ioport.h>
28#include <sound/core.h>
29#include <sound/ad1848.h>
30#include <sound/control.h>
31#include <sound/tlv.h>
32#include <sound/pcm_params.h>
33
34#include <asm/io.h>
35#include <asm/dma.h>
36
37MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
38MODULE_DESCRIPTION("Routines for control of AD1848/AD1847/CS4248");
39MODULE_LICENSE("GPL");
40
41#if 0
42#define SNDRV_DEBUG_MCE
43#endif
44
45/*
46 * Some variables
47 */
48
49static unsigned char snd_ad1848_original_image[16] =
50{
51 0x00, /* 00 - lic */
52 0x00, /* 01 - ric */
53 0x9f, /* 02 - la1ic */
54 0x9f, /* 03 - ra1ic */
55 0x9f, /* 04 - la2ic */
56 0x9f, /* 05 - ra2ic */
57 0xbf, /* 06 - loc */
58 0xbf, /* 07 - roc */
59 0x20, /* 08 - dfr */
60 AD1848_AUTOCALIB, /* 09 - ic */
61 0x00, /* 0a - pc */
62 0x00, /* 0b - ti */
63 0x00, /* 0c - mi */
64 0x00, /* 0d - lbc */
65 0x00, /* 0e - dru */
66 0x00, /* 0f - drl */
67};
68
69/*
70 * Basic I/O functions
71 */
72
73static void snd_ad1848_wait(struct snd_wss *chip)
74{
75 int timeout;
76
77 for (timeout = 250; timeout > 0; timeout--) {
78 if ((inb(chip->port + CS4231P(REGSEL)) & AD1848_INIT) == 0)
79 break;
80 udelay(100);
81 }
82}
83
84void snd_ad1848_out(struct snd_wss *chip,
85 unsigned char reg,
86 unsigned char value)
87{
88 snd_ad1848_wait(chip);
89#ifdef CONFIG_SND_DEBUG
90 if (inb(chip->port + CS4231P(REGSEL)) & AD1848_INIT)
91 snd_printk(KERN_WARNING "auto calibration time out - "
92 "reg = 0x%x, value = 0x%x\n", reg, value);
93#endif
94 outb(chip->mce_bit | reg, chip->port + CS4231P(REGSEL));
95 outb(chip->image[reg] = value, chip->port + CS4231P(REG));
96 mb();
97 snd_printdd("codec out - reg 0x%x = 0x%x\n",
98 chip->mce_bit | reg, value);
99}
100
101EXPORT_SYMBOL(snd_ad1848_out);
102
103static unsigned char snd_ad1848_in(struct snd_wss *chip, unsigned char reg)
104{
105 snd_ad1848_wait(chip);
106#ifdef CONFIG_SND_DEBUG
107 if (inb(chip->port + CS4231P(REGSEL)) & AD1848_INIT)
108 snd_printk(KERN_WARNING "auto calibration time out - "
109 "reg = 0x%x\n", reg);
110#endif
111 outb(chip->mce_bit | reg, chip->port + CS4231P(REGSEL));
112 mb();
113 return inb(chip->port + CS4231P(REG));
114}
115
116#if 0
117
118static void snd_ad1848_debug(struct snd_wss *chip)
119{
120 printk(KERN_DEBUG "AD1848 REGS: INDEX = 0x%02x ", inb(chip->port + CS4231P(REGSEL)));
121 printk(KERN_DEBUG " STATUS = 0x%02x\n", inb(chip->port + CS4231P(STATUS)));
122 printk(KERN_DEBUG " 0x00: left input = 0x%02x ", snd_ad1848_in(chip, 0x00));
123 printk(KERN_DEBUG " 0x08: playback format = 0x%02x\n", snd_ad1848_in(chip, 0x08));
124 printk(KERN_DEBUG " 0x01: right input = 0x%02x ", snd_ad1848_in(chip, 0x01));
125 printk(KERN_DEBUG " 0x09: iface (CFIG 1) = 0x%02x\n", snd_ad1848_in(chip, 0x09));
126 printk(KERN_DEBUG " 0x02: AUXA left = 0x%02x ", snd_ad1848_in(chip, 0x02));
127 printk(KERN_DEBUG " 0x0a: pin control = 0x%02x\n", snd_ad1848_in(chip, 0x0a));
128 printk(KERN_DEBUG " 0x03: AUXA right = 0x%02x ", snd_ad1848_in(chip, 0x03));
129 printk(KERN_DEBUG " 0x0b: init & status = 0x%02x\n", snd_ad1848_in(chip, 0x0b));
130 printk(KERN_DEBUG " 0x04: AUXB left = 0x%02x ", snd_ad1848_in(chip, 0x04));
131 printk(KERN_DEBUG " 0x0c: revision & mode = 0x%02x\n", snd_ad1848_in(chip, 0x0c));
132 printk(KERN_DEBUG " 0x05: AUXB right = 0x%02x ", snd_ad1848_in(chip, 0x05));
133 printk(KERN_DEBUG " 0x0d: loopback = 0x%02x\n", snd_ad1848_in(chip, 0x0d));
134 printk(KERN_DEBUG " 0x06: left output = 0x%02x ", snd_ad1848_in(chip, 0x06));
135 printk(KERN_DEBUG " 0x0e: data upr count = 0x%02x\n", snd_ad1848_in(chip, 0x0e));
136 printk(KERN_DEBUG " 0x07: right output = 0x%02x ", snd_ad1848_in(chip, 0x07));
137 printk(KERN_DEBUG " 0x0f: data lwr count = 0x%02x\n", snd_ad1848_in(chip, 0x0f));
138}
139
140#endif
141
142/*
143 * AD1848 detection / MCE routines
144 */
145
146static void snd_ad1848_mce_up(struct snd_wss *chip)
147{
148 unsigned long flags;
149 int timeout;
150
151 snd_ad1848_wait(chip);
152#ifdef CONFIG_SND_DEBUG
153 if (inb(chip->port + CS4231P(REGSEL)) & AD1848_INIT)
154 snd_printk(KERN_WARNING "mce_up - auto calibration time out (0)\n");
155#endif
156 spin_lock_irqsave(&chip->reg_lock, flags);
157 chip->mce_bit |= AD1848_MCE;
158 timeout = inb(chip->port + CS4231P(REGSEL));
159 if (timeout == 0x80)
160 snd_printk(KERN_WARNING "mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port);
161 if (!(timeout & AD1848_MCE))
162 outb(chip->mce_bit | (timeout & 0x1f),
163 chip->port + CS4231P(REGSEL));
164 spin_unlock_irqrestore(&chip->reg_lock, flags);
165}
166
167static void snd_ad1848_mce_down(struct snd_wss *chip)
168{
169 unsigned long flags, timeout;
170 int reg;
171
172 spin_lock_irqsave(&chip->reg_lock, flags);
173 for (timeout = 5; timeout > 0; timeout--)
174 inb(chip->port + CS4231P(REGSEL));
175 /* end of cleanup sequence */
176 for (timeout = 12000;
177 timeout > 0 && (inb(chip->port + CS4231P(REGSEL)) & AD1848_INIT);
178 timeout--)
179 udelay(100);
180
181 snd_printdd("(1) timeout = %ld\n", timeout);
182
183#ifdef CONFIG_SND_DEBUG
184 if (inb(chip->port + CS4231P(REGSEL)) & AD1848_INIT)
185 snd_printk(KERN_WARNING
186 "mce_down [0x%lx] - auto calibration time out (0)\n",
187 chip->port + CS4231P(REGSEL));
188#endif
189
190 chip->mce_bit &= ~AD1848_MCE;
191 reg = inb(chip->port + CS4231P(REGSEL));
192 outb(chip->mce_bit | (reg & 0x1f), chip->port + CS4231P(REGSEL));
193 if (reg == 0x80)
194 snd_printk(KERN_WARNING "mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port);
195 if ((reg & AD1848_MCE) == 0) {
196 spin_unlock_irqrestore(&chip->reg_lock, flags);
197 return;
198 }
199
200 /*
201 * Wait for auto-calibration (AC) process to finish, i.e. ACI to go low.
202 * It may take up to 5 sample periods (at most 907 us @ 5.5125 kHz) for
203 * the process to _start_, so it is important to wait at least that long
204 * before checking. Otherwise we might think AC has finished when it
205 * has in fact not begun. It could take 128 (no AC) or 384 (AC) cycles
206 * for ACI to drop. This gives a wait of at most 70 ms with a more
207 * typical value of 3-9 ms.
208 */
209 timeout = jiffies + msecs_to_jiffies(250);
210 do {
211 spin_unlock_irqrestore(&chip->reg_lock, flags);
212 msleep(1);
213 spin_lock_irqsave(&chip->reg_lock, flags);
214 reg = snd_ad1848_in(chip, AD1848_TEST_INIT) &
215 AD1848_CALIB_IN_PROGRESS;
216 } while (reg && time_before(jiffies, timeout));
217 spin_unlock_irqrestore(&chip->reg_lock, flags);
218 if (reg)
219 snd_printk(KERN_ERR
220 "mce_down - auto calibration time out (2)\n");
221
222 snd_printdd("(4) jiffies = %lu\n", jiffies);
223 snd_printd("mce_down - exit = 0x%x\n",
224 inb(chip->port + CS4231P(REGSEL)));
225}
226
227static irqreturn_t snd_ad1848_interrupt(int irq, void *dev_id)
228{
229 struct snd_wss *chip = dev_id;
230
231 if ((chip->mode & WSS_MODE_PLAY) && chip->playback_substream)
232 snd_pcm_period_elapsed(chip->playback_substream);
233 if ((chip->mode & WSS_MODE_RECORD) && chip->capture_substream)
234 snd_pcm_period_elapsed(chip->capture_substream);
235 outb(0, chip->port + CS4231P(STATUS)); /* clear global interrupt bit */
236 return IRQ_HANDLED;
237}
238
239/*
240
241 */
242
243static void snd_ad1848_thinkpad_twiddle(struct snd_wss *chip, int on)
244{
245 int tmp;
246
247 if (!chip->thinkpad_flag) return;
248
249 outb(0x1c, AD1848_THINKPAD_CTL_PORT1);
250 tmp = inb(AD1848_THINKPAD_CTL_PORT2);
251
252 if (on)
253 /* turn it on */
254 tmp |= AD1848_THINKPAD_CS4248_ENABLE_BIT;
255 else
256 /* turn it off */
257 tmp &= ~AD1848_THINKPAD_CS4248_ENABLE_BIT;
258
259 outb(tmp, AD1848_THINKPAD_CTL_PORT2);
260
261}
262
263#ifdef CONFIG_PM
264static void snd_ad1848_suspend(struct snd_wss *chip)
265{
266 snd_pcm_suspend_all(chip->pcm);
267 if (chip->thinkpad_flag)
268 snd_ad1848_thinkpad_twiddle(chip, 0);
269}
270
271static void snd_ad1848_resume(struct snd_wss *chip)
272{
273 int i;
274
275 if (chip->thinkpad_flag)
276 snd_ad1848_thinkpad_twiddle(chip, 1);
277
278 /* clear any pendings IRQ */
279 inb(chip->port + CS4231P(STATUS));
280 outb(0, chip->port + CS4231P(STATUS));
281 mb();
282
283 snd_ad1848_mce_down(chip);
284 for (i = 0; i < 16; i++)
285 snd_ad1848_out(chip, i, chip->image[i]);
286 snd_ad1848_mce_up(chip);
287 snd_ad1848_mce_down(chip);
288}
289#endif /* CONFIG_PM */
290
291static int snd_ad1848_probe(struct snd_wss *chip)
292{
293 unsigned long flags;
294 int i, id, rev, ad1847;
295 unsigned char *ptr;
296
297#if 0
298 snd_ad1848_debug(chip);
299#endif
300 id = ad1847 = 0;
301 for (i = 0; i < 1000; i++) {
302 mb();
303 if (inb(chip->port + CS4231P(REGSEL)) & AD1848_INIT)
304 udelay(500);
305 else {
306 spin_lock_irqsave(&chip->reg_lock, flags);
307 snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00);
308 snd_ad1848_out(chip, AD1848_LEFT_INPUT, 0xaa);
309 snd_ad1848_out(chip, AD1848_RIGHT_INPUT, 0x45);
310 rev = snd_ad1848_in(chip, AD1848_RIGHT_INPUT);
311 if (rev == 0x65) {
312 spin_unlock_irqrestore(&chip->reg_lock, flags);
313 id = 1;
314 ad1847 = 1;
315 break;
316 }
317 if (snd_ad1848_in(chip, AD1848_LEFT_INPUT) == 0xaa && rev == 0x45) {
318 spin_unlock_irqrestore(&chip->reg_lock, flags);
319 id = 1;
320 break;
321 }
322 spin_unlock_irqrestore(&chip->reg_lock, flags);
323 }
324 }
325 if (id != 1)
326 return -ENODEV; /* no valid device found */
327 if (chip->hardware == WSS_HW_DETECT) {
328 if (ad1847) {
329 chip->hardware = WSS_HW_AD1847;
330 } else {
331 chip->hardware = WSS_HW_AD1848;
332 rev = snd_ad1848_in(chip, AD1848_MISC_INFO);
333 if (rev & 0x80) {
334 chip->hardware = WSS_HW_CS4248;
335 } else if ((rev & 0x0f) == 0x0a) {
336 snd_ad1848_out(chip, AD1848_MISC_INFO, 0x40);
337 for (i = 0; i < 16; ++i) {
338 if (snd_ad1848_in(chip, i) != snd_ad1848_in(chip, i + 16)) {
339 chip->hardware = WSS_HW_CMI8330;
340 break;
341 }
342 }
343 snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00);
344 }
345 }
346 }
347 spin_lock_irqsave(&chip->reg_lock, flags);
348 inb(chip->port + CS4231P(STATUS)); /* clear any pendings IRQ */
349 outb(0, chip->port + CS4231P(STATUS));
350 mb();
351 spin_unlock_irqrestore(&chip->reg_lock, flags);
352
353 chip->image[AD1848_MISC_INFO] = 0x00;
354 chip->image[AD1848_IFACE_CTRL] =
355 (chip->image[AD1848_IFACE_CTRL] & ~AD1848_SINGLE_DMA) | AD1848_SINGLE_DMA;
356 ptr = (unsigned char *) &chip->image;
357 snd_ad1848_mce_down(chip);
358 spin_lock_irqsave(&chip->reg_lock, flags);
359 for (i = 0; i < 16; i++) /* ok.. fill all AD1848 registers */
360 snd_ad1848_out(chip, i, *ptr++);
361 spin_unlock_irqrestore(&chip->reg_lock, flags);
362 snd_ad1848_mce_up(chip);
363 /* init needed for WSS pcm */
364 spin_lock_irqsave(&chip->reg_lock, flags);
365 chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE |
366 AD1848_PLAYBACK_PIO |
367 AD1848_CAPTURE_ENABLE |
368 AD1848_CAPTURE_PIO |
369 AD1848_CALIB_MODE);
370 chip->image[AD1848_IFACE_CTRL] |= AD1848_AUTOCALIB;
371 snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]);
372 spin_unlock_irqrestore(&chip->reg_lock, flags);
373 snd_ad1848_mce_down(chip);
374 return 0; /* all things are ok.. */
375}
376
377/*
378
379 */
380
381static int snd_ad1848_free(struct snd_wss *chip)
382{
383 release_and_free_resource(chip->res_port);
384 if (chip->irq >= 0)
385 free_irq(chip->irq, (void *) chip);
386 if (chip->dma1 >= 0) {
387 snd_dma_disable(chip->dma1);
388 free_dma(chip->dma1);
389 }
390 kfree(chip);
391 return 0;
392}
393
394static int snd_ad1848_dev_free(struct snd_device *device)
395{
396 struct snd_wss *chip = device->device_data;
397 return snd_ad1848_free(chip);
398}
399
400int snd_ad1848_create(struct snd_card *card,
401 unsigned long port,
402 int irq, int dma,
403 unsigned short hardware,
404 struct snd_wss **rchip)
405{
406 static struct snd_device_ops ops = {
407 .dev_free = snd_ad1848_dev_free,
408 };
409 struct snd_wss *chip;
410 int err;
411
412 *rchip = NULL;
413 chip = kzalloc(sizeof(*chip), GFP_KERNEL);
414 if (chip == NULL)
415 return -ENOMEM;
416 spin_lock_init(&chip->reg_lock);
417 chip->card = card;
418 chip->port = port;
419 chip->irq = -1;
420 chip->dma1 = -1;
421 chip->dma2 = -1;
422 chip->single_dma = 1;
423 chip->hardware = hardware;
424 memcpy(&chip->image, &snd_ad1848_original_image, sizeof(snd_ad1848_original_image));
425
426 if ((chip->res_port = request_region(port, 4, "AD1848")) == NULL) {
427 snd_printk(KERN_ERR "ad1848: can't grab port 0x%lx\n", port);
428 snd_ad1848_free(chip);
429 return -EBUSY;
430 }
431 if (request_irq(irq, snd_ad1848_interrupt, IRQF_DISABLED, "AD1848", (void *) chip)) {
432 snd_printk(KERN_ERR "ad1848: can't grab IRQ %d\n", irq);
433 snd_ad1848_free(chip);
434 return -EBUSY;
435 }
436 chip->irq = irq;
437 if (request_dma(dma, "AD1848")) {
438 snd_printk(KERN_ERR "ad1848: can't grab DMA %d\n", dma);
439 snd_ad1848_free(chip);
440 return -EBUSY;
441 }
442 chip->dma1 = dma;
443 chip->dma2 = dma;
444
445 if (hardware == WSS_HW_THINKPAD) {
446 chip->thinkpad_flag = 1;
447 chip->hardware = WSS_HW_DETECT; /* reset */
448 snd_ad1848_thinkpad_twiddle(chip, 1);
449 }
450
451 if (snd_ad1848_probe(chip) < 0) {
452 snd_ad1848_free(chip);
453 return -ENODEV;
454 }
455
456 /* Register device */
457 if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
458 snd_ad1848_free(chip);
459 return err;
460 }
461
462#ifdef CONFIG_PM
463 chip->suspend = snd_ad1848_suspend;
464 chip->resume = snd_ad1848_resume;
465#endif
466
467 *rchip = chip;
468 return 0;
469}
470
471EXPORT_SYMBOL(snd_ad1848_create);
472
473/*
474 * INIT part
475 */
476
477static int __init alsa_ad1848_init(void)
478{
479 return 0;
480}
481
482static void __exit alsa_ad1848_exit(void)
483{
484}
485
486module_init(alsa_ad1848_init)
487module_exit(alsa_ad1848_exit)