Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / sound / oss / esssolo1.c
index 3002cbe..6861563 100644 (file)
 #include <linux/smp_lock.h>
 #include <linux/gameport.h>
 #include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/mutex.h>
+
 
 #include <asm/io.h>
 #include <asm/page.h>
 
 #define FMODE_DMFM 0x10
 
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#define SUPPORT_JOYSTICK 1
+#endif
+
 static struct pci_driver solo1_driver;
 
 /* --------------------------------------------------------------------- */
@@ -186,7 +193,7 @@ struct solo1_state {
        unsigned ena;
 
        spinlock_t lock;
-       struct semaphore open_sem;
+       struct mutex open_mutex;
        mode_t open_mode;
        wait_queue_head_t open_wait;
 
@@ -226,7 +233,9 @@ struct solo1_state {
                unsigned char obuf[MIDIOUTBUF];
        } midi;
 
-       struct gameport gameport;
+#if SUPPORT_JOYSTICK
+       struct gameport *gameport;
+#endif
 };
 
 /* --------------------------------------------------------------------- */
@@ -445,7 +454,7 @@ static int prog_dmabuf(struct solo1_state *s, struct dmabuf *db)
                if (!db->rawbuf)
                        return -ENOMEM;
                db->buforder = order;
-               /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
+               /* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */
                pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
                for (page = virt_to_page(db->rawbuf); page <= pend; page++)
                        SetPageReserved(page);
@@ -508,7 +517,7 @@ static inline int prog_dmabuf_adc(struct solo1_state *s)
        return 0;
 }
 
-static inline int prog_dmabuf_dac(struct solo1_state *s)
+static int prog_dmabuf_dac(struct solo1_state *s)
 {
        unsigned long va;
        int c;
@@ -1242,7 +1251,9 @@ static int solo1_mmap(struct file *file, struct vm_area_struct *vma)
        if (size > (PAGE_SIZE << db->buforder))
                goto out;
        ret = -EAGAIN;
-       if (remap_page_range(vma, vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot))
+       if (remap_pfn_range(vma, vma->vm_start,
+                               virt_to_phys(db->rawbuf) >> PAGE_SHIFT,
+                               size, vma->vm_page_prot))
                goto out;
        db->mapped = 1;
        ret = 0;
@@ -1572,7 +1583,7 @@ static int solo1_release(struct inode *inode, struct file *file)
        lock_kernel();
        if (file->f_mode & FMODE_WRITE)
                drain_dac(s, file->f_flags & O_NONBLOCK);
-       down(&s->open_sem);
+       mutex_lock(&s->open_mutex);
        if (file->f_mode & FMODE_WRITE) {
                stop_dac(s);
                outb(0, s->iobase+6);  /* disable DMA */
@@ -1586,7 +1597,7 @@ static int solo1_release(struct inode *inode, struct file *file)
        }
        s->open_mode &= ~(FMODE_READ | FMODE_WRITE);
        wake_up(&s->open_wait);
-       up(&s->open_sem);
+       mutex_unlock(&s->open_mutex);
        unlock_kernel();
        return 0;
 }
@@ -1615,21 +1626,21 @@ static int solo1_open(struct inode *inode, struct file *file)
                VALIDATE_STATE(s);
        file->private_data = s;
        /* wait for device to become free */
-       down(&s->open_sem);
+       mutex_lock(&s->open_mutex);
        while (s->open_mode & (FMODE_READ | FMODE_WRITE)) {
                if (file->f_flags & O_NONBLOCK) {
-                       up(&s->open_sem);
+                       mutex_unlock(&s->open_mutex);
                        return -EBUSY;
                }
                add_wait_queue(&s->open_wait, &wait);
                __set_current_state(TASK_INTERRUPTIBLE);
-               up(&s->open_sem);
+               mutex_unlock(&s->open_mutex);
                schedule();
                remove_wait_queue(&s->open_wait, &wait);
                set_current_state(TASK_RUNNING);
                if (signal_pending(current))
                        return -ERESTARTSYS;
-               down(&s->open_sem);
+               mutex_lock(&s->open_mutex);
        }
        s->fmt = AFMT_U8;
        s->channels = 1;
@@ -1641,7 +1652,7 @@ static int solo1_open(struct inode *inode, struct file *file)
        s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0;
        s->dma_dac.enabled = 1;
        s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
-       up(&s->open_sem);
+       mutex_unlock(&s->open_mutex);
        prog_codec(s);
        return nonseekable_open(inode, file);
 }
@@ -1902,21 +1913,21 @@ static int solo1_midi_open(struct inode *inode, struct file *file)
                VALIDATE_STATE(s);
        file->private_data = s;
        /* wait for device to become free */
-       down(&s->open_sem);
+       mutex_lock(&s->open_mutex);
        while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) {
                if (file->f_flags & O_NONBLOCK) {
-                       up(&s->open_sem);
+                       mutex_unlock(&s->open_mutex);
                        return -EBUSY;
                }
                add_wait_queue(&s->open_wait, &wait);
                __set_current_state(TASK_INTERRUPTIBLE);
-               up(&s->open_sem);
+               mutex_unlock(&s->open_mutex);
                schedule();
                remove_wait_queue(&s->open_wait, &wait);
                set_current_state(TASK_RUNNING);
                if (signal_pending(current))
                        return -ERESTARTSYS;
-               down(&s->open_sem);
+               mutex_lock(&s->open_mutex);
        }
        spin_lock_irqsave(&s->lock, flags);
        if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
@@ -1942,7 +1953,7 @@ static int solo1_midi_open(struct inode *inode, struct file *file)
        }
        spin_unlock_irqrestore(&s->lock, flags);
        s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
-       up(&s->open_sem);
+       mutex_unlock(&s->open_mutex);
        return nonseekable_open(inode, file);
 }
 
@@ -1976,7 +1987,7 @@ static int solo1_midi_release(struct inode *inode, struct file *file)
                remove_wait_queue(&s->midi.owait, &wait);
                set_current_state(TASK_RUNNING);
        }
-       down(&s->open_sem);
+       mutex_lock(&s->open_mutex);
        s->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE));
        spin_lock_irqsave(&s->lock, flags);
        if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
@@ -1985,7 +1996,7 @@ static int solo1_midi_release(struct inode *inode, struct file *file)
        }
        spin_unlock_irqrestore(&s->lock, flags);
        wake_up(&s->open_wait);
-       up(&s->open_sem);
+       mutex_unlock(&s->open_mutex);
        unlock_kernel();
        return 0;
 }
@@ -2123,24 +2134,24 @@ static int solo1_dmfm_open(struct inode *inode, struct file *file)
                VALIDATE_STATE(s);
        file->private_data = s;
        /* wait for device to become free */
-       down(&s->open_sem);
+       mutex_lock(&s->open_mutex);
        while (s->open_mode & FMODE_DMFM) {
                if (file->f_flags & O_NONBLOCK) {
-                       up(&s->open_sem);
+                       mutex_unlock(&s->open_mutex);
                        return -EBUSY;
                }
                add_wait_queue(&s->open_wait, &wait);
                __set_current_state(TASK_INTERRUPTIBLE);
-               up(&s->open_sem);
+               mutex_unlock(&s->open_mutex);
                schedule();
                remove_wait_queue(&s->open_wait, &wait);
                set_current_state(TASK_RUNNING);
                if (signal_pending(current))
                        return -ERESTARTSYS;
-               down(&s->open_sem);
+               mutex_lock(&s->open_mutex);
        }
        if (!request_region(s->sbbase, FMSYNTH_EXTENT, "ESS Solo1")) {
-               up(&s->open_sem);
+               mutex_unlock(&s->open_mutex);
                printk(KERN_ERR "solo1: FM synth io ports in use, opl3 loaded?\n");
                return -EBUSY;
        }
@@ -2152,7 +2163,7 @@ static int solo1_dmfm_open(struct inode *inode, struct file *file)
        outb(5, s->sbbase+2);
        outb(1, s->sbbase+3);  /* enable OPL3 */
        s->open_mode |= FMODE_DMFM;
-       up(&s->open_sem);
+       mutex_unlock(&s->open_mutex);
        return nonseekable_open(inode, file);
 }
 
@@ -2163,7 +2174,7 @@ static int solo1_dmfm_release(struct inode *inode, struct file *file)
 
        VALIDATE_STATE(s);
        lock_kernel();
-       down(&s->open_sem);
+       mutex_lock(&s->open_mutex);
        s->open_mode &= ~FMODE_DMFM;
        for (regb = 0xb0; regb < 0xb9; regb++) {
                outb(regb, s->sbbase);
@@ -2173,7 +2184,7 @@ static int solo1_dmfm_release(struct inode *inode, struct file *file)
        }
        release_region(s->sbbase, FMSYNTH_EXTENT);
        wake_up(&s->open_wait);
-       up(&s->open_sem);
+       mutex_unlock(&s->open_mutex);
        unlock_kernel();
        return 0;
 }
@@ -2191,7 +2202,7 @@ static /*const*/ struct file_operations solo1_dmfm_fops = {
 static struct initvol {
        int mixch;
        int vol;
-} initvol[] __initdata = {
+} initvol[] __devinitdata = {
        { SOUND_MIXER_WRITE_VOLUME, 0x4040 },
        { SOUND_MIXER_WRITE_PCM, 0x4040 },
        { SOUND_MIXER_WRITE_SYNTH, 0x4040 },
@@ -2255,7 +2266,7 @@ static int setup_solo1(struct solo1_state *s)
 }
 
 static int
-solo1_suspend(struct pci_dev *pci_dev, u32 state) {
+solo1_suspend(struct pci_dev *pci_dev, pm_message_t state) {
        struct solo1_state *s = (struct solo1_state*)pci_get_drvdata(pci_dev);
        if (!s)
                return 1;
@@ -2278,9 +2289,50 @@ solo1_resume(struct pci_dev *pci_dev) {
        return 0;
 }
 
+#ifdef SUPPORT_JOYSTICK
+static int __devinit solo1_register_gameport(struct solo1_state *s, int io_port)
+{
+       struct gameport *gp;
+
+       if (!request_region(io_port, GAMEPORT_EXTENT, "ESS Solo1")) {
+               printk(KERN_ERR "solo1: gameport io ports are in use\n");
+               return -EBUSY;
+       }
+
+       s->gameport = gp = gameport_allocate_port();
+       if (!gp) {
+               printk(KERN_ERR "solo1: can not allocate memory for gameport\n");
+               release_region(io_port, GAMEPORT_EXTENT);
+               return -ENOMEM;
+       }
+
+       gameport_set_name(gp, "ESS Solo1 Gameport");
+       gameport_set_phys(gp, "isa%04x/gameport0", io_port);
+       gp->dev.parent = &s->dev->dev;
+       gp->io = io_port;
+
+       gameport_register_port(gp);
+
+       return 0;
+}
+
+static inline void solo1_unregister_gameport(struct solo1_state *s)
+{
+       if (s->gameport) {
+               int gpio = s->gameport->io;
+               gameport_unregister_port(s->gameport);
+               release_region(gpio, GAMEPORT_EXTENT);
+       }
+}
+#else
+static inline int solo1_register_gameport(struct solo1_state *s, int io_port) { return -ENOSYS; }
+static inline void solo1_unregister_gameport(struct solo1_state *s) { }
+#endif /* SUPPORT_JOYSTICK */
+
 static int __devinit solo1_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid)
 {
        struct solo1_state *s;
+       int gpio;
        int ret;
 
        if ((ret=pci_enable_device(pcidev)))
@@ -2296,8 +2348,8 @@ static int __devinit solo1_probe(struct pci_dev *pcidev, const struct pci_device
        /* Recording requires 24-bit DMA, so attempt to set dma mask
         * to 24 bits first, then 32 bits (playback only) if that fails.
         */
-       if (pci_set_dma_mask(pcidev, 0x00ffffff) &&
-           pci_set_dma_mask(pcidev, 0xffffffff)) {
+       if (pci_set_dma_mask(pcidev, DMA_24BIT_MASK) &&
+           pci_set_dma_mask(pcidev, DMA_32BIT_MASK)) {
                printk(KERN_WARNING "solo1: architecture does not support 24bit or 32bit PCI busmaster DMA\n");
                return -ENODEV;
        }
@@ -2312,7 +2364,7 @@ static int __devinit solo1_probe(struct pci_dev *pcidev, const struct pci_device
        init_waitqueue_head(&s->open_wait);
        init_waitqueue_head(&s->midi.iwait);
        init_waitqueue_head(&s->midi.owait);
-       init_MUTEX(&s->open_sem);
+       mutex_init(&s->open_mutex);
        spin_lock_init(&s->lock);
        s->magic = SOLO1_MAGIC;
        s->dev = pcidev;
@@ -2321,7 +2373,7 @@ static int __devinit solo1_probe(struct pci_dev *pcidev, const struct pci_device
        s->vcbase = pci_resource_start(pcidev, 2);
        s->ddmabase = s->vcbase + DDMABASE_OFFSET;
        s->mpubase = pci_resource_start(pcidev, 3);
-       s->gameport.io = pci_resource_start(pcidev, 4);
+       gpio = pci_resource_start(pcidev, 4);
        s->irq = pcidev->irq;
        ret = -EBUSY;
        if (!request_region(s->iobase, IOBASE_EXTENT, "ESS Solo1")) {
@@ -2340,15 +2392,10 @@ static int __devinit solo1_probe(struct pci_dev *pcidev, const struct pci_device
                printk(KERN_ERR "solo1: io ports in use\n");
                goto err_region4;
        }
-       if (s->gameport.io && !request_region(s->gameport.io, GAMEPORT_EXTENT, "ESS Solo1")) {
-               printk(KERN_ERR "solo1: gameport io ports in use\n");
-               s->gameport.io = 0;
-       }
        if ((ret=request_irq(s->irq,solo1_interrupt,SA_SHIRQ,"ESS Solo1",s))) {
                printk(KERN_ERR "solo1: irq %u in use\n", s->irq);
                goto err_irq;
        }
-       printk(KERN_INFO "solo1: joystick port at %#x\n", s->gameport.io+1);
        /* register devices */
        if ((s->dev_audio = register_sound_dsp(&solo1_audio_fops, -1)) < 0) {
                ret = s->dev_audio;
@@ -2371,7 +2418,7 @@ static int __devinit solo1_probe(struct pci_dev *pcidev, const struct pci_device
                goto err;
        }
        /* register gameport */
-       gameport_register_port(&s->gameport);
+       solo1_register_gameport(s, gpio);
        /* store it in the driver field */
        pci_set_drvdata(pcidev, s);
        return 0;
@@ -2388,8 +2435,6 @@ static int __devinit solo1_probe(struct pci_dev *pcidev, const struct pci_device
        printk(KERN_ERR "solo1: initialisation error\n");
        free_irq(s->irq, s);
  err_irq:
-       if (s->gameport.io)
-               release_region(s->gameport.io, GAMEPORT_EXTENT);
        release_region(s->mpubase, MPUBASE_EXTENT);
  err_region4:
        release_region(s->ddmabase, DDMABASE_EXTENT);
@@ -2415,10 +2460,7 @@ static void __devexit solo1_remove(struct pci_dev *dev)
        synchronize_irq(s->irq);
        pci_write_config_word(s->dev, 0x60, 0); /* turn off DDMA controller address space */
        free_irq(s->irq, s);
-       if (s->gameport.io) {
-               gameport_unregister_port(&s->gameport);
-               release_region(s->gameport.io, GAMEPORT_EXTENT);
-       }
+       solo1_unregister_gameport(s);
        release_region(s->iobase, IOBASE_EXTENT);
        release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT);
        release_region(s->ddmabase, DDMABASE_EXTENT);
@@ -2451,11 +2493,7 @@ static struct pci_driver solo1_driver = {
 static int __init init_solo1(void)
 {
        printk(KERN_INFO "solo1: version v0.20 time " __TIME__ " " __DATE__ "\n");
-       if (!pci_register_driver(&solo1_driver)) {
-               pci_unregister_driver(&solo1_driver);
-                return -ENODEV;
-       }
-       return 0;
+       return pci_register_driver(&solo1_driver);
 }
 
 /* --------------------------------------------------------------------- */