]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - sound/pci/emu10k1/io.c
Merge branch 'core/iommu' into core/urgent
[linux-2.6-omap-h63xx.git] / sound / pci / emu10k1 / io.c
index 029e7856c43beaa7d171a22696d95410119b7b43..b5a802bdeb7c1b21f76573e72bbd16fd2af74898 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
  *                   Creative Labs, Inc.
  *  Routines for control of EMU10K1 chips
  *
  *
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/emu10k1.h>
 #include <linux/delay.h>
+#include "p17v.h"
 
 unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn)
 {
@@ -70,6 +70,11 @@ void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned i
        unsigned long flags;
        unsigned int mask;
 
+       if (!emu) {
+               snd_printk(KERN_ERR "ptr_write: emu is null!\n");
+               dump_stack();
+               return;
+       }
        mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
        regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
 
@@ -134,15 +139,23 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
        unsigned int reset, set;
        unsigned int reg, tmp;
        int n, result;
+       int err = 0;
+
+       /* This function is not re-entrant, so protect against it. */
+       spin_lock(&emu->spi_lock);
        if (emu->card_capabilities->ca0108_chip)
                reg = 0x3c; /* PTR20, reg 0x3c */
        else {
                /* For other chip types the SPI register
                 * is currently unknown. */
-               return 1;
+               err = 1;
+               goto spi_write_exit;
+       }
+       if (data > 0xffff) {
+               /* Only 16bit values allowed */
+               err = 1;
+               goto spi_write_exit;
        }
-       if (data > 0xffff) /* Only 16bit values allowed */
-               return 1;
 
        tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
        reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
@@ -160,10 +173,129 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
                        break;
                }
        }
-       if (result) /* Timed out */
-               return 1;
+       if (result) {
+               /* Timed out */
+               err = 1;
+               goto spi_write_exit;
+       }
        snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
        tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
+       err = 0;
+spi_write_exit:
+       spin_unlock(&emu->spi_lock);
+       return err;
+}
+
+/* The ADC does not support i2c read, so only write is implemented */
+int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
+                               u32 reg,
+                               u32 value)
+{
+       u32 tmp;
+       int timeout = 0;
+       int status;
+       int retry;
+       int err = 0;
+
+       if ((reg > 0x7f) || (value > 0x1ff)) {
+               snd_printk(KERN_ERR "i2c_write: invalid values.\n");
+               return -EINVAL;
+       }
+
+       /* This function is not re-entrant, so protect against it. */
+       spin_lock(&emu->i2c_lock);
+
+       tmp = reg << 25 | value << 16;
+
+       /* This controls the I2C connected to the WM8775 ADC Codec */
+       snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp);
+       tmp = snd_emu10k1_ptr20_read(emu, P17V_I2C_1, 0); /* write post */
+
+       for (retry = 0; retry < 10; retry++) {
+               /* Send the data to i2c */
+               tmp = 0;
+               tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
+               snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp);
+
+               /* Wait till the transaction ends */
+               while (1) {
+                       mdelay(1);
+                       status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0);
+                       timeout++;
+                       if ((status & I2C_A_ADC_START) == 0)
+                               break;
+
+                       if (timeout > 1000) {
+                               snd_printk("emu10k1:I2C:timeout status=0x%x\n", status);
+                               break;
+                       }
+               }
+               //Read back and see if the transaction is successful
+               if ((status & I2C_A_ADC_ABORT) == 0)
+                       break;
+       }
+
+       if (retry == 10) {
+               snd_printk(KERN_ERR "Writing to ADC failed!\n");
+               snd_printk(KERN_ERR "status=0x%x, reg=%d, value=%d\n",
+                       status, reg, value);
+               /* dump_stack(); */
+               err = -EINVAL;
+       }
+    
+       spin_unlock(&emu->i2c_lock);
+       return err;
+}
+
+int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
+{
+       unsigned long flags;
+
+       if (reg > 0x3f)
+               return 1;
+       reg += 0x40; /* 0x40 upwards are registers. */
+       if (value < 0 || value > 0x3f) /* 0 to 0x3f are values */
+               return 1;
+       spin_lock_irqsave(&emu->emu_lock, flags);
+       outl(reg, emu->port + A_IOCFG);
+       udelay(10);
+       outl(reg | 0x80, emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
+       udelay(10);
+       outl(value, emu->port + A_IOCFG);
+       udelay(10);
+       outl(value | 0x80 , emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
+       spin_unlock_irqrestore(&emu->emu_lock, flags);
+
+       return 0;
+}
+
+int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value)
+{
+       unsigned long flags;
+       if (reg > 0x3f)
+               return 1;
+       reg += 0x40; /* 0x40 upwards are registers. */
+       spin_lock_irqsave(&emu->emu_lock, flags);
+       outl(reg, emu->port + A_IOCFG);
+       udelay(10);
+       outl(reg | 0x80, emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
+       udelay(10);
+       *value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f);
+       spin_unlock_irqrestore(&emu->emu_lock, flags);
+
+       return 0;
+}
+
+/* Each Destination has one and only one Source,
+ * but one Source can feed any number of Destinations simultaneously.
+ */
+int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, u32 dst, u32 src)
+{
+       snd_emu1010_fpga_write(emu, 0x00, ((dst >> 8) & 0x3f) );
+       snd_emu1010_fpga_write(emu, 0x01, (dst & 0x3f) );
+       snd_emu1010_fpga_write(emu, 0x02, ((src >> 8) & 0x3f) );
+       snd_emu1010_fpga_write(emu, 0x03, (src & 0x3f) );
+
        return 0;
 }