?? sscape.c
字號(hào):
/* * Low-level ALSA driver for the ENSONIQ SoundScape PnP * Copyright (c) by Chris Rankin * * This driver was written in part using information obtained from * the OSS/Free SoundScape driver, written by Hannu Savolainen. * * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <sound/driver.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/pnp.h>#include <linux/spinlock.h>#include <linux/moduleparam.h>#include <asm/dma.h>#include <sound/core.h>#include <sound/hwdep.h>#include <sound/cs4231.h>#include <sound/mpu401.h>#include <sound/initval.h>#include <sound/sscape_ioctl.h>MODULE_AUTHOR("Chris Rankin");MODULE_DESCRIPTION("ENSONIQ SoundScape PnP driver");MODULE_LICENSE("GPL");static char* id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_STR;static long port[SNDRV_CARDS] __devinitdata = { [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_PORT };static int irq[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IRQ;static int mpu_irq[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IRQ;static int dma[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_DMA;static int boot_devs;module_param_array(index, int, boot_devs, 0444);MODULE_PARM_DESC(index, "Index number for SoundScape soundcard");module_param_array(id, charp, boot_devs, 0444);MODULE_PARM_DESC(id, "Description for SoundScape card");module_param_array(port, long, boot_devs, 0444);MODULE_PARM_DESC(port, "Port # for SoundScape driver.");module_param_array(irq, int, boot_devs, 0444);MODULE_PARM_DESC(irq, "IRQ # for SoundScape driver.");module_param_array(mpu_irq, int, boot_devs, 0444);MODULE_PARM_DESC(mpu_irq, "MPU401 IRQ # for SoundScape driver.");module_param_array(dma, int, boot_devs, 0444);MODULE_PARM_DESC(dma, "DMA # for SoundScape driver."); #ifdef CONFIG_PNPstatic struct pnp_card_device_id sscape_pnpids[] = { { .id = "ENS3081", .devs = { { "ENS0000" } } }, { .id = "" } /* end */};MODULE_DEVICE_TABLE(pnp_card, sscape_pnpids);#endifstatic snd_card_t *sscape_card[SNDRV_CARDS];#define MPU401_IO(i) ((i) + 0)#define MIDI_DATA_IO(i) ((i) + 0)#define MIDI_CTRL_IO(i) ((i) + 1)#define HOST_CTRL_IO(i) ((i) + 2)#define HOST_DATA_IO(i) ((i) + 3)#define ODIE_ADDR_IO(i) ((i) + 4)#define ODIE_DATA_IO(i) ((i) + 5)#define CODEC_IO(i) ((i) + 8)#define IC_ODIE 1#define IC_OPUS 2#define RX_READY 0x01#define TX_READY 0x02#define CMD_ACK 0x80#define CMD_SET_MIDI_VOL 0x84#define CMD_GET_MIDI_VOL 0x85#define CMD_XXX_MIDI_VOL 0x86#define CMD_SET_EXTMIDI 0x8a#define CMD_GET_EXTMIDI 0x8b#define CMD_SET_MT32 0x8c#define CMD_GET_MT32 0x8denum GA_REG { GA_INTSTAT_REG = 0, GA_INTENA_REG, GA_DMAA_REG, GA_DMAB_REG, GA_INTCFG_REG, GA_DMACFG_REG, GA_CDCFG_REG, GA_SMCFGA_REG, GA_SMCFGB_REG, GA_HMCTL_REG};#define DMA_8BIT 0x80#define AD1845_FREQ_SEL_MSB 0x16#define AD1845_FREQ_SEL_LSB 0x17struct soundscape { spinlock_t lock; unsigned io_base; int codec_type; int ic_type; struct resource *io_res; cs4231_t *chip; mpu401_t *mpu; snd_hwdep_t *hw; /* * The MIDI device won't work until we've loaded * its firmware via a hardware-dependent device IOCTL */ spinlock_t fwlock; int hw_in_use; unsigned long midi_usage; unsigned char midi_vol;};#define INVALID_IRQ ((unsigned)-1)static inline struct soundscape *get_card_soundscape(snd_card_t * c){ return (struct soundscape *) (c->private_data);}static inline struct soundscape *get_mpu401_soundscape(mpu401_t * mpu){ return (struct soundscape *) (mpu->private_data);}static inline struct soundscape *get_hwdep_soundscape(snd_hwdep_t * hw){ return (struct soundscape *) (hw->private_data);}/* * Allocates some kernel memory that we can use for DMA. * I think this means that the memory has to map to * contiguous pages of physical memory. */static struct snd_dma_buffer *get_dmabuf(struct snd_dma_buffer *buf, unsigned long size){ if (buf) { if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, snd_dma_isa_data(), size, buf) < 0) { snd_printk(KERN_ERR "sscape: Failed to allocate %lu bytes for DMA\n", size); return NULL; } } return buf;}/* * Release the DMA-able kernel memory ... */static void free_dmabuf(struct snd_dma_buffer *buf){ if (buf && buf->area) snd_dma_free_pages(buf);}/* * This function writes to the SoundScape's control registers, * but doesn't do any locking. It's up to the caller to do that. * This is why this function is "unsafe" ... */static inline void sscape_write_unsafe(unsigned io_base, enum GA_REG reg, unsigned char val){ outb(reg, ODIE_ADDR_IO(io_base)); outb(val, ODIE_DATA_IO(io_base));}/* * Write to the SoundScape's control registers, and do the * necessary locking ... */static void sscape_write(struct soundscape *s, enum GA_REG reg, unsigned char val){ unsigned long flags; spin_lock_irqsave(&s->lock, flags); sscape_write_unsafe(s->io_base, reg, val); spin_unlock_irqrestore(&s->lock, flags);}/* * Read from the SoundScape's control registers, but leave any * locking to the caller. This is why the function is "unsafe" ... */static inline unsigned char sscape_read_unsafe(unsigned io_base, enum GA_REG reg){ outb(reg, ODIE_ADDR_IO(io_base)); return inb(ODIE_DATA_IO(io_base));}/* * Puts the SoundScape into "host" mode, as compared to "MIDI" mode */static inline void set_host_mode_unsafe(unsigned io_base){ outb(0x0, HOST_CTRL_IO(io_base));}/* * Puts the SoundScape into "MIDI" mode, as compared to "host" mode */static inline void set_midi_mode_unsafe(unsigned io_base){ outb(0x3, HOST_CTRL_IO(io_base));}/* * Read the SoundScape's host-mode control register, but leave * any locking issues to the caller ... */static inline int host_read_unsafe(unsigned io_base){ int data = -1; if ((inb(HOST_CTRL_IO(io_base)) & RX_READY) != 0) { data = inb(HOST_DATA_IO(io_base)); } return data;}/* * Read the SoundScape's host-mode control register, performing * a limited amount of busy-waiting if the register isn't ready. * Also leaves all locking-issues to the caller ... */static int host_read_ctrl_unsafe(unsigned io_base, unsigned timeout){ int data; while (((data = host_read_unsafe(io_base)) < 0) && (timeout != 0)) { udelay(100); --timeout; } /* while */ return data;}/* * Write to the SoundScape's host-mode control registers, but * leave any locking issues to the caller ... */static inline int host_write_unsafe(unsigned io_base, unsigned char data){ if ((inb(HOST_CTRL_IO(io_base)) & TX_READY) != 0) { outb(data, HOST_DATA_IO(io_base)); return 1; } return 0;}/* * Write to the SoundScape's host-mode control registers, performing * a limited amount of busy-waiting if the register isn't ready. * Also leaves all locking-issues to the caller ... */static int host_write_ctrl_unsafe(unsigned io_base, unsigned char data, unsigned timeout){ int err; while (!(err = host_write_unsafe(io_base, data)) && (timeout != 0)) { udelay(100); --timeout; } /* while */ return err;}/* * Check that the MIDI subsystem is operational. If it isn't, * then we will hang the computer if we try to use it ... * * NOTE: This check is based upon observation, not documentation. */static inline int verify_mpu401(const mpu401_t * mpu){ return ((inb(MIDI_CTRL_IO(mpu->port)) & 0xc0) == 0x80);}/* * This is apparently the standard way to initailise an MPU-401 */static inline void initialise_mpu401(const mpu401_t * mpu){ outb(0, MIDI_DATA_IO(mpu->port));}/* * Tell the SoundScape to activate the AD1845 chip (I think). * The AD1845 detection fails if we *don't* do this, so I * think that this is a good idea ... */static inline void activate_ad1845_unsafe(unsigned io_base){ sscape_write_unsafe(io_base, GA_HMCTL_REG, (sscape_read_unsafe(io_base, GA_HMCTL_REG) & 0xcf) | 0x10); sscape_write_unsafe(io_base, GA_CDCFG_REG, 0x80);}/* * Do the necessary ALSA-level cleanup to deallocate our driver ... */static void soundscape_free(snd_card_t * c){ register struct soundscape *sscape = get_card_soundscape(c); release_resource(sscape->io_res); kfree_nocheck(sscape->io_res); free_dma(sscape->chip->dma1);}/* * Put this process into an idle wait-state for a certain number * of "jiffies". The process can almost certainly be rescheduled * while we're waiting, and so we must NOT be holding any spinlocks * when we call this function. If we are then we risk DEADLOCK in * SMP (Ha!) or pre-emptible kernels. */static inline void sleep(long jiffs, int state){ set_current_state(state); schedule_timeout(jiffs);}/* * Tell the SoundScape to begin a DMA tranfer using the given channel. * All locking issues are left to the caller. */static inline void sscape_start_dma_unsafe(unsigned io_base, enum GA_REG reg){ sscape_write_unsafe(io_base, reg, sscape_read_unsafe(io_base, reg) | 0x01); sscape_write_unsafe(io_base, reg, sscape_read_unsafe(io_base, reg) & 0xfe);}/* * Wait for a DMA transfer to complete. This is a "limited busy-wait", * and all locking issues are left to the caller. */static int sscape_wait_dma_unsafe(unsigned io_base, enum GA_REG reg, unsigned timeout){ while (!(sscape_read_unsafe(io_base, reg) & 0x01) && (timeout != 0)) { udelay(100); --timeout; } /* while */ return (sscape_read_unsafe(io_base, reg) & 0x01);}/* * Wait for the On-Board Processor to return its start-up * acknowledgement sequence. This wait is too long for * us to perform "busy-waiting", and so we must sleep. * This in turn means that we must not be holding any * spinlocks when we call this function. */static int obp_startup_ack(struct soundscape *s, unsigned timeout){ while (timeout != 0) { unsigned long flags; unsigned char x; sleep(1, TASK_INTERRUPTIBLE); spin_lock_irqsave(&s->lock, flags); x = inb(HOST_DATA_IO(s->io_base)); spin_unlock_irqrestore(&s->lock, flags); if ((x & 0xfe) == 0xfe) return 1; --timeout; } /* while */ return 0;}/* * Wait for the host to return its start-up acknowledgement * sequence. This wait is too long for us to perform * "busy-waiting", and so we must sleep. This in turn means * that we must not be holding any spinlocks when we call * this function. */static int host_startup_ack(struct soundscape *s, unsigned timeout){ while (timeout != 0) { unsigned long flags; unsigned char x; sleep(1, TASK_INTERRUPTIBLE); spin_lock_irqsave(&s->lock, flags); x = inb(HOST_DATA_IO(s->io_base)); spin_unlock_irqrestore(&s->lock, flags); if (x == 0xfe) return 1; --timeout; } /* while */ return 0;}/* * Upload a byte-stream into the SoundScape using DMA channel A. */static int upload_dma_data(struct soundscape *s, const unsigned char __user *data, size_t size){ unsigned long flags; struct snd_dma_buffer dma; int ret; if (!get_dmabuf(&dma, PAGE_ALIGN(size))) return -ENOMEM; spin_lock_irqsave(&s->lock, flags); /* * Reset the board ... */ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) & 0x3f); /* * Enable the DMA channels and configure them ... */ sscape_write_unsafe(s->io_base, GA_DMACFG_REG, 0x50); sscape_write_unsafe(s->io_base, GA_DMAA_REG, (s->chip->dma1 << 4) | DMA_8BIT); sscape_write_unsafe(s->io_base, GA_DMAB_REG, 0x20); /* * Take the board out of reset ... */ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) | 0x80); /* * Upload the user's data (firmware?) to the SoundScape * board through the DMA channel ... */ while (size != 0) { unsigned long len; /* * Apparently, copying to/from userspace can sleep. * We are therefore forbidden from holding any * spinlocks while we copy ... */ spin_unlock_irqrestore(&s->lock, flags); /* * Remember that the data that we want to DMA * comes from USERSPACE. We have already verified * the userspace pointer ... */ len = min(size, dma.bytes); __copy_from_user(dma.area, data, len); data += len; size -= len; /* * Grab that spinlock again, now that we've * finished copying! */ spin_lock_irqsave(&s->lock, flags); snd_dma_program(s->chip->dma1, dma.addr, len, DMA_MODE_WRITE); sscape_start_dma_unsafe(s->io_base, GA_DMAA_REG); if (!sscape_wait_dma_unsafe(s->io_base, GA_DMAA_REG, 5000)) { /* * Don't forget to release this spinlock we're holding ... */ spin_unlock_irqrestore(&s->lock, flags);
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -