diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/oss/ymfpci.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'sound/oss/ymfpci.c')
-rw-r--r-- | sound/oss/ymfpci.c | 2691 |
1 files changed, 2691 insertions, 0 deletions
diff --git a/sound/oss/ymfpci.c b/sound/oss/ymfpci.c new file mode 100644 index 00000000000..05203ad523f --- /dev/null +++ b/sound/oss/ymfpci.c @@ -0,0 +1,2691 @@ +/* + * Copyright 1999 Jaroslav Kysela <perex@suse.cz> + * Copyright 2000 Alan Cox <alan@redhat.com> + * Copyright 2001 Kai Germaschewski <kai@tp1.ruhr-uni-bochum.de> + * Copyright 2002 Pete Zaitcev <zaitcev@yahoo.com> + * + * Yamaha YMF7xx driver. + * + * This code is a result of high-speed collision + * between ymfpci.c of ALSA and cs46xx.c of Linux. + * -- Pete Zaitcev <zaitcev@yahoo.com>; 2000/09/18 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * TODO: + * - Use P44Slot for 44.1 playback (beware of idle buzzing in P44Slot). + * - 96KHz playback for DVD - use pitch of 2.0. + * - Retain DMA buffer on close, do not wait the end of frame. + * - 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. + * - 2001/11/29 ac97_save_state + * Talk to Kai to remove ac97_save_state before it's too late! + * - Second AC97 + * - Restore S/PDIF - Toshibas have it. + * + * Kai used pci_alloc_consistent for DMA buffer, which sounds a little + * unconventional. However, given how small our fragments can be, + * a little uncached access is perhaps better than endless flushing. + * On i386 and other I/O-coherent architectures pci_alloc_consistent + * is entirely harmless. + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/poll.h> +#include <linux/soundcard.h> +#include <linux/ac97_codec.h> +#include <linux/sound.h> + +#include <asm/io.h> +#include <asm/dma.h> +#include <asm/uaccess.h> + +#ifdef CONFIG_SOUND_YMFPCI_LEGACY +# include "sound_config.h" +# include "mpu401.h" +#endif +#include "ymfpci.h" + +/* + * I do not believe in debug levels as I never can guess what + * part of the code is going to be problematic in the future. + * Don't forget to run your klogd with -c 8. + * + * Example (do not remove): + * #define YMFDBG(fmt, arg...) do{ printk(KERN_DEBUG fmt, ##arg); }while(0) + */ +#define YMFDBGW(fmt, arg...) /* */ /* write counts */ +#define YMFDBGI(fmt, arg...) /* */ /* interrupts */ +#define YMFDBGX(fmt, arg...) /* */ /* ioctl */ + +static int ymf_playback_trigger(ymfpci_t *unit, 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 *unit, 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 void ymfpci_aclink_reset(struct pci_dev * pci); +static void ymfpci_disable_dsp(ymfpci_t *unit); +static void ymfpci_download_image(ymfpci_t *codec); +static void ymf_memload(ymfpci_t *unit); + +static DEFINE_SPINLOCK(ymf_devs_lock); +static LIST_HEAD(ymf_devs); + +/* + * constants + */ + +static struct pci_device_id ymf_id_tbl[] = { +#define DEV(v, d, data) \ + { PCI_VENDOR_ID_##v, PCI_DEVICE_ID_##v##_##d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)data } + DEV (YAMAHA, 724, "YMF724"), + DEV (YAMAHA, 724F, "YMF724F"), + DEV (YAMAHA, 740, "YMF740"), + DEV (YAMAHA, 740C, "YMF740C"), + DEV (YAMAHA, 744, "YMF744"), + DEV (YAMAHA, 754, "YMF754"), +#undef DEV + { } +}; +MODULE_DEVICE_TABLE(pci, ymf_id_tbl); + +/* + * common I/O routines + */ + +static inline void ymfpci_writeb(ymfpci_t *codec, u32 offset, u8 val) +{ + writeb(val, codec->reg_area_virt + offset); +} + +static inline u16 ymfpci_readw(ymfpci_t *codec, u32 offset) +{ + return readw(codec->reg_area_virt + offset); +} + +static inline void ymfpci_writew(ymfpci_t *codec, u32 offset, u16 val) +{ + writew(val, codec->reg_area_virt + offset); +} + +static inline u32 ymfpci_readl(ymfpci_t *codec, u32 offset) +{ + return readl(codec->reg_area_virt + offset); +} + +static inline void ymfpci_writel(ymfpci_t *codec, u32 offset, u32 val) +{ + writel(val, codec->reg_area_virt + offset); +} + +static int ymfpci_codec_ready(ymfpci_t *codec, int secondary, int sched) +{ + signed long end_time; + u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR; + + end_time = jiffies + 3 * (HZ / 4); + do { + if ((ymfpci_readw(codec, reg) & 0x8000) == 0) + return 0; + if (sched) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + } while (end_time - (signed long)jiffies >= 0); + printk(KERN_ERR "ymfpci_codec_ready: codec %i is not ready [0x%x]\n", + secondary, ymfpci_readw(codec, reg)); + return -EBUSY; +} + +static void ymfpci_codec_write(struct ac97_codec *dev, u8 reg, u16 val) +{ + ymfpci_t *codec = dev->private_data; + u32 cmd; + + spin_lock(&codec->ac97_lock); + /* XXX Do make use of dev->id */ + ymfpci_codec_ready(codec, 0, 0); + cmd = ((YDSXG_AC97WRITECMD | reg) << 16) | val; + ymfpci_writel(codec, YDSXGR_AC97CMDDATA, cmd); + spin_unlock(&codec->ac97_lock); +} + +static u16 _ymfpci_codec_read(ymfpci_t *unit, u8 reg) +{ + int i; + + if (ymfpci_codec_ready(unit, 0, 0)) + return ~0; + ymfpci_writew(unit, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg); + if (ymfpci_codec_ready(unit, 0, 0)) + return ~0; + if (unit->pci->device == PCI_DEVICE_ID_YAMAHA_744 && unit->rev < 2) { + for (i = 0; i < 600; i++) + ymfpci_readw(unit, YDSXGR_PRISTATUSDATA); + } + return ymfpci_readw(unit, YDSXGR_PRISTATUSDATA); +} + +static u16 ymfpci_codec_read(struct ac97_codec *dev, u8 reg) +{ + ymfpci_t *unit = dev->private_data; + u16 ret; + + spin_lock(&unit->ac97_lock); + ret = _ymfpci_codec_read(unit, reg); + spin_unlock(&unit->ac97_lock); + + return ret; +} + +/* + * Misc routines + */ + +/* + * Calculate the actual sampling rate relatetively to the base clock (48kHz). + */ +static u32 ymfpci_calc_delta(u32 rate) +{ + switch (rate) { + case 8000: return 0x02aaab00; + case 11025: return 0x03accd00; + case 16000: return 0x05555500; + case 22050: return 0x07599a00; + case 32000: return 0x0aaaab00; + case 44100: return 0x0eb33300; + default: return ((rate << 16) / 48000) << 12; + } +} + +static u32 def_rate[8] = { + 100, 2000, 8000, 11025, 16000, 22050, 32000, 48000 +}; + +static u32 ymfpci_calc_lpfK(u32 rate) +{ + u32 i; + static u32 val[8] = { + 0x00570000, 0x06AA0000, 0x18B20000, 0x20930000, + 0x2B9A0000, 0x35A10000, 0x3EAA0000, 0x40000000 + }; + + if (rate == 44100) + return 0x40000000; /* FIXME: What's the right value? */ + for (i = 0; i < 8; i++) + if (rate <= def_rate[i]) + return val[i]; + return val[0]; +} + +static u32 ymfpci_calc_lpfQ(u32 rate) +{ + u32 i; + static u32 val[8] = { + 0x35280000, 0x34A70000, 0x32020000, 0x31770000, + 0x31390000, 0x31C90000, 0x33D00000, 0x40000000 + }; + + if (rate == 44100) + return 0x370A0000; + for (i = 0; i < 8; i++) + if (rate <= def_rate[i]) + return val[i]; + return val[0]; +} + +static u32 ymf_calc_lend(u32 rate) +{ + return (rate * YMF_SAMPF) / 48000; +} + +/* + * We ever allow only a few formats, but let's be generic, for smaller surprise. + */ +static int ymf_pcm_format_width(int format) +{ + static int mask16 = AFMT_S16_LE|AFMT_S16_BE|AFMT_U16_LE|AFMT_U16_BE; + + if ((format & (format-1)) != 0) { + printk(KERN_ERR "ymfpci: format 0x%x is not a power of 2\n", format); + return 8; + } + + if (format == AFMT_IMA_ADPCM) return 4; + if ((format & mask16) != 0) return 16; + return 8; +} + +static void ymf_pcm_update_shift(struct ymf_pcm_format *f) +{ + f->shift = 0; + if (f->voices == 2) + f->shift++; + if (ymf_pcm_format_width(f->format) == 16) + f->shift++; +} + +/* 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 + */ +static int alloc_dmabuf(ymfpci_t *unit, struct ymf_dmabuf *dmabuf) +{ + void *rawbuf = NULL; + dma_addr_t dma_addr; + int order; + struct page *map, *mapend; + + /* alloc as big a chunk as we can */ + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) { + rawbuf = pci_alloc_consistent(unit->pci, PAGE_SIZE << order, &dma_addr); + if (rawbuf) + break; + } + if (!rawbuf) + return -ENOMEM; + +#if 0 + printk(KERN_DEBUG "ymfpci: allocated %ld (order = %d) bytes at %p\n", + PAGE_SIZE << order, order, rawbuf); +#endif + + dmabuf->ready = dmabuf->mapped = 0; + dmabuf->rawbuf = rawbuf; + dmabuf->dma_addr = dma_addr; + dmabuf->buforder = order; + + /* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */ + mapend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1); + for (map = virt_to_page(rawbuf); map <= mapend; map++) + set_bit(PG_reserved, &map->flags); + + return 0; +} + +/* + * Free DMA buffer + */ +static void dealloc_dmabuf(ymfpci_t *unit, struct ymf_dmabuf *dmabuf) +{ + struct page *map, *mapend; + + if (dmabuf->rawbuf) { + /* undo marking the pages as reserved */ + mapend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1); + for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++) + clear_bit(PG_reserved, &map->flags); + + pci_free_consistent(unit->pci, PAGE_SIZE << dmabuf->buforder, + dmabuf->rawbuf, dmabuf->dma_addr); + } + dmabuf->rawbuf = NULL; + dmabuf->mapped = dmabuf->ready = 0; +} + +static int prog_dmabuf(struct ymf_state *state, int rec) +{ + struct ymf_dmabuf *dmabuf; + int w_16; + unsigned bufsize; + unsigned long flags; + int redzone, redfrags; + 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; + dmabuf->total_bytes = 0; + dmabuf->count = 0; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + + /* allocate DMA buffer if not allocated yet */ + if (!dmabuf->rawbuf) + if ((ret = alloc_dmabuf(state->unit, dmabuf))) + return ret; + + /* + * Create fake fragment sizes and numbers for OSS ioctls. + * Import what Doom might have set with SNDCTL_DSP_SETFRAGMENT. + */ + bufsize = PAGE_SIZE << dmabuf->buforder; + /* By default we give 4 big buffers. */ + dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT - 2); + if (dmabuf->ossfragshift > 3 && + dmabuf->ossfragshift < dmabuf->fragshift) { + /* If OSS set smaller fragments, give more smaller buffers. */ + dmabuf->fragshift = dmabuf->ossfragshift; + } + dmabuf->fragsize = 1 << dmabuf->fragshift; + + dmabuf->numfrag = bufsize >> dmabuf->fragshift; + dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; + + if (dmabuf->ossmaxfrags >= 2) { + redzone = ymf_calc_lend(state->format.rate); + redzone <<= state->format.shift; + redzone *= 3; + redfrags = (redzone + dmabuf->fragsize-1) >> dmabuf->fragshift; + + if (dmabuf->ossmaxfrags + redfrags < dmabuf->numfrag) { + dmabuf->numfrag = dmabuf->ossmaxfrags + redfrags; + dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; + } + } + + memset(dmabuf->rawbuf, w_16 ? 0 : 0x80, dmabuf->dmasize); + + /* + * Now set up the ring + */ + + /* XXX ret = rec? cap_pre(): pbk_pre(); */ + spin_lock_irqsave(&state->unit->voice_lock, flags); + if (rec) { + if ((ret = ymf_capture_prepare(state)) != 0) { + spin_unlock_irqrestore(&state->unit->voice_lock, flags); + return ret; + } + } else { + if ((ret = ymf_playback_prepare(state)) != 0) { + spin_unlock_irqrestore(&state->unit->voice_lock, flags); + return ret; + } + } + spin_unlock_irqrestore(&state->unit->voice_lock, flags); + + /* set the ready flag for the dma buffer (this comment is not stupid) */ + dmabuf->ready = 1; + +#if 0 + printk(KERN_DEBUG "prog_dmabuf: rate %d format 0x%x," + " numfrag %d fragsize %d dmasize %d\n", + state->format.rate, state->format.format, dmabuf->numfrag, + dmabuf->fragsize, dmabuf->dmasize); +#endif + + return 0; +} + +static void ymf_start_dac(struct ymf_state *state) +{ + 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. + */ +static void ymf_wait_dac(struct ymf_state *state) +{ + struct ymf_unit *unit = state->unit; + struct ymf_pcm *ypcm = &state->wpcm; + DECLARE_WAITQUEUE(waita, current); + unsigned long flags; + + add_wait_queue(&ypcm->dmabuf.wait, &waita); + + spin_lock_irqsave(&unit->reg_lock, flags); + if (ypcm->dmabuf.count != 0 && !ypcm->running) { + ymf_playback_trigger(unit, ypcm, 1); + } + +#if 0 + if (file->f_flags & O_NONBLOCK) { + /* + * XXX Our mistake is to attach DMA buffer to state + * rather than to some per-device structure. + * Cannot skip waiting, can only make it shorter. + */ + } +#endif + + set_current_state(TASK_UNINTERRUPTIBLE); + while (ypcm->running) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + schedule(); + spin_lock_irqsave(&unit->reg_lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + } + spin_unlock_irqrestore(&unit->reg_lock, flags); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&ypcm->dmabuf.wait, &waita); + + /* + * This function may take up to 4 seconds to reach this point + * (32K circular buffer, 8000 Hz). User notices. + */ +} + +/* 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 + */ + +static void ymfpci_hw_start(ymfpci_t *unit) +{ + unsigned long flags; + + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->start_count++ == 0) { + ymfpci_writel(unit, YDSXGR_MODE, + ymfpci_readl(unit, YDSXGR_MODE) | 3); + unit->active_bank = ymfpci_readl(unit, YDSXGR_CTRLSELECT) & 1; + } + spin_unlock_irqrestore(&unit->reg_lock, flags); +} + +static void ymfpci_hw_stop(ymfpci_t *unit) +{ + unsigned long flags; + long timeout = 1000; + + spin_lock_irqsave(&unit->reg_lock, flags); + if (--unit->start_count == 0) { + ymfpci_writel(unit, YDSXGR_MODE, + ymfpci_readl(unit, YDSXGR_MODE) & ~3); + while (timeout-- > 0) { + if ((ymfpci_readl(unit, YDSXGR_STATUS) & 2) == 0) + break; + } + } + spin_unlock_irqrestore(&unit->reg_lock, flags); +} + +/* + * Playback voice management + */ + +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; + + for (idx = 0; idx < YDSXG_PLAYBACK_VOICES; idx += pair ? 2 : 1) { + voice = &codec->voices[idx]; + voice2 = pair ? &codec->voices[idx+1] : NULL; + if (voice->use || (voice2 && voice2->use)) + continue; + voice->use = 1; + if (voice2) + voice2->use = 1; + switch (type) { + case YMFPCI_PCM: + voice->pcm = 1; + if (voice2) + voice2->pcm = 1; + break; + case YMFPCI_SYNTH: + voice->synth = 1; + break; + case YMFPCI_MIDI: + voice->midi = 1; + break; + } + ymfpci_hw_start(codec); + rvoice[0] = voice; + if (voice2) { + ymfpci_hw_start(codec); + rvoice[1] = voice2; + } + return 0; + } + return -EBUSY; /* Your audio channel is open by someone else. */ +} + +static void ymfpci_voice_free(ymfpci_t *unit, ymfpci_voice_t *pvoice) +{ + ymfpci_hw_stop(unit); + pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; + pvoice->ypcm = NULL; +} + +/* + */ + +static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice) +{ + struct ymf_pcm *ypcm; + int redzone; + int pos, delta, swptr; + int played, distance; + struct ymf_state *state; + struct ymf_dmabuf *dmabuf; + char silence; + + if ((ypcm = voice->ypcm) == NULL) { + return; + } + if ((state = ypcm->state) == NULL) { + ypcm->running = 0; // lock it + return; + } + dmabuf = &ypcm->dmabuf; + spin_lock(&codec->reg_lock); + if (ypcm->running) { + YMFDBGI("ymfpci: %d, intr bank %d count %d start 0x%x:%x\n", + voice->number, codec->active_bank, dmabuf->count, + le32_to_cpu(voice->bank[0].start), + le32_to_cpu(voice->bank[1].start)); + silence = (ymf_pcm_format_width(state->format.format) == 16) ? + 0 : 0x80; + /* We need actual left-hand-side redzone size here. */ + redzone = ymf_calc_lend(state->format.rate); + redzone <<= (state->format.shift + 1); + swptr = dmabuf->swptr; + + pos = le32_to_cpu(voice->bank[codec->active_bank].start); + pos <<= state->format.shift; + if (pos < 0 || pos >= dmabuf->dmasize) { /* ucode bug */ + printk(KERN_ERR "ymfpci%d: runaway voice %d: hwptr %d=>%d dmasize %d\n", + codec->dev_audio, voice->number, + dmabuf->hwptr, pos, dmabuf->dmasize); + pos = 0; + } + if (pos < dmabuf->hwptr) { + delta = dmabuf->dmasize - dmabuf->hwptr; + memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta); + delta += pos; + memset(dmabuf->rawbuf, silence, pos); + } else { + delta = pos - dmabuf->hwptr; + memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta); + } + dmabuf->hwptr = pos; + + if (dmabuf->count == 0) { + printk(KERN_ERR "ymfpci%d: %d: strain: hwptr %d\n", + codec->dev_audio, voice->number, dmabuf->hwptr); + ymf_playback_trigger(codec, ypcm, 0); + } + + if (swptr <= pos) { + distance = pos - swptr; + } else { + distance = dmabuf->dmasize - (swptr - pos); + } + if (distance < redzone) { + /* + * hwptr inside redzone => DMA ran out of samples. + */ + if (delta < dmabuf->count) { + /* + * Lost interrupt or other screwage. + */ + printk(KERN_ERR "ymfpci%d: %d: lost: delta %d" + " hwptr %d swptr %d distance %d count %d\n", + codec->dev_audio, voice->number, delta, + dmabuf->hwptr, swptr, distance, dmabuf->count); + } else { + /* + * Normal end of DMA. + */ + YMFDBGI("ymfpci%d: %d: done: delta %d" + " hwptr %d swptr %d distance %d count %d\n", + codec->dev_audio, voice->number, delta, + dmabuf->hwptr, swptr, distance, dmabuf->count); + } + played = dmabuf->count; + if (ypcm->running) { + ymf_playback_trigger(codec, ypcm, 0); + } + } else { + /* + * hwptr is chipping away towards a remote swptr. + * Calculate other distance and apply it to count. + */ + if (swptr >= pos) { + distance = swptr - pos; + } else { + distance = dmabuf->dmasize - (pos - swptr); + } + if (distance < dmabuf->count) { + played = dmabuf->count - distance; + } else { + played = 0; + } + } + + dmabuf->total_bytes += played; + dmabuf->count -= played; + if (dmabuf->count < dmabuf->dmasize / 2) { + wake_up(&dmabuf->wait); + } + } + spin_unlock(&codec->reg_lock); +} + +static void ymf_cap_interrupt(ymfpci_t *unit, struct ymf_capture *cap) +{ + 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) { + redzone = ymf_calc_lend(state->format.rate); + redzone <<= (state->format.shift + 1); + + pos = le32_to_cpu(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(&unit->reg_lock); +} + +static int ymf_playback_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd) +{ + + if (ypcm->voices[0] == NULL) { + return -EINVAL; + } + if (cmd != 0) { + codec->ctrl_playback[ypcm->voices[0]->number + 1] = + cpu_to_le32(ypcm->voices[0]->bank_ba); + if (ypcm->voices[1] != NULL) + codec->ctrl_playback[ypcm->voices[1]->number + 1] = + cpu_to_le32(ypcm->voices[1]->bank_ba); + ypcm->running = 1; + } else { + codec->ctrl_playback[ypcm->voices[0]->number + 1] = 0; + if (ypcm->voices[1] != NULL) + codec->ctrl_playback[ypcm->voices[1]->number + 1] = 0; + ypcm->running = 0; + } + return 0; +} + +static void ymf_capture_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd) +{ + u32 tmp; + + if (cmd != 0) { + tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number); + ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp); + ypcm->running = 1; + } else { + tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number); + ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp); + ypcm->running = 0; + } +} + +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(unit, ypcm->voices[1]); + ypcm->voices[1] = NULL; + } + if (voices == 1 && ypcm->voices[0] != NULL) + return 0; /* already allocated */ + if (voices == 2 && ypcm->voices[0] != NULL && ypcm->voices[1] != NULL) + return 0; /* already allocated */ + if (voices > 1) { + if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) { + ymfpci_voice_free(unit, ypcm->voices[0]); + ypcm->voices[0] = NULL; + } + 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; +} + +static void ymf_pcm_init_voice(ymfpci_voice_t *voice, int stereo, + int rate, int w_16, unsigned long addr, unsigned int end, int spdif) +{ + u32 format; + u32 delta = ymfpci_calc_delta(rate); + u32 lpfQ = ymfpci_calc_lpfQ(rate); + u32 lpfK = ymfpci_calc_lpfK(rate); + ymfpci_playback_bank_t *bank; + int nbank; + + /* + * The gain is a floating point number. According to the manual, + * bit 31 indicates a sign bit, bit 30 indicates an integer part, + * and bits [29:15] indicate a decimal fraction part. Thus, + * for a gain of 1.0 the constant of 0x40000000 is loaded. + */ + unsigned default_gain = cpu_to_le32(0x40000000); + + format = (stereo ? 0x00010000 : 0) | (w_16 ? 0 : 0x80000000); + if (stereo) + end >>= 1; + if (w_16) + end >>= 1; + for (nbank = 0; nbank < 2; nbank++) { + bank = &voice->bank[nbank]; + bank->format = cpu_to_le32(format); + bank->loop_default = 0; /* 0-loops forever, otherwise count */ + bank->base = cpu_to_le32(addr); + bank->loop_start = 0; + bank->loop_end = cpu_to_le32(end); + bank->loop_frac = 0; + bank->eg_gain_end = default_gain; + bank->lpfQ = cpu_to_le32(lpfQ); + bank->status = 0; + bank->num_of_frames = 0; + bank->loop_count = 0; + bank->start = 0; + bank->start_frac = 0; + bank->delta = + bank->delta_end = cpu_to_le32(delta); + bank->lpfK = + bank->lpfK_end = cpu_to_le32(lpfK); + bank->eg_gain = default_gain; + bank->lpfD1 = + bank->lpfD2 = 0; + + bank->left_gain = + bank->right_gain = + bank->left_gain_end = + bank->right_gain_end = + bank->eff1_gain = + bank->eff2_gain = + bank->eff3_gain = + bank->eff1_gain_end = + bank->eff2_gain_end = + bank->eff3_gain_end = 0; + + if (!stereo) { + if (!spdif) { + bank->left_gain = + bank->right_gain = + bank->left_gain_end = + bank->right_gain_end = default_gain; + } else { + bank->eff2_gain = + bank->eff2_gain_end = + bank->eff3_gain = + bank->eff3_gain_end = default_gain; + } + } else { + if (!spdif) { + if ((voice->number & 1) == 0) { + bank->left_gain = + bank->left_gain_end = default_gain; + } else { + bank->format |= cpu_to_le32(1); + bank->right_gain = + bank->right_gain_end = default_gain; + } + } else { + if ((voice->number & 1) == 0) { + bank->eff2_gain = + bank->eff2_gain_end = default_gain; + } else { + bank->format |= cpu_to_le32(1); + bank->eff3_gain = + bank->eff3_gain_end = default_gain; + } + } + } + } +} + +/* + * XXX Capture channel allocation is entirely fake at the moment. + * We use only one channel and mark it busy as required. + */ +static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank) +{ + 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) { + /* Somebody started 32 mpg123's in parallel? */ + printk(KERN_INFO "ymfpci%d: cannot allocate voice\n", + state->unit->dev_audio); + return err; + } + + for (nvoice = 0; nvoice < state->format.voices; nvoice++) { + ymf_pcm_init_voice(ypcm->voices[nvoice], + state->format.voices == 2, state->format.rate, + ymf_pcm_format_width(state->format.format) == 16, + ypcm->dmabuf.dma_addr, ypcm->dmabuf.dmasize, + ypcm->spdif); + } + return 0; +} + +static int ymf_capture_prepare(struct ymf_state *state) +{ + ymfpci_t *unit = state->unit; + struct ymf_pcm *ypcm = &state->rpcm; + ymfpci_capture_bank_t * bank; + /* 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; + + 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 (state->format.voices == 2) + format |= 2; + if (ymf_pcm_format_width(state->format.format) == 8) + format |= 1; + switch (ypcm->capture_bank_number) { + case 0: + ymfpci_writel(unit, YDSXGR_RECFORMAT, format); + ymfpci_writel(unit, YDSXGR_RECSLOTSR, rate); + break; + case 1: + ymfpci_writel(unit, YDSXGR_ADCFORMAT, format); + ymfpci_writel(unit, YDSXGR_ADCSLOTSR, rate); + break; + } + for (nbank = 0; nbank < 2; nbank++) { + bank = unit->bank_capture[ypcm->capture_bank_number][nbank]; + bank->base = cpu_to_le32(ypcm->dmabuf.dma_addr); + // bank->loop_end = ypcm->dmabuf.dmasize >> state->format.shift; + bank->loop_end = cpu_to_le32(ypcm->dmabuf.dmasize); + bank->start = 0; + bank->num_of_loops = 0; + } +#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 irqreturn_t ymf_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ymfpci_t *codec = dev_id; + u32 status, nvoice, mode; + struct ymf_voice *voice; + struct ymf_capture *cap; + + status = ymfpci_readl(codec, YDSXGR_STATUS); + if (status & 0x80000000) { + codec->active_bank = ymfpci_readl(codec, YDSXGR_CTRLSELECT) & 1; + spin_lock(&codec->voice_lock); + for (nvoice = 0; nvoice < YDSXG_PLAYBACK_VOICES; nvoice++) { + voice = &codec->voices[nvoice]; + if (voice->use) + ymf_pcm_interrupt(codec, voice); + } + for (nvoice = 0; nvoice < YDSXG_CAPTURE_VOICES; nvoice++) { + cap = &codec->capture[nvoice]; + if (cap->use) + ymf_cap_interrupt(codec, cap); + } + spin_unlock(&codec->voice_lock); + spin_lock(&codec->reg_lock); + ymfpci_writel(codec, YDSXGR_STATUS, 0x80000000); + mode = ymfpci_readl(codec, YDSXGR_MODE) | 2; + ymfpci_writel(codec, YDSXGR_MODE, mode); + spin_unlock(&codec->reg_lock); + } + + status = ymfpci_readl(codec, YDSXGR_INTFLAG); + if (status & 1) { + /* timer handler */ + ymfpci_writel(codec, YDSXGR_INTFLAG, ~0); + } + return IRQ_HANDLED; +} + +static void ymf_pcm_free_substream(struct ymf_pcm *ypcm) +{ + unsigned long flags; + struct ymf_unit *unit; + + unit = ypcm->state->unit; + + if (ypcm->type == PLAYBACK_VOICE) { + spin_lock_irqsave(&unit->voice_lock, flags); + if (ypcm->voices[1]) + ymfpci_voice_free(unit, ypcm->voices[1]); + if (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 struct ymf_state *ymf_state_alloc(ymfpci_t *unit) +{ + struct ymf_pcm *ypcm; + struct ymf_state *state; + + if ((state = kmalloc(sizeof(struct ymf_state), GFP_KERNEL)) == NULL) { + goto out0; + } + memset(state, 0, sizeof(struct ymf_state)); + + ypcm = &state->wpcm; + ypcm->state = state; + 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->format.format = AFMT_U8; + state->format.rate = 8000; + state->format.voices = 1; + ymf_pcm_update_shift(&state->format); + + return state; + +out0: + return NULL; +} + +/* 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 */ +#define SND_PCM_AES0_PRO_EMPHASIS (7<<2) /* mask - emphasis */ +#define SND_PCM_AES0_PRO_EMPHASIS_NOTID (0<<2) /* emphasis not indicated */ +#define SND_PCM_AES0_PRO_EMPHASIS_NONE (1< |