patch-2.4.7 linux/drivers/sound/trident.c
Next file: linux/drivers/sound/trident.h
Previous file: linux/drivers/sound/sb_common.c
Back to the patch index
Back to the overall index
- Lines: 1305
- Date:
Sun Jul 15 16:22:23 2001
- Orig file:
v2.4.6/linux/drivers/sound/trident.c
- Orig date:
Sat May 19 17:43:10 2001
diff -u --recursive --new-file v2.4.6/linux/drivers/sound/trident.c linux/drivers/sound/trident.c
@@ -31,6 +31,17 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* History
+ * v0.14.8a
+ * July 7 2001 Alan Cox
+ * Moved Matt Wu's ac97 register cache into the card structure
+ * v0.14.8
+ * Apr 30 2001 Matt Wu
+ * Set EBUF1 and EBUF2 to still mode
+ * Add dc97/ac97 reset function
+ * Fix power management: ali_restore_regs
+ * unreleased
+ * Mar 09 2001 Matt Wu
+ * Add cache for ac97 access
* v0.14.7
* Feb 06 2001 Matt Wu
* Fix ac97 initialization
@@ -104,6 +115,11 @@
* Mmap support
* "Channel Binding" ioctl extension (done)
* new pci device driver interface for 2.4 kernel (done)
+ *
+ * Lock order (high->low)
+ * lock - hardware lock
+ * open_sem - guard opens
+ * sem - guard dmabuf, write re-entry etc
*/
#include <linux/config.h>
@@ -131,11 +147,15 @@
#include <linux/bitops.h>
#include <linux/proc_fs.h>
+#if defined CONFIG_ALPHA_NAUTILUS || CONFIG_ALPHA_GENERIC
+#include <asm/hwrpb.h>
+#endif
+
#include "trident.h"
#include <linux/pm.h>
-#define DRIVER_VERSION "0.14.6"
+#define DRIVER_VERSION "0.14.8"
/* magic numbers to protect our data structures */
#define TRIDENT_CARD_MAGIC 0x5072696E /* "Prin" */
@@ -243,6 +263,9 @@
int multi_channels_adjust_count;
unsigned chans_num;
unsigned fmt_flag:1;
+ /* Guard against mmap/write/read races */
+ struct semaphore sem;
+
};
/* hardware channels */
@@ -296,7 +319,7 @@
/* The trident has a certain amount of cross channel interaction
so we use a single per card lock */
spinlock_t lock;
-
+
/* PCI device stuff */
struct pci_dev * pci_dev;
u16 pci_id;
@@ -320,9 +343,11 @@
void (*free_pcm_channel)(struct trident_card *, unsigned int chan);
void (*address_interrupt)(struct trident_card *);
- /* Add by Matt Wu 01-05-2001 for spdif in */
+ /* Added by Matt Wu 01-05-2001 for spdif in */
int multi_channel_use_count;
int rec_channel_use_count;
+ u16 mixer_regs[64][NR_AC97]; /* Made card local by Alan */
+ int mixer_regs_ready;
};
/* table to map from CHANNELMASK to channel attribute for SiS 7018 */
@@ -338,11 +363,14 @@
DSP_BIND_I2S, DSP_BIND_CENTER_LFE, DSP_BIND_SURR, DSP_BIND_SPDIF
};
-/* Add by Matt Wu 01-05-2001 for spdif in */
+/* Added by Matt Wu 01-05-2001 for spdif in */
static int ali_close_multi_channels(void);
static void ali_delay(struct trident_card *card,int interval);
static void ali_detect_spdif_rate(struct trident_card *card);
+static void ali_ac97_write(struct ac97_codec *codec, u8 reg, u16 val);
+static u16 ali_ac97_read(struct ac97_codec *codec, u8 reg);
+
static struct trident_card *devs;
static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val);
@@ -353,15 +381,16 @@
unsigned long arg);
static loff_t trident_llseek(struct file *file, loff_t offset, int origin);
-static void ali_ac97_set(struct ac97_codec *codec, u8 reg, u16 val);
-static u16 ali_ac97_get(struct ac97_codec *codec, u8 reg);
+static void ali_ac97_set(struct trident_card *card, int secondary, u8 reg, u16 val);
+static u16 ali_ac97_get(struct trident_card *card, int secondary, u8 reg);
static void ali_set_spdif_out_rate(struct trident_card *card, unsigned int rate);
static void ali_enable_special_channel(struct trident_state *stat);
static struct trident_channel *ali_alloc_rec_pcm_channel(struct trident_card *card);
static struct trident_channel *ali_alloc_pcm_channel(struct trident_card *card);
-static int ali_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data);
static void ali_restore_regs(struct trident_card *card);
static void ali_save_regs(struct trident_card *card);
+static int trident_suspend(struct pci_dev *dev, u32 unused);
+static int trident_resume(struct pci_dev *dev);
static void ali_free_pcm_channel(struct trident_card *card, unsigned int channel);
static int ali_setup_multi_channels(struct trident_card *card, int chan_nums);
static unsigned int ali_get_spdif_in_rate(struct trident_card *card);
@@ -607,6 +636,10 @@
continue;
outl(data[i], TRID_REG(card, CHANNEL_START + 4*i));
}
+ if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+ outl(ALI_EMOD_Still, TRID_REG(card, ALI_EBUF1));
+ outl(ALI_EMOD_Still, TRID_REG(card, ALI_EBUF2));
+ }
return TRUE;
}
@@ -989,7 +1022,7 @@
static int alloc_dmabuf(struct trident_state *state)
{
struct dmabuf *dmabuf = &state->dmabuf;
- void *rawbuf=NULL;
+ void *rawbuf = NULL;
int order;
struct page *page, *pend;
@@ -1221,7 +1254,7 @@
return -EBUSY;
}
- /* No matter how much data left in the buffer, we have to wait untill
+ /* No matter how much data is left in the buffer, we have to wait until
CSO == ESO/2 or CSO == ESO when address engine interrupts */
if (state->card->pci_id == PCI_DEVICE_ID_ALI_5451)
{
@@ -1234,7 +1267,6 @@
tmo = (dmabuf->dmasize * HZ) / dmabuf->rate;
}
tmo >>= sample_shift[dmabuf->fmt];
-// printk("trident: diff=%d count= %d/%d total=%d tmo=%d hwptr=%d swptr=%d curptr=%d\n",diff,dmabuf->count,dmabuf->dmasize,dmabuf->total_bytes,tmo,dmabuf->hwptr,dmabuf->swptr,trident_get_dma_addr(state));
if (!schedule_timeout(tmo ? tmo : 1) && tmo){
break;
}
@@ -1416,7 +1448,7 @@
{
struct trident_state *state = (struct trident_state *)file->private_data;
struct dmabuf *dmabuf = &state->dmabuf;
- ssize_t ret;
+ ssize_t ret = 0;
unsigned long flags;
unsigned swptr;
int cnt;
@@ -1428,13 +1460,15 @@
VALIDATE_STATE(state);
if (ppos != &file->f_pos)
return -ESPIPE;
+
if (dmabuf->mapped)
return -ENXIO;
- if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
- return ret;
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
- ret = 0;
+
+ down(&state->sem);
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
+ goto out;
while (count > 0) {
spin_lock_irqsave(&state->card->lock, flags);
@@ -1459,8 +1493,10 @@
start_adc(state);
if (file->f_flags & O_NONBLOCK) {
if (!ret) ret = -EAGAIN;
- return ret;
+ goto out;
}
+
+ up(&state->sem);
/* No matter how much space left in the buffer, we have to wait until
CSO == ESO/2 or CSO == ESO when address engine interrupts */
tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
@@ -1483,15 +1519,22 @@
while loop begin and we REALLY have space to record */
}
if (signal_pending(current)) {
- ret = ret ? ret : -ERESTARTSYS;
- return ret;
+ if(!ret) ret = -ERESTARTSYS;
+ goto out;
+ }
+ down(&state->sem);
+ if(dmabuf->mapped)
+ {
+ if(!ret)
+ ret = -ENXIO;
+ goto out;
}
continue;
}
if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) {
if (!ret) ret = -EFAULT;
- return ret;
+ goto out;
}
swptr = (swptr + cnt) % dmabuf->dmasize;
@@ -1506,11 +1549,14 @@
ret += cnt;
start_adc(state);
}
+out:
+ up(&state->sem);
return ret;
}
/* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to
the soundcard. it is drained by the dma machine and filled by this loop. */
+
static ssize_t trident_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
{
struct trident_state *state = (struct trident_state *)file->private_data;
@@ -1528,12 +1574,27 @@
VALIDATE_STATE(state);
if (ppos != &file->f_pos)
return -ESPIPE;
+
+ /*
+ * Guard against an mmap or ioctl while writing
+ */
+
+ down(&state->sem);
+
if (dmabuf->mapped)
- return -ENXIO;
+ {
+ ret = -ENXIO;
+ goto out;
+ }
if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
- return ret;
+ goto out;
+
if (!access_ok(VERIFY_READ, buffer, count))
- return -EFAULT;
+ {
+ ret= -EFAULT;
+ goto out;
+ }
+
ret = 0;
while (count > 0) {
@@ -1559,7 +1620,7 @@
start_dac(state);
if (file->f_flags & O_NONBLOCK) {
if (!ret) ret = -EAGAIN;
- return ret;
+ goto out;
}
/* No matter how much data left in the buffer, we have to wait until
CSO == ESO/2 or CSO == ESO when address engine interrupts */
@@ -1567,6 +1628,8 @@
tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
tmo >>= sample_shift[dmabuf->fmt];
unlock_set_fmt(state);
+ up(&state->sem);
+
/* There are two situations when sleep_on_timeout returns, one is when
the interrupt is serviced correctly and the process is waked up by
ISR ON TIME. Another is when timeout is expired, which means that
@@ -1586,7 +1649,14 @@
}
if (signal_pending(current)) {
if (!ret) ret = -ERESTARTSYS;
- return ret;
+ goto out;
+ }
+ down(&state->sem);
+ if(dmabuf->mapped)
+ {
+ if(!ret)
+ ret = -ENXIO;
+ goto out;
}
continue;
}
@@ -1606,14 +1676,14 @@
ret += copy_count;
if (!ret) ret = -EFAULT;
unlock_set_fmt(state);
- return ret;
+ goto out;
}
}
else {
if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
if (!ret) ret = -EFAULT;
unlock_set_fmt(state);
- return ret;
+ goto out;
}
state_cnt = cnt;
}
@@ -1632,9 +1702,12 @@
ret += cnt;
start_dac(state);
}
+out:
+ up(&state->sem);
return ret;
}
+
/* No kernel lock - we have our own spinlock */
static unsigned int trident_poll(struct file *file, struct poll_table_struct *wait)
{
@@ -1645,17 +1718,32 @@
VALIDATE_STATE(state);
+ /*
+ * Guard against a parallel poll and write causing multiple
+ * prog_dmabuf events
+ */
+
+ down(&state->sem);
+
if (file->f_mode & FMODE_WRITE) {
if (!dmabuf->ready && prog_dmabuf(state, 0))
+ {
+ up(&state->sem);
return 0;
+ }
poll_wait(file, &dmabuf->wait, wait);
}
if (file->f_mode & FMODE_READ) {
if (!dmabuf->ready && prog_dmabuf(state, 1))
+ {
+ up(&state->sem);
return 0;
+ }
poll_wait(file, &dmabuf->wait, wait);
}
+ up(&state->sem);
+
spin_lock_irqsave(&state->card->lock, flags);
trident_update_ptr(state);
if (file->f_mode & FMODE_READ) {
@@ -1685,6 +1773,14 @@
VALIDATE_STATE(state);
lock_kernel();
+
+ /*
+ * Lock against poll read write or mmap creating buffers. Also lock
+ * a read or write against an mmap.
+ */
+
+ down(&state->sem);
+
if (vma->vm_flags & VM_WRITE) {
if ((ret = prog_dmabuf(state, 0)) != 0)
goto out;
@@ -1707,6 +1803,7 @@
dmabuf->mapped = 1;
ret = 0;
out:
+ up(&state->sem);
unlock_kernel();
return ret;
}
@@ -1718,7 +1815,7 @@
unsigned long flags;
audio_buf_info abinfo;
count_info cinfo;
- int val, mapped, ret;
+ int val, mapped, ret = 0;
struct trident_card *card = state->card;
@@ -1733,8 +1830,9 @@
switch (cmd)
{
case OSS_GETVERSION:
- return put_user(SOUND_VERSION, (int *)arg);
-
+ ret = put_user(SOUND_VERSION, (int *)arg);
+ break;
+
case SNDCTL_DSP_RESET:
/* FIXME: spin_lock ? */
if (file->f_mode & FMODE_WRITE) {
@@ -1751,16 +1849,19 @@
dmabuf->swptr = dmabuf->hwptr = 0;
dmabuf->count = dmabuf->total_bytes = 0;
}
- return 0;
+ break;
case SNDCTL_DSP_SYNC:
if (file->f_mode & FMODE_WRITE)
- return drain_dac(state, file->f_flags & O_NONBLOCK);
- return 0;
+ ret = drain_dac(state, file->f_flags & O_NONBLOCK);
+ break;
case SNDCTL_DSP_SPEED: /* set smaple rate */
if (get_user(val, (int *)arg))
- return -EFAULT;
+ {
+ ret = -EFAULT;
+ break;
+ }
if (val >= 0) {
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
@@ -1777,11 +1878,15 @@
spin_unlock_irqrestore(&state->card->lock, flags);
}
}
- return put_user(dmabuf->rate, (int *)arg);
+ ret = put_user(dmabuf->rate, (int *)arg);
+ break;
case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
if (get_user(val, (int *)arg))
- return -EFAULT;
+ {
+ ret = -EFAULT;
+ break;
+ }
lock_set_fmt(state);
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
@@ -1800,26 +1905,34 @@
dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
}
unlock_set_fmt(state);
- return 0;
+ break;
case SNDCTL_DSP_GETBLKSIZE:
if (file->f_mode & FMODE_WRITE) {
if ((val = prog_dmabuf(state, 0)))
- return val;
- return put_user(dmabuf->fragsize, (int *)arg);
+ ret = val;
+ else
+ ret = put_user(dmabuf->fragsize, (int *)arg);
+ break;
}
if (file->f_mode & FMODE_READ) {
if ((val = prog_dmabuf(state, 1)))
- return val;
- return put_user(dmabuf->fragsize, (int *)arg);
+ ret = val;
+ else
+ ret = put_user(dmabuf->fragsize, (int *)arg);
+ break;
}
case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
- return put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg);
+ ret = put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg);
+ break;
case SNDCTL_DSP_SETFMT: /* Select sample format */
if (get_user(val, (int *)arg))
- return -EFAULT;
+ {
+ ret = -EFAULT;
+ break;
+ }
lock_set_fmt(state);
if (val != AFMT_QUERY) {
if (file->f_mode & FMODE_WRITE) {
@@ -1840,12 +1953,16 @@
}
}
unlock_set_fmt(state);
- return put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ?
+ ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ?
AFMT_S16_LE : AFMT_U8, (int *)arg);
+ break;
case SNDCTL_DSP_CHANNELS:
if (get_user(val, (int *)arg))
- return -EFAULT;
+ {
+ ret = -EFAULT;
+ break;
+ }
if (val != 0) {
lock_set_fmt(state);
if (file->f_mode & FMODE_WRITE) {
@@ -1866,21 +1983,22 @@
if( card->rec_channel_use_count > 0 )
{
- printk("Err: Record is working on the card!\n");
- return -EBUSY;
+ printk(KERN_ERR "trident: Record is working on the card!\n");
+ ret = -EBUSY;
+ break;
}
ret = ali_setup_multi_channels(state->card, 6);
if (ret < 0) {
unlock_set_fmt(state);
- return ret;
+ break;
}
down(&state->card->open_sem);
ret = ali_allocate_other_states_resources(state, 6);
if (ret < 0) {
up(&state->card->open_sem);
unlock_set_fmt(state);
- return ret;
+ break;
}
state->card->multi_channel_use_count ++;
up(&state->card->open_sem);
@@ -1905,25 +2023,38 @@
}
unlock_set_fmt(state);
}
- return put_user(val, (int *)arg);
+ ret = put_user(val, (int *)arg);
+ break;
case SNDCTL_DSP_POST:
- /* FIXME: the same as RESET ?? */
- return 0;
+ /* Cause the working fragment to be output */
+ break;
case SNDCTL_DSP_SUBDIVIDE:
if (dmabuf->subdivision)
- return -EINVAL;
+ {
+ ret = -EINVAL;
+ break;
+ }
if (get_user(val, (int *)arg))
- return -EFAULT;
+ {
+ ret = -EFAULT;
+ break;
+ }
if (val != 1 && val != 2 && val != 4)
- return -EINVAL;
+ {
+ ret = -EINVAL;
+ break;
+ }
dmabuf->subdivision = val;
- return 0;
+ break;
case SNDCTL_DSP_SETFRAGMENT:
if (get_user(val, (int *)arg))
- return -EFAULT;
+ {
+ ret = -EFAULT;
+ break;
+ }
dmabuf->ossfragshift = val & 0xffff;
dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
@@ -1934,13 +2065,19 @@
if (dmabuf->ossmaxfrags < 4)
dmabuf->ossmaxfrags = 4;
- return 0;
+ break;
case SNDCTL_DSP_GETOSPACE:
if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
+ {
+ ret = -EINVAL;
+ break;
+ }
if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
- return val;
+ {
+ ret = val;
+ break;
+ }
spin_lock_irqsave(&state->card->lock, flags);
trident_update_ptr(state);
abinfo.fragsize = dmabuf->fragsize;
@@ -1948,13 +2085,20 @@
abinfo.fragstotal = dmabuf->numfrag;
abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
spin_unlock_irqrestore(&state->card->lock, flags);
- return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+ ret = copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+ break;
case SNDCTL_DSP_GETISPACE:
if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
+ {
+ ret = -EINVAL;
+ break;
+ }
if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
- return val;
+ {
+ ret = val;
+ break;
+ }
spin_lock_irqsave(&state->card->lock, flags);
trident_update_ptr(state);
abinfo.fragsize = dmabuf->fragsize;
@@ -1962,15 +2106,17 @@
abinfo.fragstotal = dmabuf->numfrag;
abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
spin_unlock_irqrestore(&state->card->lock, flags);
- return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+ ret = copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+ break;
case SNDCTL_DSP_NONBLOCK:
file->f_flags |= O_NONBLOCK;
- return 0;
+ break;
case SNDCTL_DSP_GETCAPS:
- return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP|DSP_CAP_BIND,
+ ret = put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP|DSP_CAP_BIND,
(int *)arg);
+ break;
case SNDCTL_DSP_GETTRIGGER:
val = 0;
@@ -1978,15 +2124,19 @@
val |= PCM_ENABLE_INPUT;
if ((file->f_mode & FMODE_WRITE) && dmabuf->enable)
val |= PCM_ENABLE_OUTPUT;
- return put_user(val, (int *)arg);
+ ret = put_user(val, (int *)arg);
+ break;
case SNDCTL_DSP_SETTRIGGER:
if (get_user(val, (int *)arg))
- return -EFAULT;
+ {
+ ret = -EFAULT;
+ break;
+ }
if (file->f_mode & FMODE_READ) {
if (val & PCM_ENABLE_INPUT) {
if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
- return ret;
+ break;
start_adc(state);
} else
stop_adc(state);
@@ -1994,18 +2144,24 @@
if (file->f_mode & FMODE_WRITE) {
if (val & PCM_ENABLE_OUTPUT) {
if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
- return ret;
+ break;
start_dac(state);
} else
stop_dac(state);
}
- return 0;
+ break;
case SNDCTL_DSP_GETIPTR:
if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
+ {
+ ret = -EINVAL;
+ break;
+ }
if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
- return val;
+ {
+ ret = val;
+ break;
+ }
spin_lock_irqsave(&state->card->lock, flags);
trident_update_ptr(state);
cinfo.bytes = dmabuf->total_bytes;
@@ -2014,13 +2170,21 @@
if (dmabuf->mapped)
dmabuf->count &= dmabuf->fragsize-1;
spin_unlock_irqrestore(&state->card->lock, flags);
- return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+ ret = copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+ break;
case SNDCTL_DSP_GETOPTR:
if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
+ {
+ ret = -EINVAL;
+ break;
+ }
if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
- return val;
+ {
+ ret = val;
+ break;
+ }
+
spin_lock_irqsave(&state->card->lock, flags);
trident_update_ptr(state);
cinfo.bytes = dmabuf->total_bytes;
@@ -2029,43 +2193,62 @@
if (dmabuf->mapped)
dmabuf->count &= dmabuf->fragsize-1;
spin_unlock_irqrestore(&state->card->lock, flags);
- return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+ ret = copy_to_user((void *)arg, &cinfo, sizeof(cinfo))?-EFAULT:0;
+ break;
case SNDCTL_DSP_SETDUPLEX:
- return -EINVAL;
+ ret = -EINVAL;
+ break;
case SNDCTL_DSP_GETODELAY:
if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
+ {
+ ret = -EINVAL;
+ break;
+ }
if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
- return val;
+ {
+ ret = val;
+ break;
+ }
spin_lock_irqsave(&state->card->lock, flags);
trident_update_ptr(state);
val = dmabuf->count;
spin_unlock_irqrestore(&state->card->lock, flags);
- return put_user(val, (int *)arg);
+ ret = put_user(val, (int *)arg);
+ break;
case SOUND_PCM_READ_RATE:
- return put_user(dmabuf->rate, (int *)arg);
+ ret = put_user(dmabuf->rate, (int *)arg);
+ break;
case SOUND_PCM_READ_CHANNELS:
- return put_user((dmabuf->fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
+ ret = put_user((dmabuf->fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
(int *)arg);
+ break;
case SOUND_PCM_READ_BITS:
- return put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ?
+ ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ?
AFMT_S16_LE : AFMT_U8, (int *)arg);
+ break;
case SNDCTL_DSP_GETCHANNELMASK:
- return put_user(DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE,
+ ret = put_user(DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE,
(int *)arg);
+ break;
case SNDCTL_DSP_BIND_CHANNEL:
if (state->card->pci_id != PCI_DEVICE_ID_SI_7018)
- return -EINVAL;
+ {
+ ret = -EINVAL;
+ break;
+ }
if (get_user(val, (int *)arg))
- return -EFAULT;
+ {
+ ret = -EFAULT;
+ break;
+ }
if (val == DSP_BIND_QUERY) {
val = dmabuf->channel->attribute | 0x3c00;
val = attr2mask[val >> 8];
@@ -2077,17 +2260,20 @@
dmabuf->channel->attribute = (CHANNEL_SPC_PB|SRC_ENABLE);
dmabuf->channel->attribute |= mask2attr[ffs(val)];
}
- return put_user(val, (int *)arg);
+ ret = put_user(val, (int *)arg);
+ break;
case SNDCTL_DSP_MAPINBUF:
case SNDCTL_DSP_MAPOUTBUF:
case SNDCTL_DSP_SETSYNCRO:
case SOUND_PCM_WRITE_FILTER:
case SOUND_PCM_READ_FILTER:
- return -EINVAL;
+ default:
+ ret = -EINVAL;
+ break;
}
- return -EINVAL;
+ return ret;
}
static int trident_open(struct inode *inode, struct file *file)
@@ -2098,7 +2284,16 @@
struct trident_state *state = NULL;
struct dmabuf *dmabuf = NULL;
- /* find an avaiable virtual channel (instance of /dev/dsp) */
+ /* Added by Matt Wu 01-05-2001 */
+ if(file->f_mode & FMODE_READ)
+ {
+ if(card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+ if (card->multi_channel_use_count > 0)
+ return -EBUSY;
+ }
+ }
+
+ /* find an available virtual channel (instance of /dev/dsp) */
while (card != NULL) {
down(&card->open_sem);
if(file->f_mode & FMODE_READ)
@@ -2119,6 +2314,7 @@
return -ENOMEM;
}
memset(state, 0, sizeof(struct trident_state));
+ init_MUTEX(&state->sem);
dmabuf = &state->dmabuf;
goto found_virt;
}
@@ -2183,7 +2379,10 @@
(CHANNEL_REC|PCM_LR|MONO_MIX);
}
trident_set_adc_rate(state, 8000);
- card->rec_channel_use_count ++;
+
+ /* Added by Matt Wu 01-05-2001 */
+ if(card->pci_id == PCI_DEVICE_ID_ALI_5451)
+ card->rec_channel_use_count ++;
}
state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
@@ -2225,15 +2424,15 @@
dealloc_dmabuf(state);
state->card->free_pcm_channel(state->card, dmabuf->channel->num);
- if (state->chans_num > 2)
- {
- if( card->multi_channel_use_count-- < 0 )
- card->multi_channel_use_count = 0;
-
- if (card->multi_channel_use_count == 0)
- ali_close_multi_channels();
-
- ali_free_other_states_resources(state);
+ /* Added by Matt Wu */
+ if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+ if (state->chans_num > 2) {
+ if (card->multi_channel_use_count-- < 0)
+ card->multi_channel_use_count = 0;
+ if (card->multi_channel_use_count == 0)
+ ali_close_multi_channels();
+ ali_free_other_states_resources(state);
+ }
}
}
if (file->f_mode & FMODE_READ) {
@@ -2241,8 +2440,11 @@
dealloc_dmabuf(state);
state->card->free_pcm_channel(state->card, dmabuf->channel->num);
- if( card->rec_channel_use_count-- < 0 )
- card->rec_channel_use_count = 0;
+ /* Added by Matt Wu */
+ if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+ if( card->rec_channel_use_count-- < 0 )
+ card->rec_channel_use_count = 0;
+ }
}
card->states[state->virt] = NULL;
@@ -2373,9 +2575,8 @@
}
/* Write AC97 codec registers for ALi*/
-static void ali_ac97_set(struct ac97_codec *codec, u8 reg, u16 val)
+static void ali_ac97_set(struct trident_card *card, int secondary, u8 reg, u16 val)
{
- struct trident_card *card = (struct trident_card *)codec->private_data;
unsigned int address, mask;
unsigned int wCount1 = 0xffff;
unsigned int wCount2= 0xffff;
@@ -2390,7 +2591,7 @@
address = ALI_AC97_WRITE;
mask = ALI_AC97_WRITE_ACTION | ALI_AC97_AUDIO_BUSY;
- if (codec->id)
+ if (secondary)
mask |= ALI_AC97_SECONDARY;
if (card->revision == ALI_5451_V02)
mask |= ALI_AC97_WRITE_MIXER_REGISTER;
@@ -2421,9 +2622,8 @@
}
/* Read AC97 codec registers for ALi*/
-static u16 ali_ac97_get(struct ac97_codec *codec, u8 reg)
+static u16 ali_ac97_get(struct trident_card *card, int secondary, u8 reg)
{
- struct trident_card *card = (struct trident_card *)codec->private_data;
unsigned int address, mask;
unsigned int wCount1 = 0xffff;
unsigned int wCount2= 0xffff;
@@ -2431,13 +2631,15 @@
unsigned long flags;
u32 data;
+ if(!card)
+ BUG();
+
address = ALI_AC97_READ;
if (card->revision == ALI_5451_V02) {
address = ALI_AC97_WRITE;
- mask &= ALI_AC97_READ_MIXER_REGISTER;
}
mask = ALI_AC97_READ_ACTION | ALI_AC97_AUDIO_BUSY;
- if (codec->id)
+ if (secondary)
mask |= ALI_AC97_SECONDARY;
spin_lock_irqsave(&card->lock, flags);
@@ -2480,6 +2682,56 @@
outl(s_channels, TRID_REG(card, ALI_GLOBAL_CONTROL));
}
+static u16 ali_ac97_read(struct ac97_codec *codec, u8 reg)
+{
+ int id;
+ u16 data;
+ struct trident_card *card = NULL;
+
+ /* Added by Matt Wu */
+ if (!codec)
+ BUG();
+
+ card = (struct trident_card *)codec->private_data;
+
+ if(!card->mixer_regs_ready)
+ return ali_ac97_get(card, codec->id, reg);
+
+ if(codec->id)
+ id = 1;
+ else
+ id = 0;
+
+ data = card->mixer_regs[reg/2][id];
+ return data;
+}
+
+static void ali_ac97_write(struct ac97_codec *codec, u8 reg, u16 val)
+{
+ int id;
+ struct trident_card *card;
+
+ /* Added by Matt Wu */
+ if (!codec)
+ BUG();
+
+ card = (struct trident_card *)codec->private_data;
+
+ if (!card->mixer_regs_ready)
+ {
+ ali_ac97_set(card, codec->id, reg, val);
+ return;
+ }
+
+ if(codec->id)
+ id = 1;
+ else
+ id = 0;
+
+ card->mixer_regs[reg/2][id] = val;
+ ali_ac97_set(card, codec->id, reg, val);
+}
+
/*
flag: ALI_SPDIF_OUT_TO_SPDIF_OUT
ALI_PCM_TO_SPDIF_OUT
@@ -2493,7 +2745,7 @@
char temp;
struct pci_dev *pci_dev = NULL;
- pci_dev = pci_find_device(PCI_VENDOR_ID_AL,PCI_DEVICE_ID_AL_M1533, pci_dev);
+ pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pci_dev);
if (pci_dev == NULL)
return;
pci_read_config_byte(pci_dev, 0x61, &temp);
@@ -2748,14 +3000,14 @@
mdelay(4);
dwValue = inl(TRID_REG(card, ALI_SCTRL));
if (dwValue & 0x2000000) {
- ali_ac97_set(card->ac97_codec[0], 0x02, 8080);
- ali_ac97_set(card->ac97_codec[0], 0x36, 0);
- ali_ac97_set(card->ac97_codec[0], 0x38, 0);
- ali_ac97_set(card->ac97_codec[1], 0x36, 0);
- ali_ac97_set(card->ac97_codec[1], 0x38, 0);
- ali_ac97_set(card->ac97_codec[1], 0x02, 0);
- ali_ac97_set(card->ac97_codec[1], 0x18, 0x0808);
- ali_ac97_set(card->ac97_codec[1], 0x74, 0x3);
+ ali_ac97_write(card->ac97_codec[0], 0x02, 8080);
+ ali_ac97_write(card->ac97_codec[0], 0x36, 0);
+ ali_ac97_write(card->ac97_codec[0], 0x38, 0);
+ ali_ac97_write(card->ac97_codec[1], 0x36, 0);
+ ali_ac97_write(card->ac97_codec[1], 0x38, 0);
+ ali_ac97_write(card->ac97_codec[1], 0x02, 0x0606);
+ ali_ac97_write(card->ac97_codec[1], 0x18, 0x0303);
+ ali_ac97_write(card->ac97_codec[1], 0x74, 0x3);
return 1;
}
}
@@ -2853,7 +3105,7 @@
outl(ALI_DISABLE_ALL_IRQ, TRID_REG(card, T4D_MISCINT));
for (i = 1; i < ALI_MIXER_REGS; i++)
- ali_registers.mixer_regs[i] = ali_ac97_get (card->ac97_codec[0], i*2);
+ ali_registers.mixer_regs[i] = ali_ac97_read (card->ac97_codec[0], i*2);
for (i = 0; i < ALI_GLOBAL_REGS; i++)
{
@@ -2884,7 +3136,7 @@
cli();
for (i = 1; i < ALI_MIXER_REGS; i++)
- ali_ac97_set(card->ac97_codec[0], i*2, ali_registers.mixer_regs[i]);
+ ali_ac97_write(card->ac97_codec[0], i*2, ali_registers.mixer_regs[i]);
for (i = 0; i < ALI_CHANNELS; i++)
{
@@ -2908,19 +3160,22 @@
restore_flags(flags);
}
-static int ali_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
+static int trident_suspend(struct pci_dev *dev, u32 unused)
{
- struct trident_card *card = (struct trident_card *)dev->data;
-
- if (card) {
- switch (rqst) {
- case PM_SUSPEND:
- ali_save_regs(card);
- break;
- case PM_RESUME:
- ali_restore_regs(card);
- break;
- }
+ struct trident_card *card = (struct trident_card *) dev;
+
+ if(card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+ ali_save_regs(card);
+ }
+ return 0;
+}
+
+static int trident_resume(struct pci_dev *dev)
+{
+ struct trident_card *card = (struct trident_card *) dev;
+
+ if(card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+ ali_restore_regs(card);
}
return 0;
}
@@ -3236,29 +3491,75 @@
open: trident_open_mixdev,
};
+static int ali_reset_5451(struct trident_card *card)
+{
+ struct pci_dev *pci_dev = NULL;
+ unsigned int dwVal;
+ unsigned short wCount, wReg;
+
+ pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pci_dev);
+ if (pci_dev == NULL)
+ return -1;
+
+ pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+ pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000);
+ udelay(5000);
+ pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+ pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff);
+ udelay(5000);
+
+ pci_dev = card->pci_dev;
+ if (pci_dev == NULL)
+ return -1;
+
+ pci_read_config_dword(pci_dev, 0x44, &dwVal);
+ pci_write_config_dword(pci_dev, 0x44, dwVal | 0x000c0000);
+ udelay(500);
+ pci_read_config_dword(pci_dev, 0x44, &dwVal);
+ pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff);
+ udelay(5000);
+
+ wCount = 200;
+ while(wCount--) {
+ wReg = ali_ac97_get(card, 0, AC97_POWER_CONTROL);
+ if((wReg & 0x000f) == 0x000f)
+ return 0;
+ udelay(500);
+ }
+ return 0;
+}
+
/* AC97 codec initialisation. */
static int __init trident_ac97_init(struct trident_card *card)
{
int num_ac97 = 0;
unsigned long ready_2nd = 0;
struct ac97_codec *codec;
+ int i = 0;
+
/* initialize controller side of AC link, and find out if secondary codes
really exist */
switch (card->pci_id)
{
case PCI_DEVICE_ID_ALI_5451:
- outl(0x80000000,TRID_REG(card, ALI_GLOBAL_CONTROL));
- outl(0x00000000,TRID_REG(card, 0xa4));
- outl(0xffffffff,TRID_REG(card, 0x98));
- outl(0x00000000,TRID_REG(card, 0xa8));
- outb(0x10, TRID_REG(card, 0x22));
+ if (ali_reset_5451(card))
+ {
+ printk(KERN_ERR "trident_ac97_init: error resetting 5451.\n");
+ return -1;
+ }
+ outl(0x80000001,TRID_REG(card, ALI_GLOBAL_CONTROL));
+ outl(0x00000000,TRID_REG(card, T4D_AINTEN_A));
+ outl(0xffffffff,TRID_REG(card, T4D_AINT_A));
+ outl(0x00000000,TRID_REG(card, T4D_MUSICVOL_WAVEVOL));
+ outb(0x10, TRID_REG(card, ALI_MPUR2));
ready_2nd = inl(TRID_REG(card, ALI_SCTRL));
ready_2nd &= 0x3fff;
outl(ready_2nd | PCMOUT | 0x8000, TRID_REG(card, ALI_SCTRL));
ready_2nd = inl(TRID_REG(card, ALI_SCTRL));
ready_2nd &= SI_AC97_SECONDARY_READY;
-// printk("codec 2 ready flag= %lx\n", ready_2nd);
+ if (card->revision < ALI_5451_V02)
+ ready_2nd = 0;
break;
case PCI_DEVICE_ID_SI_7018:
/* disable AC97 GPIO interrupt */
@@ -3295,8 +3596,8 @@
codec->id = num_ac97;
if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
- codec->codec_read = ali_ac97_get;
- codec->codec_write = ali_ac97_set;
+ codec->codec_read = ali_ac97_read;
+ codec->codec_write = ali_ac97_write;
}
else {
codec->codec_read = trident_ac97_get;
@@ -3316,10 +3617,18 @@
/* if there is no secondary codec at all, don't probe any more */
if (!ready_2nd)
- return num_ac97+1;
+ break;
}
- return 1/*num_ac97*/;
+ if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+ for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
+ if (card->ac97_codec[num_ac97] == NULL)
+ break;
+ for (i=0; i<64;i++)
+ card->mixer_regs[i][num_ac97] = ali_ac97_get(card, num_ac97,i*2);
+ }
+ }
+ return num_ac97+1;
}
/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered
@@ -3331,20 +3640,14 @@
dma_addr_t mask;
int bits;
u8 revision;
+ int i = 0;
if (pci_enable_device(pci_dev))
return -ENODEV;
- if (pci_dev->device == PCI_DEVICE_ID_ALI_5451) {
- mask = 0xffffffff;
- bits = 32;
- } else {
- mask = TRIDENT_DMA_MASK;
- bits = 30;
- }
- if (pci_set_dma_mask(pci_dev, mask)) {
+ if (pci_set_dma_mask(pci_dev, TRIDENT_DMA_MASK)) {
printk(KERN_ERR "trident: architecture does not support"
- " %dbit PCI busmaster DMA\n", bits);
+ " 30bit PCI busmaster DMA\n");
return -ENODEV;
}
pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision);
@@ -3383,14 +3686,6 @@
card_names[pci_id->driver_data], card->iobase, card->irq);
if(card->pci_id == PCI_DEVICE_ID_ALI_5451) {
- /* ALi Power Management */
- struct pm_dev *pmdev;
-
- pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(pci_dev),
- ali_pm_callback);
- if (pmdev)
- pmdev->data = card;
-
/* ALi channel Management */
card->alloc_pcm_channel = ali_alloc_pcm_channel;
card->alloc_rec_pcm_channel = ali_alloc_rec_pcm_channel;
@@ -3438,22 +3733,38 @@
kfree(card);
return -ENODEV;
}
+ card->mixer_regs_ready = 0;
/* initialize AC97 codec and register /dev/mixer */
if (trident_ac97_init(card) <= 0) {
+ /* unregister audio devices */
+ for (i = 0; i < NR_AC97; i++) {
+ if (card->ac97_codec[i] != NULL) {
+ unregister_sound_mixer(card->ac97_codec[i]->dev_mixer);
+ kfree (card->ac97_codec[i]);
+ }
+ }
unregister_sound_dsp(card->dev_audio);
release_region(iobase, 256);
free_irq(card->irq, card);
kfree(card);
return -ENODEV;
}
+ card->mixer_regs_ready = 1;
outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL));
if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+ if(card->revision == ALI_5451_V02)
+ ali_close_multi_channels();
/* edited by HMSEO for GT sound */
-#ifdef CONFIG_ALPHA_NAUTILUS
+#if defined CONFIG_ALPHA_NAUTILUS || CONFIG_ALPHA_GENERIC
u16 ac97_data;
- ac97_data = ali_ac97_get (card->ac97_codec[0], AC97_POWER_CONTROL);
- ali_ac97_set (card->ac97_codec[0], AC97_POWER_CONTROL, ac97_data | ALI_EAPD_POWER_DOWN);
+ extern struct hwrpb_struct *hwrpb;
+
+ if ((hwrpb->sys_type) == 201) {
+ printk(KERN_INFO "trident: Running on Alpha system type Nautilus\n");
+ ac97_data = ali_ac97_get(card, 0, AC97_POWER_CONTROL);
+ ali_ac97_set(card, 0, AC97_POWER_CONTROL, ac97_data |
+ }
#endif
/* edited by HMSEO for GT sound*/
}
@@ -3479,7 +3790,6 @@
#ifdef CONFIG_PROC_FS
remove_proc_entry("ALi5451", NULL);
#endif
- pm_unregister_all(ali_pm_callback);
}
/* Kill interrupts, and SP/DIF */
@@ -3512,6 +3822,8 @@
id_table: trident_pci_tbl,
probe: trident_probe,
remove: trident_remove,
+ suspend: trident_suspend,
+ resume: trident_resume
};
static int __init trident_init_module (void)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)