?? ymfpci.c
字號:
/* * Copyright 1999 Jaroslav Kysela <perex@suse.cz> * Copyright 2000 Alan Cox <alan@redhat.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. * - Use new 2.3.x cache coherent PCI DMA routines instead of virt_to_bus. * - Make the thing big endian compatible. ALSA has it done. * - 2001/11/29 ac97_save_state */#include <linux/config.h>#include <linux/module.h>#include <linux/init.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 LIST_HEAD(ymf_devs);/* * constants */static struct pci_device_id ymf_id_tbl[] __devinitdata = {#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 u8 ymfpci_readb(ymfpci_t *codec, u32 offset){ return readb(codec->reg_area_virt + offset);}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; /* 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);}static u16 ymfpci_codec_read(struct ac97_codec *dev, u8 reg){ ymfpci_t *unit = dev->private_data; 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);}/* * 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, playback and recording buffer should be allocated seperately */static int alloc_dmabuf(struct ymf_dmabuf *dmabuf){ void *rawbuf = NULL; int order; struct page * map, * mapend; /* alloc as big a chunk as we can */ for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) if((rawbuf = (void *)__get_free_pages(GFP_KERNEL|GFP_DMA, order))) 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->buforder = order; /* now mark the pages as reserved; otherwise remap_page_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(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); free_pages((unsigned long)dmabuf->rawbuf,dmabuf->buforder); } 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(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);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -