patch-2.3.99-pre2 linux/drivers/sound/ad1816.c
Next file: linux/drivers/sound/bin2hex.c
Previous file: linux/drivers/sound/ac97_codec.c
Back to the patch index
Back to the overall index
- Lines: 710
- Date:
Sat Mar 18 16:51:35 2000
- Orig file:
v2.3.99-pre1/linux/drivers/sound/ad1816.c
- Orig date:
Fri Mar 10 16:40:43 2000
diff -u --recursive --new-file v2.3.99-pre1/linux/drivers/sound/ad1816.c linux/drivers/sound/ad1816.c
@@ -6,16 +6,8 @@
*
* Based on the CS4232/AD1848 driver Copyright (C) by Hannu Savolainen 1993-1996
*
- * This software is still under development. New versions of the driver
- * are available from:
- * http://www.student.informatik.tu-darmstadt.de/~tek/projects/linux.html
- * or http://www.tu-darmstadt.de/~tek01/projects/linux.html
- *
- * Please report any bugs to: tek@rbg.informatik.tu-darmstadt.de
*
- *
- * version: 1.3
- * cvs: $Header: /home/tek/CVSROOT/sound22/ad1816.c,v 1.3 1999/04/18 16:41:41 tek Exp $
+ * version: 1.3.1
* status: experimental
* date: 1999/4/18
*
@@ -34,18 +26,23 @@
* required by some Aztech/Newcom SC-16 cards. 1999/04/18
*
* Christoph Hellwig: Adapted to module_init/module_exit. 2000/03/03
+ *
+ * Christoph Hellwig: Added isapnp support 2000/03/15
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/isapnp.h>
#include <linux/stddef.h>
+
#include "soundmodule.h"
#include "sound_config.h"
#define DEBUGNOISE(x)
-#define DEBUGINFO(x)
-#define DEBUGLOG(x) x
-#define DEBUGWARN(x) x
+#define DEBUGINFO(x)
+#define DEBUGLOG(x)
+#define DEBUGWARN(x)
#define CHECK_FOR_POWER { int timeout=100; \
while (timeout > 0 && (inb(devc->base)&0x80)!= 0x80) {\
@@ -356,7 +353,7 @@
unsigned long flags;
ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc;
- DEBUGINFO (printk("ad1816: trigger called! (devc=%d,devc->base=%d\n",devc,devc->base));
+ DEBUGINFO (printk("ad1816: trigger called! (devc=%d,devc->base=%d\n", devc, devc->base));
/* mode may have changed */
@@ -427,70 +424,38 @@
{
ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc;
- static struct format_tbl
- {
+ static struct format_tbl {
int format;
unsigned char bits;
- }
- format2bits[] =
- {
- {
- 0, 0
- }
- ,
- {
- AFMT_MU_LAW, 1
- }
- ,
- {
- AFMT_A_LAW, 3
- }
- ,
- {
- AFMT_IMA_ADPCM, 0
- }
- ,
- {
- AFMT_U8, 0
- }
- ,
- {
- AFMT_S16_LE, 2
- }
- ,
- {
- AFMT_S16_BE, 6
- }
- ,
- {
- AFMT_S8, 0
- }
- ,
- {
- AFMT_U16_LE, 0
- }
- ,
- {
- AFMT_U16_BE, 0
- }
- };
+ } format2bits[] = {
+ { 0, 0 },
+ { AFMT_MU_LAW, 1 },
+ { AFMT_A_LAW, 3 },
+ { AFMT_IMA_ADPCM, 0 },
+ { AFMT_U8, 0 },
+ { AFMT_S16_LE, 2 },
+ { AFMT_S16_BE, 6 },
+ { AFMT_S8, 0 },
+ { AFMT_U16_LE, 0 },
+ { AFMT_U16_BE, 0 }
+ };
+
int i, n = sizeof (format2bits) / sizeof (struct format_tbl);
/* return current format */
- if (arg == 0) {
+ if (arg == 0)
return devc->audio_format;
- }
devc->audio_format = arg;
/* search matching format bits */
- for (i = 0; i < n; i++) {
+ for (i = 0; i < n; i++)
if (format2bits[i].format == arg) {
devc->format_bits = format2bits[i].bits;
devc->audio_format = arg;
return arg;
}
- }
+
/* Still hanging here. Something must be terribly wrong */
devc->format_bits = 0;
return devc->audio_format = AFMT_U8;
@@ -500,9 +465,8 @@
{
ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc;
- if (arg != 1 && arg != 2) {
+ if (arg != 1 && arg != 2)
return devc->channels;
- }
devc->channels = arg;
return arg;
@@ -515,9 +479,8 @@
unsigned long flags;
/* is device number valid ? */
- if (dev < 0 || dev >= num_audiodevs) {
+ if (dev < 0 || dev >= num_audiodevs)
return -(ENXIO);
- }
/* get device info of this dev */
devc = (ad1816_info *) audio_devs[dev]->devc;
@@ -600,10 +563,10 @@
static void ad1816_interrupt (int irq, void *dev_id, struct pt_regs *dummy)
{
- unsigned char status;
- ad1816_info *devc;
- int dev;
- unsigned long flags;
+ unsigned char status;
+ ad1816_info *devc;
+ int dev;
+ unsigned long flags;
if (irq < 0 || irq > 15) {
@@ -632,18 +595,15 @@
devc->irq_ok=1;
- if (status == 0) {
+ if (status == 0)
DEBUGWARN(printk ("ad1816: interrupt: Got interrupt, but no reason?\n"));
- }
- if (devc->opened && (devc->audio_mode & PCM_ENABLE_INPUT)
- && (status&64)){
+
+ if (devc->opened && (devc->audio_mode & PCM_ENABLE_INPUT) && (status&64))
DMAbuf_inputintr (dev);
- }
- if (devc->opened && (devc->audio_mode & PCM_ENABLE_OUTPUT) &&
- (status & 128)) {
+ if (devc->opened && (devc->audio_mode & PCM_ENABLE_OUTPUT) && (status & 128))
DMAbuf_outputintr (dev, 1);
- }
+
restore_flags(flags);
}
@@ -659,7 +619,7 @@
};
static char mix_cvt[101] = {
- 0, 0,3,7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42,
+ 0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42,
43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65,
65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79,
80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90,
@@ -705,23 +665,23 @@
static unsigned short default_mixer_levels[SOUND_MIXER_NRDEVICES] =
{
- 0x4343, /* Master Volume */
- 0x3232, /* Bass */
- 0x3232, /* Treble */
- 0x0000, /* FM */
- 0x4343, /* PCM */
- 0x0000, /* PC Speaker */
- 0x0000, /* Ext Line */
- 0x0000, /* Mic */
- 0x0000, /* CD */
- 0x0000, /* Recording monitor */
- 0x0000, /* SB PCM */
- 0x0000, /* Recording level */
- 0x0000, /* Input gain */
- 0x0000, /* Output gain */
- 0x0000, /* Line1 */
- 0x0000, /* Line2 */
- 0x0000 /* Line3 (usually line in)*/
+ 0x4343, /* Master Volume */
+ 0x3232, /* Bass */
+ 0x3232, /* Treble */
+ 0x0000, /* FM */
+ 0x4343, /* PCM */
+ 0x0000, /* PC Speaker */
+ 0x0000, /* Ext Line */
+ 0x0000, /* Mic */
+ 0x0000, /* CD */
+ 0x0000, /* Recording monitor */
+ 0x0000, /* SB PCM */
+ 0x0000, /* Recording level */
+ 0x0000, /* Input gain */
+ 0x0000, /* Output gain */
+ 0x0000, /* Line1 */
+ 0x0000, /* Line2 */
+ 0x0000 /* Line3 (usually line in)*/
};
#define LEFT_CHN 0
@@ -739,29 +699,24 @@
n = 0;
/* Count selected device bits */
- for (i = 0; i < 32; i++) {
- if (mask & (1 << i)) {
+ for (i = 0; i < 32; i++)
+ if (mask & (1 << i))
n++;
- }
- }
- if (n == 0) {
+ if (n == 0)
mask = SOUND_MASK_MIC;
- } else if (n != 1) { /* Too many devices selected */
+ else if (n != 1) { /* Too many devices selected */
/* Filter out active settings */
mask &= ~devc->recmask;
n = 0;
/* Count selected device bits */
- for (i = 0; i < 32; i++) {
- if (mask & (1 << i)) {
+ for (i = 0; i < 32; i++)
+ if (mask & (1 << i))
n++;
- }
- }
- if (n != 1) {
+ if (n != 1)
mask = SOUND_MASK_MIC;
- }
}
switch (mask) {
@@ -810,9 +765,8 @@
/* Reverse polarity*/
- if (mix_devices[dev][chn].polarity == 1) {
+ if (mix_devices[dev][chn].polarity == 1)
newval = 100 - newval;
- }
mask = (1 << mix_devices[dev][chn].nbits) - 1;
shift = mix_devices[dev][chn].bitpos;
@@ -830,12 +784,10 @@
DEBUGINFO(printk("ad1816: mixer_get called!\n"));
/* range check + supported mixer check */
- if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES ) {
+ if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES )
return (-(EINVAL));
- }
- if (!((1 << dev) & devc->supported_devices)) {
+ if (!((1 << dev) & devc->supported_devices))
return -(EINVAL);
- }
return devc->levels[dev];
}
@@ -853,27 +805,21 @@
DEBUGINFO(printk("ad1816: mixer_set called!\n"));
- if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES ) {
+ if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES )
return -(EINVAL);
- }
- if (left > 100) {
+ if (left > 100)
left = 100;
- }
- if (left < 0) {
+ if (left < 0)
left = 0;
- }
- if (right > 100) {
+ if (right > 100)
right = 100;
- }
- if (right < 0) {
+ if (right < 0)
right = 0;
- }
/* Mono control */
- if (mix_devices[dev][RIGHT_CHN].nbits == 0) {
+ if (mix_devices[dev][RIGHT_CHN].nbits == 0)
right = left;
- }
retvol = left | (right << 8);
/* Scale it */
@@ -882,14 +828,12 @@
right = mix_cvt[right];
/* reject all mixers that are not supported */
- if (!(devc->supported_devices & (1 << dev))) {
+ if (!(devc->supported_devices & (1 << dev)))
return -(EINVAL);
- }
/* sanity check */
- if (mix_devices[dev][LEFT_CHN].nbits == 0) {
+ if (mix_devices[dev][LEFT_CHN].nbits == 0)
return -(EINVAL);
- }
/* keep precise volume internal */
devc->levels[dev] = retvol;
@@ -905,11 +849,10 @@
if ( regoffs==5 || regoffs==14 || regoffs==15 ||
regoffs==16 || regoffs==17 || regoffs==18 ||
regoffs==19 || regoffs==39) {
- if (left==0) {
+ if (left==0)
valmute |= 0x8000;
- } else {
+ else
valmute &= ~0x8000;
- }
}
ad_write (devc, regoffs, valmute); /* mute */
@@ -918,9 +861,9 @@
*/
/* Was just a mono channel */
- if (mix_devices[dev][RIGHT_CHN].nbits == 0) {
+ if (mix_devices[dev][RIGHT_CHN].nbits == 0)
return retvol;
- }
+
regoffs = mix_devices[dev][RIGHT_CHN].regno;
val = ad_read (devc, regoffs);
change_bits (&val, dev, RIGHT_CHN, right);
@@ -929,11 +872,10 @@
if ( regoffs==5 || regoffs==14 || regoffs==15 ||
regoffs==16 || regoffs==17 || regoffs==18 ||
regoffs==19 || regoffs==39) {
- if (right==0) {
+ if (right==0)
valmute |= 0x80;
- } else {
+ else
valmute &= ~0x80;
- }
}
ad_write (devc, regoffs, valmute); /* mute */
@@ -968,11 +910,9 @@
devc->supported_rec_devices = REC_DEVICES;
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- if (devc->supported_devices & (1 << i)) {
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (devc->supported_devices & (1 << i))
ad1816_mixer_set (devc, i, default_mixer_levels[i]);
- }
- }
ad1816_set_recmask (devc, SOUND_MASK_MIC);
}
@@ -992,22 +932,19 @@
switch (cmd & 0xff){
case SOUND_MIXER_RECSRC:
- if (get_user(val, (int *)arg)) {
+ if (get_user(val, (int *)arg))
return -EFAULT;
- }
val=ad1816_set_recmask (devc, val);
- return put_user(val, (int *)arg);
+ return put_user(val, (int *)arg);
break;
default:
- if (get_user(val, (int *)arg)){
+ if (get_user(val, (int *)arg))
return -EFAULT;
- }
- if ((val=ad1816_mixer_set (devc, cmd & 0xff, val))<0) {
+ if ((val=ad1816_mixer_set (devc, cmd & 0xff, val))<0)
return val;
- } else {
+ else
return put_user(val, (int *)arg);
- }
}
} else {
/* read ioctl */
@@ -1039,25 +976,22 @@
break;
default:
- if ((val=ad1816_mixer_get (devc, cmd & 0xff))<0) {
+ if ((val=ad1816_mixer_get (devc, cmd & 0xff))<0)
return val;
- } else {
+ else
return put_user(val, (int *)arg);
- }
}
}
- } else {
+ } else
/* not for mixer */
return -(EINVAL);
- }
}
/* ------------------------------------------------------------------- */
/* Mixer structure */
-static struct mixer_operations ad1816_mixer_operations =
-{
+static struct mixer_operations ad1816_mixer_operations = {
"AD1816",
"AD1816 Mixer",
ad1816_mixer_ioctl
@@ -1076,7 +1010,7 @@
int io_base=hw_config->io_base;
int *osp=hw_config->osp;
int tmp;
-
+
printk("ad1816: AD1816 sounddriver Copyright (C) 1998 by Thorsten Knabe\n");
printk("ad1816: io=0x%x, irq=%d, dma=%d, dma2=%d, clockfreq=%d, options=%d isadmabug=%d\n",
hw_config->io_base,
@@ -1098,7 +1032,7 @@
printk ("ad1816: detect error - step 0\n");
return 0;
}
-
+
devc->base = io_base;
devc->irq_ok = 0;
devc->irq = 0;
@@ -1126,7 +1060,8 @@
DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 2)\n"));
return(0);
}
-
+
+
/* writes to ireg 10 are copied to ireg 11 */
ad_write(devc,10,54321);
if (ad_read(devc,11)!=54321) {
@@ -1140,7 +1075,7 @@
DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 4)\n"));
return(0);
}
-
+
/* bit in base +1 cannot be set to 1 */
tmp=inb(devc->base+1);
outb(0xff,devc->base+1);
@@ -1152,7 +1087,7 @@
DEBUGLOG (printk ("ad1816: detect() - Detected OK\n"));
DEBUGLOG (printk ("ad1816: AD1816 Version: %d\n",ad_read(devc,45)));
-
+
/* detection was successful */
return 1;
}
@@ -1330,6 +1265,22 @@
static int __initdata dma = -1;
static int __initdata dma2 = -1;
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+struct pci_dev *ad1816_dev = NULL,
+ *mpu_dev = NULL;
+
+static int activated = 1;
+
+static int isapnp = 1;
+static int isapnpjump = 0;
+
+MODULE_PARM(isapnp, "i");
+MODULE_PARM(isapnpjump, "i");
+
+#else
+static int isapnp = 0;
+#endif
+
MODULE_PARM(io,"i");
MODULE_PARM(irq,"i");
MODULE_PARM(dma,"i");
@@ -1337,12 +1288,132 @@
MODULE_PARM(ad1816_clockfreq,"i");
MODULE_PARM(options,"i");
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+
+static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev *dev)
+{
+ int err;
+
+ if(dev->active) {
+ activated = 0;
+ return(dev);
+ }
+
+ if((err = dev->activate(dev)) < 0) {
+ printk(KERN_ERR "ad1816: %s %s config failed (out of resources?)[%d]\n",
+ devname, resname, err);
+ dev->deactivate(dev);
+ return(NULL);
+ }
+
+ return(dev);
+}
+
+static struct pci_dev *ad1816_init_generic(struct pci_bus *bus, struct pci_dev *card,
+ struct address_info *hw_config)
+{
+ if((ad1816_dev = isapnp_find_dev(bus, card->vendor, card->device, NULL))) {
+ ad1816_dev->prepare(ad1816_dev);
+
+ if((ad1816_dev = activate_dev("Analog Devices 1816(A)", "ad1816", ad1816_dev))) {
+ hw_config->io_base = ad1816_dev->resource[2].start;
+ hw_config->irq = ad1816_dev->irq_resource[0].start;
+ hw_config->dma = ad1816_dev->dma_resource[0].start;
+ hw_config->dma2 = ad1816_dev->dma_resource[1].start;
+ }
+ }
+
+ return(ad1816_dev);
+}
+
+static struct {
+ unsigned short vendor;
+ unsigned short function;
+ struct pci_dev * (*initfunc)(struct pci_bus*, struct pci_dev *, struct address_info *);
+ char *name;
+} isapnp_ad1816_list[] __initdata = {
+ {ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7150), &ad1816_init_generic, "Analog Devices 1815" },
+ {ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7180), &ad1816_init_generic, "Analog Devices 1816A" },
+ {0}
+};
+
+static int __init ad1816_init_isapnp(struct address_info *hw_config,
+ struct pci_bus *bus, struct pci_dev *card, int slot)
+{
+ struct pci_dev *idev = NULL;
+
+ /* You missed the init func? That's bad. */
+ if(isapnp_ad1816_list[slot].initfunc) {
+ char *busname = bus->name[0] ? bus->name : isapnp_ad1816_list[slot].name;
+
+ printk(KERN_INFO "ad1816: %s detected\n", busname);
+
+ /* Initialize this baby. */
+ if((idev = isapnp_ad1816_list[slot].initfunc(bus, card, hw_config))) {
+ /* We got it. */
+
+ printk(KERN_NOTICE "ad1816: ISAPnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n",
+ busname,
+ hw_config->io_base, hw_config->irq, hw_config->dma,
+ hw_config->dma2);
+ return 1;
+ } else
+ printk(KERN_INFO "ad1816: Failed to initialize %s\n", busname);
+ } else
+ printk(KERN_ERR "ad1816: Bad entry in ad1816.c PnP table\n");
+
+ return 0;
+}
+
+/*
+ * Actually this routine will detect and configure only the first card with successful
+ * initialization. isapnpjump could be used to jump to a specific entry.
+ * Please always add entries at the end of the array.
+ * Should this be fixed? - azummo
+ */
+
+int __init ad1816_probe_isapnp(struct address_info *hw_config)
+{
+ int i;
+
+ /* Count entries in isapnp_ad1816_list */
+ for (i = 0; isapnp_ad1816_list[i].vendor != 0; i++)
+ ;
+ /* Check and adjust isapnpjump */
+ if( isapnpjump < 0 || isapnpjump > ( i - 1 ) ) {
+ printk(KERN_ERR "ad1816: Valid range for isapnpjump is 0-%d. Adjusted to 0.\n", i-1);
+ isapnpjump = 0;
+ }
+
+ for (i = isapnpjump; isapnp_ad1816_list[i].vendor != 0; i++) {
+ struct pci_dev *card = NULL;
+
+ while ((card = isapnp_find_dev(NULL, isapnp_ad1816_list[i].vendor,
+ isapnp_ad1816_list[i].function, card)))
+ if(ad1816_init_isapnp(hw_config, card->bus, card, i))
+ return 0;
+ }
+
+ return -ENODEV;
+}
+#endif
+
static int __init init_ad1816(void)
{
- cfg.io_base = io;
- cfg.irq = irq;
- cfg.dma = dma;
- cfg.dma2 = dma2;
+
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+ if(isapnp && (ad1816_probe_isapnp(&cfg) < 0) ) {
+ printk(KERN_NOTICE "ad1816: No ISAPnP cards found, trying standard ones...\n");
+ isapnp = 0;
+ }
+#endif
+
+ if( isapnp == 0) {
+ cfg.io_base = io;
+ cfg.irq = irq;
+ cfg.dma = dma;
+ cfg.dma2 = dma2;
+ }
if (cfg.io_base == -1 || cfg.irq == -1 || cfg.dma == -1 || cfg.dma2 == -1) {
printk(KERN_INFO "ad1816: dma, dma2, irq and io must be set.\n");
@@ -1352,9 +1423,11 @@
if (probe_ad1816(&cfg) == 0) {
return -ENODEV;
}
-
+
attach_ad1816(&cfg);
SOUND_LOCK;
+
+ return 0;
}
static void __exit cleanup_ad1816 (void)
@@ -1370,6 +1443,11 @@
nr_ad1816_devs=0;
SOUND_LOCK_END;
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+ if(activated)
+ if(ad1816_dev)
+ ad1816_dev->deactivate(ad1816_dev);
+#endif
}
module_init(init_ad1816);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)