diff options
author | David Woodhouse <dwmw2@infradead.org> | 2007-07-23 10:20:10 +0100 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2007-07-23 10:20:10 +0100 |
commit | 39fe5434cb9de5da40510028b17b96bc4eb312b3 (patch) | |
tree | 7a02a317b9ad57da51ca99887c119e779ccf3f13 /sound/oss/ad1816.c | |
parent | 0fc72b81d3111d114ab378935b1cf07680ca1289 (diff) | |
parent | f695baf2df9e0413d3521661070103711545207a (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'sound/oss/ad1816.c')
-rw-r--r-- | sound/oss/ad1816.c | 1368 |
1 files changed, 0 insertions, 1368 deletions
diff --git a/sound/oss/ad1816.c b/sound/oss/ad1816.c deleted file mode 100644 index caabf31193f..00000000000 --- a/sound/oss/ad1816.c +++ /dev/null @@ -1,1368 +0,0 @@ -/* - * - * AD1816 lowlevel sound driver for Linux 2.6.0 and above - * - * Copyright (C) 1998-2003 by Thorsten Knabe <linux@thorsten-knabe.de> - * - * Based on the CS4232/AD1848 driver Copyright (C) by Hannu Savolainen 1993-1996 - * - * - * version: 1.5 - * status: beta - * date: 2003/07/15 - * - * Changes: - * Oleg Drokin: Some cleanup of load/unload functions. 1998/11/24 - * - * Thorsten Knabe: attach and unload rewritten, - * some argument checks added 1998/11/30 - * - * Thorsten Knabe: Buggy isa bridge workaround added 1999/01/16 - * - * David Moews/Thorsten Knabe: Introduced options - * parameter. Added slightly modified patch from - * David Moews to disable dsp audio sources by setting - * bit 0 of options parameter. This seems to be - * 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 - * - * Arnaldo Carvalho de Melo: get rid of check_region 2001/10/07 - * - * Thorsten Knabe: Compiling with CONFIG_PNP enabled - * works again. It is now possible to use more than one - * AD1816 sound card. Sample rate now may be changed during - * playback/capture. printk() uses log levels everywhere. - * SMP fixes. DMA handling fixes. - * Other minor code cleanup. 2003/07/15 - * - */ - - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/isapnp.h> -#include <linux/stddef.h> -#include <linux/spinlock.h> -#include "sound_config.h" - -#define DEBUGNOISE(x) - -#define CHECK_FOR_POWER { int timeout=100; \ - while (timeout > 0 && (inb(devc->base)&0x80)!= 0x80) {\ - timeout--; \ - } \ - if (timeout==0) {\ - printk(KERN_WARNING "ad1816: Check for power failed in %s line: %d\n",__FILE__,__LINE__); \ - } \ -} - -/* structure to hold device specific information */ -typedef struct -{ - int base; /* set in attach */ - int irq; - int dma_playback; - int dma_capture; - - int opened; /* open */ - int speed; - int channels; - int audio_format; - int audio_mode; - - int recmask; /* setup */ - unsigned char format_bits; - int supported_devices; - int supported_rec_devices; - unsigned short levels[SOUND_MIXER_NRDEVICES]; - /* misc */ - struct pnp_dev *pnpdev; /* configured via pnp */ - int dev_no; /* this is the # in audio_devs and NOT - in ad1816_info */ - spinlock_t lock; -} ad1816_info; - -static int nr_ad1816_devs; -static int ad1816_clockfreq = 33000; -static int options; - -/* supported audio formats */ -static int ad_format_mask = -AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE | AFMT_MU_LAW | AFMT_A_LAW; - -/* array of device info structures */ -static ad1816_info dev_info[MAX_AUDIO_DEV]; - - -/* ------------------------------------------------------------------- */ - -/* functions for easier access to inderect registers */ - -static int ad_read (ad1816_info * devc, int reg) -{ - int result; - - CHECK_FOR_POWER; - outb ((unsigned char) (reg & 0x3f), devc->base+0); - result = inb(devc->base+2); - result+= inb(devc->base+3)<<8; - return (result); -} - - -static void ad_write (ad1816_info * devc, int reg, int data) -{ - CHECK_FOR_POWER; - outb ((unsigned char) (reg & 0xff), devc->base+0); - outb ((unsigned char) (data & 0xff),devc->base+2); - outb ((unsigned char) ((data>>8)&0xff),devc->base+3); -} - -/* ------------------------------------------------------------------- */ - -/* function interface required by struct audio_driver */ - -static void ad1816_halt_input (int dev) -{ - unsigned long flags; - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - unsigned char buffer; - - DEBUGNOISE(printk(KERN_DEBUG "ad1816: halt_input called\n")); - - spin_lock_irqsave(&devc->lock,flags); - - if(!isa_dma_bridge_buggy) { - disable_dma(audio_devs[dev]->dmap_in->dma); - } - - buffer=inb(devc->base+9); - if (buffer & 0x01) { - /* disable capture */ - outb(buffer & ~0x01,devc->base+9); - } - - if(!isa_dma_bridge_buggy) { - enable_dma(audio_devs[dev]->dmap_in->dma); - } - - /* Clear interrupt status */ - outb (~0x40, devc->base+1); - - devc->audio_mode &= ~PCM_ENABLE_INPUT; - spin_unlock_irqrestore(&devc->lock,flags); -} - -static void ad1816_halt_output (int dev) -{ - unsigned long flags; - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - - unsigned char buffer; - - DEBUGNOISE(printk(KERN_DEBUG "ad1816: halt_output called!\n")); - - spin_lock_irqsave(&devc->lock,flags); - /* Mute pcm output */ - ad_write(devc, 4, ad_read(devc,4)|0x8080); - - if(!isa_dma_bridge_buggy) { - disable_dma(audio_devs[dev]->dmap_out->dma); - } - - buffer=inb(devc->base+8); - if (buffer & 0x01) { - /* disable capture */ - outb(buffer & ~0x01,devc->base+8); - } - - if(!isa_dma_bridge_buggy) { - enable_dma(audio_devs[dev]->dmap_out->dma); - } - - /* Clear interrupt status */ - outb ((unsigned char)~0x80, devc->base+1); - - devc->audio_mode &= ~PCM_ENABLE_OUTPUT; - spin_unlock_irqrestore(&devc->lock,flags); -} - -static void ad1816_output_block (int dev, unsigned long buf, - int count, int intrflag) -{ - unsigned long flags; - unsigned long cnt; - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - - DEBUGNOISE(printk(KERN_DEBUG "ad1816: output_block called buf=%ld count=%d flags=%d\n",buf,count,intrflag)); - - cnt = count/4 - 1; - - spin_lock_irqsave(&devc->lock,flags); - - /* set transfer count */ - ad_write (devc, 8, cnt & 0xffff); - - devc->audio_mode |= PCM_ENABLE_OUTPUT; - spin_unlock_irqrestore(&devc->lock,flags); -} - - -static void ad1816_start_input (int dev, unsigned long buf, int count, - int intrflag) -{ - unsigned long flags; - unsigned long cnt; - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - - DEBUGNOISE(printk(KERN_DEBUG "ad1816: start_input called buf=%ld count=%d flags=%d\n",buf,count,intrflag)); - - cnt = count/4 - 1; - - spin_lock_irqsave(&devc->lock,flags); - - /* set transfer count */ - ad_write (devc, 10, cnt & 0xffff); - devc->audio_mode |= PCM_ENABLE_INPUT; - spin_unlock_irqrestore(&devc->lock,flags); -} - -static int ad1816_prepare_for_input (int dev, int bsize, int bcount) -{ - unsigned long flags; - unsigned int freq; - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - unsigned char fmt_bits; - - DEBUGNOISE(printk(KERN_DEBUG "ad1816: prepare_for_input called: bsize=%d bcount=%d\n",bsize,bcount)); - - spin_lock_irqsave(&devc->lock,flags); - fmt_bits= (devc->format_bits&0x7)<<3; - - /* set mono/stereo mode */ - if (devc->channels > 1) { - fmt_bits |=0x4; - } - /* set Mono/Stereo in playback/capture register */ - outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8); - outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9); - - freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; - - /* write playback/capture speeds */ - ad_write (devc, 2, freq & 0xffff); - ad_write (devc, 3, freq & 0xffff); - - spin_unlock_irqrestore(&devc->lock,flags); - - ad1816_halt_input(dev); - return 0; -} - -static int ad1816_prepare_for_output (int dev, int bsize, int bcount) -{ - unsigned long flags; - unsigned int freq; - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - unsigned char fmt_bits; - - DEBUGNOISE(printk(KERN_DEBUG "ad1816: prepare_for_output called: bsize=%d bcount=%d\n",bsize,bcount)); - - spin_lock_irqsave(&devc->lock,flags); - - fmt_bits= (devc->format_bits&0x7)<<3; - /* set mono/stereo mode */ - if (devc->channels > 1) { - fmt_bits |=0x4; - } - - /* write format bits to playback/capture registers */ - outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8); - outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9); - - freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; - - /* write playback/capture speeds */ - ad_write (devc, 2, freq & 0xffff); - ad_write (devc, 3, freq & 0xffff); - - spin_unlock_irqrestore(&devc->lock,flags); - - ad1816_halt_output(dev); - return 0; - -} - -static void ad1816_trigger (int dev, int state) -{ - unsigned long flags; - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - - DEBUGNOISE(printk(KERN_DEBUG "ad1816: trigger called! (devc=%d,devc->base=%d\n", devc, devc->base)); - - /* mode may have changed */ - - spin_lock_irqsave(&devc->lock,flags); - - /* mask out modes not specified on open call */ - state &= devc->audio_mode; - - /* setup soundchip to new io-mode */ - if (state & PCM_ENABLE_INPUT) { - /* enable capture */ - outb(inb(devc->base+9)|0x01, devc->base+9); - } else { - /* disable capture */ - outb(inb(devc->base+9)&~0x01, devc->base+9); - } - - if (state & PCM_ENABLE_OUTPUT) { - /* enable playback */ - outb(inb(devc->base+8)|0x01, devc->base+8); - /* unmute pcm output */ - ad_write(devc, 4, ad_read(devc,4)&~0x8080); - } else { - /* mute pcm output */ - ad_write(devc, 4, ad_read(devc,4)|0x8080); - /* disable capture */ - outb(inb(devc->base+8)&~0x01, devc->base+8); - } - spin_unlock_irqrestore(&devc->lock,flags); -} - - -/* halt input & output */ -static void ad1816_halt (int dev) -{ - ad1816_halt_input(dev); - ad1816_halt_output(dev); -} - -static void ad1816_reset (int dev) -{ - ad1816_halt (dev); -} - -/* set playback speed */ -static int ad1816_set_speed (int dev, int arg) -{ - unsigned long flags; - unsigned int freq; - int ret; - - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - - spin_lock_irqsave(&devc->lock, flags); - if (arg == 0) { - ret = devc->speed; - spin_unlock_irqrestore(&devc->lock, flags); - return ret; - } - /* range checking */ - if (arg < 4000) { - arg = 4000; - } - if (arg > 55000) { - arg = 55000; - } - devc->speed = arg; - - /* change speed during playback */ - freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; - /* write playback/capture speeds */ - ad_write (devc, 2, freq & 0xffff); - ad_write (devc, 3, freq & 0xffff); - - ret = devc->speed; - spin_unlock_irqrestore(&devc->lock, flags); - return ret; - -} - -static unsigned int ad1816_set_bits (int dev, unsigned int arg) -{ - unsigned long flags; - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - - 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 } - }; - - int i, n = sizeof (format2bits) / sizeof (struct format_tbl); - - spin_lock_irqsave(&devc->lock, flags); - /* return current format */ - if (arg == 0) { - arg = devc->audio_format; - spin_unlock_irqrestore(&devc->lock, flags); - return arg; - } - devc->audio_format = arg; - - /* search matching format bits */ - for (i = 0; i < n; i++) - if (format2bits[i].format == arg) { - devc->format_bits = format2bits[i].bits; - devc->audio_format = arg; - spin_unlock_irqrestore(&devc->lock, flags); - return arg; - } - - /* Still hanging here. Something must be terribly wrong */ - devc->format_bits = 0; - devc->audio_format = AFMT_U8; - spin_unlock_irqrestore(&devc->lock, flags); - return(AFMT_U8); -} - -static short ad1816_set_channels (int dev, short arg) -{ - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - - if (arg != 1 && arg != 2) - return devc->channels; - - devc->channels = arg; - return arg; -} - -/* open device */ -static int ad1816_open (int dev, int mode) -{ - ad1816_info *devc = NULL; - unsigned long flags; - - /* is device number valid ? */ - if (dev < 0 || dev >= num_audiodevs) - return -(ENXIO); - - /* get device info of this dev */ - devc = (ad1816_info *) audio_devs[dev]->devc; - - /* make check if device already open atomic */ - spin_lock_irqsave(&devc->lock,flags); - - if (devc->opened) { - spin_unlock_irqrestore(&devc->lock,flags); - return -(EBUSY); - } - - /* mark device as open */ - devc->opened = 1; - - devc->audio_mode = 0; - devc->speed = 8000; - devc->audio_format=AFMT_U8; - devc->channels=1; - spin_unlock_irqrestore(&devc->lock,flags); - ad1816_reset(devc->dev_no); /* halt all pending output */ - return 0; -} - -static void ad1816_close (int dev) /* close device */ -{ - unsigned long flags; - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - - /* halt all pending output */ - ad1816_reset(devc->dev_no); - - spin_lock_irqsave(&devc->lock,flags); - devc->opened = 0; - devc->audio_mode = 0; - devc->speed = 8000; - devc->audio_format=AFMT_U8; - devc->format_bits = 0; - spin_unlock_irqrestore(&devc->lock,flags); -} - - -/* ------------------------------------------------------------------- */ - -/* Audio driver structure */ - -static struct audio_driver ad1816_audio_driver = -{ - .owner = THIS_MODULE, - .open = ad1816_open, - .close = ad1816_close, - .output_block = ad1816_output_block, - .start_input = ad1816_start_input, - .prepare_for_input = ad1816_prepare_for_input, - .prepare_for_output = ad1816_prepare_for_output, - .halt_io = ad1816_halt, - .halt_input = ad1816_halt_input, - .halt_output = ad1816_halt_output, - .trigger = ad1816_trigger, - .set_speed = ad1816_set_speed, - .set_bits = ad1816_set_bits, - .set_channels = ad1816_set_channels, -}; - - -/* ------------------------------------------------------------------- */ - -/* Interrupt handler */ - - -static irqreturn_t ad1816_interrupt (int irq, void *dev_id) -{ - unsigned char status; - ad1816_info *devc = (ad1816_info *)dev_id; - - if (irq < 0 || irq > 15) { - printk(KERN_WARNING "ad1816: Got bogus interrupt %d\n", irq); - return IRQ_NONE; - } - - spin_lock(&devc->lock); - - /* read interrupt register */ - status = inb (devc->base+1); - /* Clear all interrupt */ - outb (~status, devc->base+1); - - DEBUGNOISE(printk(KERN_DEBUG "ad1816: Got interrupt subclass %d\n",status)); - - if (status == 0) { - DEBUGNOISE(printk(KERN_DEBUG "ad1816: interrupt: Got interrupt, but no source.\n")); - spin_unlock(&devc->lock); - return IRQ_NONE; - } - - if (devc->opened && (devc->audio_mode & PCM_ENABLE_INPUT) && (status&64)) - DMAbuf_inputintr (devc->dev_no); - - if (devc->opened && (devc->audio_mode & PCM_ENABLE_OUTPUT) && (status & 128)) - DMAbuf_outputintr (devc->dev_no, 1); - - spin_unlock(&devc->lock); - return IRQ_HANDLED; -} - -/* ------------------------------------------------------------------- */ - -/* Mixer stuff */ - -struct mixer_def { - unsigned int regno: 7; - unsigned int polarity:1; /* 0=normal, 1=reversed */ - unsigned int bitpos:4; - unsigned int nbits:4; -}; - -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, - 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, - 91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99, - 100 -}; - -typedef struct mixer_def mixer_ent; - -/* - * Most of the mixer entries work in backwards. Setting the polarity field - * makes them to work correctly. - * - * The channel numbering used by individual soundcards is not fixed. Some - * cards have assigned different meanings for the AUX1, AUX2 and LINE inputs. - * The current version doesn't try to compensate this. - */ - -#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r) \ - {{reg_l, pola_l, pos_l, len_l}, {reg_r, pola_r, pos_r, len_r}} - - -static mixer_ent mix_devices[SOUND_MIXER_NRDEVICES][2] = { -MIX_ENT(SOUND_MIXER_VOLUME, 14, 1, 8, 5, 14, 1, 0, 5), -MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 5, 1, 8, 6, 5, 1, 0, 6), -MIX_ENT(SOUND_MIXER_PCM, 4, 1, 8, 6, 4, 1, 0, 6), -MIX_ENT(SOUND_MIXER_SPEAKER, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_LINE, 18, 1, 8, 5, 18, 1, 0, 5), -MIX_ENT(SOUND_MIXER_MIC, 19, 1, 8, 5, 19, 1, 0, 5), -MIX_ENT(SOUND_MIXER_CD, 15, 1, 8, 5, 15, 1, 0, 5), -MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 20, 0, 8, 4, 20, 0, 0, 4), -MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_LINE1, 17, 1, 8, 5, 17, 1, 0, 5), -MIX_ENT(SOUND_MIXER_LINE2, 16, 1, 8, 5, 16, 1, 0, 5), -MIX_ENT(SOUND_MIXER_LINE3, 39, 0, 9, 4, 39, 1, 0, 5) -}; - - -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)*/ -}; - -#define LEFT_CHN 0 -#define RIGHT_CHN 1 - - - -static int -ad1816_set_recmask (ad1816_info * devc, int mask) -{ - unsigned long flags; - unsigned char recdev; - int i, n; - - spin_lock_irqsave(&devc->lock, flags); - mask &= devc->supported_rec_devices; - - n = 0; - /* Count selected device bits */ - for (i = 0; i < 32; i++) - if (mask & (1 << i)) - n++; - - if (n == 0) - mask = SOUND_MASK_MIC; - 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)) - n++; - - if (n != 1) - mask = SOUND_MASK_MIC; - } - - switch (mask) { - case SOUND_MASK_MIC: - recdev = 5; - break; - - case SOUND_MASK_LINE: - recdev = 0; - break; - - case SOUND_MASK_CD: - recdev = 2; - break; - - case SOUND_MASK_LINE1: - recdev = 4; - break; - - case SOUND_MASK_LINE2: - recdev = 3; - break; - - case SOUND_MASK_VOLUME: - recdev = 1; - break; - - default: - mask = SOUND_MASK_MIC; - recdev = 5; - } - - recdev <<= 4; - ad_write (devc, 20, - (ad_read (devc, 20) & 0x8f8f) | recdev | (recdev<<8)); - - devc->recmask = mask; - spin_unlock_irqrestore(&devc->lock, flags); - return mask; -} - -static void -change_bits (int *regval, int dev, int chn, int newval) -{ - unsigned char mask; - int shift; - - /* Reverse polarity*/ - - if (mix_devices[dev][chn].polarity == 1) - newval = 100 - newval; - - mask = (1 << mix_devices[dev][chn].nbits) - 1; - shift = mix_devices[dev][chn].bitpos; - /* Scale it */ - newval = (int) ((newval * mask) + 50) / 100; - /* Clear bits */ - *regval &= ~(mask << shift); - /* Set new value */ - *regval |= (newval & mask) << shift; -} - -static int -ad1816_mixer_get (ad1816_info * devc, int dev) -{ - DEBUGNOISE(printk(KERN_DEBUG "ad1816: mixer_get called!\n")); - - /* range check + supported mixer check */ - if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES ) - return (-(EINVAL)); - if (!((1 << dev) & devc->supported_devices)) - return -(EINVAL); - - return devc->levels[dev]; -} - -static int -ad1816_mixer_set (ad1816_info * devc, int dev, int value) -{ - int left = value & 0x000000ff; - int right = (value & 0x0000ff00) >> 8; - int retvol; - - int regoffs; - int val; - int valmute; - unsigned long flags; - - DEBUGNOISE(printk(KERN_DEBUG "ad1816: mixer_set called!\n")); - - if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES ) - return -(EINVAL); - - if (left > 100) - left = 100; - if (left < 0) - left = 0; - if (right > 100) - right = 100; - if (right < 0) - right = 0; - - /* Mono control */ - if (mix_devices[dev][RIGHT_CHN].nbits == 0) - right = left; - retvol = left | (right << 8); - - /* Scale it */ - - left = mix_cvt[left]; - right = mix_cvt[right]; - - /* reject all mixers that are not supported */ - if (!(devc->supported_devices & (1 << dev))) - return -(EINVAL); - - /* sanity check */ - if (mix_devices[dev][LEFT_CHN].nbits == 0) - return -(EINVAL); - spin_lock_irqsave(&devc->lock, flags); - - /* keep precise volume internal */ - devc->levels[dev] = retvol; - - /* Set the left channel */ - regoffs = mix_devices[dev][LEFT_CHN].regno; - val = ad_read (devc, regoffs); - change_bits (&val, dev, LEFT_CHN, left); - - valmute=val; - - /* Mute bit masking on some registers */ - if ( regoffs==5 || regoffs==14 || regoffs==15 || - regoffs==16 || regoffs==17 || regoffs==18 || - regoffs==19 || regoffs==39) { - if (left==0) - valmute |= 0x8000; - else - valmute &= ~0x8000; - } - ad_write (devc, regoffs, valmute); /* mute */ - - /* - * Set the right channel - */ - - /* Was just a mono channel */ - if (mix_devices[dev][RIGHT_CHN].nbits == 0) { - spin_unlock_irqrestore(&devc->lock, flags); - return retvol; - } - - regoffs = mix_devices[dev][RIGHT_CHN].regno; - val = ad_read (devc, regoffs); - change_bits (&val, dev, RIGHT_CHN, right); - - valmute=val; - if ( regoffs==5 || regoffs==14 || regoffs==15 || - regoffs==16 || regoffs==17 || regoffs==18 || - regoffs==19 || regoffs==39) { - if (right==0) - valmute |= 0x80; - else - valmute &= ~0x80; - } - ad_write (devc, regoffs, valmute); /* mute */ - spin_unlock_irqrestore(&devc->lock, flags); - return retvol; -} - -#define MIXER_DEVICES ( SOUND_MASK_VOLUME | \ - SOUND_MASK_SYNTH | \ - SOUND_MASK_PCM | \ - SOUND_MASK_LINE | \ - SOUND_MASK_LINE1 | \ - SOUND_MASK_LINE2 | \ - SOUND_MASK_LINE3 | \ - SOUND_MASK_MIC | \ - SOUND_MASK_CD | \ - SOUND_MASK_RECLEV \ - ) -#define REC_DEVICES ( SOUND_MASK_LINE2 |\ - SOUND_MASK_LINE |\ - SOUND_MASK_LINE1 |\ - SOUND_MASK_MIC |\ - SOUND_MASK_CD |\ - SOUND_MASK_VOLUME \ - ) - -static void -ad1816_mixer_reset (ad1816_info * devc) -{ - int i; - - devc->supported_devices = MIXER_DEVICES; - - devc->supported_rec_devices = REC_DEVICES; - - 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); -} - -static int -ad1816_mixer_ioctl (int dev, unsigned int cmd, void __user * arg) -{ - ad1816_info *devc = mixer_devs[dev]->devc; - int val; - int __user *p = arg; - - DEBUGNOISE(printk(KERN_DEBUG "ad1816: mixer_ioctl called!\n")); - - /* Mixer ioctl */ - if (((cmd >> 8) & 0xff) == 'M') { - - /* set ioctl */ - if (_SIOC_DIR (cmd) & _SIOC_WRITE) { - switch (cmd & 0xff){ - case SOUND_MIXER_RECSRC: - - if (get_user(val, p)) - return -EFAULT; - val=ad1816_set_recmask (devc, val); - return put_user(val, p); - break; - - default: - if (get_user(val, p)) - return -EFAULT; - if ((val=ad1816_mixer_set (devc, cmd & 0xff, val))<0) - return val; - else - return put_user(val, p); - } - } else { - /* read ioctl */ - switch (cmd & 0xff) { - - case SOUND_MIXER_RECSRC: - val=devc->recmask; - return put_user(val, p); - break; - - case SOUND_MIXER_DEVMASK: - val=devc->supported_devices; - return put_user(val, p); - break; - - case SOUND_MIXER_STEREODEVS: - val=devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); - return put_user(val, p); - break; - - case SOUND_MIXER_RECMASK: - val=devc->supported_rec_devices; - return put_user(val, p); - break; - - case SOUND_MIXER_CAPS: - val=SOUND_CAP_EXCL_INPUT; - return put_user(val, p); - break; - - default: - if ((val=ad1816_mixer_get (devc, cmd & 0xff))<0) - return val; - else - return put_user(val, p); - } - } - } else - /* not for mixer */ - return -(EINVAL); -} - -/* ------------------------------------------------------------------- */ - -/* Mixer structure */ - -static struct mixer_operations ad1816_mixer_operations = { - .owner = THIS_MODULE, - .id = "AD1816", - .name = "AD1816 Mixer", - .ioctl = ad1816_mixer_ioctl -}; - - -/* ------------------------------------------------------------------- */ - -/* stuff for card recognition, init and unloading PNP ...*/ - - -/* check if AD1816 present at specified hw_config and register device with OS - * return 1 if initialization was successful, 0 otherwise - */ -static int __init ad1816_init_card (struct address_info *hw_config, - struct pnp_dev *pnp) -{ - ad1816_info *devc = NULL; - int tmp; - int oss_devno = -1; - - printk(KERN_INFO "ad1816: initializing card: io=0x%x, irq=%d, dma=%d, " - "dma2=%d, clockfreq=%d, options=%d isadmabug=%d " - "%s\n", - hw_config->io_base, - hw_config->irq, - hw_config->dma, - hw_config->dma2, - ad1816_clockfreq, - options, - isa_dma_bridge_buggy, - pnp?"(PNP)":""); - - /* ad1816_info structure remaining ? */ - if (nr_ad1816_devs >= MAX_AUDIO_DEV) { - printk(KERN_WARNING "ad1816: no more ad1816_info structures " - "left\n"); - goto out; - } - - devc = &dev_info[nr_ad1816_devs]; - devc->base = hw_config->io_base; - devc->irq = hw_config->irq; - devc->dma_playback=hw_config->dma; - devc->dma_capture=hw_config->dma2; - devc->opened = 0; - devc->pnpdev = pnp; - spin_lock_init(&devc->lock); - - if (!request_region(devc->base, 16, "AD1816 Sound")) { - printk(KERN_WARNING "ad1816: I/O port 0x%03x not free\n", - devc->base); - goto out; - } - - printk(KERN_INFO "ad1816: Examining AD1816 at address 0x%03x.\n", - devc->base); - - - /* tests for ad1816 */ - /* base+0: bit 1 must be set but not 255 */ - tmp=inb(devc->base); - if ( (tmp&0x80)==0 || tmp==255 ) { - printk (KERN_INFO "ad1816: Chip is not an AD1816 or chip " - "is not active (Test 0)\n"); - goto out_release_region; - } - - /* writes to ireg 8 are copied to ireg 9 */ - ad_write(devc,8,12345); - if (ad_read(devc,9)!=12345) { - printk(KERN_INFO "ad1816: Chip is not an AD1816 (Test 1)\n"); - goto out_release_region; - } - - /* writes to ireg 8 are copied to ireg 9 */ - ad_write(devc,8,54321); - if (ad_read(devc,9)!=54321) { - printk(KERN_INFO "ad1816: Chip is not an AD1816 (Test 2)\n"); - goto out_release_region; - } - - /* writes to ireg 10 are copied to ireg 11 */ - ad_write(devc,10,54321); - if (ad_read(devc,11)!=54321) { - printk (KERN_INFO "ad1816: Chip is not an AD1816 (Test 3)\n"); - goto out_release_region; - } - - /* writes to ireg 10 are copied to ireg 11 */ - ad_write(devc,10,12345); - if (ad_read(devc,11)!=12345) { - printk (KERN_INFO "ad1816: Chip is not an AD1816 (Test 4)\n"); - goto out_release_region; - } - - /* bit in base +1 cannot be set to 1 */ - tmp=inb(devc->base+1); - outb(0xff,devc->base+1); - if (inb(devc->base+1)!=tmp) { - printk(KERN_INFO "ad1816: Chip is not an AD1816 (Test 5)\n"); - goto out_release_region; - } - - printk(KERN_INFO "ad1816: AD1816 (version %d) successfully detected!\n", - ad_read(devc,45)); - - /* disable all interrupts */ - ad_write(devc,1,0); - - /* Clear pending interrupts */ - outb (0, devc->base+1); - - /* allocate irq */ - if (devc->irq < 0 || devc->irq > 15) - goto out_release_region; - if (request_irq(devc->irq, ad1816_interrupt,0, - "SoundPort", devc) < 0) { - printk(KERN_WARNING "ad1816: IRQ in use\n"); - goto out_release_region; - } - - /* DMA stuff */ - if (sound_alloc_dma (devc->dma_playback, "Sound System")) { - printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n", - devc->dma_playback); - goto out_free_irq; - } - - if ( devc->dma_capture >= 0 && - devc->dma_capture != devc->dma_playback) { - if (sound_alloc_dma(devc->dma_capture, - "Sound System (capture)")) { - printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n", - devc->dma_capture); - goto out_free_dma; - } - devc->audio_mode=DMA_AUTOMODE|DMA_DUPLEX; - } else { - printk(KERN_WARNING "ad1816: Only one DMA channel " - "available/configured. No duplex operation possible\n"); - devc->audio_mode=DMA_AUTOMODE; - } - - conf_printf2 ("AD1816 audio driver", - devc->base, devc->irq, devc->dma_playback, - devc->dma_capture); - - /* register device */ - if ((oss_devno = sound_install_audiodrv (AUDIO_DRIVER_VERSION, - "AD1816 audio driver", - &ad1816_audio_driver, - sizeof (struct audio_driver), - devc->audio_mode, - ad_format_mask, - devc, - devc->dma_playback, - devc->dma_capture)) < 0) { - printk(KERN_WARNING "ad1816: Can't install sound driver\n"); - goto out_free_dma_2; - } - - - ad_write(devc,32,0x80f0); /* sound system mode */ - if (options&1) { - ad_write(devc,33,0); /* disable all audiosources for dsp */ - } else { - ad_write(devc,33,0x03f8); /* enable all audiosources for dsp */ - } - ad_write(devc,4,0x8080); /* default values for volumes (muted)*/ - ad_write(devc,5,0x8080); - ad_write(devc,6,0x8080); - ad_write(devc,7,0x8080); - ad_write(devc,15,0x8888); - ad_write(devc,16,0x8888); - ad_write(devc,17,0x8888); - ad_write(devc,18,0x8888); - ad_write(devc,19,0xc888); /* +20db mic active */ - ad_write(devc,14,0x0000); /* Master volume unmuted */ - ad_write(devc,39,0x009f); /* 3D effect on 0% phone out muted */ - ad_write(devc,44,0x0080); /* everything on power, 3d enabled for d/a */ - outb(0x10,devc->base+8); /* set dma mode */ - outb(0x10,devc->base+9); - - /* enable capture + playback interrupt */ - ad_write(devc,1,0xc000); - - /* set mixer defaults */ - ad1816_mixer_reset (devc); - - /* register mixer */ - if ((audio_devs[oss_devno]->mixer_dev=sound_install_mixer( - MIXER_DRIVER_VERSION, - "AD1816 audio driver", - &ad1816_mixer_operations, - sizeof (struct mixer_operations), - devc)) < 0) { - printk(KERN_WARNING "Can't install mixer\n"); - } - /* make ad1816_info active */ - nr_ad1816_devs++; - printk(KERN_INFO "ad1816: card successfully installed!\n"); - return 1; - /* error handling */ -out_free_dma_2: - if (devc->dma_capture >= 0 && devc->dma_capture != devc->dma_playback) - sound_free_dma(devc->dma_capture); -out_free_dma: - sound_free_dma(devc->dma_playback); -out_free_irq: - free_irq(devc->irq, devc); -out_release_region: - release_region(devc->base, 16); -out: - return 0; -} - -static void __exit unload_card(ad1816_info *devc) -{ - int mixer, dev = 0; - - if (devc != NULL) { - printk("ad1816: Unloading card at address 0x%03x\n",devc->base); - - dev = devc->dev_no; - mixer = audio_devs[dev]->mixer_dev; - - /* unreg mixer*/ - if(mixer>=0) { - sound_unload_mixerdev(mixer); - } - /* unreg audiodev */ - sound_unload_audiodev(dev); - - /* free dma channels */ - if (devc->dma_capture>=0 && - devc->dma_capture != devc->dma_playback) { - sound_free_dma(devc->dma_capture); - } - sound_free_dma (devc->dma_playback); - /* free irq */ - free_irq(devc->irq, devc); - /* free io */ - release_region (devc->base, 16); -#ifdef __ISAPNP__ - if (devc->pnpdev) { - pnp_disable_dev(devc->pnpdev); - pnp_device_detach(devc->pnpdev); - } -#endif - - } else - printk(KERN_WARNING "ad1816: no device/card specified\n"); -} - -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata dma2 = -1; - -#ifdef __ISAPNP__ -/* use isapnp for configuration */ -static int isapnp = 1; -static int isapnpjump; -module_param(isapnp, bool, 0); -module_param(isapnpjump, int, 0); -#endif - -module_param(io, int, 0); -module_param(irq, int, 0); -module_param(dma, int, 0); -module_param(dma2, int, 0); -module_param(ad1816_clockfreq, int, 0); -module_param(options, int, 0); - -#ifdef __ISAPNP__ -static struct { - unsigned short card_vendor, card_device; - unsigned short vendor; - unsigned short function; - struct ad1816_data *data; -} isapnp_ad1816_list[] __initdata = { - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7150), - NULL }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7180), - NULL }, - {0} -}; - -MODULE_DEVICE_TABLE(isapnp, isapnp_ad1816_list); - - -static void __init ad1816_config_pnp_card(struct pnp_card *card, - unsigned short vendor, - unsigned short function) -{ - struct address_info cfg; - struct pnp_dev *card_dev = pnp_find_dev(card, vendor, function, NULL); - if (!card_dev) return; - if (pnp_device_attach(card_dev) < 0) { - printk(KERN_WARNING "ad1816: Failed to attach PnP device\n"); - return; - } - if (pnp_activate_dev(card_dev) < 0) { - printk(KERN_WARNING "ad1816: Failed to activate PnP device\n"); - pnp_device_detach(card_dev); - return; - } - cfg.io_base = pnp_port_start(card_dev, 2); - cfg.irq = pnp_irq(card_dev, 0); - cfg.dma = pnp_irq(card_dev, 0); - cfg.dma2 = pnp_irq(card_dev, 1); - if (!ad1816_init_card(&cfg, card_dev)) { - pnp_disable_dev(card_dev); - pnp_device_detach(card_dev); - } -} - -static void __init ad1816_config_pnp_cards(void) -{ - int nr_pnp_cfg; - int i; - - /* Count entries in isapnp_ad1816_list */ - for (nr_pnp_cfg = 0; isapnp_ad1816_list[nr_pnp_cfg].card_vendor != 0; - nr_pnp_cfg++); - /* Check and adjust isapnpjump */ - if( isapnpjump < 0 || isapnpjump >= nr_pnp_cfg) { - printk(KERN_WARNING - "ad1816: Valid range for isapnpjump is 0-%d. " - "Adjusted to 0.\n", nr_pnp_cfg-1); - isapnpjump = 0; - } - for (i = isapnpjump; isapnp_ad1816_list[i].card_vendor != 0; i++) { - struct pnp_card *card = NULL; - /* iterate over all pnp cards */ - while ((card = pnp_find_card(isapnp_ad1816_list[i].card_vendor, - isapnp_ad1816_list[i].card_device, card))) - ad1816_config_pnp_card(card, - isapnp_ad1816_list[i].vendor, - isapnp_ad1816_list[i].function); - } -} -#endif - -/* module initialization */ -static int __init init_ad1816(void) -{ - printk(KERN_INFO "ad1816: AD1816 sounddriver " - "Copyright (C) 1998-2003 by Thorsten Knabe and " - "others\n"); -#ifdef AD1816_CLOCK - /* set ad1816_clockfreq if set during compilation */ - ad1816_clockfreq=AD1816_CLOCK; -#endif - if (ad1816_clockfreq<5000 || ad1816_clockfreq>100000) { - ad1816_clockfreq=33000; - } - -#ifdef __ISAPNP__ - /* configure PnP cards */ - if(isapnp) ad1816_config_pnp_cards(); -#endif - /* configure card by module params */ - if (io != -1 && irq != -1 && dma != -1) { - struct address_info cfg; - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma2; - ad1816_init_card(&cfg, NULL); - } - if (nr_ad1816_devs <= 0) - return -ENODEV; - return 0; -} - -/* module cleanup */ -static void __exit cleanup_ad1816 (void) -{ - int i; - ad1816_info *devc = NULL; - - /* remove any soundcard */ - for (i = 0; i < nr_ad1816_devs; i++) { - devc = &dev_info[i]; - unload_card(devc); - } - nr_ad1816_devs=0; - printk(KERN_INFO "ad1816: driver unloaded!\n"); -} - -module_init(init_ad1816); -module_exit(cleanup_ad1816); - -#ifndef MODULE -/* kernel command line parameter evaluation */ -static int __init setup_ad1816(char *str) -{ - /* io, irq, dma, dma2 */ - int ints[5]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma2 = ints[4]; - return 1; -} - -__setup("ad1816=", setup_ad1816); -#endif -MODULE_LICENSE("GPL"); |