patch-2.4.0-test10 linux/drivers/sound/trident.c
Next file: linux/drivers/sound/trident.h
Previous file: linux/drivers/sound/sound_core.c
Back to the patch index
Back to the overall index
- Lines: 1414
- Date:
Fri Oct 27 10:35:07 2000
- Orig file:
v2.4.0-test9/linux/drivers/sound/trident.c
- Orig date:
Sun Oct 8 10:50:29 2000
diff -u --recursive --new-file v2.4.0-test9/linux/drivers/sound/trident.c linux/drivers/sound/trident.c
@@ -12,7 +12,7 @@
* Hacked up by:
* Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
* Ollie Lho <ollie@sis.com.tw> SiS 7018 Audio Core Support
- * Ching Ling Lee <cling-li@ali.com.tw> ALi 5451 Audio Core Support
+ * Ching-Ling Lee <cling-li@ali.com.tw> ALi 5451 Audio Core Support
*
*
* This program is free software; you can redistribute it and/or modify
@@ -30,6 +30,12 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* History
+ * v0.14.6
+ * Oct 18 2000 Ching-Ling Lee
+ * 5.1-channels support for ALi
+ * June 28 2000 Ching-Ling Lee
+ * S/PDIF out/in(playback/record) support for ALi 1535+, using /proc to be selected by user
+ * Simple Power Management support for ALi
* v0.14.5 May 23 2000 Ollie Lho
* Misc bug fix from the Net
* v0.14.4 May 20 2000 Aaron Holtzman
@@ -38,10 +44,10 @@
* remove open_wait wq (which appears to be unused)
* v0.14.3 May 10 2000 Ollie Lho
* fixed a small bug in trident_update_ptr, xmms 1.0.1 no longer uses 100% CPU
- * v0.14.2 Mar 29 2000 Ching Ling Lee
+ * v0.14.2 Mar 29 2000 Ching-Ling Lee
* Add clear to silence advance in trident_update_ptr
* fix invalid data of the end of the sound
- * v0.14.1 Mar 24 2000 Ching Ling Lee
+ * v0.14.1 Mar 24 2000 Ching-Ling Lee
* ALi 5451 support added, playback and recording O.K.
* ALi 5451 originally developed and structured based on sonicvibes, and
* suggested to merge into this file by Alan Cox.
@@ -113,10 +119,13 @@
#include <asm/uaccess.h>
#include <asm/hardirq.h>
#include <linux/bitops.h>
+#include <linux/proc_fs.h>
#include "trident.h"
-#define DRIVER_VERSION "0.14.5"
+#include <linux/pm.h>
+
+#define DRIVER_VERSION "0.14.6"
/* magic numbers to protect our data structures */
#define TRIDENT_CARD_MAGIC 0x5072696E /* "Prin" */
@@ -133,6 +142,8 @@
/* minor number of /dev/swmodem (temporary, experimental) */
#define SND_DEV_SWMODEM 7
+static const unsigned ali_multi_channels[] = { ALI_SURR_LEFT_CHANNEL, ALI_SURR_RIGHT_CHANNEL,ALI_CENTER_CHANNEL, ALI_LEF_CHANNEL/*, ALI_SURR_LEFT_CHANNEL, ALI_SURR_RIGHT_CHANNEL*/};
+
static const unsigned sample_size[] = { 1, 2, 2, 4 };
static const unsigned sample_shift[] = { 0, 1, 1, 2 };
@@ -215,6 +226,11 @@
int ossmaxfrags;
unsigned subdivision;
} dmabuf;
+
+ /* 5.1channels */
+ struct trident_state *other_states[4];
+ int multi_channels_adjust_count;
+ unsigned chans_num;
};
/* hardware channels */
@@ -306,10 +322,7 @@
DSP_BIND_I2S, DSP_BIND_CENTER_LFE, DSP_BIND_SURR, DSP_BIND_SPDIF
};
-static struct trident_card *devs = NULL;
-
-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 struct trident_card *devs;
static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val);
static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg);
@@ -319,6 +332,37 @@
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_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 ali_allocate_multi_channels(struct trident_state *state, int chan_nums);
+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);
+static void ali_setup_spdif_in(struct trident_card *card);
+static void ali_disable_spdif_in(struct trident_card *card);
+static void ali_disable_special_channel(struct trident_card *card, int ch);
+static void ali_setup_spdif_out(struct trident_card *card, int flag);
+static unsigned int ali_write_5_1(struct trident_state *state, const char *buffer,int cnt_for_multi_channel);
+
+
+/* save registers for ALi Power Management */
+static struct ali_saved_registers {
+ unsigned long global_regs[ALI_GLOBAL_REGS];
+ unsigned long channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS];
+ unsigned mixer_regs[ALI_MIXER_REGS];
+} ali_registers;
+
+#define seek_offset(dma_ptr, buffer, cnt, offset) (dma_ptr) += (offset); \
+ (buffer) += (offset); \
+ (cnt) -= (offset);
+
static int trident_enable_loop_interrupts(struct trident_card * card)
{
u32 global_control;
@@ -496,44 +540,6 @@
return NULL;
}
-static struct trident_channel *ali_alloc_pcm_channel(struct trident_card *card)
-{
- struct trident_pcm_bank *bank;
- int idx;
-
- bank = &card->banks[BANK_A];
-
- for (idx = ALI_PCM_OUT_CHANNEL_FIRST; idx <= ALI_PCM_OUT_CHANNEL_LAST ; idx++) {
- if (!(bank->bitmap & (1 << idx))) {
- struct trident_channel *channel = &bank->channels[idx];
- bank->bitmap |= 1 << idx;
- channel->num = idx;
- return channel;
- }
- }
-
- /* no more free channels avaliable */
- printk(KERN_ERR "trident: no more channels available on Bank B.\n");
- return NULL;
-}
-
-static struct trident_channel *ali_alloc_rec_pcm_channel(struct trident_card *card)
-{
- struct trident_pcm_bank *bank;
- int idx = ALI_PCM_IN_CHANNEL;
-
- bank = &card->banks[BANK_A];
-
- if (!(bank->bitmap & (1 << idx))) {
- struct trident_channel *channel = &bank->channels[idx];
- bank->bitmap |= 1 << idx;
- channel->num = idx;
- return channel;
- }
- return NULL;
-}
-
-
static void trident_free_pcm_channel(struct trident_card *card, unsigned int channel)
{
int bank;
@@ -547,20 +553,6 @@
card->banks[bank].bitmap &= ~(1 << (channel));
}
-static void ali_free_pcm_channel(struct trident_card *card, unsigned int channel)
-{
- int bank;
-
- if (channel > 31)
- return;
-
- bank = channel >> 5;
- channel = channel & 0x1f;
-
- card->banks[bank].bitmap &= ~(1 << (channel));
-}
-
-
/* called with spin lock held */
static int trident_load_channel_registers(struct trident_card *card, u32 *data, unsigned int channel)
@@ -713,6 +705,16 @@
if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) {
channel->attribute = 0;
+ if (state->card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+ if ((channel->num == ALI_SPDIF_IN_CHANNEL) || (channel->num == ALI_PCM_IN_CHANNEL))
+ ali_disable_special_channel(state->card, channel->num);
+ else if ((inl(TRID_REG(state->card, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_OUT_CH_ENABLE)
+ && (channel->num == ALI_SPDIF_OUT_CHANNEL))
+ {
+ ali_set_spdif_out_rate(state->card, state->dmabuf.rate);
+ state->dmabuf.channel->delta = 0x1000;
+ }
+ }
}
channel->fm_vol = 0x0;
@@ -742,11 +744,14 @@
struct trident_card *card = state->card;
struct dmabuf *dmabuf = &state->dmabuf;
struct trident_channel *channel = dmabuf->channel;
+ unsigned int rate;
/* Enable AC-97 ADC (capture) */
switch (card->pci_id)
{
case PCI_DEVICE_ID_ALI_5451:
+ ali_enable_special_channel(state);
+ break;
case PCI_DEVICE_ID_SI_7018:
/* for 7018, the ac97 is always in playback/record (duplex) mode */
break;
@@ -768,7 +773,12 @@
channel->lba = virt_to_bus(dmabuf->rawbuf);
channel->delta = compute_rate_rec(dmabuf->rate);
-
+ if ((card->pci_id == PCI_DEVICE_ID_ALI_5451) && (channel->num == ALI_SPDIF_IN_CHANNEL)) {
+ rate = ali_get_spdif_in_rate(card);
+ if (rate != 48000)
+ channel->delta = ((rate << 12) / dmabuf->rate) & 0x0000ffff;
+ }
+
channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt];
channel->eso -= 1;
@@ -882,6 +892,12 @@
dmabuf->enable &= ~DAC_RUNNING;
trident_stop_voice(card, chan_num);
+ if (state->chans_num == 6) {
+ trident_stop_voice(card, state->other_states[0]->dmabuf.channel->num);
+ trident_stop_voice(card, state->other_states[1]->dmabuf.channel->num);
+ trident_stop_voice(card, state->other_states[2]->dmabuf.channel->num);
+ trident_stop_voice(card, state->other_states[3]->dmabuf.channel->num);
+ }
trident_disable_voice_irq(card, chan_num);
}
@@ -907,6 +923,12 @@
dmabuf->enable |= DAC_RUNNING;
trident_enable_voice_irq(card, chan_num);
trident_start_voice(card, chan_num);
+ if (state->chans_num == 6) {
+ trident_start_voice(card, state->other_states[0]->dmabuf.channel->num);
+ trident_start_voice(card, state->other_states[1]->dmabuf.channel->num);
+ trident_start_voice(card, state->other_states[2]->dmabuf.channel->num);
+ trident_start_voice(card, state->other_states[3]->dmabuf.channel->num);
+ }
}
spin_unlock_irqrestore(&card->lock, flags);
}
@@ -970,64 +992,101 @@
{
struct dmabuf *dmabuf = &state->dmabuf;
unsigned bytepersec;
- unsigned bufsize;
+ struct trident_state *s = state;
+ unsigned bufsize, dma_nums, default_order;
unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&state->card->lock, flags);
- dmabuf->hwptr = dmabuf->swptr = dmabuf->total_bytes = 0;
- dmabuf->count = dmabuf->error = 0;
- spin_unlock_irqrestore(&state->card->lock, flags);
-
- /* allocate DMA buffer if not allocated yet */
- if (!dmabuf->rawbuf)
- if ((ret = alloc_dmabuf(state)))
- return ret;
-
- /* FIXME: figure out all this OSS fragment stuff */
- bytepersec = dmabuf->rate << sample_shift[dmabuf->fmt];
- bufsize = PAGE_SIZE << dmabuf->buforder;
- if (dmabuf->ossfragshift) {
- if ((1000 << dmabuf->ossfragshift) < bytepersec)
- dmabuf->fragshift = ld2(bytepersec/1000);
- else
- dmabuf->fragshift = dmabuf->ossfragshift;
- } else {
- /* lets hand out reasonable big ass buffers by default */
- dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT -2);
- }
- dmabuf->numfrag = bufsize >> dmabuf->fragshift;
- while (dmabuf->numfrag < 4 && dmabuf->fragshift > 3) {
- dmabuf->fragshift--;
- dmabuf->numfrag = bufsize >> dmabuf->fragshift;
+ int ret, i, order;
+ struct page *page, *pend;
+
+ default_order = DMABUF_DEFAULTORDER;
+ if (s->chans_num == 6) {
+ dma_nums = 5;
}
- dmabuf->fragsize = 1 << dmabuf->fragshift;
- if (dmabuf->ossmaxfrags >= 4 && dmabuf->ossmaxfrags < dmabuf->numfrag)
- dmabuf->numfrag = dmabuf->ossmaxfrags;
- dmabuf->fragsamples = dmabuf->fragsize >> sample_shift[dmabuf->fmt];
- dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift;
-
- memset(dmabuf->rawbuf, (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80,
- dmabuf->dmasize);
-
- spin_lock_irqsave(&state->card->lock, flags);
- if (rec) {
- trident_rec_setup(state);
- } else {
- trident_play_setup(state);
+ else {
+ dma_nums = 1;
}
- spin_unlock_irqrestore(&state->card->lock, flags);
+
+ for (i = 0; i < dma_nums; i++) {
+ if (i > 0) {
+ s = state->other_states[i - 1];
+ dmabuf = &s->dmabuf;
+ dmabuf->fmt = state->dmabuf.fmt;
+ dmabuf->rate = state->dmabuf.rate;
+ }
+
+ spin_lock_irqsave(&s->card->lock, flags);
+ dmabuf->hwptr = dmabuf->swptr = dmabuf->total_bytes = 0;
+ dmabuf->count = dmabuf->error = 0;
+ spin_unlock_irqrestore(&s->card->lock, flags);
+
+ /* allocate DMA buffer if not allocated yet */
+ if (!dmabuf->rawbuf) {
+ if (i == 0) {
+ if ((ret = alloc_dmabuf(state)))
+ return ret;
+ }
+ else {
+ if ((order = state->dmabuf.buforder - 1) >= DMABUF_MINORDER)
+ dmabuf->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order);
+ if (!dmabuf->rawbuf) {
+ free_pages((unsigned long)state->dmabuf.rawbuf, state->dmabuf.buforder);
+ state->dmabuf.rawbuf = NULL;
+ i-=2;
+ for (; i >= 0; i--)
+ free_pages((unsigned long)state->other_states[i]->dmabuf.rawbuf, state->other_states[i]->dmabuf.buforder);
+ return -ENOMEM;
+ }
+ dmabuf->ready = dmabuf->mapped = 0;
+ dmabuf->buforder = order;
+ pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << order) - 1);
+ for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++)
+ mem_map_reserve(page);
+ }
+ }
+ /* FIXME: figure out all this OSS fragment stuff */
+ bytepersec = dmabuf->rate << sample_shift[dmabuf->fmt];
+ bufsize = PAGE_SIZE << dmabuf->buforder;
+ if (dmabuf->ossfragshift) {
+ if ((1000 << dmabuf->ossfragshift) < bytepersec)
+ dmabuf->fragshift = ld2(bytepersec/1000);
+ else
+ dmabuf->fragshift = dmabuf->ossfragshift;
+ } else {
+ /* lets hand out reasonable big ass buffers by default */
+ dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT -2);
+ }
+ dmabuf->numfrag = bufsize >> dmabuf->fragshift;
+ while (dmabuf->numfrag < 4 && dmabuf->fragshift > 3) {
+ dmabuf->fragshift--;
+ dmabuf->numfrag = bufsize >> dmabuf->fragshift;
+ }
+ dmabuf->fragsize = 1 << dmabuf->fragshift;
+ if (dmabuf->ossmaxfrags >= 4 && dmabuf->ossmaxfrags < dmabuf->numfrag)
+ dmabuf->numfrag = dmabuf->ossmaxfrags;
+ dmabuf->fragsamples = dmabuf->fragsize >> sample_shift[dmabuf->fmt];
+ dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift;
+
+ memset(dmabuf->rawbuf, (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80,
+ dmabuf->dmasize);
+
+ spin_lock_irqsave(&s->card->lock, flags);
+ if (rec) {
+ trident_rec_setup(s);
+ } else {
+ trident_play_setup(s);
+ }
+ spin_unlock_irqrestore(&s->card->lock, flags);
- /* set the ready flag for the dma buffer */
- dmabuf->ready = 1;
+ /* set the ready flag for the dma buffer */
+ dmabuf->ready = 1;
#ifdef DEBUG
- printk("trident: prog_dmabuf, sample rate = %d, format = %d, numfrag = %d, "
+ printk("trident: prog_dmabuf(%d), sample rate = %d, format = %d, numfrag = %d, "
"fragsize = %d dmasize = %d\n",
- dmabuf->rate, dmabuf->fmt, dmabuf->numfrag,
+ dmabuf->channel->num, dmabuf->rate, dmabuf->fmt, dmabuf->numfrag,
dmabuf->fragsize, dmabuf->dmasize);
#endif
-
+ }
return 0;
}
@@ -1151,6 +1210,27 @@
__stop_adc(state);
dmabuf->error++;
}
+ if (dmabuf->count < (signed)dmabuf->dmasize/2)
+ wake_up(&dmabuf->wait);
+ }
+ }
+
+ /* error handling and process wake up for DAC */
+ if (dmabuf->enable == DAC_RUNNING) {
+ if (dmabuf->mapped) {
+ dmabuf->count += diff;
+ if (dmabuf->count >= (signed)dmabuf->fragsize)
+ wake_up(&dmabuf->wait);
+ } else {
+ dmabuf->count -= diff;
+
+ if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) {
+ /* buffer underrun or buffer overrun, we have no way to recover
+ it here, just stop the machine and let the process force hwptr
+ and swptr to sync */
+ __stop_dac(state);
+ dmabuf->error++;
+ }
else if (!dmabuf->endcleared) {
swptr = dmabuf->swptr;
silence = (dmabuf->fmt & TRIDENT_FMT_16BIT ? 0 : 0x80);
@@ -1168,7 +1248,18 @@
//clear the invalid data
memset (dmabuf->rawbuf + swptr,
silence, clear_cnt);
-
+ if (state->chans_num == 6) {
+ clear_cnt = clear_cnt / 2;
+ swptr = swptr / 2;
+ memset (state->other_states[0]->dmabuf.rawbuf + swptr,
+ silence, clear_cnt);
+ memset (state->other_states[1]->dmabuf.rawbuf + swptr,
+ silence, clear_cnt);
+ memset (state->other_states[2]->dmabuf.rawbuf + swptr,
+ silence, clear_cnt);
+ memset (state->other_states[3]->dmabuf.rawbuf + swptr,
+ silence, clear_cnt);
+ }
dmabuf->endcleared = 1;
}
} else if (dmabuf->count < (signed) dmabuf->fragsize) {
@@ -1176,35 +1267,23 @@
if ((swptr + clear_cnt) > dmabuf->dmasize)
clear_cnt = dmabuf->dmasize - swptr;
memset (dmabuf->rawbuf + swptr, silence, clear_cnt);
+ if (state->chans_num == 6) {
+ clear_cnt = clear_cnt / 2;
+ swptr = swptr / 2;
+ memset (state->other_states[0]->dmabuf.rawbuf + swptr,
+ silence, clear_cnt);
+ memset (state->other_states[1]->dmabuf.rawbuf + swptr,
+ silence, clear_cnt);
+ memset (state->other_states[2]->dmabuf.rawbuf + swptr,
+ silence, clear_cnt);
+ memset (state->other_states[3]->dmabuf.rawbuf + swptr,
+ silence, clear_cnt);
+ }
dmabuf->endcleared = 1;
}
}
/* trident_update_ptr is called by interrupt handler or by process via
ioctl/poll, we only wake up the waiting process when we have more
- than 1/2 buffer of data to process (always true for interrupt handler) */
- if (dmabuf->count > (signed)dmabuf->dmasize/2)
- wake_up(&dmabuf->wait);
- }
- }
-
- /* error handling and process wake up for DAC */
- if (dmabuf->enable == DAC_RUNNING) {
- if (dmabuf->mapped) {
- dmabuf->count += diff;
- if (dmabuf->count >= (signed)dmabuf->fragsize)
- wake_up(&dmabuf->wait);
- } else {
- dmabuf->count -= diff;
-
- if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) {
- /* buffer underrun or buffer overrun, we have no way to recover
- it here, just stop the machine and let the process force hwptr
- and swptr to sync */
- __stop_dac(state);
- dmabuf->error++;
- }
- /* trident_update_ptr is called by interrupt handler or by process via
- ioctl/poll, we only wake up the waiting process when we have more
than 1/2 buffer free (always true for interrupt handler) */
if (dmabuf->count < (signed)dmabuf->dmasize/2)
wake_up(&dmabuf->wait);
@@ -1235,55 +1314,6 @@
}
}
-static void ali_address_interrupt(struct trident_card *card)
-{
- int i;
- u32 mask = trident_get_interrupt_mask (card, BANK_A);
-
-#ifdef DEBUG
- /* Sanity check to make sure that every state has a channel
- and vice versa. */
- u32 done = 0;
- unsigned ns = 0;
- unsigned nc = 0;
- for (i = 0; i < NR_HW_CH; i++) {
- if (card->banks[BANK_A].bitmap & (1<<i))
- nc ++;
-
- if (card->states[i]) {
- u32 bit = 1 << card->states[i]->dmabuf.channel->num;
- if (bit & done)
- printk (KERN_ERR "trident: channel allocated to two states\n");
- ns++;
-
- done |= bit;
- }
- }
- if (ns != nc)
- printk (KERN_ERR "trident: number of states != number of channels\n");
-#endif
-
- for (i = 0; mask && i < NR_HW_CH; i++) {
- struct trident_state *state = card->states[i];
- if (!state)
- continue;
-
- trident_ack_channel_interrupt(card, state->dmabuf.channel->num);
- mask &= ~ (1<<state->dmabuf.channel->num);
- state->dmabuf.update_flag |= ALI_ADDRESS_INT_UPDATE;
- trident_update_ptr(state);
- }
-
- if (mask)
- for (i = 0; i < NR_HW_CH; i++)
- if (mask & (1<<i)) {
- printk("ali: spurious channel irq %d.\n", i);
- trident_stop_voice(card, i);
- trident_disable_voice_irq(card, i);
- }
-
-}
-
static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct trident_card *card = (struct trident_card *)dev_id;
@@ -1337,10 +1367,6 @@
return -EFAULT;
ret = 0;
- if (state->card->pci_id == PCI_DEVICE_ID_ALI_5451)
- outl(inl(TRID_REG (state->card, ALI_GLOBAL_CONTROL)) | ALI_PCM_IN_ENABLE,
- TRID_REG (state->card, ALI_GLOBAL_CONTROL));
-
while (count > 0) {
spin_lock_irqsave(&state->card->lock, flags);
if (dmabuf->count > (signed) dmabuf->dmasize) {
@@ -1424,11 +1450,11 @@
unsigned long flags;
unsigned swptr;
int cnt;
+ unsigned int state_cnt;
#ifdef DEBUG
printk("trident: trident_write called, count = %d\n", count);
#endif
-
VALIDATE_STATE(state);
if (ppos != &file->f_pos)
return -ESPIPE;
@@ -1440,11 +1466,6 @@
return -EFAULT;
ret = 0;
- if (state->card->pci_id == PCI_DEVICE_ID_ALI_5451)
- if (dmabuf->channel->num == ALI_PCM_IN_CHANNEL)
- outl ( inl (TRID_REG (state->card, ALI_GLOBAL_CONTROL)) &
- ALI_PCM_IN_DISABLE, TRID_REG (state->card, ALI_GLOBAL_CONTROL));
-
while (count > 0) {
spin_lock_irqsave(&state->card->lock, flags);
if (dmabuf->count < 0) {
@@ -1497,21 +1518,28 @@
}
continue;
}
- if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
- if (!ret) ret = -EFAULT;
- return ret;
+
+ if (state->chans_num == 6) {
+ state_cnt = ali_write_5_1(state, buffer, cnt);
}
-
- swptr = (swptr + cnt) % dmabuf->dmasize;
-
+ else {
+ if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
+ if (!ret) ret = -EFAULT;
+ return ret;
+ }
+ state_cnt = cnt;
+ }
+
+ swptr = (swptr + state_cnt) % dmabuf->dmasize;
+
spin_lock_irqsave(&state->card->lock, flags);
dmabuf->swptr = swptr;
- dmabuf->count += cnt;
+ dmabuf->count += state_cnt;
dmabuf->endcleared = 0;
spin_unlock_irqrestore(&state->card->lock, flags);
count -= cnt;
- buffer += cnt;
+ buffer += cnt;
ret += cnt;
start_dac(state);
}
@@ -1720,22 +1748,38 @@
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
dmabuf->ready = 0;
- if (val >= 2)
+ if (val >= 2) {
dmabuf->fmt |= TRIDENT_FMT_STEREO;
+ if (val == 6) {
+ val = ali_setup_multi_channels(state->card, 6);
+ if (val < 0)
+ return val;
+ down(&state->card->open_sem);
+ val = ali_allocate_multi_channels(state, 6);
+ up(&state->card->open_sem);
+ if (val < 0)
+ return val;
+ val = 6;
+ }
+ else val = 2;
+ }
else
dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
dmabuf->ready = 0;
- if (val >= 2)
+ if (val >= 2) {
+ if (val!=6)
+ val = 2;
dmabuf->fmt |= TRIDENT_FMT_STEREO;
+ }
else
dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
}
}
- return put_user((dmabuf->fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
- (int *)arg);
+ state->chans_num = val;
+ return put_user(val, (int *)arg);
case SNDCTL_DSP_POST:
/* FIXME: the same as RESET ?? */
@@ -1982,11 +2026,6 @@
}
if (file->f_mode & FMODE_READ) {
- if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
- card->states[ALI_PCM_IN_CHANNEL] = state;
- card->states[i] = NULL;
- state->virt = ALI_PCM_IN_CHANNEL;
- }
/* FIXME: Trident 4d can only record in signed 16-bits stereo, 48kHz sample,
to be dealed with in trident_set_adc_rate() ?? */
dmabuf->fmt &= ~TRIDENT_FMT_MASK;
@@ -2036,6 +2075,24 @@
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
+ if (state->chans_num == 6) {
+ dealloc_dmabuf(state->other_states[0]);
+ dealloc_dmabuf(state->other_states[1]);
+ dealloc_dmabuf(state->other_states[2]);
+ dealloc_dmabuf(state->other_states[3]);
+ state->card->free_pcm_channel(state->card, state->other_states[0]->dmabuf.channel->num);
+ state->card->free_pcm_channel(state->card, state->other_states[1]->dmabuf.channel->num);
+ state->card->free_pcm_channel(state->card, state->other_states[2]->dmabuf.channel->num);
+ state->card->free_pcm_channel(state->card, state->other_states[3]->dmabuf.channel->num);
+ card->states[state->other_states[0]->virt] = NULL;
+ kfree(state->other_states[0]);
+ card->states[state->other_states[1]->virt] = NULL;
+ kfree(state->other_states[1]);
+ card->states[state->other_states[2]->virt] = NULL;
+ kfree(state->other_states[2]);
+ card->states[state->other_states[3]->virt] = NULL;
+ kfree(state->other_states[3]);
+ }
dealloc_dmabuf(state);
state->card->free_pcm_channel(state->card, dmabuf->channel->num);
}
@@ -2189,7 +2246,7 @@
mask = ALI_AC97_WRITE_ACTION | ALI_AC97_AUDIO_BUSY;
if (codec->id)
mask |= ALI_AC97_SECONDARY;
- if (card->revision == 0x02)
+ if (card->revision == ALI_5451_V02)
mask |= ALI_AC97_WRITE_MIXER_REGISTER;
spin_lock_irqsave(&card->lock, flags);
@@ -2209,7 +2266,7 @@
spin_unlock_irqrestore(&card->lock, flags);
return; //success
}
- inw(TRID_REG(card, address)); //wait a read cycle
+ inw(TRID_REG(card, address)); //wait for a read cycle
}
printk(KERN_ERR "ali: AC97 CODEC write timed out.\n");
@@ -2229,7 +2286,7 @@
u32 data;
address = ALI_AC97_READ;
- if (card->revision == 0x02) {
+ if (card->revision == ALI_5451_V02) {
address = ALI_AC97_WRITE;
mask &= ALI_AC97_READ_MIXER_REGISTER;
}
@@ -2267,6 +2324,586 @@
return 0;
}
+static void ali_enable_special_channel(struct trident_state *stat)
+{
+ struct trident_card *card = stat->card;
+ unsigned long s_channels;
+
+ s_channels = inl(TRID_REG(card, ALI_GLOBAL_CONTROL));
+ s_channels |= (1<<stat->dmabuf.channel->num);
+ outl(s_channels, TRID_REG(card, ALI_GLOBAL_CONTROL));
+}
+
+/*
+flag: ALI_SPDIF_OUT_TO_SPDIF_OUT
+ ALI_PCM_TO_SPDIF_OUT
+*/
+static void ali_setup_spdif_out(struct trident_card *card, int flag)
+{
+ unsigned long spdif;
+ unsigned char ch;
+
+ ch = inb(TRID_REG(card, ALI_SCTRL));
+ outb(ch | ALI_SPDIF_OUT_ENABLE, TRID_REG(card, ALI_SCTRL));
+ ch = inb(TRID_REG(card, ALI_SPDIF_CTRL));
+ outb(ch & ALI_SPDIF_OUT_CH_STATUS, TRID_REG(card, ALI_SPDIF_CTRL));
+
+ if (flag & ALI_SPDIF_OUT_TO_SPDIF_OUT) {
+ spdif = inw(TRID_REG(card, ALI_GLOBAL_CONTROL));
+ spdif |= ALI_SPDIF_OUT_CH_ENABLE;
+ spdif &= ALI_SPDIF_OUT_SEL_SPDIF;
+ outw(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL));
+ spdif = inw(TRID_REG(card, ALI_SPDIF_CS));
+ if (flag & ALI_SPDIF_OUT_NON_PCM)
+ spdif |= 0x0002;
+ else spdif &= (~0x0002);
+ outw(spdif, TRID_REG(card, ALI_SPDIF_CS));
+ }
+ else {
+ spdif = inw(TRID_REG(card, ALI_GLOBAL_CONTROL));
+ spdif |= ALI_SPDIF_OUT_SEL_PCM;
+ outw(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL));
+ }
+}
+
+static void ali_disable_special_channel(struct trident_card *card, int ch)
+{
+ unsigned long sc;
+
+ sc = inl(TRID_REG(card, ALI_GLOBAL_CONTROL));
+ sc &= ~(1 << ch);
+ outl(sc, TRID_REG(card, ALI_GLOBAL_CONTROL));
+}
+
+static void ali_disable_spdif_in(struct trident_card *card)
+{
+ unsigned long spdif;
+
+ spdif = inl(TRID_REG(card, ALI_GLOBAL_CONTROL));
+ spdif &= (~ALI_SPDIF_IN_SUPPORT);
+ outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL));
+
+ ali_disable_special_channel(card, ALI_SPDIF_IN_CHANNEL);
+}
+
+static void ali_setup_spdif_in(struct trident_card *card)
+{
+ unsigned long spdif;
+
+ //Set SPDIF IN Supported
+ spdif = inl(TRID_REG(card, ALI_GLOBAL_CONTROL));
+ spdif |= ALI_SPDIF_IN_SUPPORT;
+ outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL));
+
+ spdif = inb(TRID_REG(card, ALI_SPDIF_CTRL));
+ spdif |= ALI_SPDIF_IN_CH_STATUS;
+ outb(spdif, TRID_REG(card, ALI_SPDIF_CTRL));
+
+ //Set SPDIF IN Rec
+ spdif = inl(TRID_REG(card, ALI_GLOBAL_CONTROL));
+ spdif |= ALI_SPDIF_IN_CH_ENABLE;
+ outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL));
+
+ spdif = inb(TRID_REG(card, ALI_SPDIF_CTRL));
+ spdif |= ALI_SPDIF_IN_FUNC_ENABLE;
+ outb(spdif, TRID_REG(card, ALI_SPDIF_CTRL));
+}
+
+static unsigned int ali_get_spdif_in_rate(struct trident_card *card)
+{
+ unsigned long spdif, time1, time2;
+ unsigned count1, count2, count3;
+ unsigned char R1, R2 = 0;
+
+ outb(0xAA, TRID_REG(card, ALI_SPDIF_CTRL + 1));
+ count1 = 0xFFFF;
+ while(--count1)
+ {
+ count2 = 0xffff;
+ do{
+ count3 = 0xffff;
+ time1 = inl(TRID_REG(card, ALI_STIMER));
+
+ do{
+ time2 = inl(TRID_REG(card, ALI_STIMER));
+ }while((count3--) && (time2 <= (time1 + 5)));
+ if (!count3) {
+ printk("ali: STimer is stopped! Error!\n");
+ return FALSE;
+ }
+ R1 = inb(TRID_REG(card, ALI_SPDIF_CTRL + 1));
+ }while((count2--) && (!((R1 == 0x0B)||(R1 == 0x0C)||(R1 == 0x0D)||(R1 == 0x0E)||(R1 == 0x12))));
+ if (!count2)
+ continue;
+
+ count2 = 0xffff;
+ time1 = inl(TRID_REG(card, ALI_STIMER));
+ do{
+ time2 = inl(TRID_REG(card, ALI_STIMER));
+ }while((count2--) && (time2 <= (time1 + 5)));
+ if (!count2)
+ continue;
+
+ R2 = inb(TRID_REG(card, ALI_SPDIF_CTRL + 1));
+ count2 = 0xffff;
+ while((--count2) && (R2 != R1))
+ {
+ R1 = R2;
+ count3 = 0xffff;
+ time1 = inl(TRID_REG(card, ALI_STIMER));
+ do{
+ time2 = inl(TRID_REG(card, ALI_STIMER));
+ }while((count3--) && (time2 <= (time1 + 5)));
+ if (!count3) {
+ printk("ali: STimer is stopped! Error!\n");
+ return FALSE;
+ }
+ R2 = inb(TRID_REG(card, ALI_SPDIF_CTRL + 1));
+ }
+ if(R2 == R1)
+ break;
+ }
+
+ if(!count1) {
+ printk("ali: Can not Detect the sample rate from SPDIF IN!\n");
+ return FALSE;
+ }
+
+ spdif = inb(TRID_REG(card, ALI_SPDIF_CTRL)) | ALI_SPDIF_IN_CH_STATUS;
+ outb(spdif, TRID_REG(card, ALI_SPDIF_CTRL));
+
+ /* SPDIF only supprts 48k, 44.1k, 32k */
+ switch(R2) {
+ case 0x12:
+ outw(0x0E08, TRID_REG(card, ALI_SPDIF_CTRL + 2));
+ return 32000;
+ case 0x0B:
+ case 0x0C:
+ case 0x0D:
+ case 0x0E:
+ default:
+ outw(0x0905, TRID_REG(card, ALI_SPDIF_CTRL + 2));
+ break;
+ }
+
+ spdif = inb(TRID_REG(card, ALI_SPDIF_CS + 3)) & 0xf;
+ if (spdif == 0)
+ return 44100;
+ else return 48000;
+}
+
+static int ali_setup_multi_channels(struct trident_card *card, int chan_nums)
+{
+ unsigned long dwValue;
+ if (chan_nums == 6) {
+ dwValue = inl(TRID_REG(card, ALI_SCTRL)) | 0x000f0000;
+ outl(dwValue, TRID_REG(card, ALI_SCTRL));
+ dwValue = inl(TRID_REG(card, ALI_GLOBAL_CONTROL)) | 0x07800000;
+ outl(dwValue, TRID_REG(card, ALI_GLOBAL_CONTROL));
+ }
+ return 1;
+}
+
+static void ali_free_pcm_channel(struct trident_card *card, unsigned int channel)
+{
+ int bank;
+
+ if (channel > 31)
+ return;
+
+ bank = channel >> 5;
+ channel = channel & 0x1f;
+
+ card->banks[bank].bitmap &= ~(1 << (channel));
+}
+
+static int ali_allocate_multi_channels(struct trident_state *state, int chan_nums)
+{
+ struct trident_card *card = state->card;
+ struct trident_state *s;
+ int i, state_count = 0;
+ struct trident_pcm_bank *bank;
+ struct trident_channel *channel;
+
+ bank = &card->banks[BANK_A];
+
+ if (chan_nums == 6) {
+ for(i = 0;(i < ALI_CHANNELS) && (state_count != 4); i++) {
+ if (!card->states[i]) {
+ if (!(bank->bitmap & (1 << ali_multi_channels[state_count]))) {
+ bank->bitmap |= (1 << ali_multi_channels[state_count]);
+ channel = &bank->channels[ali_multi_channels[state_count]];
+ channel->num = ali_multi_channels[state_count];
+ }
+ else {
+ state_count--;
+ for (; state_count >= 0; state_count--) {
+ kfree(state->other_states[state_count]);
+ ali_free_pcm_channel(card, ali_multi_channels[state_count]);
+ }
+ return -EBUSY;
+ }
+ s = card->states[i] = (struct trident_state *)
+ kmalloc(sizeof(struct trident_state), GFP_KERNEL);
+ if (!s) {
+ ali_free_pcm_channel(card, ali_multi_channels[state_count]);
+ state_count--;
+ for (; state_count >= 0; state_count--) {
+ ali_free_pcm_channel(card, ali_multi_channels[state_count]);
+ kfree(state->other_states[state_count]);
+ }
+ return -ENOMEM;
+ }
+ memset(s, 0, sizeof(struct trident_state));
+
+ s->dmabuf.channel = channel;
+ s->dmabuf.ossfragshift = s->dmabuf.ossmaxfrags = s->dmabuf.subdivision = 0;
+ init_waitqueue_head(&s->dmabuf.wait);
+ s->magic = card->magic;
+ s->card = card;
+ s->virt = i;
+
+ state->other_states[state_count++] = s;
+ }
+ }
+
+ if (state_count != 4) {
+ state_count--;
+ for (; state_count >= 0; state_count--) {
+ kfree(state->other_states[state_count]);
+ ali_free_pcm_channel(card, ali_multi_channels[state_count]);
+ }
+ return -EBUSY;
+ }
+ }
+ return 0;
+}
+
+static void ali_save_regs(struct trident_card *card)
+{
+ unsigned long flags;
+ int i, j;
+
+ save_flags(flags);
+ cli();
+
+ ali_registers.global_regs[0x2c] = inl(TRID_REG(card,T4D_MISCINT));
+ //ali_registers.global_regs[0x20] = inl(TRID_REG(card,T4D_START_A));
+ ali_registers.global_regs[0x21] = inl(TRID_REG(card,T4D_STOP_A));
+
+ //disable all IRQ bits
+ 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);
+
+ for (i = 0; i < ALI_GLOBAL_REGS; i++)
+ {
+ if ((i*4 == T4D_MISCINT) || (i*4 == T4D_STOP_A))
+ continue;
+ ali_registers.global_regs[i] = inl(TRID_REG(card, i*4));
+ }
+
+ for (i = 0; i < ALI_CHANNELS; i++)
+ {
+ outb(i,TRID_REG(card, T4D_LFO_GC_CIR));
+ for (j = 0; j < ALI_CHANNEL_REGS; j++)
+ ali_registers.channel_regs[i][j] = inl(TRID_REG(card, j*4 + 0xe0));
+ }
+
+ //Stop all HW channel
+ outl(ALI_STOP_ALL_CHANNELS, TRID_REG(card, T4D_STOP_A));
+
+ restore_flags(flags);
+}
+
+static void ali_restore_regs(struct trident_card *card)
+{
+ unsigned long flags;
+ int i, j;
+
+ save_flags(flags);
+ cli();
+
+ for (i = 1; i < ALI_MIXER_REGS; i++)
+ ali_ac97_set(card->ac97_codec[0], i*2, ali_registers.mixer_regs[i]);
+
+ for (i = 0; i < ALI_CHANNELS; i++)
+ {
+ outb(i,TRID_REG(card, T4D_LFO_GC_CIR));
+ for (j = 0; j < ALI_CHANNEL_REGS; j++)
+ outl(ali_registers.channel_regs[i][j], TRID_REG(card, j*4 + 0xe0));
+ }
+
+ for (i = 0; i < ALI_GLOBAL_REGS; i++)
+ {
+ if ((i*4 == T4D_MISCINT) || (i*4 == T4D_STOP_A) || (i*4 == T4D_START_A))
+ continue;
+ ali_registers.global_regs[i] = inl(TRID_REG(card, i*4));
+ }
+
+ //start HW channel
+ outl(ali_registers.global_regs[0x20], TRID_REG(card,T4D_START_A));
+ //restore IRQ enable bits
+ outl(ali_registers.global_regs[0x2c], TRID_REG(card,T4D_MISCINT));
+
+ restore_flags(flags);
+}
+
+static int ali_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+ 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;
+ }
+ }
+ return 0;
+}
+
+static struct trident_channel *ali_alloc_pcm_channel(struct trident_card *card)
+{
+ struct trident_pcm_bank *bank;
+ int idx;
+
+ bank = &card->banks[BANK_A];
+
+ if (inl(TRID_REG(card, ALI_GLOBAL_CONTROL)) & (ALI_SPDIF_OUT_CH_ENABLE)) {
+ idx = ALI_SPDIF_OUT_CHANNEL;
+ if (!(bank->bitmap & (1 << idx))) {
+ struct trident_channel *channel = &bank->channels[idx];
+ bank->bitmap |= 1 << idx;
+ channel->num = idx;
+ return channel;
+ }
+ }
+
+ for (idx = ALI_PCM_OUT_CHANNEL_FIRST; idx <= ALI_PCM_OUT_CHANNEL_LAST ; idx++) {
+ if (!(bank->bitmap & (1 << idx))) {
+ struct trident_channel *channel = &bank->channels[idx];
+ bank->bitmap |= 1 << idx;
+ channel->num = idx;
+ return channel;
+ }
+ }
+
+ /* no more free channels avaliable */
+ printk(KERN_ERR "ali: no more channels available on Bank A.\n");
+ return NULL;
+}
+
+static struct trident_channel *ali_alloc_rec_pcm_channel(struct trident_card *card)
+{
+ struct trident_pcm_bank *bank;
+ int idx;
+
+ if (inl(TRID_REG(card, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_IN_SUPPORT)
+ idx = ALI_SPDIF_IN_CHANNEL;
+ else idx = ALI_PCM_IN_CHANNEL;
+
+ bank = &card->banks[BANK_A];
+
+ if (!(bank->bitmap & (1 << idx))) {
+ struct trident_channel *channel = &bank->channels[idx];
+ bank->bitmap |= 1 << idx;
+ channel->num = idx;
+ return channel;
+ }
+
+ /* no free recordable channels avaliable */
+ printk(KERN_ERR "ali: no recordable channels available on Bank A.\n");
+ return NULL;
+}
+
+static void ali_set_spdif_out_rate(struct trident_card *card, unsigned int rate)
+{
+ unsigned char ch_st_sel;
+ unsigned short status_rate;
+
+#ifdef DEBUG
+ printk("ali: spdif out rate =%d\n", rate);
+#endif
+ switch(rate) {
+ case 44100:
+ status_rate = 0;
+ break;
+ case 32000:
+ status_rate = 0x300;
+ break;
+ case 48000:
+ default:
+ status_rate = 0x200;
+ break;
+ }
+
+ ch_st_sel = inb(TRID_REG(card, ALI_SPDIF_CTRL)) & ALI_SPDIF_OUT_CH_STATUS; //select spdif_out
+
+ ch_st_sel |= 0x80; //select right
+ outb(ch_st_sel, TRID_REG(card, ALI_SPDIF_CTRL));
+ outb(status_rate | 0x20, TRID_REG(card, ALI_SPDIF_CS + 2));
+
+ ch_st_sel &= (~0x80); //select left
+ outb(ch_st_sel, TRID_REG(card, ALI_SPDIF_CTRL));
+ outw(status_rate | 0x10, TRID_REG(card, ALI_SPDIF_CS + 2));
+#ifdef DEBUG
+ printk("ali: SPDIF_CS=%lxh\n", inl(TRID_REG(card, ALI_SPDIF_CS)));
+#endif
+}
+
+static void ali_address_interrupt(struct trident_card *card)
+{
+ int i, channel;
+ struct trident_state *state;
+ u32 mask, channel_mask;
+
+ mask = trident_get_interrupt_mask (card, 0);
+ for (i = 0; i < NR_HW_CH; i++) {
+ if ((state = card->states[i]) == NULL)
+ continue;
+ channel = state->dmabuf.channel->num;
+ if ((channel_mask = 1 << channel) & mask) {
+ mask &= ~channel_mask;
+ trident_ack_channel_interrupt(card, channel);
+ state->dmabuf.update_flag |= ALI_ADDRESS_INT_UPDATE;
+ trident_update_ptr(state);
+ }
+ }
+ if (mask) {
+ for (i = 0; i < NR_HW_CH; i++) {
+ if (mask & (1 << i)) {
+ printk("ali: spurious channel irq %d.\n", i);
+ trident_ack_channel_interrupt(card, i);
+ trident_stop_voice(card, i);
+ trident_disable_voice_irq(card, i);
+ }
+ }
+ }
+}
+
+/* Updating the values of counters of other_states' DMAs without lock
+protection is no harm because all DMAs of multi-channels and interrupt
+depend on a master state's DMA, and changing the counters of the master
+state DMA is protected by a spinlock.
+*/
+static unsigned int ali_write_5_1(struct trident_state *state, const char *buf, int cnt_for_multi_channel)
+{
+
+ struct dmabuf *dmabuf = &state->dmabuf;
+ struct dmabuf *dmabuf_temp;
+ const char *buffer = buf;
+ unsigned swptr, other_dma_nums, sample_s;
+ unsigned int i, loop, state_cnt = 0;
+
+ other_dma_nums = 4;
+ sample_s = sample_size[dmabuf->fmt] >> 1;
+ swptr = dmabuf->swptr;
+
+ if ((i = state->multi_channels_adjust_count) > 0) {
+ if (i == 1) {
+ copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s);
+ seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s);
+ i--;
+ state_cnt += sample_s;
+ state->multi_channels_adjust_count++;
+ }
+ else i = i - (state->chans_num - other_dma_nums);
+ for (; (i < other_dma_nums) && (cnt_for_multi_channel > 0); i++) {
+ dmabuf_temp = &state->other_states[i]->dmabuf;
+ copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s);
+ seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s);
+ }
+ if (cnt_for_multi_channel == 0)
+ state->multi_channels_adjust_count += i;
+ }
+ if (cnt_for_multi_channel > 0) {
+ loop = cnt_for_multi_channel / (state->chans_num * sample_s);
+ for (i = 0; i < loop; i++) {
+ copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s * 2);
+ seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s * 2);
+
+ dmabuf_temp = &state->other_states[0]->dmabuf;
+ copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s);
+ seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s);
+
+ dmabuf_temp = &state->other_states[1]->dmabuf;
+ copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s);
+ seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s);
+
+ dmabuf_temp = &state->other_states[2]->dmabuf;
+ copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s);
+ seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s);
+
+ dmabuf_temp = &state->other_states[3]->dmabuf;
+ copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s);
+ seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s);
+ }
+
+ state_cnt += (sample_s * 2 * loop);
+
+ if (cnt_for_multi_channel > 0) {
+ state->multi_channels_adjust_count = cnt_for_multi_channel / sample_s;
+
+ copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s);
+ seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s);
+ state_cnt += sample_s;
+
+ if (cnt_for_multi_channel > 0) {
+ copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s);
+ seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s);
+ state_cnt += sample_s;
+
+ if (cnt_for_multi_channel > 0) {
+ loop = state->multi_channels_adjust_count - (state->chans_num - other_dma_nums);
+ for (i = 0; i < loop; i++) {
+ dmabuf_temp = &state->other_states[i]->dmabuf;
+ copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s);
+ seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s);
+ }
+ }
+ }
+ }
+ else
+ state->multi_channels_adjust_count = 0;
+ }
+ for (i = 0; i < other_dma_nums; i++) {
+ dmabuf_temp = &state->other_states[i]->dmabuf;
+ dmabuf_temp->swptr = dmabuf_temp->swptr % dmabuf_temp->dmasize;
+ }
+ return state_cnt;
+}
+
+#ifdef CONFIG_PROC_FS
+struct proc_dir_entry *res = NULL;
+static int ali_write_proc(struct file *file, const char *buffer, unsigned long count, void *data)
+{
+ struct trident_card *card = (struct trident_card *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+ if (*buffer == '0') { //default
+ ali_setup_spdif_out(card, ALI_PCM_TO_SPDIF_OUT);
+ ali_disable_special_channel(card, ALI_SPDIF_OUT_CHANNEL);
+ }
+ else if (*buffer == '1')
+ ali_setup_spdif_out(card, ALI_SPDIF_OUT_TO_SPDIF_OUT|ALI_SPDIF_OUT_PCM);
+ else if (*buffer == '2') //AC3 data
+ ali_setup_spdif_out(card, ALI_SPDIF_OUT_TO_SPDIF_OUT|ALI_SPDIF_OUT_NON_PCM);
+ else if (*buffer == '3')
+ ali_disable_spdif_in(card); //default
+ else if (*buffer == '4')
+ ali_setup_spdif_in(card);
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ return count;
+}
+#endif
+
/* OSS /dev/mixer file operation methods */
static int trident_open_mixdev(struct inode *inode, struct file *file)
{
@@ -2309,7 +2946,7 @@
static int __init trident_ac97_init(struct trident_card *card)
{
int num_ac97 = 0;
- int ready_2nd = 0;
+ unsigned long ready_2nd = 0;
struct ac97_codec *codec;
/* initialize controller side of AC link, and find out if secondary codes
@@ -2317,8 +2954,9 @@
switch (card->pci_id)
{
case PCI_DEVICE_ID_ALI_5451:
- outl(PCMOUT|SECONDARY_ID, TRID_REG(card, SI_SERIAL_INTF_CTRL));
- ready_2nd = inl(TRID_REG(card, SI_SERIAL_INTF_CTRL));
+ ready_2nd = inl(TRID_REG(card, ALI_SCTRL));
+ outl(ready_2nd | PCMOUT | SECONDARY_ID, TRID_REG(card, ALI_SCTRL));
+ ready_2nd = inl(TRID_REG(card, ALI_SCTRL));
ready_2nd &= SI_AC97_SECONDARY_READY;
break;
case PCI_DEVICE_ID_SI_7018:
@@ -2435,10 +3073,32 @@
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;
card->free_pcm_channel = ali_free_pcm_channel;
+
card->address_interrupt = ali_address_interrupt;
+
+ /* ALi SPDIF OUT function */
+ if(card->revision == ALI_5451_V02) {
+ ali_setup_spdif_out(card, ALI_PCM_TO_SPDIF_OUT);
+#ifdef CONFIG_PROC_FS
+ res = create_proc_entry("ALi5451", 0, NULL);
+ if (res) {
+ res->write_proc = ali_write_proc;
+ res->data = card;
+ }
+#endif
+ }
}
else {
card->alloc_pcm_channel = trident_alloc_pcm_channel;
@@ -2477,9 +3137,9 @@
if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
/* edited by HMSEO for GT sound */
#ifdef CONFIG_ALPHA_NAUTILUS
- u16 ac97_data = trident_ac97_get (card->ac97_codec[0], AC97_POWER_CONTROL);
- trident_ac97_set (card->ac97_codec[0], AC97_POWER_CONTROL,
- ac97_data | ALI_EAPD_POWER_DOWN);
+ 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);
#endif
/* edited by HMSEO for GT sound*/
}
@@ -2497,6 +3157,17 @@
{
int i;
struct trident_card *card = pci_get_drvdata(pci_dev);
+
+ /* ALi S/PDIF and Power Management */
+ if(card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+ ali_setup_spdif_out(card, ALI_PCM_TO_SPDIF_OUT);
+ ali_disable_special_channel(card, ALI_SPDIF_OUT_CHANNEL);
+ ali_disable_spdif_in(card);
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry("ALi5451", NULL);
+#endif
+ pm_unregister_all(ali_pm_callback);
+ }
/* Kill interrupts, and SP/DIF */
trident_disable_loop_interrupts(card);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)