summaryrefslogtreecommitdiffstats
path: root/drivers/sound
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2001-01-31 22:22:27 +0000
committerRalf Baechle <ralf@linux-mips.org>2001-01-31 22:22:27 +0000
commit825423e4c4f18289df2393951cfd2a7a31fc0464 (patch)
tree4ad80e981c3d9effa910d2247d118d254f9a5d09 /drivers/sound
parentc4693dc4856ab907a5c02187a8d398861bebfc7e (diff)
Merge with Linux 2.4.1.
Diffstat (limited to 'drivers/sound')
-rw-r--r--drivers/sound/Config.in6
-rw-r--r--drivers/sound/Makefile4
-rw-r--r--drivers/sound/emu10k1/audio.c33
-rw-r--r--drivers/sound/trix.c1
-rw-r--r--drivers/sound/via82cxxx_audio.c707
-rw-r--r--drivers/sound/ymfpci.c998
-rw-r--r--drivers/sound/ymfpci.h116
7 files changed, 1104 insertions, 761 deletions
diff --git a/drivers/sound/Config.in b/drivers/sound/Config.in
index 9fd35efcb..10235dd2e 100644
--- a/drivers/sound/Config.in
+++ b/drivers/sound/Config.in
@@ -142,9 +142,9 @@ if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND_OSS" = "m" ]; then
dep_tristate ' Yamaha FM synthesizer (YM3812/OPL-3) support' CONFIG_SOUND_YM3812 $CONFIG_SOUND_OSS
dep_tristate ' Yamaha OPL3-SA1 audio controller' CONFIG_SOUND_OPL3SA1 $CONFIG_SOUND_OSS
dep_tristate ' Yamaha OPL3-SA2, SA3, and SAx based PnP cards' CONFIG_SOUND_OPL3SA2 $CONFIG_SOUND_OSS
- dep_tristate ' Yamaha YMF7xx PCI audio (legacy mode)' CONFIG_SOUND_YMPCI $CONFIG_SOUND_OSS $CONFIG_PCI
- if [ "$CONFIG_SOUND_YMPCI" = "n" ]; then
- dep_tristate ' Yamaha YMF7xx PCI audio (native mode) (EXPERIMENTAL)' CONFIG_SOUND_YMFPCI $CONFIG_SOUND_OSS $CONFIG_PCI $CONFIG_EXPERIMENTAL
+ dep_tristate ' Yamaha YMF7xx PCI audio (native mode)' CONFIG_SOUND_YMFPCI $CONFIG_SOUND_OSS $CONFIG_PCI
+ if [ "$CONFIG_SOUND_YMFPCI" != "n" ]; then
+ bool ' Yamaha PCI legacy ports support' CONFIG_SOUND_YMFPCI_LEGACY
fi
dep_tristate ' 6850 UART support' CONFIG_SOUND_UART6850 $CONFIG_SOUND_OSS
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile
index e7ee317e6..824f08ec8 100644
--- a/drivers/sound/Makefile
+++ b/drivers/sound/Makefile
@@ -47,8 +47,10 @@ obj-$(CONFIG_SOUND_ACI_MIXER) += aci.o
obj-$(CONFIG_SOUND_AWE32_SYNTH) += awe_wave.o
obj-$(CONFIG_SOUND_VIA82CXXX) += via82cxxx_audio.o ac97_codec.o
-obj-$(CONFIG_SOUND_YMPCI) += ymf_sb.o sb_lib.o uart401.o
obj-$(CONFIG_SOUND_YMFPCI) += ymfpci.o ac97_codec.o
+ifeq ($(CONFIG_SOUND_YMFPCI_LEGACY),y)
+ obj-$(CONFIG_SOUND_YMFPCI) += opl3.o uart401.o
+endif
obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o
obj-$(CONFIG_SOUND_MSNDPIN) += msnd.o msnd_pinnacle.o
obj-$(CONFIG_SOUND_VWSND) += vwsnd.o
diff --git a/drivers/sound/emu10k1/audio.c b/drivers/sound/emu10k1/audio.c
index 9623dcb2e..2280ca236 100644
--- a/drivers/sound/emu10k1/audio.c
+++ b/drivers/sound/emu10k1/audio.c
@@ -375,8 +375,10 @@ static int emu10k1_audio_ioctl(struct inode *inode, struct file *file, unsigned
format = wiinst->format;
format.samplingrate = val;
- if (emu10k1_wavein_setformat(wave_dev, &format) < 0)
+ if (emu10k1_wavein_setformat(wave_dev, &format) < 0) {
+ spin_unlock_irqrestore(&wiinst->lock, flags);
return -EINVAL;
+ }
val = wiinst->format.samplingrate;
@@ -393,8 +395,10 @@ static int emu10k1_audio_ioctl(struct inode *inode, struct file *file, unsigned
format = woinst->format;
format.samplingrate = val;
- if (emu10k1_waveout_setformat(wave_dev, &format) < 0)
+ if (emu10k1_waveout_setformat(wave_dev, &format) < 0) {
+ spin_unlock_irqrestore(&woinst->lock, flags);
return -EINVAL;
+ }
val = woinst->format.samplingrate;
@@ -430,8 +434,10 @@ static int emu10k1_audio_ioctl(struct inode *inode, struct file *file, unsigned
format = wiinst->format;
format.channels = val ? 2 : 1;
- if (emu10k1_wavein_setformat(wave_dev, &format) < 0)
+ if (emu10k1_wavein_setformat(wave_dev, &format) < 0) {
+ spin_unlock_irqrestore(&wiinst->lock, flags);
return -EINVAL;
+ }
val = wiinst->format.channels - 1;
@@ -447,8 +453,10 @@ static int emu10k1_audio_ioctl(struct inode *inode, struct file *file, unsigned
format = woinst->format;
format.channels = val ? 2 : 1;
- if (emu10k1_waveout_setformat(wave_dev, &format) < 0)
+ if (emu10k1_waveout_setformat(wave_dev, &format) < 0) {
+ spin_unlock_irqrestore(&woinst->lock, flags);
return -EINVAL;
+ }
val = woinst->format.channels - 1;
@@ -478,8 +486,10 @@ static int emu10k1_audio_ioctl(struct inode *inode, struct file *file, unsigned
format = wiinst->format;
format.channels = val;
- if (emu10k1_wavein_setformat(wave_dev, &format) < 0)
+ if (emu10k1_wavein_setformat(wave_dev, &format) < 0) {
+ spin_unlock_irqrestore(&wiinst->lock, flags);
return -EINVAL;
+ }
val = wiinst->format.channels;
@@ -495,8 +505,10 @@ static int emu10k1_audio_ioctl(struct inode *inode, struct file *file, unsigned
format = woinst->format;
format.channels = val;
- if (emu10k1_waveout_setformat(wave_dev, &format) < 0)
+ if (emu10k1_waveout_setformat(wave_dev, &format) < 0) {
+ spin_unlock_irqrestore(&woinst->lock, flags);
return -EINVAL;
+ }
val = woinst->format.channels;
@@ -542,8 +554,10 @@ static int emu10k1_audio_ioctl(struct inode *inode, struct file *file, unsigned
format = wiinst->format;
format.bitsperchannel = val;
- if (emu10k1_wavein_setformat(wave_dev, &format) < 0)
+ if (emu10k1_wavein_setformat(wave_dev, &format) < 0) {
+ spin_unlock_irqrestore(&wiinst->lock, flags);
return -EINVAL;
+ }
val = wiinst->format.bitsperchannel;
@@ -559,8 +573,10 @@ static int emu10k1_audio_ioctl(struct inode *inode, struct file *file, unsigned
format = woinst->format;
format.bitsperchannel = val;
- if (emu10k1_waveout_setformat(wave_dev, &format) < 0)
+ if (emu10k1_waveout_setformat(wave_dev, &format) < 0) {
+ spin_unlock_irqrestore(&woinst->lock, flags);
return -EINVAL;
+ }
val = woinst->format.bitsperchannel;
@@ -968,6 +984,7 @@ static int emu10k1_audio_mmap(struct file *file, struct vm_area_struct *vma)
for (i = 0; i < woinst->buffer.pages; i++) {
if (remap_page_range(vma->vm_start + (i * PAGE_SIZE), virt_to_phys(woinst->buffer.addr[i]), PAGE_SIZE, vma->vm_page_prot)) {
spin_unlock_irqrestore(&woinst->lock, flags);
+ unlock_kernel();
return -EAGAIN;
}
}
diff --git a/drivers/sound/trix.c b/drivers/sound/trix.c
index 73c9dcf54..03b4b1999 100644
--- a/drivers/sound/trix.c
+++ b/drivers/sound/trix.c
@@ -17,7 +17,6 @@
* Arnaldo C. de Melo Got rid of attach_uart401
*/
-#include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
diff --git a/drivers/sound/via82cxxx_audio.c b/drivers/sound/via82cxxx_audio.c
index f609d1e2b..fbe02bffd 100644
--- a/drivers/sound/via82cxxx_audio.c
+++ b/drivers/sound/via82cxxx_audio.c
@@ -15,7 +15,7 @@
*/
-#define VIA_VERSION "1.1.14"
+#define VIA_VERSION "1.1.14a"
#include <linux/config.h>
@@ -76,8 +76,16 @@
#define VIA_COUNTER_LIMIT 100000
/* size of DMA buffers */
-#define VIA_DMA_BUFFERS 16
-#define VIA_DMA_BUF_SIZE PAGE_SIZE
+#define VIA_MAX_BUFFER_DMA_PAGES 32
+
+/* buffering default values in ms */
+#define VIA_DEFAULT_FRAG_TIME 20
+#define VIA_DEFAULT_BUFFER_TIME 500
+
+#define VIA_MAX_FRAG_SIZE PAGE_SIZE
+#define VIA_MIN_FRAG_SIZE 64
+
+#define VIA_MIN_FRAG_NUMBER 2
#ifndef AC97_PCM_LR_ADC_RATE
# define AC97_PCM_LR_ADC_RATE AC97_PCM_LR_DAC_RATE
@@ -102,7 +110,6 @@
#define VIA_BASE0_PCM_OUT_CHAN_STATUS 0x00
#define VIA_BASE0_PCM_OUT_CHAN_CTRL 0x01
#define VIA_BASE0_PCM_OUT_CHAN_TYPE 0x02
-#define VIA_BASE0_PCM_OUT_BLOCK_COUNT 0x0C
#define VIA_BASE0_PCM_IN_CHAN 0x10 /* input PCM from user */
#define VIA_BASE0_PCM_IN_CHAN_STATUS 0x10
@@ -114,6 +121,7 @@
#define VIA_PCM_CONTROL 0x01
#define VIA_PCM_TYPE 0x02
#define VIA_PCM_TABLE_ADDR 0x04
+#define VIA_PCM_BLOCK_COUNT 0x0C
/* XXX unused DMA channel for FM PCM data */
#define VIA_BASE0_FM_OUT_CHAN 0x20
@@ -223,14 +231,14 @@ enum via_channel_states {
};
-struct via_sgd_data {
+struct via_buffer_pgtbl {
dma_addr_t handle;
void *cpuaddr;
};
struct via_channel {
- atomic_t n_bufs;
+ atomic_t n_frags;
atomic_t hw_ptr;
wait_queue_head_t wait;
@@ -246,11 +254,14 @@ struct via_channel {
u8 pcm_fmt; /* VIA_PCM_FMT_xxx */
unsigned rate; /* sample rate */
+ unsigned int frag_size;
+ unsigned int frag_number;
volatile struct via_sgd_table *sgtable;
dma_addr_t sgt_handle;
- struct via_sgd_data sgbuf [VIA_DMA_BUFFERS];
+ unsigned int page_number;
+ struct via_buffer_pgtbl pgtbl[VIA_MAX_BUFFER_DMA_PAGES];
long iobase;
@@ -301,17 +312,16 @@ static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wa
static int via_dsp_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
static int via_dsp_open (struct inode *inode, struct file *file);
static int via_dsp_release(struct inode *inode, struct file *file);
-#ifdef VIA_SUPPORT_MMAP
static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma);
-#endif
static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg);
static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value);
static u8 via_ac97_wait_idle (struct via_info *card);
static void via_chan_free (struct via_info *card, struct via_channel *chan);
-static void via_chan_clear (struct via_channel *chan);
+static void via_chan_clear (struct via_info *card, struct via_channel *chan);
static void via_chan_pcm_fmt (struct via_channel *chan, int reset);
+static void via_chan_buffer_free (struct via_info *card, struct via_channel *chan);
#ifdef VIA_PROC_FS
static int via_init_proc (void);
@@ -569,25 +579,53 @@ static void via_chan_init_defaults (struct via_info *card, struct via_channel *c
chan->pcm_fmt = VIA_PCM_FMT_MASK;
chan->is_enabled = 1;
- if (chan->is_record)
- atomic_set (&chan->n_bufs, 0);
- else
- atomic_set (&chan->n_bufs, VIA_DMA_BUFFERS);
+ chan->frag_number = 0;
+ chan->frag_size = 0;
+ atomic_set(&chan->n_frags, 0);
atomic_set (&chan->hw_ptr, 0);
}
+/**
+ * via_chan_init - Initialize PCM channel
+ * @card: Private audio chip info
+ * @chan: Channel to be initialized
+ *
+ * Performs some of the preparations necessary to begin
+ * using a PCM channel.
+ *
+ * Currently the preparations consist in
+ * setting the
+ * PCM channel to a known state.
+ */
+
+
+static void via_chan_init (struct via_info *card, struct via_channel *chan)
+{
+
+ DPRINTK ("ENTER\n");
+
+ /* bzero channel structure, and init members to defaults */
+ via_chan_init_defaults (card, chan);
+
+ /* stop any existing channel output */
+ via_chan_clear (card, chan);
+ via_chan_status_clear (chan->iobase);
+ via_chan_pcm_fmt (chan, 1);
+
+ DPRINTK ("EXIT\n");
+}
/**
- * via_chan_init - Initialize PCM channel
+ * via_chan_buffer_init - Initialize PCM channel buffer
* @card: Private audio chip info
* @chan: Channel to be initialized
*
- * Performs all the preparations necessary to begin
+ * Performs some of the preparations necessary to begin
* using a PCM channel.
*
* Currently the preparations include allocating the
- * scatter-gather DMA table and buffers, setting the
- * PCM channel to a known state, and passing the
+ * scatter-gather DMA table and buffers,
+ * and passing the
* address of the DMA table to the hardware.
*
* Note that special care is taken when passing the
@@ -596,18 +634,21 @@ static void via_chan_init_defaults (struct via_info *card, struct via_channel *c
* always "take" the address.
*/
-static int via_chan_init (struct via_info *card, struct via_channel *chan)
+static int via_chan_buffer_init (struct via_info *card, struct via_channel *chan)
{
+ int page, offset;
int i;
DPRINTK ("ENTER\n");
- /* bzero channel structure, and init members to defaults */
- via_chan_init_defaults (card, chan);
+ if (chan->sgtable != NULL) {
+ DPRINTK ("EXIT\n");
+ return 0;
+ }
/* alloc DMA-able memory for scatter-gather table */
chan->sgtable = pci_alloc_consistent (card->pdev,
- (sizeof (struct via_sgd_table) * VIA_DMA_BUFFERS),
+ (sizeof (struct via_sgd_table) * chan->frag_number),
&chan->sgt_handle);
if (!chan->sgtable) {
printk (KERN_ERR PFX "DMA table alloc fail, aborting\n");
@@ -616,45 +657,54 @@ static int via_chan_init (struct via_info *card, struct via_channel *chan)
}
memset ((void*)chan->sgtable, 0,
- (sizeof (struct via_sgd_table) * VIA_DMA_BUFFERS));
+ (sizeof (struct via_sgd_table) * chan->frag_number));
/* alloc DMA-able memory for scatter-gather buffers */
- for (i = 0; i < VIA_DMA_BUFFERS; i++) {
- chan->sgbuf[i].cpuaddr =
- pci_alloc_consistent (card->pdev, VIA_DMA_BUF_SIZE,
- &chan->sgbuf[i].handle);
- if (!chan->sgbuf[i].cpuaddr)
- goto err_out_nomem;
+ chan->page_number = (chan->frag_number * chan->frag_size) / PAGE_SIZE +
+ (((chan->frag_number * chan->frag_size) % PAGE_SIZE) ? 1 : 0);
+
+ for (i = 0; i < chan->page_number; i++) {
+ chan->pgtbl[i].cpuaddr = pci_alloc_consistent (card->pdev, PAGE_SIZE,
+ &chan->pgtbl[i].handle);
- if (i < (VIA_DMA_BUFFERS - 1))
- chan->sgtable[i].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_FLAG);
- else
- chan->sgtable[i].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_EOL);
- chan->sgtable[i].addr = cpu_to_le32 (chan->sgbuf[i].handle);
+ if (!chan->pgtbl[i].cpuaddr) {
+ chan->page_number = i;
+ goto err_out_nomem;
+ }
#ifndef VIA_NDEBUG
- memset (chan->sgbuf[i].cpuaddr, 0xBC, VIA_DMA_BUF_SIZE);
+ memset (chan->pgtbl[i].cpuaddr, 0xBC, chan->frag_size);
#endif
#if 1
- DPRINTK ("dmabuf #%d (h=%lx, 32(h)=%lx, v2p=%lx, a=%p)\n",
- i, (long)chan->sgbuf[i].handle,
- (long)chan->sgtable[i].addr,
- virt_to_phys(chan->sgbuf[i].cpuaddr),
- chan->sgbuf[i].cpuaddr);
+ DPRINTK ("dmabuf_pg #%d (h=%lx, v2p=%lx, a=%p)\n",
+ i, (long)chan->pgtbl[i].handle,
+ virt_to_phys(chan->pgtbl[i].cpuaddr),
+ chan->pgtbl[i].cpuaddr);
#endif
-
- assert ((VIA_DMA_BUF_SIZE % PAGE_SIZE) == 0);
}
- /* stop any existing channel output */
- via_chan_clear (chan);
- via_chan_status_clear (chan->iobase);
- via_chan_pcm_fmt (chan, 1);
+ for (i = 0; i < chan->frag_number; i++) {
+
+ page = i / (PAGE_SIZE / chan->frag_size);
+ offset = (i % (PAGE_SIZE / chan->frag_size)) * chan->frag_size;
+
+ chan->sgtable[i].count = cpu_to_le32 (chan->frag_size | VIA_FLAG);
+ chan->sgtable[i].addr = cpu_to_le32 (chan->pgtbl[page].handle + offset);
+
+#if 1
+ DPRINTK ("dmabuf #%d (32(h)=%lx)\n",
+ i,
+ (long)chan->sgtable[i].addr);
+#endif
+ }
+
+ /* overwrite the last buffer information */
+ chan->sgtable[chan->frag_number - 1].count = cpu_to_le32 (chan->frag_size | VIA_EOL);
/* set location of DMA-able scatter-gather info table */
- DPRINTK("outl (0x%X, 0x%04lX)\n",
+ DPRINTK ("outl (0x%X, 0x%04lX)\n",
cpu_to_le32 (chan->sgt_handle),
chan->iobase + VIA_PCM_TABLE_ADDR);
@@ -664,7 +714,7 @@ static int via_chan_init (struct via_info *card, struct via_channel *chan)
udelay (20);
via_ac97_wait_idle (card);
- DPRINTK("inl (0x%lX) = %x\n",
+ DPRINTK ("inl (0x%lX) = %x\n",
chan->iobase + VIA_PCM_TABLE_ADDR,
inl(chan->iobase + VIA_PCM_TABLE_ADDR));
@@ -673,7 +723,7 @@ static int via_chan_init (struct via_info *card, struct via_channel *chan)
err_out_nomem:
printk (KERN_ERR PFX "DMA buffer alloc fail, aborting\n");
- via_chan_free (card, chan);
+ via_chan_buffer_free (card, chan);
DPRINTK ("EXIT\n");
return -ENOMEM;
}
@@ -695,8 +745,6 @@ err_out_nomem:
static void via_chan_free (struct via_info *card, struct via_channel *chan)
{
- int i;
-
DPRINTK ("ENTER\n");
synchronize_irq();
@@ -710,23 +758,33 @@ static void via_chan_free (struct via_info *card, struct via_channel *chan)
spin_unlock_irq (&card->lock);
+ DPRINTK ("EXIT\n");
+}
+
+static void via_chan_buffer_free (struct via_info *card, struct via_channel *chan)
+{
+ int i;
+
+ DPRINTK ("ENTER\n");
+
/* zero location of DMA-able scatter-gather info table */
via_ac97_wait_idle(card);
outl (0, chan->iobase + VIA_PCM_TABLE_ADDR);
- for (i = 0; i < VIA_DMA_BUFFERS; i++)
- if (chan->sgbuf[i].cpuaddr) {
- assert ((VIA_DMA_BUF_SIZE % PAGE_SIZE) == 0);
- pci_free_consistent (card->pdev, VIA_DMA_BUF_SIZE,
- chan->sgbuf[i].cpuaddr,
- chan->sgbuf[i].handle);
- chan->sgbuf[i].cpuaddr = NULL;
- chan->sgbuf[i].handle = 0;
+ for (i = 0; i < chan->page_number; i++)
+ if (chan->pgtbl[i].cpuaddr) {
+ pci_free_consistent (card->pdev, PAGE_SIZE,
+ chan->pgtbl[i].cpuaddr,
+ chan->pgtbl[i].handle);
+ chan->pgtbl[i].cpuaddr = NULL;
+ chan->pgtbl[i].handle = 0;
}
+ chan->page_number = 0;
+
if (chan->sgtable) {
pci_free_consistent (card->pdev,
- (sizeof (struct via_sgd_table) * VIA_DMA_BUFFERS),
+ (sizeof (struct via_sgd_table) * chan->frag_number),
(void*)chan->sgtable, chan->sgt_handle);
chan->sgtable = NULL;
}
@@ -771,11 +829,11 @@ static void via_chan_pcm_fmt (struct via_channel *chan, int reset)
if (!chan->is_record)
chan->pcm_fmt |= VIA_CHAN_TYPE_INT_SELECT;
- outb (chan->pcm_fmt, chan->iobase + 2);
+ outb (chan->pcm_fmt, chan->iobase + VIA_PCM_TYPE);
DPRINTK ("EXIT, pcm_fmt = 0x%02X, reg = 0x%02X\n",
chan->pcm_fmt,
- inb (chan->iobase + 2));
+ inb (chan->iobase + VIA_PCM_TYPE));
}
@@ -787,10 +845,11 @@ static void via_chan_pcm_fmt (struct via_channel *chan, int reset)
* all software pointers which track DMA operation.
*/
-static void via_chan_clear (struct via_channel *chan)
+static void via_chan_clear (struct via_info *card, struct via_channel *chan)
{
DPRINTK ("ENTER\n");
via_chan_stop (chan->iobase);
+ via_chan_buffer_free(card, chan);
chan->is_active = 0;
chan->is_mapped = 0;
chan->is_enabled = 1;
@@ -798,10 +857,6 @@ static void via_chan_clear (struct via_channel *chan)
chan->sw_ptr = 0;
chan->n_irqs = 0;
atomic_set (&chan->hw_ptr, 0);
- if (chan->is_record)
- atomic_set (&chan->n_bufs, 0);
- else
- atomic_set (&chan->n_bufs, VIA_DMA_BUFFERS);
DPRINTK ("EXIT\n");
}
@@ -826,7 +881,7 @@ static int via_chan_set_speed (struct via_info *card,
{
DPRINTK ("ENTER, requested rate = %d\n", val);
- via_chan_clear (chan);
+ via_chan_clear (card, chan);
val = via_set_rate (&card->ac97, chan, val);
@@ -858,7 +913,7 @@ static int via_chan_set_fmt (struct via_info *card,
val == AFMT_S16_LE ? "AFMT_S16_LE" :
"unknown");
- via_chan_clear (chan);
+ via_chan_clear (card, chan);
assert (val != AFMT_QUERY); /* this case is handled elsewhere */
@@ -907,7 +962,7 @@ static int via_chan_set_stereo (struct via_info *card,
{
DPRINTK ("ENTER, channels = %d\n", val);
- via_chan_clear (chan);
+ via_chan_clear (card, chan);
switch (val) {
@@ -934,6 +989,78 @@ static int via_chan_set_stereo (struct via_info *card,
return val;
}
+static int via_chan_set_buffering (struct via_info *card,
+ struct via_channel *chan, int val)
+{
+ int shift;
+
+ DPRINTK ("ENTER\n");
+
+ /* in both cases the buffer cannot be changed */
+ if (chan->is_active || chan->is_mapped) {
+ DPRINTK ("EXIT\n");
+ return -EINVAL;
+ }
+
+ /* called outside SETFRAGMENT */
+ /* set defaults or do nothing */
+ if (val < 0) {
+
+ if (chan->frag_size && chan->frag_number)
+ goto out;
+
+ DPRINTK ("\n");
+
+ chan->frag_size = (VIA_DEFAULT_FRAG_TIME * chan->rate *
+ ((chan->pcm_fmt & VIA_PCM_FMT_STEREO) ? 2 : 1) *
+ ((chan->pcm_fmt & VIA_PCM_FMT_16BIT) ? 2 : 1)) / 1000 - 1;
+
+ shift = 0;
+ while (chan->frag_size) {
+ chan->frag_size >>= 1;
+ shift++;
+ }
+ chan->frag_size = 1 << shift;
+
+ chan->frag_number = (VIA_DEFAULT_BUFFER_TIME / VIA_DEFAULT_FRAG_TIME);
+
+ DPRINTK ("setting default values %d %d\n", chan->frag_size, chan->frag_number);
+ } else {
+ chan->frag_size = 1 << (val & 0xFFFF);
+ chan->frag_number = (val >> 16) & 0xFFFF;
+
+ DPRINTK ("using user values %d %d\n", chan->frag_size, chan->frag_number);
+ }
+
+ /* quake3 wants frag_number to be a power of two */
+ shift = 0;
+ while (chan->frag_number) {
+ chan->frag_number >>= 1;
+ shift++;
+ }
+ chan->frag_number = 1 << shift;
+
+ if (chan->frag_size > VIA_MAX_FRAG_SIZE)
+ chan->frag_size = VIA_MAX_FRAG_SIZE;
+ else if (chan->frag_size < VIA_MIN_FRAG_SIZE)
+ chan->frag_size = VIA_MIN_FRAG_SIZE;
+
+ if (chan->frag_number < VIA_MIN_FRAG_NUMBER)
+ chan->frag_number = VIA_MIN_FRAG_NUMBER;
+
+ if ((chan->frag_number * chan->frag_size) / PAGE_SIZE > VIA_MAX_BUFFER_DMA_PAGES)
+ chan->frag_number = (VIA_MAX_BUFFER_DMA_PAGES * PAGE_SIZE) / chan->frag_size;
+
+out:
+ if (chan->is_record)
+ atomic_set (&chan->n_frags, 0);
+ else
+ atomic_set (&chan->n_frags, chan->frag_number);
+
+ DPRINTK ("EXIT\n");
+
+ return 0;
+}
#ifdef VIA_CHAN_DUMP_BUFS
/**
@@ -948,7 +1075,7 @@ static void via_chan_dump_bufs (struct via_channel *chan)
{
int i;
- for (i = 0; i < VIA_DMA_BUFFERS; i++) {
+ for (i = 0; i < chan->frag_number; i++) {
DPRINTK ("#%02d: addr=%x, count=%u, flag=%d, eol=%d\n",
i, chan->sgtable[i].addr,
chan->sgtable[i].count & 0x00FFFFFF,
@@ -975,15 +1102,15 @@ static void via_chan_flush_frag (struct via_channel *chan)
assert (chan->slop_len > 0);
- if (chan->sw_ptr == (VIA_DMA_BUFFERS - 1))
+ if (chan->sw_ptr == (chan->frag_number - 1))
chan->sw_ptr = 0;
else
chan->sw_ptr++;
chan->slop_len = 0;
- assert (atomic_read (&chan->n_bufs) > 0);
- atomic_dec (&chan->n_bufs);
+ assert (atomic_read (&chan->n_frags) > 0);
+ atomic_dec (&chan->n_frags);
DPRINTK ("EXIT\n");
}
@@ -1003,7 +1130,7 @@ static inline void via_chan_maybe_start (struct via_channel *chan)
if (!chan->is_active && chan->is_enabled) {
chan->is_active = 1;
sg_begin (chan);
- DPRINTK("starting channel %s\n", chan->name);
+ DPRINTK ("starting channel %s\n", chan->name);
}
}
@@ -1213,7 +1340,7 @@ static loff_t via_llseek(struct file *file, loff_t offset, int origin)
{
DPRINTK ("ENTER\n");
- DPRINTK("EXIT, returning -ESPIPE\n");
+ DPRINTK ("EXIT, returning -ESPIPE\n");
return -ESPIPE;
}
@@ -1245,7 +1372,7 @@ static int __init via_ac97_reset (struct via_info *card)
pci_read_config_byte (card->pdev, 0x43, &r43);
pci_read_config_byte (card->pdev, 0x44, &r44);
pci_read_config_byte (card->pdev, 0x48, &r48);
- DPRINTK("PCI config: %02X %02X %02X %02X %02X %02X\n",
+ DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n",
r40,r41,r42,r43,r44,r48);
spin_lock_irq (&card->lock);
@@ -1334,7 +1461,7 @@ static int __init via_ac97_init (struct via_info *card)
card->ac97.dev_mixer = register_sound_mixer (&via_mixer_fops, -1);
if (card->ac97.dev_mixer < 0) {
printk (KERN_ERR PFX "unable to register AC97 mixer, aborting\n");
- DPRINTK("EXIT, returning -EIO\n");
+ DPRINTK ("EXIT, returning -EIO\n");
return -EIO;
}
@@ -1359,21 +1486,21 @@ static int __init via_ac97_init (struct via_info *card)
err_out:
unregister_sound_mixer (card->ac97.dev_mixer);
- DPRINTK("EXIT, returning %d\n", rc);
+ DPRINTK ("EXIT, returning %d\n", rc);
return rc;
}
static void via_ac97_cleanup (struct via_info *card)
{
- DPRINTK("ENTER\n");
+ DPRINTK ("ENTER\n");
assert (card != NULL);
assert (card->ac97.dev_mixer >= 0);
unregister_sound_mixer (card->ac97.dev_mixer);
- DPRINTK("EXIT\n");
+ DPRINTK ("EXIT\n");
}
@@ -1414,24 +1541,24 @@ static void via_intr_channel (struct via_channel *chan)
/* sanity check: make sure our h/w ptr doesn't have a weird value */
assert (n >= 0);
- assert (n < VIA_DMA_BUFFERS);
+ assert (n < chan->frag_number);
/* reset SGD data structure in memory to reflect a full buffer,
* and advance the h/w ptr, wrapping around to zero if needed
*/
- if (n == (VIA_DMA_BUFFERS - 1)) {
- chan->sgtable[n].count = (VIA_DMA_BUF_SIZE | VIA_EOL);
+ if (n == (chan->frag_number - 1)) {
+ chan->sgtable[n].count = (chan->frag_size | VIA_EOL);
atomic_set (&chan->hw_ptr, 0);
} else {
- chan->sgtable[n].count = (VIA_DMA_BUF_SIZE | VIA_FLAG);
+ chan->sgtable[n].count = (chan->frag_size | VIA_FLAG);
atomic_inc (&chan->hw_ptr);
}
/* accounting crap for SNDCTL_DSP_GETxPTR */
chan->n_irqs++;
- chan->bytes += VIA_DMA_BUF_SIZE;
+ chan->bytes += chan->frag_size;
if (chan->bytes < 0) /* handle overflow of 31-bit value */
- chan->bytes = VIA_DMA_BUF_SIZE;
+ chan->bytes = chan->frag_size;
/* wake up anyone listening to see when interrupts occur */
if (waitqueue_active (&chan->wait))
@@ -1445,25 +1572,25 @@ static void via_intr_channel (struct via_channel *chan)
if (chan->is_mapped)
return;
- /* If we are recording, then n_bufs represents the number
- * of buffers waiting to be handled by userspace.
- * If we are playback, then n_bufs represents the number
- * of buffers remaining to be filled by userspace.
- * We increment here. If we reach max buffers (VIA_DMA_BUFFERS),
+ /* If we are recording, then n_frags represents the number
+ * of fragments waiting to be handled by userspace.
+ * If we are playback, then n_frags represents the number
+ * of fragments remaining to be filled by userspace.
+ * We increment here. If we reach max number of fragments,
* this indicates an underrun/overrun. For this case under OSS,
* we stop the record/playback process.
*/
- if (atomic_read (&chan->n_bufs) < VIA_DMA_BUFFERS)
- atomic_inc (&chan->n_bufs);
- assert (atomic_read (&chan->n_bufs) <= VIA_DMA_BUFFERS);
+ if (atomic_read (&chan->n_frags) < chan->frag_number)
+ atomic_inc (&chan->n_frags);
+ assert (atomic_read (&chan->n_frags) <= chan->frag_number);
- if (atomic_read (&chan->n_bufs) == VIA_DMA_BUFFERS) {
+ if (atomic_read (&chan->n_frags) == chan->frag_number) {
chan->is_active = 0;
via_chan_stop (chan->iobase);
}
- DPRINTK ("%s intr, channel n_bufs == %d\n", chan->name,
- atomic_read (&chan->n_bufs));
+ DPRINTK ("%s intr, channel n_frags == %d\n", chan->name,
+ atomic_read (&chan->n_frags));
}
@@ -1618,9 +1745,7 @@ static struct file_operations via_dsp_fops = {
poll: via_dsp_poll,
llseek: via_llseek,
ioctl: via_dsp_ioctl,
-#ifdef VIA_SUPPORT_MMAP
mmap: via_dsp_mmap,
-#endif
};
@@ -1668,7 +1793,6 @@ static void via_dsp_cleanup (struct via_info *card)
}
-#ifdef VIA_SUPPORT_MMAP
static struct page * via_mm_nopage (struct vm_area_struct * vma,
unsigned long address, int write_access)
{
@@ -1685,8 +1809,6 @@ static struct page * via_mm_nopage (struct vm_area_struct * vma,
address,
write_access);
- assert (VIA_DMA_BUF_SIZE == PAGE_SIZE);
-
if (address > vma->vm_end) {
DPRINTK ("EXIT, returning NOPAGE_SIGBUS\n");
return NOPAGE_SIGBUS; /* Disallow mremap */
@@ -1702,7 +1824,7 @@ static struct page * via_mm_nopage (struct vm_area_struct * vma,
#ifndef VIA_NDEBUG
{
- unsigned long max_bufs = VIA_DMA_BUFFERS;
+ unsigned long max_bufs = chan->frag_number;
if (rd && wr) max_bufs *= 2;
/* via_dsp_mmap() should ensure this */
assert (pgoff < max_bufs);
@@ -1711,17 +1833,17 @@ static struct page * via_mm_nopage (struct vm_area_struct * vma,
/* if full-duplex (read+write) and we have two sets of bufs,
* then the playback buffers come first, sez soundcard.c */
- if (pgoff >= VIA_DMA_BUFFERS) {
- pgoff -= VIA_DMA_BUFFERS;
+ if (pgoff >= chan->page_number) {
+ pgoff -= chan->page_number;
chan = &card->ch_in;
} else if (!wr)
chan = &card->ch_in;
- assert ((((unsigned long)chan->sgbuf[pgoff].cpuaddr) % PAGE_SIZE) == 0);
+ assert ((((unsigned long)chan->pgtbl[pgoff].cpuaddr) % PAGE_SIZE) == 0);
- dmapage = virt_to_page (chan->sgbuf[pgoff].cpuaddr);
+ dmapage = virt_to_page (chan->pgtbl[pgoff].cpuaddr);
DPRINTK ("EXIT, returning page %p for cpuaddr %lXh\n",
- dmapage, (unsigned long) chan->sgbuf[pgoff].cpuaddr);
+ dmapage, (unsigned long) chan->pgtbl[pgoff].cpuaddr);
get_page (dmapage);
return dmapage;
}
@@ -1761,16 +1883,18 @@ static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma)
vma->vm_end - vma->vm_start,
vma->vm_pgoff);
- assert (VIA_DMA_BUF_SIZE == PAGE_SIZE);
-
max_size = 0;
- if (file->f_mode & FMODE_READ) {
+ if (vma->vm_flags & VM_READ) {
rd = 1;
- max_size += (VIA_DMA_BUFFERS * VIA_DMA_BUF_SIZE);
+ via_chan_set_buffering(card, &card->ch_in, -1);
+ via_chan_buffer_init (card, &card->ch_in);
+ max_size += card->ch_in.page_number << PAGE_SHIFT;
}
- if (file->f_mode & FMODE_WRITE) {
+ if (vma->vm_flags & VM_WRITE) {
wr = 1;
- max_size += (VIA_DMA_BUFFERS * VIA_DMA_BUF_SIZE);
+ via_chan_set_buffering(card, &card->ch_out, -1);
+ via_chan_buffer_init (card, &card->ch_out);
+ max_size += card->ch_out.page_number << PAGE_SHIFT;
}
start = vma->vm_start;
@@ -1802,10 +1926,9 @@ static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma)
rc = 0;
out:
- DPRINTK("EXIT, returning %d\n", rc);
+ DPRINTK ("EXIT, returning %d\n", rc);
return rc;
}
-#endif /* VIA_SUPPORT_MMAP */
static ssize_t via_dsp_do_read (struct via_info *card,
@@ -1831,13 +1954,13 @@ handle_one_block:
*/
n = chan->sw_ptr;
- /* n_bufs represents the number of buffers waiting
+ /* n_frags represents the number of fragments waiting
* to be copied to userland. sleep until at least
* one buffer has been read from the audio hardware.
*/
- tmp = atomic_read (&chan->n_bufs);
+ tmp = atomic_read (&chan->n_frags);
assert (tmp >= 0);
- assert (tmp <= VIA_DMA_BUFFERS);
+ assert (tmp <= chan->frag_number);
while (tmp == 0) {
if (nonblock || !chan->is_active)
return -EAGAIN;
@@ -1848,18 +1971,18 @@ handle_one_block:
if (signal_pending (current))
return -ERESTARTSYS;
- tmp = atomic_read (&chan->n_bufs);
+ tmp = atomic_read (&chan->n_frags);
}
/* Now that we have a buffer we can read from, send
* as much as sample data possible to userspace.
*/
- while ((count > 0) && (chan->slop_len < VIA_DMA_BUF_SIZE)) {
- size_t slop_left = VIA_DMA_BUF_SIZE - chan->slop_len;
+ while ((count > 0) && (chan->slop_len < chan->frag_size)) {
+ size_t slop_left = chan->frag_size - chan->slop_len;
size = (count < slop_left) ? count : slop_left;
if (copy_to_user (userbuf,
- chan->sgbuf[n].cpuaddr + chan->slop_len,
+ chan->pgtbl[n / (PAGE_SIZE / chan->frag_size)].cpuaddr + n % (PAGE_SIZE / chan->frag_size) + chan->slop_len,
size))
return -EFAULT;
@@ -1871,7 +1994,7 @@ handle_one_block:
/* If we didn't copy the buffer completely to userspace,
* stop now.
*/
- if (chan->slop_len < VIA_DMA_BUF_SIZE)
+ if (chan->slop_len < chan->frag_size)
goto out;
/*
@@ -1882,20 +2005,20 @@ handle_one_block:
/* advance channel software pointer to point to
* the next buffer from which we will copy
*/
- if (chan->sw_ptr == (VIA_DMA_BUFFERS - 1))
+ if (chan->sw_ptr == (chan->frag_number - 1))
chan->sw_ptr = 0;
else
chan->sw_ptr++;
/* mark one less buffer waiting to be processed */
- assert (atomic_read (&chan->n_bufs) > 0);
- atomic_dec (&chan->n_bufs);
+ assert (atomic_read (&chan->n_frags) > 0);
+ atomic_dec (&chan->n_frags);
/* we are at a block boundary, there is no fragment data */
chan->slop_len = 0;
- DPRINTK("Flushed block %u, sw_ptr now %u, n_bufs now %d\n",
- n, chan->sw_ptr, atomic_read (&chan->n_bufs));
+ DPRINTK ("Flushed block %u, sw_ptr now %u, n_frags now %d\n",
+ n, chan->sw_ptr, atomic_read (&chan->n_frags));
DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
inb (card->baseaddr + 0x00),
@@ -1941,12 +2064,18 @@ static ssize_t via_dsp_read(struct file *file, char *buffer, size_t count, loff_
goto out_up;
}
+ via_chan_set_buffering(card, &card->ch_in, -1);
+ rc = via_chan_buffer_init (card, &card->ch_in);
+
+ if (rc)
+ goto out_up;
+
rc = via_dsp_do_read (card, buffer, count, nonblock);
out_up:
up (&card->syscall_sem);
out:
- DPRINTK("EXIT, returning %ld\n",(long) rc);
+ DPRINTK ("EXIT, returning %ld\n",(long) rc);
return rc;
}
@@ -1966,40 +2095,40 @@ handle_one_block:
if (current->need_resched)
schedule ();
- /* grab current channel software pointer. In the case of
- * playback, this is pointing to the next buffer that
+ /* grab current channel fragment pointer. In the case of
+ * playback, this is pointing to the next fragment that
* should receive data from userland.
*/
n = chan->sw_ptr;
- /* n_bufs represents the number of buffers remaining
+ /* n_frags represents the number of fragments remaining
* to be filled by userspace. Sleep until
- * at least one buffer is available for our use.
+ * at least one fragment is available for our use.
*/
- tmp = atomic_read (&chan->n_bufs);
+ tmp = atomic_read (&chan->n_frags);
assert (tmp >= 0);
- assert (tmp <= VIA_DMA_BUFFERS);
+ assert (tmp <= chan->frag_number);
while (tmp == 0) {
if (nonblock || !chan->is_enabled)
return -EAGAIN;
- DPRINTK ("Sleeping on block %d, tmp==%d, ir==%d\n", n, tmp, chan->is_record);
+ DPRINTK ("Sleeping on page %d, tmp==%d, ir==%d\n", n, tmp, chan->is_record);
interruptible_sleep_on (&chan->wait);
if (signal_pending (current))
return -ERESTARTSYS;
- tmp = atomic_read (&chan->n_bufs);
+ tmp = atomic_read (&chan->n_frags);
}
- /* Now that we have a buffer we can write to, fill it up
+ /* Now that we have at least one fragment we can write to, fill the buffer
* as much as possible with data from userspace.
*/
- while ((count > 0) && (chan->slop_len < VIA_DMA_BUF_SIZE)) {
- size_t slop_left = VIA_DMA_BUF_SIZE - chan->slop_len;
+ while ((count > 0) && (chan->slop_len < chan->frag_size)) {
+ size_t slop_left = chan->frag_size - chan->slop_len;
size = (count < slop_left) ? count : slop_left;
- if (copy_from_user (chan->sgbuf[n].cpuaddr + chan->slop_len,
+ if (copy_from_user (chan->pgtbl[n / (PAGE_SIZE / chan->frag_size)].cpuaddr + (n % (PAGE_SIZE / chan->frag_size)) * chan->frag_size + chan->slop_len,
userbuf, size))
return -EFAULT;
@@ -2009,36 +2138,36 @@ handle_one_block:
}
/* If we didn't fill up the buffer with data, stop now.
- * Put a 'stop' marker in the DMA table too, to tell the
- * audio hardware to stop if it gets here.
- */
- if (chan->slop_len < VIA_DMA_BUF_SIZE) {
+ * Put a 'stop' marker in the DMA table too, to tell the
+ * audio hardware to stop if it gets here.
+ */
+ if (chan->slop_len < chan->frag_size) {
sgtable[n].count = cpu_to_le32 (chan->slop_len | VIA_EOL | VIA_STOP);
goto out;
}
/*
- * If we get to this point, we have filled a buffer with
- * audio data, flush the buffer to audio hardware.
- */
+ * If we get to this point, we have filled a buffer with
+ * audio data, flush the buffer to audio hardware.
+ */
/* Record the true size for the audio hardware to notice */
- if (n == (VIA_DMA_BUFFERS - 1))
- sgtable[n].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_EOL);
- else
- sgtable[n].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_FLAG);
+ if (n == (chan->frag_number - 1))
+ sgtable[n].count = cpu_to_le32 (chan->frag_size | VIA_EOL);
+ else
+ sgtable[n].count = cpu_to_le32 (chan->frag_size | VIA_FLAG);
/* advance channel software pointer to point to
* the next buffer we will fill with data
*/
- if (chan->sw_ptr == (VIA_DMA_BUFFERS - 1))
+ if (chan->sw_ptr == (chan->frag_number - 1))
chan->sw_ptr = 0;
else
chan->sw_ptr++;
/* mark one less buffer as being available for userspace consumption */
- assert (atomic_read (&chan->n_bufs) > 0);
- atomic_dec (&chan->n_bufs);
+ assert (atomic_read (&chan->n_frags) > 0);
+ atomic_dec (&chan->n_frags);
/* we are at a block boundary, there is no fragment data */
chan->slop_len = 0;
@@ -2046,8 +2175,8 @@ handle_one_block:
/* if SGD has not yet been started, start it */
via_chan_maybe_start (chan);
- DPRINTK("Flushed block %u, sw_ptr now %u, n_bufs now %d\n",
- n, chan->sw_ptr, atomic_read (&chan->n_bufs));
+ DPRINTK ("Flushed block %u, sw_ptr now %u, n_frags now %d\n",
+ n, chan->sw_ptr, atomic_read (&chan->n_frags));
DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
inb (card->baseaddr + 0x00),
@@ -2093,12 +2222,18 @@ static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count
goto out_up;
}
+ via_chan_set_buffering(card, &card->ch_out, -1);
+ rc = via_chan_buffer_init (card, &card->ch_out);
+
+ if (rc)
+ goto out_up;
+
rc = via_dsp_do_write (card, buffer, count, nonblock);
out_up:
up (&card->syscall_sem);
out:
- DPRINTK("EXIT, returning %ld\n",(long) rc);
+ DPRINTK ("EXIT, returning %ld\n",(long) rc);
return rc;
}
@@ -2117,23 +2252,27 @@ static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wa
rd = (file->f_mode & FMODE_READ);
wr = (file->f_mode & FMODE_WRITE);
- if (wr && (atomic_read (&card->ch_out.n_bufs) == 0)) {
+ if (wr && (atomic_read (&card->ch_out.n_frags) == 0)) {
assert (card->ch_out.is_active);
poll_wait(file, &card->ch_out.wait, wait);
}
if (rd) {
/* XXX is it ok, spec-wise, to start DMA here? */
+ if (!card->ch_in.is_active) {
+ via_chan_set_buffering(card, &card->ch_in, -1);
+ via_chan_buffer_init(card, &card->ch_in);
+ }
via_chan_maybe_start (&card->ch_in);
- if (atomic_read (&card->ch_in.n_bufs) == 0)
+ if (atomic_read (&card->ch_in.n_frags) == 0)
poll_wait(file, &card->ch_in.wait, wait);
}
- if (wr && (atomic_read (&card->ch_out.n_bufs) > 0))
+ if (wr && ((atomic_read (&card->ch_out.n_frags) > 0) || !card->ch_out.is_active))
mask |= POLLOUT | POLLWRNORM;
- if (rd && (atomic_read (&card->ch_in.n_bufs) > 0))
+ if (rd && (atomic_read (&card->ch_in.n_frags) > 0))
mask |= POLLIN | POLLRDNORM;
- DPRINTK("EXIT, returning %u\n", mask);
+ DPRINTK ("EXIT, returning %u\n", mask);
return mask;
}
@@ -2158,12 +2297,12 @@ static int via_dsp_drain_playback (struct via_info *card,
if (chan->slop_len > 0)
via_chan_flush_frag (chan);
- if (atomic_read (&chan->n_bufs) == VIA_DMA_BUFFERS)
+ if (atomic_read (&chan->n_frags) == chan->frag_number)
goto out;
via_chan_maybe_start (chan);
- while (atomic_read (&chan->n_bufs) < VIA_DMA_BUFFERS) {
+ while (atomic_read (&chan->n_frags) < chan->frag_number) {
if (nonblock) {
DPRINTK ("EXIT, returning -EAGAIN\n");
return -EAGAIN;
@@ -2178,7 +2317,7 @@ static int via_dsp_drain_playback (struct via_info *card,
pci_read_config_byte (card->pdev, 0x43, &r43);
pci_read_config_byte (card->pdev, 0x44, &r44);
pci_read_config_byte (card->pdev, 0x48, &r48);
- DPRINTK("PCI config: %02X %02X %02X %02X %02X %02X\n",
+ DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n",
r40,r41,r42,r43,r44,r48);
DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
@@ -2195,7 +2334,7 @@ static int via_dsp_drain_playback (struct via_info *card,
printk (KERN_ERR "sleeping but not active\n");
#endif
- DPRINTK ("sleeping, nbufs=%d\n", atomic_read (&chan->n_bufs));
+ DPRINTK ("sleeping, nbufs=%d\n", atomic_read (&chan->n_frags));
interruptible_sleep_on (&chan->wait);
if (signal_pending (current)) {
@@ -2213,7 +2352,7 @@ static int via_dsp_drain_playback (struct via_info *card,
pci_read_config_byte (card->pdev, 0x43, &r43);
pci_read_config_byte (card->pdev, 0x44, &r44);
pci_read_config_byte (card->pdev, 0x48, &r48);
- DPRINTK("PCI config: %02X %02X %02X %02X %02X %02X\n",
+ DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n",
r40,r41,r42,r43,r44,r48);
DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
@@ -2225,7 +2364,7 @@ static int via_dsp_drain_playback (struct via_info *card,
inl (card->baseaddr + 0x80),
inl (card->baseaddr + 0x84));
- DPRINTK ("final nbufs=%d\n", atomic_read (&chan->n_bufs));
+ DPRINTK ("final nbufs=%d\n", atomic_read (&chan->n_frags));
}
#endif
@@ -2252,21 +2391,23 @@ static int via_dsp_ioctl_space (struct via_info *card,
{
audio_buf_info info;
- info.fragstotal = VIA_DMA_BUFFERS;
- info.fragsize = VIA_DMA_BUF_SIZE;
+ via_chan_set_buffering(card, chan, -1);
+
+ info.fragstotal = chan->frag_number;
+ info.fragsize = chan->frag_size;
/* number of full fragments we can read/write without blocking */
- info.fragments = atomic_read (&chan->n_bufs);
+ info.fragments = atomic_read (&chan->n_frags);
- if ((chan->slop_len > 0) && (info.fragments > 0))
+ if ((chan->slop_len % chan->frag_size > 0) && (info.fragments > 0))
info.fragments--;
/* number of bytes that can be read or written immediately
* without blocking.
*/
- info.bytes = (info.fragments * VIA_DMA_BUF_SIZE);
- if (chan->slop_len > 0)
- info.bytes += VIA_DMA_BUF_SIZE - chan->slop_len;
+ info.bytes = (info.fragments * chan->frag_size);
+ if (chan->slop_len % chan->frag_size > 0)
+ info.bytes += chan->frag_size - (chan->slop_len % chan->frag_size);
DPRINTK ("EXIT, returning fragstotal=%d, fragsize=%d, fragments=%d, bytes=%d\n",
info.fragstotal,
@@ -2305,8 +2446,8 @@ static int via_dsp_ioctl_ptr (struct via_info *card,
if (chan->is_active) {
unsigned long extra;
- info.ptr = atomic_read (&chan->hw_ptr) * VIA_DMA_BUF_SIZE;
- extra = VIA_DMA_BUF_SIZE - inl (chan->iobase + VIA_BASE0_PCM_OUT_BLOCK_COUNT);
+ info.ptr = atomic_read (&chan->hw_ptr) * chan->frag_size;
+ extra = chan->frag_size - inl (chan->iobase + VIA_PCM_BLOCK_COUNT);
info.ptr += extra;
info.bytes += extra;
} else {
@@ -2386,13 +2527,13 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
/* OSS API version. XXX unverified */
case OSS_GETVERSION:
- DPRINTK("ioctl OSS_GETVERSION, EXIT, returning SOUND_VERSION\n");
+ DPRINTK ("ioctl OSS_GETVERSION, EXIT, returning SOUND_VERSION\n");
rc = put_user (SOUND_VERSION, (int *)arg);
break;
/* list of supported PCM data formats */
case SNDCTL_DSP_GETFMTS:
- DPRINTK("DSP_GETFMTS, EXIT, returning AFMT U8|S16_LE\n");
+ DPRINTK ("DSP_GETFMTS, EXIT, returning AFMT U8|S16_LE\n");
rc = put_user (AFMT_U8 | AFMT_S16_LE, (int *)arg);
break;
@@ -2402,20 +2543,19 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
rc = -EFAULT;
break;
}
- DPRINTK("DSP_SETFMT, val==%d\n", val);
+ DPRINTK ("DSP_SETFMT, val==%d\n", val);
if (val != AFMT_QUERY) {
rc = 0;
- if (rc == 0 && rd)
+ if (rd)
rc = via_chan_set_fmt (card, &card->ch_in, val);
- if (rc == 0 && wr)
+
+ if (rc >= 0 && wr)
rc = via_chan_set_fmt (card, &card->ch_out, val);
- if (rc <= 0) {
- if (rc == 0)
- rc = -EINVAL;
+ if (rc < 0)
break;
- }
+
val = rc;
} else {
if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_16BIT)) ||
@@ -2424,7 +2564,7 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
else
val = AFMT_U8;
}
- DPRINTK("SETFMT EXIT, returning %d\n", val);
+ DPRINTK ("SETFMT EXIT, returning %d\n", val);
rc = put_user (val, (int *)arg);
break;
@@ -2434,18 +2574,19 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
rc = -EFAULT;
break;
}
- DPRINTK("DSP_CHANNELS, val==%d\n", val);
+ DPRINTK ("DSP_CHANNELS, val==%d\n", val);
if (val != 0) {
rc = 0;
- if (rc == 0 && rd)
+
+ if (rd)
rc = via_chan_set_stereo (card, &card->ch_in, val);
- if (rc == 0 && wr)
+
+ if (rc >= 0 && wr)
rc = via_chan_set_stereo (card, &card->ch_out, val);
- if (rc <= 0) {
- if (rc == 0)
- rc = -EINVAL;
+
+ if (rc < 0)
break;
- }
+
val = rc;
} else {
if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_STEREO)) ||
@@ -2454,7 +2595,7 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
else
val = 1;
}
- DPRINTK("CHANNELS EXIT, returning %d\n", val);
+ DPRINTK ("CHANNELS EXIT, returning %d\n", val);
rc = put_user (val, (int *)arg);
break;
@@ -2464,21 +2605,21 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
rc = -EFAULT;
break;
}
- DPRINTK("DSP_STEREO, val==%d\n", val);
+ DPRINTK ("DSP_STEREO, val==%d\n", val);
rc = 0;
- if (rc == 0 && rd)
+ if (rd)
rc = via_chan_set_stereo (card, &card->ch_in, val ? 2 : 1);
- if (rc == 0 && wr)
+ if (rc >= 0 && wr)
rc = via_chan_set_stereo (card, &card->ch_out, val ? 2 : 1);
- if (rc <= 0) {
- if (rc == 0)
- rc = -EINVAL;
+ if (rc < 0)
break;
- }
- DPRINTK("STEREO EXIT, returning %d\n", val);
- rc = 0;
+
+ val = rc - 1;
+
+ DPRINTK ("STEREO EXIT, returning %d\n", val);
+ rc = put_user(val, (int *) arg);
break;
/* query or set sampling rate */
@@ -2487,7 +2628,7 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
rc = -EFAULT;
break;
}
- DPRINTK("DSP_SPEED, val==%d\n", val);
+ DPRINTK ("DSP_SPEED, val==%d\n", val);
if (val < 0) {
rc = -EINVAL;
break;
@@ -2495,16 +2636,14 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
if (val > 0) {
rc = 0;
- if (rc == 0 && rd)
+ if (rd)
rc = via_chan_set_speed (card, &card->ch_in, val);
- if (rc == 0 && wr)
+ if (rc >= 0 && wr)
rc = via_chan_set_speed (card, &card->ch_out, val);
- if (rc <= 0) {
- if (rc == 0)
- rc = -EINVAL;
+ if (rc < 0)
break;
- }
+
val = rc;
} else {
if (rd)
@@ -2514,7 +2653,7 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
else
val = 0;
}
- DPRINTK("SPEED EXIT, returning %d\n", val);
+ DPRINTK ("SPEED EXIT, returning %d\n", val);
rc = put_user (val, (int *)arg);
break;
@@ -2522,7 +2661,7 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
case SNDCTL_DSP_SYNC:
DPRINTK ("DSP_SYNC\n");
if (wr) {
- DPRINTK("SYNC EXIT (after calling via_dsp_drain_playback)\n");
+ DPRINTK ("SYNC EXIT (after calling via_dsp_drain_playback)\n");
rc = via_dsp_drain_playback (card, &card->ch_out, nonblock);
}
break;
@@ -2531,12 +2670,19 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
case SNDCTL_DSP_RESET:
DPRINTK ("DSP_RESET\n");
if (rd) {
- via_chan_clear (&card->ch_in);
+ via_chan_clear (card, &card->ch_in);
via_chan_pcm_fmt (&card->ch_in, 1);
+ card->ch_in.frag_number = 0;
+ card->ch_in.frag_size = 0;
+ atomic_set(&card->ch_in.n_frags, 0);
}
+
if (wr) {
- via_chan_clear (&card->ch_out);
+ via_chan_clear (card, &card->ch_out);
via_chan_pcm_fmt (&card->ch_out, 1);
+ card->ch_out.frag_number = 0;
+ card->ch_out.frag_size = 0;
+ atomic_set(&card->ch_out.n_frags, 0);
}
rc = 0;
@@ -2544,40 +2690,47 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
/* obtain bitmask of device capabilities, such as mmap, full duplex, etc. */
case SNDCTL_DSP_GETCAPS:
- DPRINTK("DSP_GETCAPS\n");
+ DPRINTK ("DSP_GETCAPS\n");
rc = put_user(VIA_DSP_CAP, (int *)arg);
break;
- /* obtain bitmask of device capabilities, such as mmap, full duplex, etc. */
+ /* obtain buffer fragment size */
case SNDCTL_DSP_GETBLKSIZE:
- DPRINTK("DSP_GETBLKSIZE\n");
- rc = put_user(VIA_DMA_BUF_SIZE, (int *)arg);
+ DPRINTK ("DSP_GETBLKSIZE\n");
+
+ if (rd) {
+ via_chan_set_buffering(card, &card->ch_in, -1);
+ rc = put_user(card->ch_in.frag_size, (int *)arg);
+ } else if (wr) {
+ via_chan_set_buffering(card, &card->ch_out, -1);
+ rc = put_user(card->ch_out.frag_size, (int *)arg);
+ }
break;
/* obtain information about input buffering */
case SNDCTL_DSP_GETISPACE:
- DPRINTK("DSP_GETISPACE\n");
+ DPRINTK ("DSP_GETISPACE\n");
if (rd)
rc = via_dsp_ioctl_space (card, &card->ch_in, (void*) arg);
break;
/* obtain information about output buffering */
case SNDCTL_DSP_GETOSPACE:
- DPRINTK("DSP_GETOSPACE\n");
+ DPRINTK ("DSP_GETOSPACE\n");
if (wr)
rc = via_dsp_ioctl_space (card, &card->ch_out, (void*) arg);
break;
/* obtain information about input hardware pointer */
case SNDCTL_DSP_GETIPTR:
- DPRINTK("DSP_GETIPTR\n");
+ DPRINTK ("DSP_GETIPTR\n");
if (rd)
rc = via_dsp_ioctl_ptr (card, &card->ch_in, (void*) arg);
break;
/* obtain information about output hardware pointer */
case SNDCTL_DSP_GETOPTR:
- DPRINTK("DSP_GETOPTR\n");
+ DPRINTK ("DSP_GETOPTR\n");
if (wr)
rc = via_dsp_ioctl_ptr (card, &card->ch_out, (void*) arg);
break;
@@ -2585,25 +2738,29 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
/* return number of bytes remaining to be played by DMA engine */
case SNDCTL_DSP_GETODELAY:
{
- DPRINTK("DSP_GETODELAY\n");
+ DPRINTK ("DSP_GETODELAY\n");
chan = &card->ch_out;
if (!wr)
break;
- val = VIA_DMA_BUFFERS - atomic_read (&chan->n_bufs);
+ if (chan->is_active) {
- if (val > 0) {
- val *= VIA_DMA_BUF_SIZE;
- val -= VIA_DMA_BUF_SIZE -
- inl (chan->iobase + VIA_BASE0_PCM_OUT_BLOCK_COUNT);
- }
- val += chan->slop_len;
+ val = chan->frag_number - atomic_read (&chan->n_frags);
- assert (val <= (VIA_DMA_BUF_SIZE * VIA_DMA_BUFFERS));
+ if (val > 0) {
+ val *= chan->frag_size;
+ val -= chan->frag_size -
+ inl (chan->iobase + VIA_PCM_BLOCK_COUNT);
+ }
+ val += chan->slop_len % chan->frag_size;
+ } else
+ val = 0;
- DPRINTK("GETODELAY EXIT, val = %d bytes\n", val);
+ assert (val <= (chan->frag_size * chan->frag_number));
+
+ DPRINTK ("GETODELAY EXIT, val = %d bytes\n", val);
rc = put_user (val, (int *)arg);
break;
}
@@ -2617,7 +2774,7 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
rc = -EFAULT;
break;
}
- DPRINTK("DSP_SETTRIGGER, rd=%d, wr=%d, act=%d/%d, en=%d/%d\n",
+ DPRINTK ("DSP_SETTRIGGER, rd=%d, wr=%d, act=%d/%d, en=%d/%d\n",
rd, wr, card->ch_in.is_active, card->ch_out.is_active,
card->ch_in.is_enabled, card->ch_out.is_enabled);
@@ -2625,6 +2782,7 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
if (rd)
rc = via_dsp_ioctl_trigger (&card->ch_in, val);
+
if (!rc && wr)
rc = via_dsp_ioctl_trigger (&card->ch_out, val);
@@ -2634,7 +2792,7 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
* with O_RDWR, this is mainly a no-op that always returns success.
*/
case SNDCTL_DSP_SETDUPLEX:
- DPRINTK("DSP_SETDUPLEX\n");
+ DPRINTK ("DSP_SETDUPLEX\n");
if (!rd || !wr)
break;
rc = 0;
@@ -2646,7 +2804,13 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
rc = -EFAULT;
break;
}
- DPRINTK("DSP_SETFRAGMENT, val==%d\n", val);
+ DPRINTK ("DSP_SETFRAGMENT, val==%d\n", val);
+
+ if (rd)
+ rc = via_chan_set_buffering(card, &card->ch_in, val);
+
+ if (wr)
+ rc = via_chan_set_buffering(card, &card->ch_out, val);
DPRINTK ("SNDCTL_DSP_SETFRAGMENT (fragshift==0x%04X (%d), maxfrags==0x%04X (%d))\n",
val & 0xFFFF,
@@ -2654,13 +2818,12 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
(val >> 16) & 0xFFFF,
(val >> 16) & 0xFFFF);
- /* just to shut up some programs */
rc = 0;
break;
/* inform device of an upcoming pause in input (or output). */
case SNDCTL_DSP_POST:
- DPRINTK("DSP_POST\n");
+ DPRINTK ("DSP_POST\n");
if (wr) {
if (card->ch_out.slop_len > 0)
via_chan_flush_frag (&card->ch_out);
@@ -2678,15 +2841,14 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
}
up (&card->syscall_sem);
- DPRINTK("EXIT, returning %d\n", rc);
+ DPRINTK ("EXIT, returning %d\n", rc);
return rc;
}
static int via_dsp_open (struct inode *inode, struct file *file)
{
- int rc, minor = MINOR(inode->i_rdev);
- int got_read_chan = 0;
+ int minor = MINOR(inode->i_rdev);
struct via_info *card;
struct pci_dev *pdev;
struct via_channel *chan;
@@ -2733,17 +2895,13 @@ match:
}
file->private_data = card;
- DPRINTK("file->f_mode == 0x%x\n", file->f_mode);
+ DPRINTK ("file->f_mode == 0x%x\n", file->f_mode);
/* handle input from analog source */
if (file->f_mode & FMODE_READ) {
chan = &card->ch_in;
- rc = via_chan_init (card, chan);
- if (rc)
- goto err_out;
-
- got_read_chan = 1;
+ via_chan_init (card, chan);
/* why is this forced to 16-bit stereo in all drivers? */
chan->pcm_fmt = VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO;
@@ -2756,30 +2914,28 @@ match:
if (file->f_mode & FMODE_WRITE) {
chan = &card->ch_out;
- rc = via_chan_init (card, chan);
- if (rc)
- goto err_out_read_chan;
+ via_chan_init (card, chan);
- if ((minor & 0xf) == SND_DEV_DSP16) {
- chan->pcm_fmt |= VIA_PCM_FMT_16BIT;
- via_set_rate (&card->ac97, chan, 44100);
+ if (file->f_mode & FMODE_READ) {
+ /* if in duplex mode make the recording and playback channels
+ have the same settings */
+ chan->pcm_fmt = VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO;
+ via_chan_pcm_fmt (chan, 0);
+ via_set_rate (&card->ac97, chan, 44100);
} else {
- via_set_rate (&card->ac97, chan, 8000);
+ if ((minor & 0xf) == SND_DEV_DSP16) {
+ chan->pcm_fmt = VIA_PCM_FMT_16BIT;
+ via_chan_pcm_fmt (chan, 0);
+ via_set_rate (&card->ac97, chan, 44100);
+ } else {
+ via_chan_pcm_fmt (chan, 0);
+ via_set_rate (&card->ac97, chan, 8000);
+ }
}
-
- via_chan_pcm_fmt (chan, 0);
}
DPRINTK ("EXIT, returning 0\n");
return 0;
-
-err_out_read_chan:
- if (got_read_chan)
- via_chan_free (card, &card->ch_in);
-err_out:
- up (&card->open_sem);
- DPRINTK("ERROR EXIT, returning %d\n", rc);
- return rc;
}
@@ -2807,15 +2963,18 @@ static int via_dsp_release(struct inode *inode, struct file *file)
printk (KERN_DEBUG "via_audio: ignoring drain playback error %d\n", rc);
via_chan_free (card, &card->ch_out);
+ via_chan_buffer_free(card, &card->ch_out);
}
- if (file->f_mode & FMODE_READ)
+ if (file->f_mode & FMODE_READ) {
via_chan_free (card, &card->ch_in);
+ via_chan_buffer_free (card, &card->ch_in);
+ }
up (&card->syscall_sem);
up (&card->open_sem);
- DPRINTK("EXIT, returning 0\n");
+ DPRINTK ("EXIT, returning 0\n");
return 0;
}
@@ -2934,9 +3093,9 @@ static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id
tmp &= 0xF0;
tmp |= pdev->irq;
pci_write_config_byte (pdev, 0x3C, tmp);
- DPRINTK("new 0x3c==0x%02x\n", tmp);
+ DPRINTK ("new 0x3c==0x%02x\n", tmp);
} else {
- DPRINTK("IRQ reg 0x3c==0x%02x, irq==%d\n",
+ DPRINTK ("IRQ reg 0x3c==0x%02x, irq==%d\n",
tmp, tmp & 0x0F);
}
@@ -3036,12 +3195,12 @@ static int __init init_via82cxxx_audio(void)
static void __exit cleanup_via82cxxx_audio(void)
{
- DPRINTK("ENTER\n");
+ DPRINTK ("ENTER\n");
pci_unregister_driver (&via_driver);
via_cleanup_proc ();
- DPRINTK("EXIT\n");
+ DPRINTK ("EXIT\n");
}
@@ -3133,7 +3292,7 @@ static int via_info_read_proc (char *page, char **start, off_t off,
);
- DPRINTK("EXIT, returning %d\n", len);
+ DPRINTK ("EXIT, returning %d\n", len);
return len;
#undef YN
diff --git a/drivers/sound/ymfpci.c b/drivers/sound/ymfpci.c
index a8cdc58f2..9670951f8 100644
--- a/drivers/sound/ymfpci.c
+++ b/drivers/sound/ymfpci.c
@@ -24,19 +24,28 @@
*
* TODO:
* - Use P44Slot for 44.1 playback.
- * - Capture and duplex
* - 96KHz playback for DVD - use pitch of 2.0.
* - uLaw for Sun apps.
+ * : Alan says firmly "no software format conversion in kernel".
* - Retain DMA buffer on close, do not wait the end of frame.
* - Cleanup
- * ? merge ymf_pcm and state
- * ? pcm interrupt no pointer
* ? underused structure members
* - Remove remaining P3 tags (debug messages).
* - Resolve XXX tagged questions.
* - Cannot play 5133Hz.
+ * - 2001/01/07 Consider if we can remove voice_lock, like so:
+ * : Allocate/deallocate voices in open/close under semafore.
+ * : We access voices in interrupt, that only for pcms that open.
+ * voice_lock around playback_prepare closes interrupts for insane duration.
+ * - Revisit the way voice_alloc is done - too confusing, overcomplicated.
+ * Should support various channel types, however.
+ * - Remove prog_dmabuf from read/write, leave it in open.
+ * - 2001/01/07 Replace the OPL3 part of CONFIG_SOUND_YMFPCI_LEGACY code with
+ * native synthesizer through a playback slot.
+ * - Use new 2.3.x cache coherent PCI DMA routines instead of virt_to_bus.
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h>
@@ -50,19 +59,19 @@
#include <asm/dma.h>
#include <asm/uaccess.h>
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+# include "sound_config.h"
+# include "mpu401.h"
+#endif
#include "ymfpci.h"
-#define snd_magic_cast(t, p, err) ((t *)(p))
-
-/* Channels, such as play and record. I do only play a.t.m. XXX */
-#define NR_HW_CH 1
-
-static int ymf_playback_trigger(ymfpci_t *codec, ymfpci_pcm_t *ypcm, int cmd);
-static int ymfpci_voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type,
- int pair, ymfpci_voice_t **rvoice);
-static int ymfpci_voice_free(ymfpci_t *codec, ymfpci_voice_t *pvoice);
-static int ymf_playback_prepare(ymfpci_t *codec, struct ymf_state *state);
-static int ymf_state_alloc(ymfpci_t *unit, int nvirt);
+static int ymf_playback_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd);
+static void ymf_capture_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd);
+static void ymfpci_voice_free(ymfpci_t *codec, ymfpci_voice_t *pvoice);
+static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank);
+static int ymf_playback_prepare(struct ymf_state *state);
+static int ymf_capture_prepare(struct ymf_state *state);
+static struct ymf_state *ymf_state_alloc(ymfpci_t *unit);
static LIST_HEAD(ymf_devs);
@@ -87,7 +96,7 @@ MODULE_DEVICE_TABLE(pci, ymf_id_tbl);
/*
* Mindlessly copied from cs46xx XXX
*/
-extern __inline__ unsigned ld2(unsigned int x)
+static inline unsigned ld2(unsigned int x)
{
unsigned r = 0;
@@ -281,18 +290,13 @@ static void ymf_pcm_update_shift(struct ymf_pcm_format *f)
f->shift++;
}
-/*
- * Whole OSS-style DMA machinery is taken from cs46xx.
- */
-
/* Are you sure 32K is not too much? See if mpg123 skips on loaded systems. */
#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT)
#define DMABUF_MINORDER 1
/* allocate DMA buffer, playback and recording buffer should be allocated seperately */
-static int alloc_dmabuf(struct ymf_state *state)
+static int alloc_dmabuf(struct ymf_dmabuf *dmabuf)
{
- struct ymf_dmabuf *dmabuf = &state->dmabuf;
void *rawbuf = NULL;
int order;
struct page * map, * mapend;
@@ -323,9 +327,8 @@ static int alloc_dmabuf(struct ymf_state *state)
}
/* free DMA buffer */
-static void dealloc_dmabuf(struct ymf_state *state)
+static void dealloc_dmabuf(struct ymf_dmabuf *dmabuf)
{
- struct ymf_dmabuf *dmabuf = &state->dmabuf;
struct page *map, *mapend;
if (dmabuf->rawbuf) {
@@ -339,9 +342,9 @@ static void dealloc_dmabuf(struct ymf_state *state)
dmabuf->mapped = dmabuf->ready = 0;
}
-static int prog_dmabuf(struct ymf_state *state, unsigned rec)
+static int prog_dmabuf(struct ymf_state *state, int rec)
{
- struct ymf_dmabuf *dmabuf = &state->dmabuf;
+ struct ymf_dmabuf *dmabuf;
int w_16;
unsigned bytepersec;
unsigned bufsize;
@@ -350,6 +353,7 @@ static int prog_dmabuf(struct ymf_state *state, unsigned rec)
int ret;
w_16 = ymf_pcm_format_width(state->format.format) == 16;
+ dmabuf = rec ? &state->rpcm.dmabuf : &state->wpcm.dmabuf;
spin_lock_irqsave(&state->unit->reg_lock, flags);
dmabuf->hwptr = dmabuf->swptr = 0;
@@ -359,7 +363,7 @@ static int prog_dmabuf(struct ymf_state *state, unsigned rec)
/* allocate DMA buffer if not allocated yet */
if (!dmabuf->rawbuf)
- if ((ret = alloc_dmabuf(state)))
+ if ((ret = alloc_dmabuf(dmabuf)))
return ret;
bytepersec = state->format.rate << state->format.shift;
@@ -383,7 +387,6 @@ static int prog_dmabuf(struct ymf_state *state, unsigned rec)
dmabuf->numfrag = bufsize >> dmabuf->fragshift;
}
dmabuf->fragsize = 1 << dmabuf->fragshift;
- dmabuf->fragsamples = dmabuf->fragsize >> state->format.shift;
dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift;
/*
@@ -414,15 +417,20 @@ static int prog_dmabuf(struct ymf_state *state, unsigned rec)
* Now set up the ring
*/
- spin_lock_irqsave(&state->unit->reg_lock, flags);
+ /* XXX ret = rec? cap_pre(): pbk_pre(); */
+ spin_lock_irqsave(&state->unit->voice_lock, flags);
if (rec) {
- /* ymf_rec_setup(state); */
+ if ((ret = ymf_capture_prepare(state)) != 0) {
+ spin_unlock_irqrestore(&state->unit->voice_lock, flags);
+ return ret;
+ }
} else {
- if ((ret = ymf_playback_prepare(state->unit, state)) != 0) {
+ if ((ret = ymf_playback_prepare(state)) != 0) {
+ spin_unlock_irqrestore(&state->unit->voice_lock, flags);
return ret;
}
}
- spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+ spin_unlock_irqrestore(&state->unit->voice_lock, flags);
/* set the ready flag for the dma buffer (this comment is not stupid) */
dmabuf->ready = 1;
@@ -439,9 +447,14 @@ static int prog_dmabuf(struct ymf_state *state, unsigned rec)
static void ymf_start_dac(struct ymf_state *state)
{
- ymf_playback_trigger(state->unit, &state->ypcm, 1);
+ ymf_playback_trigger(state->unit, &state->wpcm, 1);
}
+// static void ymf_start_adc(struct ymf_state *state)
+// {
+// ymf_capture_trigger(state->unit, &state->rpcm, 1);
+// }
+
/*
* Wait until output is drained.
* This does not kill the hardware for the sake of ioctls.
@@ -449,14 +462,14 @@ static void ymf_start_dac(struct ymf_state *state)
static void ymf_wait_dac(struct ymf_state *state)
{
struct ymf_unit *unit = state->unit;
- ymfpci_pcm_t *ypcm = &state->ypcm;
+ struct ymf_pcm *ypcm = &state->wpcm;
DECLARE_WAITQUEUE(waita, current);
unsigned long flags;
- add_wait_queue(&state->dmabuf.wait, &waita);
+ add_wait_queue(&ypcm->dmabuf.wait, &waita);
spin_lock_irqsave(&unit->reg_lock, flags);
- if (state->dmabuf.count != 0 && !state->ypcm.running) {
+ if (ypcm->dmabuf.count != 0 && !ypcm->running) {
ymf_playback_trigger(unit, ypcm, 1);
}
@@ -479,7 +492,7 @@ static void ymf_wait_dac(struct ymf_state *state)
spin_unlock_irqrestore(&unit->reg_lock, flags);
set_current_state(TASK_RUNNING);
- remove_wait_queue(&state->dmabuf.wait, &waita);
+ remove_wait_queue(&ypcm->dmabuf.wait, &waita);
/*
* This function may take up to 4 seconds to reach this point
@@ -487,6 +500,17 @@ static void ymf_wait_dac(struct ymf_state *state)
*/
}
+/* Can just stop, without wait. Or can we? */
+static void ymf_stop_adc(struct ymf_state *state)
+{
+ struct ymf_unit *unit = state->unit;
+ unsigned long flags;
+
+ spin_lock_irqsave(&unit->reg_lock, flags);
+ ymf_capture_trigger(unit, &state->rpcm, 0);
+ spin_unlock_irqrestore(&unit->reg_lock, flags);
+}
+
/*
* Hardware start management
*/
@@ -523,12 +547,11 @@ static void ymfpci_hw_stop(ymfpci_t *codec)
* Playback voice management
*/
-static int voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice)
+static int voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type, int pair, ymfpci_voice_t *rvoice[])
{
ymfpci_voice_t *voice, *voice2;
int idx;
-
- *rvoice = NULL;
+
for (idx = 0; idx < 64; idx += pair ? 2 : 1) {
voice = &codec->voices[idx];
voice2 = pair ? &codec->voices[idx+1] : NULL;
@@ -551,52 +574,29 @@ static int voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type, int pair, ymfp
break;
}
ymfpci_hw_start(codec);
- if (voice2)
+ rvoice[0] = voice;
+ if (voice2) {
ymfpci_hw_start(codec);
- *rvoice = voice;
+ rvoice[1] = voice2;
+ }
return 0;
}
- return -ENOMEM;
+ return -EBUSY; /* Your audio channel is open by someone else. */
}
-static int ymfpci_voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type,
- int pair, ymfpci_voice_t **rvoice)
+static void ymfpci_voice_free(ymfpci_t *unit, ymfpci_voice_t *pvoice)
{
- unsigned long flags;
- int result;
-
- spin_lock_irqsave(&codec->voice_lock, flags);
- for (;;) {
- result = voice_alloc(codec, type, pair, rvoice);
- if (result == 0 || type != YMFPCI_PCM)
- break;
- /* TODO: synth/midi voice deallocation */
- break;
- }
- spin_unlock_irqrestore(&codec->voice_lock, flags);
- return result;
-}
-
-static int ymfpci_voice_free(ymfpci_t *codec, ymfpci_voice_t *pvoice)
-{
- unsigned long flags;
-
- ymfpci_hw_stop(codec);
- spin_lock_irqsave(&codec->voice_lock, flags);
+ ymfpci_hw_stop(unit);
pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0;
pvoice->ypcm = NULL;
- pvoice->interrupt = NULL;
- spin_unlock_irqrestore(&codec->voice_lock, flags);
- return 0;
}
/*
- * PCM part
*/
static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice)
{
- ymfpci_pcm_t *ypcm;
+ struct ymf_pcm *ypcm;
int redzone;
int pos, delta, swptr;
int played, distance;
@@ -611,7 +611,7 @@ static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice)
ypcm->running = 0; // lock it
return;
}
- dmabuf = &state->dmabuf;
+ dmabuf = &ypcm->dmabuf;
spin_lock(&codec->reg_lock);
if (ypcm->running) {
/* P3 */ /** printk("ymfpci: %d, intr bank %d count %d start 0x%x:%x\n",
@@ -627,10 +627,9 @@ static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice)
pos = voice->bank[codec->active_bank].start;
pos <<= state->format.shift;
if (pos < 0 || pos >= dmabuf->dmasize) { /* ucode bug */
- printk(KERN_ERR
- "ymfpci%d: %d: runaway: hwptr %d dmasize %d\n",
+ printk(KERN_ERR "ymfpci%d: runaway voice %d: hwptr %d=>%d dmasize %d\n",
codec->dev_audio, voice->number,
- dmabuf->hwptr, dmabuf->dmasize);
+ dmabuf->hwptr, pos, dmabuf->dmasize);
pos = 0;
}
if (pos < dmabuf->hwptr) {
@@ -706,36 +705,66 @@ static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice)
spin_unlock(&codec->reg_lock);
}
-#if HAVE_RECORD
-static void ymfpci_pcm_capture_interrupt(snd_pcm_subchn_t *substream)
+static void ymf_cap_interrupt(ymfpci_t *unit, struct ymf_capture *cap)
{
- snd_pcm_runtime_t *runtime = substream->runtime;
- ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, );
- ymfpci_t *codec = ypcm->codec;
- u32 pos, delta;
-
- spin_lock(&codec->reg_lock);
+ struct ymf_pcm *ypcm;
+ int redzone;
+ struct ymf_state *state;
+ struct ymf_dmabuf *dmabuf;
+ int pos, delta;
+ int cnt;
+
+ if ((ypcm = cap->ypcm) == NULL) {
+ return;
+ }
+ if ((state = ypcm->state) == NULL) {
+ ypcm->running = 0; // lock it
+ return;
+ }
+ dmabuf = &ypcm->dmabuf;
+ spin_lock(&unit->reg_lock);
if (ypcm->running) {
- pos = codec->bank_capture[ypcm->capture_bank_number][codec->active_bank]->start << ypcm->shift_offset;
- if (pos < ypcm->last_pos) // <-- dmabuf->hwptr
- delta = pos + (ypcm->buffer_size - ypcm->last_pos);
- else
- delta = pos - ypcm->last_pos;
- ypcm->frag_pos += delta;
- ypcm->last_pos = pos;
- while (ypcm->frag_pos >= ypcm->frag_size) {
- ypcm->frag_pos -= ypcm->frag_size;
- // printk("done - active_bank = 0x%x, start = 0x%x\n", codec->active_bank, voice->bank[codec->active_bank].start);
- spin_unlock(&codec->reg_lock);
- snd_pcm_transfer_done(substream);
- spin_lock(&codec->reg_lock);
+ redzone = ymf_calc_lend(state->format.rate);
+ redzone <<= (state->format.shift + 1);
+
+ pos = cap->bank[unit->active_bank].start;
+ // pos <<= state->format.shift;
+ if (pos < 0 || pos >= dmabuf->dmasize) { /* ucode bug */
+ printk(KERN_ERR "ymfpci%d: runaway capture %d: hwptr %d=>%d dmasize %d\n",
+ unit->dev_audio, ypcm->capture_bank_number,
+ dmabuf->hwptr, pos, dmabuf->dmasize);
+ pos = 0;
+ }
+ if (pos < dmabuf->hwptr) {
+ delta = dmabuf->dmasize - dmabuf->hwptr;
+ delta += pos;
+ } else {
+ delta = pos - dmabuf->hwptr;
+ }
+ dmabuf->hwptr = pos;
+
+ cnt = dmabuf->count;
+ cnt += delta;
+ if (cnt + redzone > dmabuf->dmasize) {
+ /* Overflow - bump swptr */
+ dmabuf->count = dmabuf->dmasize - redzone;
+ dmabuf->swptr = dmabuf->hwptr + redzone;
+ if (dmabuf->swptr >= dmabuf->dmasize) {
+ dmabuf->swptr -= dmabuf->dmasize;
+ }
+ } else {
+ dmabuf->count = cnt;
+ }
+
+ dmabuf->total_bytes += delta;
+ if (dmabuf->count) { /* && is_sleeping XXX */
+ wake_up(&dmabuf->wait);
}
}
- spin_unlock(&codec->reg_lock);
+ spin_unlock(&unit->reg_lock);
}
-#endif
-static int ymf_playback_trigger(ymfpci_t *codec, ymfpci_pcm_t *ypcm, int cmd)
+static int ymf_playback_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd)
{
if (ypcm->voices[0] == NULL) {
@@ -755,40 +784,29 @@ static int ymf_playback_trigger(ymfpci_t *codec, ymfpci_pcm_t *ypcm, int cmd)
return 0;
}
-#if HAVE_RECORD
-static int ymfpci_capture_trigger(void *private_data,
- snd_pcm_subchn_t * substream,
- int cmd)
+static void ymf_capture_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd)
{
- unsigned long flags;
- ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO);
- ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, substream->runtime->private_data, -ENXIO);
- int result = 0;
u32 tmp;
- spin_lock_irqsave(&codec->reg_lock, flags);
- if (cmd == SND_PCM_TRIGGER_GO) {
+ if (cmd != 0) {
tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number);
ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp);
ypcm->running = 1;
- } else if (cmd == SND_PCM_TRIGGER_STOP) {
+ } else {
tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number);
ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp);
ypcm->running = 0;
- } else {
- result = -EINVAL;
}
- spin_unlock_irqrestore(&codec->reg_lock, flags);
- return result;
}
-#endif
-static int ymfpci_pcm_voice_alloc(ymfpci_pcm_t *ypcm, int voices)
+static int ymfpci_pcm_voice_alloc(struct ymf_pcm *ypcm, int voices)
{
+ struct ymf_unit *unit;
int err;
+ unit = ypcm->state->unit;
if (ypcm->voices[1] != NULL && voices < 2) {
- ymfpci_voice_free(ypcm->codec, ypcm->voices[1]);
+ ymfpci_voice_free(unit, ypcm->voices[1]);
ypcm->voices[1] = NULL;
}
if (voices == 1 && ypcm->voices[0] != NULL)
@@ -797,18 +815,17 @@ static int ymfpci_pcm_voice_alloc(ymfpci_pcm_t *ypcm, int voices)
return 0; /* already allocated */
if (voices > 1) {
if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) {
- ymfpci_voice_free(ypcm->codec, ypcm->voices[0]);
+ ymfpci_voice_free(unit, ypcm->voices[0]);
ypcm->voices[0] = NULL;
}
- }
- err = ymfpci_voice_alloc(ypcm->codec, YMFPCI_PCM, voices > 1, &ypcm->voices[0]);
- if (err < 0)
- return err;
- ypcm->voices[0]->ypcm = ypcm;
- ypcm->voices[0]->interrupt = ymf_pcm_interrupt;
- if (voices > 1) {
- ypcm->voices[1] = &ypcm->codec->voices[ypcm->voices[0]->number + 1];
+ if ((err = voice_alloc(unit, YMFPCI_PCM, 1, ypcm->voices)) < 0)
+ return err;
+ ypcm->voices[0]->ypcm = ypcm;
ypcm->voices[1]->ypcm = ypcm;
+ } else {
+ if ((err = voice_alloc(unit, YMFPCI_PCM, 0, ypcm->voices)) < 0)
+ return err;
+ ypcm->voices[0]->ypcm = ypcm;
}
return 0;
}
@@ -901,17 +918,32 @@ static void ymf_pcm_init_voice(ymfpci_voice_t *voice, int stereo,
}
/*
- * XXX Use new cache coherent PCI DMA routines instead of virt_to_bus.
+ * XXX Capture channel allocation is entirely fake at the moment.
+ * We use only one channel and mark it busy as required.
*/
-static int ymf_playback_prepare(ymfpci_t *codec, struct ymf_state *state)
+static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank)
{
- ymfpci_pcm_t *ypcm = &state->ypcm;
+ struct ymf_capture *cap;
+ int cbank;
+
+ cbank = 1; /* Only ADC slot is used for now. */
+ cap = &unit->capture[cbank];
+ if (cap->use)
+ return -EBUSY;
+ cap->use = 1;
+ *pbank = cbank;
+ return 0;
+}
+
+static int ymf_playback_prepare(struct ymf_state *state)
+{
+ struct ymf_pcm *ypcm = &state->wpcm;
int err, nvoice;
if ((err = ymfpci_pcm_voice_alloc(ypcm, state->format.voices)) < 0) {
- /* Cannot be unless we leak voices in ymf_release! */
- printk(KERN_ERR "ymfpci%d: cannot allocate voice!\n",
- codec->dev_audio);
+ /* Somebody started 32 mpg123's in parallel? */
+ /* P3 */ printk("ymfpci%d: cannot allocate voice\n",
+ state->unit->dev_audio);
return err;
}
@@ -919,101 +951,76 @@ static int ymf_playback_prepare(ymfpci_t *codec, struct ymf_state *state)
ymf_pcm_init_voice(ypcm->voices[nvoice],
state->format.voices == 2, state->format.rate,
ymf_pcm_format_width(state->format.format) == 16,
- virt_to_bus(state->dmabuf.rawbuf), state->dmabuf.dmasize,
+ virt_to_bus(ypcm->dmabuf.rawbuf), ypcm->dmabuf.dmasize,
ypcm->spdif);
}
return 0;
}
-#if 0 /* old */
-static int ymfpci_capture_prepare(void *private_data,
- snd_pcm_subchn_t * substream)
+static int ymf_capture_prepare(struct ymf_state *state)
{
- ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO);
- snd_pcm_runtime_t *runtime = substream->runtime;
- ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, -ENXIO);
+ ymfpci_t *unit = state->unit;
+ struct ymf_pcm *ypcm = &state->rpcm;
ymfpci_capture_bank_t * bank;
- int nbank;
+ /* XXX This is confusing, gotta rename one of them banks... */
+ int nbank; /* flip-flop bank */
+ int cbank; /* input [super-]bank */
+ struct ymf_capture *cap;
u32 rate, format;
- ypcm->frag_size = snd_pcm_lib_transfer_fragment(substream);
- ypcm->buffer_size = snd_pcm_lib_transfer_size(substream);
- ypcm->frag_pos = 0;
- ypcm->last_pos = 0;
- ypcm->shift_offset = 0;
- rate = ((48000 * 4096) / runtime->format.rate) - 1;
+ if (ypcm->capture_bank_number == -1) {
+ if (ymf_capture_alloc(unit, &cbank) != 0)
+ return -EBUSY;
+
+ ypcm->capture_bank_number = cbank;
+
+ cap = &unit->capture[cbank];
+ cap->bank = unit->bank_capture[cbank][0];
+ cap->ypcm = ypcm;
+ ymfpci_hw_start(unit);
+ }
+
+ // ypcm->frag_size = snd_pcm_lib_transfer_fragment(substream);
+ // frag_size is replaced with nonfragged byte-aligned rolling buffer
+ rate = ((48000 * 4096) / state->format.rate) - 1;
format = 0;
- if (runtime->format.voices == 2)
+ if (state->format.voices == 2)
format |= 2;
- if (snd_pcm_format_width(runtime->format.format) == 8)
+ if (ymf_pcm_format_width(state->format.format) == 8)
format |= 1;
switch (ypcm->capture_bank_number) {
case 0:
- ymfpci_writel(codec, YDSXGR_RECFORMAT, format);
- ymfpci_writel(codec, YDSXGR_RECSLOTSR, rate);
+ ymfpci_writel(unit, YDSXGR_RECFORMAT, format);
+ ymfpci_writel(unit, YDSXGR_RECSLOTSR, rate);
break;
case 1:
- ymfpci_writel(codec, YDSXGR_ADCFORMAT, format);
- ymfpci_writel(codec, YDSXGR_ADCSLOTSR, rate);
+ ymfpci_writel(unit, YDSXGR_ADCFORMAT, format);
+ ymfpci_writel(unit, YDSXGR_ADCSLOTSR, rate);
break;
}
for (nbank = 0; nbank < 2; nbank++) {
- bank = codec->bank_capture[ypcm->capture_bank_number][nbank];
- bank->base = virt_to_bus(runtime->dma_area->buf);
- bank->loop_end = ypcm->buffer_size;
+ bank = unit->bank_capture[ypcm->capture_bank_number][nbank];
+ bank->base = virt_to_bus(ypcm->dmabuf.rawbuf);
+ // bank->loop_end = ypcm->dmabuf.dmasize >> state->format.shift;
+ bank->loop_end = ypcm->dmabuf.dmasize;
bank->start = 0;
bank->num_of_loops = 0;
}
- if (runtime->digital.dig_valid)
- /*runtime->digital.type == SND_PCM_DIG_AES_IEC958*/
- ymfpci_writew(codec, YDSXGR_SPDIFOUTSTATUS, runtime->digital.dig_status[0] |
- (runtime->digital.dig_status[1] << 8));
+#if 0 /* s/pdif */
+ if (state->digital.dig_valid)
+ /*state->digital.type == SND_PCM_DIG_AES_IEC958*/
+ ymfpci_writew(codec, YDSXGR_SPDIFOUTSTATUS,
+ state->digital.dig_status[0] | (state->digital.dig_status[1] << 8));
+#endif
return 0;
}
-static unsigned int ymfpci_playback_pointer(void *private_data,
- snd_pcm_subchn_t * substream)
-{
- ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO);
- snd_pcm_runtime_t *runtime = substream->runtime;
- ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, -ENXIO);
- ymfpci_voice_t *voice = ypcm->voices[0];
- unsigned long flags;
- unsigned int result;
-
- spin_lock_irqsave(&codec->reg_lock, flags);
- if (ypcm->running && voice)
- result = voice->bank[codec->active_bank].start << ypcm->shift_offset;
- else
- result = 0;
- spin_unlock_irqrestore(&codec->reg_lock, flags);
- return result;
-}
-
-static unsigned int ymfpci_capture_pointer(void *private_data,
- snd_pcm_subchn_t * substream)
-{
- ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO);
- snd_pcm_runtime_t *runtime = substream->runtime;
- ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, -ENXIO);
- unsigned long flags;
- unsigned int result;
-
- spin_lock_irqsave(&codec->reg_lock, flags);
- if (ypcm->running)
- result = codec->bank_capture[ypcm->capture_bank_number][codec->active_bank]->start << ypcm->shift_offset;
- else
- result = 0;
- spin_unlock_irqrestore(&codec->reg_lock, flags);
- return result;
-}
-#endif /* old */
-
void ymf_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
ymfpci_t *codec = dev_id;
u32 status, nvoice, mode;
- ymfpci_voice_t *voice;
+ struct ymf_voice *voice;
+ struct ymf_capture *cap;
status = ymfpci_readl(codec, YDSXGR_STATUS);
if (status & 0x80000000) {
@@ -1026,8 +1033,13 @@ void ymf_interrupt(int irq, void *dev_id, struct pt_regs *regs)
spin_lock(&codec->voice_lock);
for (nvoice = 0; nvoice < 64; nvoice++) {
voice = &codec->voices[nvoice];
- if (voice->interrupt)
- voice->interrupt(codec, voice);
+ if (voice->use)
+ ymf_pcm_interrupt(codec, voice);
+ }
+ for (nvoice = 0; nvoice < 5; nvoice++) {
+ cap = &codec->capture[nvoice];
+ if (cap->use)
+ ymf_cap_interrupt(codec, cap);
}
spin_unlock(&codec->voice_lock);
}
@@ -1039,22 +1051,32 @@ void ymf_interrupt(int irq, void *dev_id, struct pt_regs *regs)
}
}
-static void ymf_pcm_free_substream(ymfpci_pcm_t *ypcm)
+static void ymf_pcm_free_substream(struct ymf_pcm *ypcm)
{
- ymfpci_t *codec;
+ unsigned long flags;
+ struct ymf_unit *unit;
+
+ unit = ypcm->state->unit;
- if (ypcm) {
- codec = ypcm->codec;
+ if (ypcm->type == PLAYBACK_VOICE) {
+ spin_lock_irqsave(&unit->voice_lock, flags);
if (ypcm->voices[1])
- ymfpci_voice_free(codec, ypcm->voices[1]);
+ ymfpci_voice_free(unit, ypcm->voices[1]);
if (ypcm->voices[0])
- ymfpci_voice_free(codec, ypcm->voices[0]);
+ ymfpci_voice_free(unit, ypcm->voices[0]);
+ spin_unlock_irqrestore(&unit->voice_lock, flags);
+ } else {
+ if (ypcm->capture_bank_number != -1) {
+ unit->capture[ypcm->capture_bank_number].use = 0;
+ ypcm->capture_bank_number = -1;
+ ymfpci_hw_stop(unit);
+ }
}
}
-static int ymf_state_alloc(ymfpci_t *unit, int nvirt)
+static struct ymf_state *ymf_state_alloc(ymfpci_t *unit)
{
- ymfpci_pcm_t *ypcm;
+ struct ymf_pcm *ypcm;
struct ymf_state *state;
if ((state = kmalloc(sizeof(struct ymf_state), GFP_KERNEL)) == NULL) {
@@ -1062,61 +1084,31 @@ static int ymf_state_alloc(ymfpci_t *unit, int nvirt)
}
memset(state, 0, sizeof(struct ymf_state));
- init_waitqueue_head(&state->dmabuf.wait);
-
- ypcm = &state->ypcm;
+ ypcm = &state->wpcm;
ypcm->state = state;
- ypcm->codec = unit;
ypcm->type = PLAYBACK_VOICE;
+ ypcm->capture_bank_number = -1;
+ init_waitqueue_head(&ypcm->dmabuf.wait);
+
+ ypcm = &state->rpcm;
+ ypcm->state = state;
+ ypcm->type = CAPTURE_AC97;
+ ypcm->capture_bank_number = -1;
+ init_waitqueue_head(&ypcm->dmabuf.wait);
state->unit = unit;
- state->virt = nvirt;
state->format.format = AFMT_U8;
state->format.rate = 8000;
state->format.voices = 1;
ymf_pcm_update_shift(&state->format);
- unit->states[nvirt] = state;
- return 0;
+ return state;
out0:
- return -ENOMEM;
+ return NULL;
}
-#if HAVE_RECORD
-
-static int ymfpci_capture_open(void *private_data,
- snd_pcm_subchn_t * substream,
- u32 capture_bank_number)
-{
- ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO);
- snd_pcm_runtime_t *runtime = substream->runtime;
- ymfpci_pcm_t *ypcm;
- int err;
-
- if ((err = snd_pcm_dma_alloc(substream, !capture_bank_number ? codec->dma2ptr : codec->dma3ptr, "YMFPCI - ADC")) < 0)
- return err;
- ypcm = snd_magic_kcalloc(ymfpci_pcm_t, 0, GFP_KERNEL);
- if (ypcm == NULL) {
- snd_pcm_dma_free(substream);
- return -ENOMEM;
- }
- ypcm->codec = codec;
- ypcm->type = capture_bank_number + CAPTURE_REC;
- ypcm->substream = substream;
- ypcm->capture_bank_number = capture_bank_number;
- codec->capture_substream[capture_bank_number] = substream;
- runtime->hw = &ymfpci_capture;
- snd_pcm_set_mixer(substream, codec->mixer->device, codec->ac97->me_capture);
- runtime->private_data = ypcm;
- runtime->private_free = ymfpci_pcm_free_substream;
- ymfpci_hw_start(codec);
- return 0;
-}
-
-#endif /* old */
-
/* AES/IEC958 channel status bits */
#define SND_PCM_AES0_PROFESSIONAL (1<<0) /* 0 = consumer, 1 = professional */
#define SND_PCM_AES0_NONAUDIO (1<<1) /* 0 = audio, 1 = non-audio */
@@ -1202,24 +1194,6 @@ static int ymfpci_capture_open(void *private_data,
#define SND_PCM_AES3_CON_CLOCK_50PPM (1<<4) /* 50 ppm */
#define SND_PCM_AES3_CON_CLOCK_VARIABLE (2<<4) /* variable pitch */
-#if HAVE_RECORD /* old */
-
-static int ymfpci_capture_close(void *private_data,
- snd_pcm_subchn_t * substream)
-{
- ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO);
- snd_pcm_runtime_t *runtime = substream->runtime;
- ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, -ENXIO);
-
- if (ypcm != NULL) {
- codec->capture_substream[ypcm->capture_bank_number] = NULL;
- ymfpci_hw_stop(codec);
- }
- snd_pcm_dma_free(substream);
- return 0;
-}
-#endif
-
/*
* User interface
*/
@@ -1229,21 +1203,21 @@ static loff_t ymf_llseek(struct file *file, loff_t offset, int origin)
return -ESPIPE;
}
-/* in this loop, dmabuf.count signifies the amount of data that is waiting to be copied to
- the user's buffer. it is filled by the dma machine and drained by this loop. */
-static ssize_t ymf_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+/*
+ * in this loop, dmabuf.count signifies the amount of data that is
+ * waiting to be copied to the user's buffer. it is filled by the dma
+ * machine and drained by this loop.
+ */
+static ssize_t
+ymf_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{
-#if HAVE_RECORD
- struct cs_state *state = (struct cs_state *)file->private_data;
- struct dmabuf *dmabuf = &state->dmabuf;
+ struct ymf_state *state = (struct ymf_state *)file->private_data;
+ struct ymf_dmabuf *dmabuf = &state->rpcm.dmabuf;
+ DECLARE_WAITQUEUE(waita, current);
ssize_t ret;
unsigned long flags;
- unsigned swptr;
- int cnt;
-
-#ifdef DEBUG
- printk("cs461x: cs_read called, count = %d\n", count);
-#endif
+ unsigned int swptr;
+ int cnt; /* This many to go in this revolution */
if (ppos != &file->f_pos)
return -ESPIPE;
@@ -1255,19 +1229,14 @@ static ssize_t ymf_read(struct file *file, char *buffer, size_t count, loff_t *p
return -EFAULT;
ret = 0;
+ add_wait_queue(&dmabuf->wait, &waita);
while (count > 0) {
- spin_lock_irqsave(&state->card->lock, flags);
- if (dmabuf->count > (signed) dmabuf->dmasize) {
- /* buffer overrun, we are recovering from sleep_on_timeout,
- resync hwptr and swptr, make process flush the buffer */
- dmabuf->count = dmabuf->dmasize;
- dmabuf->swptr = dmabuf->hwptr;
- }
+ spin_lock_irqsave(&state->unit->reg_lock, flags);
swptr = dmabuf->swptr;
cnt = dmabuf->dmasize - swptr;
if (dmabuf->count < cnt)
cnt = dmabuf->count;
- spin_unlock_irqrestore(&state->card->lock, flags);
+ spin_unlock_irqrestore(&state->unit->reg_lock, flags);
if (cnt > count)
cnt = count;
@@ -1275,14 +1244,18 @@ static ssize_t ymf_read(struct file *file, char *buffer, size_t count, loff_t *p
unsigned long tmo;
/* buffer is empty, start the dma machine and wait for data to be
recorded */
- start_adc(state);
+ spin_lock_irqsave(&state->unit->reg_lock, flags);
+ if (!state->rpcm.running) {
+ ymf_capture_trigger(state->unit, &state->rpcm, 1);
+ }
+ spin_unlock_irqrestore(&state->unit->reg_lock, flags);
if (file->f_flags & O_NONBLOCK) {
if (!ret) ret = -EAGAIN;
- return ret;
+ break;
}
/* This isnt strictly right for the 810 but it'll do */
- tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
- tmo >>= sample_shift[dmabuf->fmt];
+ tmo = (dmabuf->dmasize * HZ) / (state->format.rate * 2);
+ tmo >>= state->format.shift;
/* 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
@@ -1290,50 +1263,56 @@ static ssize_t ymf_read(struct file *file, char *buffer, size_t count, loff_t *p
is TOO LATE for the process to be scheduled to run (scheduler latency)
which results in a (potential) buffer overrun. And worse, there is
NOTHING we can do to prevent it. */
- if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) {
-#ifdef DEBUG
- printk(KERN_ERR "cs461x: recording schedule timeout, "
- "dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
- dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
- dmabuf->hwptr, dmabuf->swptr);
-#endif
- /* a buffer overrun, we delay the recovery untill next time the
- while loop begin and we REALLY have space to record */
+ set_current_state(TASK_INTERRUPTIBLE);
+ tmo = schedule_timeout(tmo);
+ spin_lock_irqsave(&state->unit->reg_lock, flags);
+ if (tmo == 0 && dmabuf->count == 0) {
+ printk(KERN_ERR "ymfpci%d: recording schedule timeout, "
+ "dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+ state->unit->dev_audio,
+ dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
+ dmabuf->hwptr, dmabuf->swptr);
}
+ spin_unlock_irqrestore(&state->unit->reg_lock, flags);
if (signal_pending(current)) {
ret = ret ? ret : -ERESTARTSYS;
- return ret;
+ break;
}
continue;
}
if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) {
if (!ret) ret = -EFAULT;
- return ret;
+ break;
}
swptr = (swptr + cnt) % dmabuf->dmasize;
- spin_lock_irqsave(&state->card->lock, flags);
+ spin_lock_irqsave(&state->unit->reg_lock, flags);
dmabuf->swptr = swptr;
dmabuf->count -= cnt;
- spin_unlock_irqrestore(&state->card->lock, flags);
+ // spin_unlock_irqrestore(&state->unit->reg_lock, flags);
count -= cnt;
buffer += cnt;
ret += cnt;
- start_adc(state);
+ // spin_lock_irqsave(&state->unit->reg_lock, flags);
+ if (!state->rpcm.running) {
+ ymf_capture_trigger(state->unit, &state->rpcm, 1);
+ }
+ spin_unlock_irqrestore(&state->unit->reg_lock, flags);
}
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&dmabuf->wait, &waita);
+
return ret;
-#else
- return -EINVAL;
-#endif
}
-static ssize_t ymf_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+static ssize_t
+ymf_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
{
struct ymf_state *state = (struct ymf_state *)file->private_data;
- struct ymf_dmabuf *dmabuf = &state->dmabuf;
+ struct ymf_dmabuf *dmabuf = &state->wpcm.dmabuf;
DECLARE_WAITQUEUE(waita, current);
ssize_t ret;
unsigned long flags;
@@ -1375,7 +1354,7 @@ static ssize_t ymf_write(struct file *file, const char *buffer, size_t count, lo
}
if (dmabuf->count == 0) {
swptr = dmabuf->hwptr;
- if (state->ypcm.running) {
+ if (state->wpcm.running) {
/*
* Add uncertainty reserve.
*/
@@ -1410,8 +1389,8 @@ static ssize_t ymf_write(struct file *file, const char *buffer, size_t count, lo
* wait for data to be played
*/
spin_lock_irqsave(&state->unit->reg_lock, flags);
- if (!state->ypcm.running) {
- ymf_playback_trigger(state->unit, &state->ypcm, 1);
+ if (!state->wpcm.running) {
+ ymf_playback_trigger(state->unit, &state->wpcm, 1);
}
spin_unlock_irqrestore(&state->unit->reg_lock, flags);
if (file->f_flags & O_NONBLOCK) {
@@ -1448,8 +1427,8 @@ static ssize_t ymf_write(struct file *file, const char *buffer, size_t count, lo
*/
delay = state->format.rate / 20; /* 50ms */
delay <<= state->format.shift;
- if (dmabuf->count >= delay && !state->ypcm.running) {
- ymf_playback_trigger(state->unit, &state->ypcm, 1);
+ if (dmabuf->count >= delay && !state->wpcm.running) {
+ ymf_playback_trigger(state->unit, &state->wpcm, 1);
}
spin_unlock_irqrestore(&state->unit->reg_lock, flags);
@@ -1469,19 +1448,23 @@ static ssize_t ymf_write(struct file *file, const char *buffer, size_t count, lo
static unsigned int ymf_poll(struct file *file, struct poll_table_struct *wait)
{
struct ymf_state *state = (struct ymf_state *)file->private_data;
- struct ymf_dmabuf *dmabuf = &state->dmabuf;
+ struct ymf_dmabuf *dmabuf;
unsigned long flags;
unsigned int mask = 0;
- if (file->f_mode & (FMODE_WRITE | FMODE_READ))
- poll_wait(file, &dmabuf->wait, wait);
+ if (file->f_mode & FMODE_WRITE)
+ poll_wait(file, &state->wpcm.dmabuf.wait, wait);
+ // if (file->f_mode & FMODE_READ)
+ // poll_wait(file, &dmabuf->wait, wait);
spin_lock_irqsave(&state->unit->reg_lock, flags);
if (file->f_mode & FMODE_READ) {
+ dmabuf = &state->rpcm.dmabuf;
if (dmabuf->count >= (signed)dmabuf->fragsize)
mask |= POLLIN | POLLRDNORM;
}
if (file->f_mode & FMODE_WRITE) {
+ dmabuf = &state->wpcm.dmabuf;
if (dmabuf->mapped) {
if (dmabuf->count >= (signed)dmabuf->fragsize)
mask |= POLLOUT | POLLWRNORM;
@@ -1498,10 +1481,9 @@ static unsigned int ymf_poll(struct file *file, struct poll_table_struct *wait)
static int ymf_mmap(struct file *file, struct vm_area_struct *vma)
{
struct ymf_state *state = (struct ymf_state *)file->private_data;
- struct ymf_dmabuf *dmabuf = &state->dmabuf;
+ struct ymf_dmabuf *dmabuf = &state->wpcm.dmabuf;
int ret;
unsigned long size;
-
if (vma->vm_flags & VM_WRITE) {
if ((ret = prog_dmabuf(state, 0)) != 0)
@@ -1529,7 +1511,7 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct ymf_state *state = (struct ymf_state *)file->private_data;
- struct ymf_dmabuf *dmabuf = &state->dmabuf;
+ struct ymf_dmabuf *dmabuf;
unsigned long flags;
audio_buf_info abinfo;
count_info cinfo;
@@ -1542,28 +1524,30 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
case SNDCTL_DSP_RESET:
if (file->f_mode & FMODE_WRITE) {
ymf_wait_dac(state);
+ dmabuf = &state->wpcm.dmabuf;
spin_lock_irqsave(&state->unit->reg_lock, flags);
dmabuf->ready = 0;
- dmabuf->swptr = dmabuf->hwptr = 0;
+ dmabuf->swptr = dmabuf->hwptr;
dmabuf->count = dmabuf->total_bytes = 0;
spin_unlock_irqrestore(&state->unit->reg_lock, flags);
}
-#if HAVE_RECORD
if (file->f_mode & FMODE_READ) {
- stop_adc(state);
- synchronize_irq();
+ ymf_stop_adc(state);
+ dmabuf = &state->rpcm.dmabuf;
+ spin_lock_irqsave(&state->unit->reg_lock, flags);
dmabuf->ready = 0;
- dmabuf->swptr = dmabuf->hwptr = 0;
+ dmabuf->swptr = dmabuf->hwptr;
dmabuf->count = dmabuf->total_bytes = 0;
+ spin_unlock_irqrestore(&state->unit->reg_lock, flags);
}
-#endif
return 0;
case SNDCTL_DSP_SYNC:
if (file->f_mode & FMODE_WRITE) {
+ dmabuf = &state->wpcm.dmabuf;
if (file->f_flags & O_NONBLOCK) {
spin_lock_irqsave(&state->unit->reg_lock, flags);
- if (dmabuf->count != 0 && !state->ypcm.running) {
+ if (dmabuf->count != 0 && !state->wpcm.running) {
ymf_start_dac(state);
}
spin_unlock_irqrestore(&state->unit->reg_lock, flags);
@@ -1571,6 +1555,7 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
ymf_wait_dac(state);
}
}
+ /* XXX What does this do for reading? dmabuf->count=0; ? */
return 0;
case SNDCTL_DSP_SPEED: /* set smaple rate */
@@ -1579,17 +1564,22 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
if (val >= 8000 && val <= 48000) {
if (file->f_mode & FMODE_WRITE) {
ymf_wait_dac(state);
+ dmabuf = &state->wpcm.dmabuf;
+ spin_lock_irqsave(&state->unit->reg_lock, flags);
+ dmabuf->ready = 0;
+ state->format.rate = val;
+ ymf_pcm_update_shift(&state->format);
+ spin_unlock_irqrestore(&state->unit->reg_lock, flags);
}
-#if HAVE_RECORD
if (file->f_mode & FMODE_READ) {
- stop_adc(state);
+ ymf_stop_adc(state);
+ dmabuf = &state->rpcm.dmabuf;
+ spin_lock_irqsave(&state->unit->reg_lock, flags);
+ dmabuf->ready = 0;
+ state->format.rate = val;
+ ymf_pcm_update_shift(&state->format);
+ spin_unlock_irqrestore(&state->unit->reg_lock, flags);
}
-#endif
- spin_lock_irqsave(&state->unit->reg_lock, flags);
- dmabuf->ready = 0;
- state->format.rate = val;
- ymf_pcm_update_shift(&state->format);
- spin_unlock_irqrestore(&state->unit->reg_lock, flags);
}
return put_user(state->format.rate, (int *)arg);
@@ -1597,41 +1587,41 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
* OSS manual does not mention SNDCTL_DSP_STEREO at all.
* All channels are mono and if you want stereo, you
* play into two channels with SNDCTL_DSP_CHANNELS.
- * However, mpg123 uses it. I wonder, why Michael Hipp uses it.
+ * However, mpg123 calls it. I wonder, why Michael Hipp used it.
*/
case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
if (get_user(val, (int *)arg))
return -EFAULT;
if (file->f_mode & FMODE_WRITE) {
ymf_wait_dac(state);
+ dmabuf = &state->wpcm.dmabuf;
spin_lock_irqsave(&state->unit->reg_lock, flags);
dmabuf->ready = 0;
state->format.voices = val ? 2 : 1;
ymf_pcm_update_shift(&state->format);
spin_unlock_irqrestore(&state->unit->reg_lock, flags);
}
-#if HAVE_RECORD
if (file->f_mode & FMODE_READ) {
- /* stop_adc(state); */
+ ymf_stop_adc(state);
+ dmabuf = &state->rpcm.dmabuf;
+ spin_lock_irqsave(&state->unit->reg_lock, flags);
dmabuf->ready = 0;
- if(val)
- dmabuf->fmt |= CS_FMT_STEREO;
- else
- dmabuf->fmt &= ~CS_FMT_STEREO;
+ state->format.voices = val ? 2 : 1;
+ ymf_pcm_update_shift(&state->format);
+ spin_unlock_irqrestore(&state->unit->reg_lock, flags);
}
-#endif
return 0;
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);
+ return put_user(state->wpcm.dmabuf.fragsize, (int *)arg);
}
if (file->f_mode & FMODE_READ) {
if ((val = prog_dmabuf(state, 1)))
return val;
- return put_user(dmabuf->fragsize, (int *)arg);
+ return put_user(state->rpcm.dmabuf.fragsize, (int *)arg);
}
return -EINVAL;
@@ -1644,17 +1634,22 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
if (val == AFMT_S16_LE || val == AFMT_U8) {
if (file->f_mode & FMODE_WRITE) {
ymf_wait_dac(state);
+ dmabuf = &state->wpcm.dmabuf;
+ spin_lock_irqsave(&state->unit->reg_lock, flags);
+ dmabuf->ready = 0;
+ state->format.format = val;
+ ymf_pcm_update_shift(&state->format);
+ spin_unlock_irqrestore(&state->unit->reg_lock, flags);
}
-#if HAVE_RECORD
if (file->f_mode & FMODE_READ) {
- stop_adc(state);
+ ymf_stop_adc(state);
+ dmabuf = &state->rpcm.dmabuf;
+ spin_lock_irqsave(&state->unit->reg_lock, flags);
+ dmabuf->ready = 0;
+ state->format.format = val;
+ ymf_pcm_update_shift(&state->format);
+ spin_unlock_irqrestore(&state->unit->reg_lock, flags);
}
-#endif
- spin_lock_irqsave(&state->unit->reg_lock, flags);
- dmabuf->ready = 0;
- state->format.format = val;
- ymf_pcm_update_shift(&state->format);
- spin_unlock_irqrestore(&state->unit->reg_lock, flags);
}
return put_user(state->format.format, (int *)arg);
@@ -1667,20 +1662,24 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
ymf_wait_dac(state);
if (val == 1 || val == 2) {
spin_lock_irqsave(&state->unit->reg_lock, flags);
+ dmabuf = &state->wpcm.dmabuf;
dmabuf->ready = 0;
state->format.voices = val;
ymf_pcm_update_shift(&state->format);
spin_unlock_irqrestore(&state->unit->reg_lock, flags);
}
}
-#if HAVE_RECORD
if (file->f_mode & FMODE_READ) {
- spin_lock_irqsave(&state->unit->reg_lock, flags);
- stop_adc(state);
- dmabuf->ready = 0;
- spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+ ymf_stop_adc(state);
+ if (val == 1 || val == 2) {
+ spin_lock_irqsave(&state->unit->reg_lock, flags);
+ dmabuf = &state->rpcm.dmabuf;
+ dmabuf->ready = 0;
+ state->format.voices = val;
+ ymf_pcm_update_shift(&state->format);
+ spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+ }
}
-#endif
}
return put_user(state->format.voices, (int *)arg);
@@ -1696,15 +1695,16 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
* The paragraph above is a clumsy way to say "flush ioctl".
* This ioctl is used by mpg123.
*/
- /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_POST\n"); */
spin_lock_irqsave(&state->unit->reg_lock, flags);
- if (dmabuf->count != 0 && !state->ypcm.running) {
+ if (state->wpcm.dmabuf.count != 0 && !state->wpcm.running) {
ymf_start_dac(state);
}
spin_unlock_irqrestore(&state->unit->reg_lock, flags);
return 0;
+#if 0 /* XXX Was dummy implementation anyways. Make sense of this. */
case SNDCTL_DSP_SUBDIVIDE:
+ dmabuf = &state->wpcm.dmabuf;
if (dmabuf->subdivision)
return -EINVAL;
if (get_user(val, (int *)arg))
@@ -1713,6 +1713,7 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
return -EINVAL;
dmabuf->subdivision = val;
return 0;
+#endif
case SNDCTL_DSP_SETFRAGMENT:
if (get_user(val, (int *)arg))
@@ -1720,6 +1721,7 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
/* P3: these frags are for Doom. Amasingly, it sets [2,2**11]. */
/* P3 */ // printk("ymfpci: ioctl SNDCTL_DSP_SETFRAGMENT 0x%x\n", val);
+ dmabuf = &state->wpcm.dmabuf;
dmabuf->ossfragshift = val & 0xffff;
dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
switch (dmabuf->ossmaxfrags) {
@@ -1736,10 +1738,10 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
case SNDCTL_DSP_GETOSPACE:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
+ dmabuf = &state->wpcm.dmabuf;
if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
return val;
spin_lock_irqsave(&state->unit->reg_lock, flags);
- /* cs_update_ptr(state); */ /* XXX Always up to date? */
abinfo.fragsize = dmabuf->fragsize;
abinfo.bytes = dmabuf->dmasize - dmabuf->count;
abinfo.fragstotal = dmabuf->numfrag;
@@ -1747,21 +1749,19 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
spin_unlock_irqrestore(&state->unit->reg_lock, flags);
return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
-#if HAVE_RECORD
case SNDCTL_DSP_GETISPACE:
if (!(file->f_mode & FMODE_READ))
return -EINVAL;
+ dmabuf = &state->rpcm.dmabuf;
if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
return val;
- spin_lock_irqsave(&state->card->lock, flags);
- cs_update_ptr(state);
+ spin_lock_irqsave(&state->unit->reg_lock, flags);
abinfo.fragsize = dmabuf->fragsize;
abinfo.bytes = dmabuf->count;
abinfo.fragstotal = dmabuf->numfrag;
abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
- spin_unlock_irqrestore(&state->card->lock, flags);
+ spin_unlock_irqrestore(&state->unit->reg_lock, flags);
return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
-#endif
case SNDCTL_DSP_NONBLOCK:
file->f_flags |= O_NONBLOCK;
@@ -1772,9 +1772,10 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
(int *)arg); */
return put_user(0, (int *)arg);
-#if 0 /* old */
+#if 0 /* not implememnted, yet? */
case SNDCTL_DSP_GETTRIGGER:
val = 0;
+ dmabuf = &state->wpcm.dmabuf;
if (file->f_mode & FMODE_READ && dmabuf->enable)
val |= PCM_ENABLE_INPUT;
if (file->f_mode & FMODE_WRITE && dmabuf->enable)
@@ -1785,6 +1786,7 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
if (get_user(val, (int *)arg))
return -EFAULT;
if (file->f_mode & FMODE_READ) {
+ dmabuf = &state->rpcm.dmabuf;
if (val & PCM_ENABLE_INPUT) {
if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
return ret;
@@ -1793,6 +1795,7 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
stop_adc(state);
}
if (file->f_mode & FMODE_WRITE) {
+ dmabuf = &state->wpcm.dmabuf;
if (val & PCM_ENABLE_OUTPUT) {
if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
return ret;
@@ -1801,48 +1804,48 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
stop_dac(state);
}
return 0;
-
#endif
-#if HAVE_RECORD
case SNDCTL_DSP_GETIPTR:
if (!(file->f_mode & FMODE_READ))
return -EINVAL;
+ dmabuf = &state->rpcm.dmabuf;
spin_lock_irqsave(&state->unit->reg_lock, flags);
- cs_update_ptr(state);
cinfo.bytes = dmabuf->total_bytes;
cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
cinfo.ptr = dmabuf->hwptr;
+ /* XXX fishy - breaks invariant count=hwptr-swptr */
if (dmabuf->mapped)
dmabuf->count &= dmabuf->fragsize-1;
spin_unlock_irqrestore(&state->unit->reg_lock, flags);
- return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
-#endif
+ return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0;
case SNDCTL_DSP_GETOPTR:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
+ dmabuf = &state->wpcm.dmabuf;
spin_lock_irqsave(&state->unit->reg_lock, flags);
- /* cs_update_ptr(state); */ /* Always up to date */
cinfo.bytes = dmabuf->total_bytes;
cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
cinfo.ptr = dmabuf->hwptr;
+ /* XXX fishy - breaks invariant count=swptr-hwptr */
if (dmabuf->mapped)
dmabuf->count &= dmabuf->fragsize-1;
spin_unlock_irqrestore(&state->unit->reg_lock, flags);
- return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+ return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0;
case SNDCTL_DSP_SETDUPLEX: /* XXX TODO */
return -EINVAL;
-#if 0 /* old */
+#if 0 /* XXX implement when an app found that uses it. */
case SNDCTL_DSP_GETODELAY:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
spin_lock_irqsave(&state->unit->reg_lock, flags);
cs_update_ptr(state);
+ dmabuf = &state->wpcm.dmabuf;
val = dmabuf->count;
- spin_unlock_irqrestore(&state->card->lock, flags);
+ spin_unlock_irqrestore(&state->unit->reg_lock, flags);
return put_user(val, (int *)arg);
#endif
@@ -1850,7 +1853,7 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
return put_user(state->format.rate, (int *)arg);
case SOUND_PCM_READ_CHANNELS:
- return put_user(state->format.voices, (int *)arg);
+ return put_user(state->format.voices, (int *)arg);
case SOUND_PCM_READ_BITS:
return put_user(AFMT_S16_LE, (int *)arg);
@@ -1873,28 +1876,25 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
return -ENOTTY;
}
+/*
+ * open(2)
+ * We use upper part of the minor to distinguish between soundcards.
+ * Channels are opened with a clone open.
+ */
static int ymf_open(struct inode *inode, struct file *file)
{
struct list_head *list;
ymfpci_t *unit;
int minor;
struct ymf_state *state;
- int nvirt;
int err;
- /*
- * This is how we do it currently: only one channel is used
- * in every board, so that we could use several boards in one machine.
- * We waste 63 out of 64 playback slots, but so what.
- * OSS model is constructed for devices with single playback channel.
- */
minor = MINOR(inode->i_rdev);
if ((minor & 0x0F) == 3) { /* /dev/dspN */
;
} else {
return -ENXIO;
}
- nvirt = 0; /* Such is the partitioning of minor */
for (list = ymf_devs.next; list != &ymf_devs; list = list->next) {
unit = list_entry(list, ymfpci_t, ymf_devs);
@@ -1905,34 +1905,36 @@ static int ymf_open(struct inode *inode, struct file *file)
return -ENODEV;
down(&unit->open_sem);
- if (unit->states[nvirt] != NULL) {
- up(&unit->open_sem);
- return -EBUSY;
- }
- if ((err = ymf_state_alloc(unit, nvirt)) != 0) {
+ if ((state = ymf_state_alloc(unit)) == NULL) {
up(&unit->open_sem);
- return err;
+ return -ENOMEM;
}
- state = unit->states[nvirt];
+ list_add_tail(&state->chain, &unit->states);
file->private_data = state;
/*
- * XXX This ymf_playback_prepare is totally unneeded here.
- * The question is if we want to allow write to fail if
- * prog_dmabuf fails... Say, no memory in DMA zone?
+ * ymf_read and ymf_write that we borrowed from cs46xx
+ * allocate buffers with prog_dmabuf(). We call prog_dmabuf
+ * here so that in case of DMA memory exhaustion open
+ * fails rather than write.
+ *
+ * XXX prog_dmabuf allocates voice. Should allocate explicitly, above.
*/
- if ((err = ymf_playback_prepare(unit, state)) != 0) {
- /* XXX This recovery is ugly as hell. */
-
- ymf_pcm_free_substream(&state->ypcm);
-
- unit->states[state->virt] = NULL;
- kfree(state);
-
- up(&unit->open_sem);
- return err;
+ if (file->f_mode & FMODE_WRITE) {
+ if (!state->wpcm.dmabuf.ready) {
+ if ((err = prog_dmabuf(state, 0)) != 0) {
+ goto out_nodma;
+ }
+ }
+ }
+ if (file->f_mode & FMODE_READ) {
+ if (!state->rpcm.dmabuf.ready) {
+ if ((err = prog_dmabuf(state, 1)) != 0) {
+ goto out_nodma;
+ }
+ }
}
#if 0 /* test if interrupts work */
@@ -1941,10 +1943,26 @@ static int ymf_open(struct inode *inode, struct file *file)
(YDSXGR_TIMERCTRL_TEN|YDSXGR_TIMERCTRL_TIEN));
#endif
up(&unit->open_sem);
- /* XXX Is it correct to have MOD_INC_USE_COUNT outside of sem.? */
MOD_INC_USE_COUNT;
return 0;
+
+out_nodma:
+ /*
+ * XXX Broken custom: "goto out_xxx" in other place is
+ * a nestable exception, but here it is not nestable due to semaphore.
+ * XXX Doubtful technique of self-describing objects....
+ */
+ dealloc_dmabuf(&state->wpcm.dmabuf);
+ dealloc_dmabuf(&state->rpcm.dmabuf);
+ ymf_pcm_free_substream(&state->wpcm);
+ ymf_pcm_free_substream(&state->rpcm);
+
+ list_del(&state->chain);
+ kfree(state);
+
+ up(&unit->open_sem);
+ return err;
}
static int ymf_release(struct inode *inode, struct file *file)
@@ -1956,12 +1974,6 @@ static int ymf_release(struct inode *inode, struct file *file)
ymfpci_writeb(codec, YDSXGR_TIMERCTRL, 0);
#endif
- if (state != codec->states[state->virt]) {
- printk(KERN_ERR "ymfpci%d.%d: state mismatch\n",
- state->unit->dev_audio, state->virt);
- return -EIO;
- }
-
down(&codec->open_sem);
/*
@@ -1969,10 +1981,14 @@ static int ymf_release(struct inode *inode, struct file *file)
* Deallocate when unloading the driver and we can wait.
*/
ymf_wait_dac(state);
- dealloc_dmabuf(state);
- ymf_pcm_free_substream(&state->ypcm);
-
- codec->states[state->virt] = NULL;
+ ymf_stop_adc(state); /* fortunately, it's immediate */
+ dealloc_dmabuf(&state->wpcm.dmabuf);
+ dealloc_dmabuf(&state->rpcm.dmabuf);
+ ymf_pcm_free_substream(&state->wpcm);
+ ymf_pcm_free_substream(&state->rpcm);
+
+ list_del(&state->chain);
+ file->private_data = NULL; /* Can you tell I programmed Solaris */
kfree(state);
up(&codec->open_sem);
@@ -2045,6 +2061,83 @@ static /*const*/ struct file_operations ymf_mixer_fops = {
* initialization routines
*/
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+
+static int ymfpci_setup_legacy(ymfpci_t *unit, struct pci_dev *pcidev)
+{
+ int v;
+ int mpuio = -1, oplio = -1;
+
+ switch (unit->iomidi) {
+ case 0x330:
+ mpuio = 0;
+ break;
+ case 0x300:
+ mpuio = 1;
+ break;
+ case 0x332:
+ mpuio = 2;
+ break;
+ case 0x334:
+ mpuio = 3;
+ break;
+ default: ;
+ }
+
+ switch (unit->iosynth) {
+ case 0x388:
+ oplio = 0;
+ break;
+ case 0x398:
+ oplio = 1;
+ break;
+ case 0x3a0:
+ oplio = 2;
+ break;
+ case 0x3a8:
+ oplio = 3;
+ break;
+ default: ;
+ }
+
+ if (mpuio >= 0 || oplio >= 0) {
+ v = 0x003e;
+ pci_write_config_word(pcidev, PCIR_LEGCTRL, v);
+
+ switch (pcidev->device) {
+ case PCI_DEVICE_ID_YAMAHA_724:
+ case PCI_DEVICE_ID_YAMAHA_740:
+ case PCI_DEVICE_ID_YAMAHA_724F:
+ case PCI_DEVICE_ID_YAMAHA_740C:
+ v = 0x8800;
+ if (mpuio >= 0) { v |= mpuio<<4; }
+ if (oplio >= 0) { v |= oplio; }
+ pci_write_config_word(pcidev, PCIR_ELEGCTRL, v);
+ break;
+
+ case PCI_DEVICE_ID_YAMAHA_744:
+ case PCI_DEVICE_ID_YAMAHA_754:
+ v = 0x8800;
+ pci_write_config_word(pcidev, PCIR_ELEGCTRL, v);
+ if (oplio >= 0) {
+ pci_write_config_word(pcidev, PCIR_OPLADR, unit->iosynth);
+ }
+ if (mpuio >= 0) {
+ pci_write_config_word(pcidev, PCIR_MPUADR, unit->iomidi);
+ }
+ break;
+
+ default:
+ printk(KERN_ERR "ymfpci: Unknown device ID: 0x%x\n",
+ pcidev->device);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+#endif /* CONFIG_SOUND_YMFPCI_LEGACY */
+
static void ymfpci_aclink_reset(struct pci_dev * pci)
{
u8 cmd;
@@ -2149,6 +2242,12 @@ static int ymfpci_memalloc(ymfpci_t *codec)
ptr += 0x00ff;
(long)ptr &= ~0x00ff;
+ /*
+ * Hardware requires only ptr[playback_ctrl_size] zeroed,
+ * but in our judgement it is a wrong kind of savings, so clear it all.
+ */
+ memset(ptr, 0, size);
+
codec->bank_base_playback = ptr;
codec->ctrl_playback = (u32 *)ptr;
codec->ctrl_playback[0] = YDSXG_PLAYBACK_VOICES;
@@ -2213,7 +2312,7 @@ static void ymfpci_memfree(ymfpci_t *codec)
kfree(codec->work_ptr);
}
-static int ymf_ac97_init(ymfpci_t *card, int num_ac97)
+static int ymf_ac97_init(ymfpci_t *unit, int num_ac97)
{
struct ac97_codec *codec;
u16 eid;
@@ -2224,7 +2323,7 @@ static int ymf_ac97_init(ymfpci_t *card, int num_ac97)
/* initialize some basic codec information, other fields will be filled
in ac97_probe_codec */
- codec->private_data = card;
+ codec->private_data = unit;
codec->id = num_ac97;
codec->codec_read = ymfpci_codec_read;
@@ -2241,14 +2340,14 @@ static int ymf_ac97_init(ymfpci_t *card, int num_ac97)
goto out_kfree;
}
- card->ac97_features = eid;
+ unit->ac97_features = eid;
if ((codec->dev_mixer = register_sound_mixer(&ymf_mixer_fops, -1)) < 0) {
printk(KERN_ERR "ymfpci: couldn't register mixer!\n");
goto out_kfree;
}
- card->ac97_codec[num_ac97] = codec;
+ unit->ac97_codec[num_ac97] = codec;
return 0;
out_kfree:
@@ -2256,6 +2355,19 @@ static int ymf_ac97_init(ymfpci_t *card, int num_ac97)
return -ENODEV;
}
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+# ifdef MODULE
+static int mpu_io = 0;
+static int synth_io = 0;
+MODULE_PARM(mpu_io, "i");
+MODULE_PARM(synth_io, "i");
+# else
+static int mpu_io = 0x330;
+static int synth_io = 0x388;
+# endif
+static int assigned;
+#endif /* CONFIG_SOUND_YMFPCI_LEGACY */
+
static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_device_id *ent)
{
u16 ctrl;
@@ -2277,10 +2389,15 @@ static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_devi
spin_lock_init(&codec->reg_lock);
spin_lock_init(&codec->voice_lock);
init_MUTEX(&codec->open_sem);
+ INIT_LIST_HEAD(&codec->states);
codec->pci = pcidev;
pci_read_config_byte(pcidev, PCI_REVISION_ID, &codec->rev);
codec->reg_area_virt = ioremap(pci_resource_start(pcidev, 0), 0x8000);
+ if (codec->reg_area_virt == NULL) {
+ printk(KERN_ERR "ymfpci: unable to map registers\n");
+ goto out_free;
+ }
printk(KERN_INFO "ymfpci: %s at 0x%lx IRQ %d\n",
(char *)ent->driver_data, pci_resource_start(pcidev, 0), pcidev->irq);
@@ -2289,6 +2406,16 @@ static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_devi
if (ymfpci_codec_ready(codec, 0, 1) < 0)
goto out_unmap;
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+ if (assigned == 0) {
+ codec->iomidi = mpu_io;
+ codec->iosynth = synth_io;
+ if (ymfpci_setup_legacy(codec, pcidev) < 0)
+ goto out_unmap;
+ assigned = 1;
+ }
+#endif
+
ymfpci_download_image(codec);
udelay(100); /* seems we need some delay after downloading image.. */
@@ -2296,11 +2423,11 @@ static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_devi
if (ymfpci_memalloc(codec) < 0)
goto out_disable_dsp;
- /* ymfpci_proc_init(card, codec); */
+ /* ymfpci_proc_init(unit, codec); */
if (request_irq(pcidev->irq, ymf_interrupt, SA_SHIRQ, "ymfpci", codec) != 0) {
- printk(KERN_ERR "ymfpci%d: unable to request IRQ %d\n",
- codec->dev_audio, pcidev->irq);
+ printk(KERN_ERR "ymfpci: unable to request IRQ %d\n",
+ pcidev->irq);
goto out_memfree;
}
@@ -2317,6 +2444,23 @@ static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_devi
if ((err = ymf_ac97_init(codec, 0)) != 0)
goto out_unregister_sound_dsp;
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+ codec->opl3_data.name = "ymfpci";
+ codec->mpu_data.name = "ymfpci";
+
+ codec->opl3_data.io_base = codec->iosynth;
+ codec->opl3_data.irq = -1;
+
+ codec->mpu_data.io_base = codec->iomidi;
+ codec->mpu_data.irq = -1; /* XXX Make it ours. */
+
+ if (codec->iomidi) {
+ if (!probe_uart401(&codec->mpu_data, THIS_MODULE)) {
+ codec->iomidi = 0; /* XXX kludge */
+ }
+ }
+#endif /* CONFIG_SOUND_YMFPCI_LEGACY */
+
/* put it into driver list */
list_add_tail(&codec->ymf_devs, &ymf_devs);
pci_set_drvdata(pcidev, codec);
@@ -2336,6 +2480,7 @@ static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_devi
ymfpci_writel(codec, YDSXGR_STATUS, ~0);
out_unmap:
iounmap(codec->reg_area_virt);
+ out_free:
kfree(codec);
return -ENODEV;
}
@@ -2358,6 +2503,11 @@ static void __devexit ymf_remove_one(struct pci_dev *pcidev)
ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL);
ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
iounmap(codec->reg_area_virt);
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+ if (codec->iomidi) {
+ unload_uart401(&codec->mpu_data);
+ }
+#endif /* CONFIG_SOUND_YMFPCI_LEGACY */
kfree(codec);
}
diff --git a/drivers/sound/ymfpci.h b/drivers/sound/ymfpci.h
index 6e1a8d5f3..bd2f4c2a3 100644
--- a/drivers/sound/ymfpci.h
+++ b/drivers/sound/ymfpci.h
@@ -21,6 +21,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
+#include <linux/config.h>
/*
* Direct registers
@@ -131,7 +132,12 @@
#define YDSXG_AC97READCMD 0x8000
#define YDSXG_AC97WRITECMD 0x0000
+#define PCIR_LEGCTRL 0x40
+#define PCIR_ELEGCTRL 0x42
#define PCIR_DSXGCTRL 0x48
+#define PCIR_OPLADR 0x60
+#define PCIR_SBADR 0x62
+#define PCIR_MPUADR 0x64
#define YDSXG_DSPLENGTH 0x0080
#define YDSXG_CTRLLENGTH 0x3000
@@ -185,8 +191,8 @@ typedef struct stru_ymfpci_playback_bank {
} ymfpci_playback_bank_t;
typedef struct stru_ymfpci_capture_bank {
- u32 base; /* 32-bit address */
- u32 loop_end; /* 32-bit offset */
+ u32 base; /* 32-bit address (aligned at 4) */
+ u32 loop_end; /* size in BYTES (aligned at 4) */
u32 start; /* 32-bit offset */
u32 num_of_loops; /* counter */
} ymfpci_capture_bank_t;
@@ -198,8 +204,7 @@ typedef struct stru_ymfpci_effect_bank {
u32 temp;
} ymfpci_effect_bank_t;
-typedef struct stru_ymfpci_voice ymfpci_voice_t;
-typedef struct ymf_pcm ymfpci_pcm_t;
+typedef struct ymf_voice ymfpci_voice_t;
/*
* Throughout the code Yaroslav names YMF unit pointer "codec"
* even though it does not correspond to any codec. Must be historic.
@@ -214,52 +219,35 @@ typedef enum {
YMFPCI_MIDI
} ymfpci_voice_type_t;
-struct stru_ymfpci_voice {
- ymfpci_t *codec;
+struct ymf_voice {
+ // ymfpci_t *codec;
int number;
- int use: 1,
- pcm: 1,
- synth: 1,
- midi: 1;
+ char use, pcm, synth, midi; // bool
ymfpci_playback_bank_t *bank;
- void (*interrupt)(ymfpci_t *codec, ymfpci_voice_t *voice);
- ymfpci_pcm_t *ypcm;
+ struct ymf_pcm *ypcm;
};
-typedef enum {
- PLAYBACK_VOICE,
- CAPTURE_REC,
- CAPTURE_AC97,
- EFFECT_DRY_LEFT,
- EFFECT_DRY_RIGHT,
- EFFECT_EFF1,
- EFFECT_EFF2,
- EFFECT_EFF3
-} ymfpci_pcm_type_t;
-
-struct ymf_pcm {
- ymfpci_t *codec;
- ymfpci_pcm_type_t type;
- struct ymf_state *state;
- ymfpci_voice_t *voices[2]; /* playback only */
- int running; // +
- int spdif;
+struct ymf_capture {
+ // struct ymf_unit *unit;
+ int use;
+ ymfpci_capture_bank_t *bank;
+ struct ymf_pcm *ypcm;
};
struct ymf_unit {
u8 rev; /* PCI revision */
void *reg_area_virt;
- void *work_ptr; // +
+ void *work_ptr;
unsigned int bank_size_playback;
unsigned int bank_size_capture;
unsigned int bank_size_effect;
unsigned int work_size;
- void *bank_base_playback; // +
- void *bank_base_capture; // +
- void *bank_base_effect; // +
- void *work_base; // +
+ void *bank_base_playback;
+ void *bank_base_capture;
+ void *bank_base_effect;
+ void *work_base;
u32 *ctrl_playback;
ymfpci_playback_bank_t *bank_playback[YDSXG_PLAYBACK_VOICES][2];
@@ -269,13 +257,20 @@ struct ymf_unit {
int start_count;
u32 active_bank;
- ymfpci_voice_t voices[64];
+ struct ymf_voice voices[64];
+ struct ymf_capture capture[5];
struct ac97_codec *ac97_codec[NR_AC97];
u16 ac97_features;
struct pci_dev *pci;
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+ /* legacy hardware resources */
+ unsigned int iosynth, iomidi;
+ struct address_info opl3_data, mpu_data;
+#endif
+
spinlock_t reg_lock;
spinlock_t voice_lock;
@@ -284,14 +279,11 @@ struct ymf_unit {
struct semaphore open_sem;
struct list_head ymf_devs;
- struct ymf_state *states[1]; // *
- /* ypcm may be the same thing as state, but not for record, effects. */
+ struct list_head states; /* List of states for this unit */
+ /* For the moment we do not traverse list of states so it is
+ * entirely useless. Will be used (PM) or killed. XXX */
};
-/*
- * "Software" or virtual channel, an instance of opened /dev/dsp.
- */
-
struct ymf_dmabuf {
/* OSS buffer management stuff */
@@ -312,7 +304,6 @@ struct ymf_dmabuf {
/* redundant, but makes calculations easier */
unsigned fragsize;
unsigned dmasize; /* Total rawbuf[] size */
- unsigned fragsamples;
/* OSS stuff */
unsigned mapped:1;
@@ -329,15 +320,40 @@ struct ymf_pcm_format {
int shift; /* redundant, computed from the above */
};
-struct ymf_state {
- struct ymf_unit *unit; /* backpointer */
+typedef enum {
+ PLAYBACK_VOICE,
+ CAPTURE_REC,
+ CAPTURE_AC97,
+ EFFECT_DRY_LEFT,
+ EFFECT_DRY_RIGHT,
+ EFFECT_EFF1,
+ EFFECT_EFF2,
+ EFFECT_EFF3
+} ymfpci_pcm_type_t;
- /* virtual channel number */
- int virt; // * unused a.t.m.
+/* This is variant record, but we hate unions. Little waste on pointers []. */
+struct ymf_pcm {
+ ymfpci_pcm_type_t type;
+ struct ymf_state *state;
+
+ ymfpci_voice_t *voices[2];
+ int capture_bank_number;
+
+ struct ymf_dmabuf dmabuf;
+ int running;
+ int spdif;
+};
- struct ymf_pcm ypcm; // *
- struct ymf_dmabuf dmabuf; // *
- struct ymf_pcm_format format; // *
+/*
+ * "Software" or virtual channel, an instance of opened /dev/dsp.
+ * It may have two physical channels (pcms) for duplex operations.
+ */
+
+struct ymf_state {
+ struct list_head chain;
+ struct ymf_unit *unit; /* backpointer */
+ struct ymf_pcm rpcm, wpcm;
+ struct ymf_pcm_format format;
};
#endif /* __YMFPCI_H */