📄 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@metabyte.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. * - Capture and duplex * - 96KHz playback for DVD - use pitch of 2.0. * - uLaw for Sun apps. * - 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. */#include <linux/module.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/pci.h>#include <linux/malloc.h>#include <linux/poll.h>#include <linux/ac97_codec.h>#include <linux/sound.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/uaccess.h>#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 1static 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 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);/* * Mindlessly copied from cs46xx XXX */extern __inline__ unsigned ld2(unsigned int x){ unsigned r = 0; if (x >= 0x10000) { x >>= 16; r += 16; } if (x >= 0x100) { x >>= 8; r += 8; } if (x >= 0x10) { x >>= 4; r += 4; } if (x >= 4) { x >>= 2; r += 2; } if (x >= 2) r++; return r;}/* * 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("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 *codec = dev->private_data; if (ymfpci_codec_ready(codec, 0, 0)) return ~0; ymfpci_writew(codec, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg); if (ymfpci_codec_ready(codec, 0, 0)) return ~0; if (codec->pci->device == PCI_DEVICE_ID_YAMAHA_744 && codec->rev < 2) { int i; for (i = 0; i < 600; i++) ymfpci_readw(codec, YDSXGR_PRISTATUSDATA); } return ymfpci_readw(codec, 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;}/* * XXX Find if this function exists in the OSS framework. * XXX Make sure we do no panic when ADPCM is selected. */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++;}/* * 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){ struct ymf_dmabuf *dmabuf = &state->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_state *state){ struct ymf_dmabuf *dmabuf = &state->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, unsigned rec){ struct ymf_dmabuf *dmabuf = &state->dmabuf; int w_16; unsigned bytepersec; unsigned bufsize; unsigned long flags; int redzone; int ret; w_16 = ymf_pcm_format_width(state->format.format) == 16; spin_lock_irqsave(&state->unit->reg_lock, flags); dmabuf->hwptr = dmabuf->swptr = 0; dmabuf->total_bytes = 0; dmabuf->count = dmabuf->error = 0; spin_unlock_irqrestore(&state->unit->reg_lock, flags); /* allocate DMA buffer if not allocated yet */ if (!dmabuf->rawbuf) if ((ret = alloc_dmabuf(state))) return ret; bytepersec = state->format.rate << state->format.shift; /* * Create fake fragment sizes and numbers for OSS ioctls. */ bufsize = PAGE_SIZE << dmabuf->buforder; if (dmabuf->ossfragshift) { if ((1000 << dmabuf->ossfragshift) < bytepersec) dmabuf->fragshift = ld2(bytepersec/1000); else dmabuf->fragshift = dmabuf->ossfragshift; } else { /* lets hand out reasonable big ass buffers by default */ dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT -2); } dmabuf->numfrag = bufsize >> dmabuf->fragshift; while (dmabuf->numfrag < 4 && dmabuf->fragshift > 3) { dmabuf->fragshift--; dmabuf->numfrag = bufsize >> dmabuf->fragshift; } dmabuf->fragsize = 1 << dmabuf->fragshift; dmabuf->fragsamples = dmabuf->fragsize >> state->format.shift; dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; /* * Import what Doom might have set with SNDCTL_DSD_SETFRAGMENT. */ if (dmabuf->ossmaxfrags >= 2 && dmabuf->ossmaxfrags < dmabuf->numfrag) { dmabuf->numfrag = dmabuf->ossmaxfrags; dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; redzone = ymf_calc_lend(state->format.rate); redzone <<= (state->format.shift + 1); if (dmabuf->dmasize < redzone*3) { /* * The driver works correctly with minimum dmasize * of redzone*2, but it produces stoppage and clicks. * So, make it little larger for smoother sound. * XXX Make dmasize a wholy divisible by fragsize. */// printk(KERN_ERR "ymfpci: dmasize=%d < redzone=%d * 3\n",// dmabuf->dmasize, redzone); dmabuf->dmasize = redzone*3; } } memset(dmabuf->rawbuf, w_16 ? 0 : 0x80, dmabuf->dmasize); /* * Now set up the ring */ spin_lock_irqsave(&state->unit->reg_lock, flags); if (rec) { /* ymf_rec_setup(state); */ } else { if ((ret = ymf_playback_prepare(state->unit, state)) != 0) { return ret; } } spin_unlock_irqrestore(&state->unit->reg_lock, flags); /* set the ready flag for the dma buffer (this comment is not stupid) */ dmabuf->ready = 1;#if 0 printk("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->ypcm, 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; ymfpci_pcm_t *ypcm = &state->ypcm; DECLARE_WAITQUEUE(waita, current); unsigned long flags; add_wait_queue(&state->dmabuf.wait, &waita); spin_lock_irqsave(&unit->reg_lock, flags); if (state->dmabuf.count != 0 && !state->ypcm.running) { ymf_playback_trigger(unit, ypcm, 1); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -