]> err.no Git - linux-2.6/blobdiff - sound/pci/ca0106/ca0106_main.c
[ALSA] sound/pci/ca0106: Use the DMA_32BIT_MASK constant
[linux-2.6] / sound / pci / ca0106 / ca0106_main.c
index d4cb8edf70804ba554ef76e27ada620d7dfeaef3..85caf1bbcc11e544dd3b13d0cd95f631751f9815 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
  *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
- *  Version: 0.0.22
+ *  Version: 0.0.23
  *
  *  FEATURES currently supported:
  *    Front, Rear and Center/LFE.
@@ -77,6 +77,8 @@
  *    Add SPDIF capture using optional digital I/O module for SB Live 24bit. (Analog capture does not yet work.)
  *  0.0.22
  *    Add support for MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97. From kiksen, bug #901
+ *  0.0.23
+ *    Implement support for Line-in capture on SB Live 24bit.
  *
  *  BUGS:
  *    Some stability problems when unloading the snd-ca0106 kernel module.
 #include <linux/pci.h>
 #include <linux/slab.h>
 #include <linux/moduleparam.h>
+#include <linux/dma-mapping.h>
 #include <sound/core.h>
 #include <sound/initval.h>
 #include <sound/pcm.h>
@@ -161,18 +164,32 @@ MODULE_PARM_DESC(enable, "Enable the CA0106 soundcard.");
 
 #include "ca0106.h"
 
-typedef struct {
-       u32 serial;
-       char * name;
-} ca0106_names_t;
-
-static ca0106_names_t ca0106_chip_names[] = {
-        { 0x10021102, "AudigyLS [SB0310]"} , 
-        { 0x10051102, "AudigyLS [SB0310b]"} , /* Unknown AudigyLS that also says SB0310 on it */
-        { 0x10061102, "Live! 7.1 24bit [SB0410]"} , /* New Sound Blaster Live! 7.1 24bit. This does not have an AC97. 53SB041000001 */
-        { 0x10071102, "Live! 7.1 24bit [SB0413]"} , /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97.  */
-        { 0x10091462, "MSI K8N Diamond MB [SB0438]"}, /* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */
-        { 0, "AudigyLS [Unknown]" }
+static ca0106_details_t ca0106_chip_details[] = {
+        /* AudigyLS[SB0310] */
+        { .serial = 0x10021102,
+          .name   = "AudigyLS [SB0310]",
+          .ac97   = 1 } , 
+        /* Unknown AudigyLS that also says SB0310 on it */
+        { .serial = 0x10051102,
+          .name   = "AudigyLS [SB0310b]",
+          .ac97   = 1 } ,
+        /* New Sound Blaster Live! 7.1 24bit. This does not have an AC97. 53SB041000001 */
+        { .serial = 0x10061102,
+          .name   = "Live! 7.1 24bit [SB0410]",
+          .gpio_type = 1,
+          .i2c_adc = 1 } ,
+        /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97.  */
+        { .serial = 0x10071102,
+          .name   = "Live! 7.1 24bit [SB0413]",
+          .gpio_type = 1,
+          .i2c_adc = 1 } ,
+        /* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */
+        { .serial = 0x10091462,
+          .name   = "MSI K8N Diamond MB [SB0438]",
+          .gpio_type = 1,
+          .i2c_adc = 1 } ,
+        { .serial = 0,
+          .name   = "AudigyLS [Unknown]" }
 };
 
 /* hardware definition */
@@ -200,10 +217,10 @@ static snd_pcm_hardware_t snd_ca0106_capture_hw = {
                                 SNDRV_PCM_INFO_INTERLEAVED |
                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
                                 SNDRV_PCM_INFO_MMAP_VALID),
-       .formats =              SNDRV_PCM_FMTBIT_S16_LE,
-       .rates =                SNDRV_PCM_RATE_48000,
-       .rate_min =             48000,
-       .rate_max =             48000,
+       .formats =              SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+       .rates =                SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
+       .rate_min =             44100,
+       .rate_max =             192000,
        .channels_min =         2,
        .channels_max =         2,
        .buffer_bytes_max =     ((65536 - 64) * 8),
@@ -246,6 +263,62 @@ void snd_ca0106_ptr_write(ca0106_t *emu,
        spin_unlock_irqrestore(&emu->emu_lock, flags);
 }
 
+int snd_ca0106_i2c_write(ca0106_t *emu,
+                               u32 reg,
+                               u32 value)
+{
+       u32 tmp;
+       int timeout=0;
+       int status;
+       int retry;
+       if ((reg > 0x7f) || (value > 0x1ff))
+       {
+                snd_printk("i2c_write: invalid values.\n");
+               return -EINVAL;
+       }
+
+       tmp = reg << 25 | value << 16;
+       /* Not sure what this I2C channel controls. */
+       /* snd_ca0106_ptr_write(emu, I2C_D0, 0, tmp); */
+
+       /* This controls the I2C connected to the WM8775 ADC Codec */
+       snd_ca0106_ptr_write(emu, I2C_D1, 0, tmp);
+
+       for(retry=0;retry<10;retry++)
+       {
+               /* Send the data to i2c */
+               tmp = snd_ca0106_ptr_read(emu, I2C_A, 0);
+               tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK);
+               tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
+               snd_ca0106_ptr_write(emu, I2C_A, 0, tmp);
+
+               /* Wait till the transaction ends */
+               while(1)
+               {
+                       status = snd_ca0106_ptr_read(emu, I2C_A, 0);
+                       //snd_printk("I2C:status=0x%x\n", status);
+                       timeout++;
+                       if((status & I2C_A_ADC_START)==0)
+                               break;
+
+                       if(timeout>1000)
+                               break;
+               }
+               //Read back and see if the transaction is successful
+               if((status & I2C_A_ADC_ABORT)==0)
+                       break;
+       }
+
+       if(retry==10)
+       {
+                snd_printk("Writing to ADC failed!\n");
+               return -EINVAL;
+       }
+    
+       return 0;
+}
+
+
 static void snd_ca0106_intr_enable(ca0106_t *emu, unsigned int intrenb)
 {
        unsigned long flags;
@@ -538,6 +611,61 @@ static int snd_ca0106_pcm_prepare_capture(snd_pcm_substream_t *substream)
        snd_pcm_runtime_t *runtime = substream->runtime;
        ca0106_pcm_t *epcm = runtime->private_data;
        int channel = epcm->channel_id;
+       u32 hcfg_mask = HCFG_CAPTURE_S32_LE;
+       u32 hcfg_set = 0x00000000;
+       u32 hcfg;
+       u32 over_sampling=0x2;
+       u32 reg71_mask = 0x0000c000 ; /* Global. Set ADC rate. */
+       u32 reg71_set = 0;
+       u32 reg71;
+       
+        //snd_printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, periods=%u, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, runtime->periods, frames_to_bytes(runtime, 1));
+        //snd_printk("dma_addr=%x, dma_area=%p, table_base=%p\n",runtime->dma_addr, runtime->dma_area, table_base);
+       //snd_printk("dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",emu->buffer.addr, emu->buffer.area, emu->buffer.bytes);
+       /* reg71 controls ADC rate. */
+       switch (runtime->rate) {
+       case 44100:
+               reg71_set = 0x00004000;
+               break;
+        case 48000:
+               reg71_set = 0; 
+               break;
+       case 96000:
+               reg71_set = 0x00008000;
+               over_sampling=0xa;
+               break;
+       case 192000:
+               reg71_set = 0x0000c000; 
+               over_sampling=0xa;
+               break;
+       default:
+               reg71_set = 0; 
+               break;
+       }
+       /* Format is a global setting */
+       /* FIXME: Only let the first channel accessed set this. */
+       switch (runtime->format) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               hcfg_set = 0;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               hcfg_set = HCFG_CAPTURE_S32_LE;
+               break;
+       default:
+               hcfg_set = 0;
+               break;
+       }
+       hcfg = inl(emu->port + HCFG) ;
+       hcfg = (hcfg & ~hcfg_mask) | hcfg_set;
+       outl(hcfg, emu->port + HCFG);
+       reg71 = snd_ca0106_ptr_read(emu, 0x71, 0);
+       reg71 = (reg71 & ~reg71_mask) | reg71_set;
+       snd_ca0106_ptr_write(emu, 0x71, 0, reg71);
+        if (emu->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */
+               snd_ca0106_i2c_write(emu, ADC_MASTER, over_sampling); /* Adjust the over sampler to better suit the capture rate. */
+       }
+
+
         //printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size,  frames_to_bytes(runtime, 1));
        snd_ca0106_ptr_write(emu, 0x13, channel, 0);
        snd_ca0106_ptr_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr);
@@ -994,6 +1122,7 @@ static int __devinit snd_ca0106_create(snd_card_t *card,
                                         ca0106_t **rchip)
 {
        ca0106_t *chip;
+       ca0106_details_t *c;
        int err;
        int ch;
        static snd_device_ops_t ops = {
@@ -1004,8 +1133,8 @@ static int __devinit snd_ca0106_create(snd_card_t *card,
   
        if ((err = pci_enable_device(pci)) < 0)
                return err;
-       if (pci_set_dma_mask(pci, 0xffffffffUL) < 0 ||
-           pci_set_consistent_dma_mask(pci, 0xffffffffUL) < 0) {
+       if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0 ||
+           pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0) {
                printk(KERN_ERR "error to set 32bit mask DMA\n");
                pci_disable_device(pci);
                return -ENXIO;
@@ -1055,6 +1184,15 @@ static int __devinit snd_ca0106_create(snd_card_t *card,
        printk(KERN_INFO "Model %04x Rev %08x Serial %08x\n", chip->model,
               chip->revision, chip->serial);
 #endif
+       strcpy(card->driver, "CA0106");
+       strcpy(card->shortname, "CA0106");
+
+       for (c=ca0106_chip_details; c->serial; c++) {
+               if (c->serial == chip->serial) break;
+       }
+       chip->details = c;
+       sprintf(card->longname, "%s at 0x%lx irq %i",
+               c->name, chip->port, chip->irq);
 
        outl(0, chip->port + INTE);
 
@@ -1114,7 +1252,7 @@ static int __devinit snd_ca0106_create(snd_card_t *card,
        //snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); /* OSS drivers set this. */
        /* Analog or Digital output */
        snd_ca0106_ptr_write(chip, SPDIF_SELECT1, 0, 0xf);
-       snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000b0000); /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers */
+       snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000f0000); /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers. Use 0x000f0000 for surround71 */
        chip->spdif_enable = 0; /* Set digital SPDIF output off */
        chip->capture_source = 3; /* Set CAPTURE_SOURCE */
        //snd_ca0106_ptr_write(chip, 0x45, 0, 0); /* Analogue out */
@@ -1139,13 +1277,11 @@ static int __devinit snd_ca0106_create(snd_card_t *card,
         snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC, Line in, TAD in, AUX in */
        chip->capture_source = 3; /* Set CAPTURE_SOURCE */
 
-        if ((chip->serial == 0x10061102) || 
-           (chip->serial == 0x10071102) ||
-           (chip->serial == 0x10091462)) { /* The SB0410 and SB0413 use GPIO differently. */
+        if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */
                /* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */
                outl(0x0, chip->port+GPIO);
                //outl(0x00f0e000, chip->port+GPIO); /* Analog */
-               outl(0x005f4300, chip->port+GPIO); /* Analog */
+               outl(0x005f4301, chip->port+GPIO); /* Analog */
        } else {
                outl(0x0, chip->port+GPIO);
                outl(0x005f03a3, chip->port+GPIO); /* Analog */
@@ -1158,6 +1294,10 @@ static int __devinit snd_ca0106_create(snd_card_t *card,
        //outl(0x00000009, chip->port+HCFG);
        outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG); /* AC97 2.0, Enable outputs. */
 
+        if (chip->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */
+               snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); /* Enable Line-in capture. MIC in currently untested. */
+       }
+
        if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
                                  chip, &ops)) < 0) {
                snd_ca0106_free(chip);
@@ -1173,7 +1313,6 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
        static int dev;
        snd_card_t *card;
        ca0106_t *chip;
-       ca0106_names_t *c;
        int err;
 
        if (dev >= SNDRV_CARDS)
@@ -1208,9 +1347,7 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
                snd_card_free(card);
                return err;
        }
-        if ((chip->serial != 0x10061102) && 
-           (chip->serial != 0x10071102) && 
-           (chip->serial != 0x10091462) ) { /* The SB0410 and SB0413 do not have an ac97 chip. */
+        if (chip->details->ac97 == 1) { /* The SB0410 and SB0413 do not have an AC97 chip. */
                if ((err = snd_ca0106_ac97(chip)) < 0) {
                        snd_card_free(card);
                        return err;
@@ -1223,15 +1360,6 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
 
        snd_ca0106_proc_init(chip);
 
-       strcpy(card->driver, "CA0106");
-       strcpy(card->shortname, "CA0106");
-
-       for (c=ca0106_chip_names; c->serial; c++) {
-               if (c->serial == chip->serial) break;
-       }
-       sprintf(card->longname, "%s at 0x%lx irq %i",
-               c->name, chip->port, chip->irq);
-
        if ((err = snd_card_register(card)) < 0) {
                snd_card_free(card);
                return err;