diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2001-01-31 22:22:27 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2001-01-31 22:22:27 +0000 |
commit | 825423e4c4f18289df2393951cfd2a7a31fc0464 (patch) | |
tree | 4ad80e981c3d9effa910d2247d118d254f9a5d09 /drivers/sound | |
parent | c4693dc4856ab907a5c02187a8d398861bebfc7e (diff) |
Merge with Linux 2.4.1.
Diffstat (limited to 'drivers/sound')
-rw-r--r-- | drivers/sound/Config.in | 6 | ||||
-rw-r--r-- | drivers/sound/Makefile | 4 | ||||
-rw-r--r-- | drivers/sound/emu10k1/audio.c | 33 | ||||
-rw-r--r-- | drivers/sound/trix.c | 1 | ||||
-rw-r--r-- | drivers/sound/via82cxxx_audio.c | 707 | ||||
-rw-r--r-- | drivers/sound/ymfpci.c | 998 | ||||
-rw-r--r-- | drivers/sound/ymfpci.h | 116 |
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 */ |