[PATCH] make i810_audio mmio aware/support for >2 codecs

From: Juergen Sawinski (juergen.sawinski@mpimf-heidelberg.mpg.de)
Date: Thu Aug 08 2002 - 20:35:18 EST


patch-2.4.20-pre1-ac1-ac97_codec-4codecs:
  -cosmetics (report about primary and secondary codec
   expanded for tertiary codec)

patch-2.4.20-pre1-ac1-i810_audio-mmio-step-1:
  -mmio for accessing the AC97 with ICH4
  -workaround for a bug on the Intel D845GBV mother board:
     Board reports a tertiary codec while it is
     accessed as a primary codec. Maybe IO/MMIO space ordering
     is consecutive instead of ID based???

     Don't know how to automatically work around this bug,
     so for now there is a module option "d845gbv_bug=1"
     (try setting this option if your log says
      "Primary codec not ready."
      but you are sure, you have a builtin codec)

  TODO:
    -access the bus master operations via mmio, too

George

-- 
Juergen "George" Sawinski
Max-Planck Institute for Medical Research
Dept. of Biomedical Optics
Jahnstr. 29
D-69120 Heidelberg
Germany

Phone: +49-6221-486-308 Fax: +49-6221-486-325

priv. Phone: +49-6221-418 858 Mobile: +49-171-532 5302

--- linux-2.4.20-pre1-ac1/drivers/sound/ac97_codec.c Sat Aug 3 02:39:44 2002 +++ linux-2.4.20-pre1-ac1-jsaw/drivers/sound/ac97_codec.c Fri Aug 9 01:18:17 2002 @@ -698,7 +698,8 @@ if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) { printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n", - codec->id ? "Secondary" : "Primary"); + (codec->id & 0x2) ? (codec->id&1 ? "4th" : "Tertiary") + : (codec->id&1 ? "Secondary": "Primary")); return 0; }

--- linux-2.4.20-pre1-ac1/drivers/sound/i810_audio.c Thu Aug 8 00:51:01 2002 +++ linux-2.4.20-pre1-ac1-jsaw/drivers/sound/i810_audio.c Fri Aug 9 03:13:05 2002 @@ -121,11 +121,13 @@ static int strict_clocking=0; static unsigned int clocking=0; static int spdif_locked=0; +static int d845gbv_bug=0; //#define DEBUG //#define DEBUG2 //#define DEBUG_INTERRUPTS //#define DEBUG_MMAP +//#define DEBUG_MMIO #define ADC_RUNNING 1 #define DAC_RUNNING 2 @@ -168,6 +170,9 @@ * each dma engine has controlling registers. These goofy * names are from the datasheet, but make it easy to write * code while leafing through it. + * + * ICH4 has 6 dma engines, pcm in, pcm out, mic, pcm in 2, + * mic in 2, s/pdif. */ #define ENUM_ENGINE(PRE,DIG) \ @@ -192,6 +197,14 @@ CAS = 0x34 /* Codec Write Semaphore Register */ }; +ENUM_ENGINE(MC2,4); /* Mic. 2 */ +ENUM_ENGINE(PI2,5); /* PCM In 2 */ +ENUM_ENGINE(SP,6); /* S/PDIF */ + +enum { + SDM = 0x80 /* SDATA_IN Map Register */ +}; + /* interrupts for a dma engine */ #define DMA_INT_FIFO (1<<4) /* fifo under/over flow */ #define DMA_INT_COMPLETE (1<<3) /* buffer read/write complete and ioc set */ @@ -221,7 +234,7 @@ #define NR_HW_CH 3 /* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ -#define NR_AC97 2 +#define NR_AC97 4 /* Please note that an 8bit mono stream is not valid on this card, you must have a 16bit */ /* stream at a minimum for this card to be happy */ @@ -383,9 +396,16 @@ u16 channels; /* hardware resources */ - unsigned long iobase; unsigned long ac97base; + unsigned long iobase; u32 irq; + + unsigned long ac97base_mmio_phys; + unsigned long iobase_mmio_phys; + u_int8_t *ac97base_mmio; + u_int8_t *iobase_mmio; + + int use_mmio; /* Function support */ struct i810_channel *(*alloc_pcm_channel)(struct i810_card *); @@ -405,6 +425,10 @@ unsigned int cmd, unsigned long arg); static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg); static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data); +static u16 i810_ac97_get_mmio(struct ac97_codec *dev, u8 reg); +static void i810_ac97_set_mmio(struct ac97_codec *dev, u8 reg, u16 data); +static u16 i810_ac97_get_io(struct ac97_codec *dev, u8 reg); +static void i810_ac97_set_io(struct ac97_codec *dev, u8 reg, u16 data); static struct i810_channel *i810_alloc_pcm_channel(struct i810_card *card) { @@ -2478,27 +2502,88 @@ /* Write AC97 codec registers */ -static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg) +static u16 i810_ac97_get_mmio(struct ac97_codec *dev, u8 reg) { struct i810_card *card = dev->private_data; int count = 100; - u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f)); + u16 reg_set = ((u16) reg) & 0x7f; + reg_set |= ((u16) dev->id) << 7; + + while(count-- && (readb(card->iobase_mmio + CAS) & 1)) + udelay(1); + +#ifdef DEBUG_MMIO + { + u16 ans = readw(card->ac97base_mmio + reg_set); + printk(KERN_DEBUG "i810_audio: ac97_get_mmio(%d) -> 0x%04X\n", ((int) reg_set) & 0xffff, (u32) ans); + return ans; + } +#else + return readw(card->ac97base_mmio + reg_set); +#endif +} +static u16 i810_ac97_get_io(struct ac97_codec *dev, u8 reg) +{ + struct i810_card *card = dev->private_data; + int count = 100; + u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f)); + while(count-- && (inb(card->iobase + CAS) & 1)) udelay(1); return inw(card->ac97base + reg_set); } -static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data) +static void i810_ac97_set_mmio(struct ac97_codec *dev, u8 reg, u16 data) { struct i810_card *card = dev->private_data; int count = 100; - u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f)); + u16 reg_set = ((u16) reg) & 0x7f; + reg_set |= ((u16) dev->id) << 7; + + while(count-- && (readb(card->iobase_mmio + CAS) & 1)) + udelay(1); + + writew(data, card->ac97base_mmio + reg_set); +#ifdef DEBUG_MMIO + printk(KERN_DEBUG "i810_audio: ac97_set_mmio(0x%04X, %d)\n", (u32) data, ((int) reg_set) & 0xffff); +#endif +} + +static void i810_ac97_set_io(struct ac97_codec *dev, u8 reg, u16 data) +{ + struct i810_card *card = dev->private_data; + int count = 100; + u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f)); + while(count-- && (inb(card->iobase + CAS) & 1)) udelay(1); - outw(data, card->ac97base + reg_set); + + outw(data, card->ac97base + reg_set); +} + +static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg) +{ + struct i810_card *card = dev->private_data; + if (card->use_mmio) { + return i810_ac97_get_mmio(dev, reg); + } + else { + return i810_ac97_get_io(dev, reg); + } +} + +static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data) +{ + struct i810_card *card = dev->private_data; + if (card->use_mmio) { + i810_ac97_set_mmio(dev, reg, data); + } + else { + i810_ac97_set_io(dev, reg, data); + } } @@ -2523,7 +2608,7 @@ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/20); } - for (i = 0; i < NR_AC97 && card && !card->initializing; i++) + for (i = 0; i < NR_AC97 && card && !card->initializing; i++) if (card->ac97_codec[i] != NULL && card->ac97_codec[i]->dev_mixer == minor) { file->private_data = card->ac97_codec[i]; @@ -2551,10 +2636,18 @@ /* AC97 codec initialisation. These small functions exist so we don't duplicate code between module init and apm resume */ -static inline int i810_ac97_exists(struct i810_card *card,int ac97_number) +static inline int i810_ac97_exists(struct i810_card *card, int ac97_number) { u32 reg = inl(card->iobase + GLOB_STA); - return (reg & (0x100 << ac97_number)); + switch (ac97_number) { + case 0: + return reg & (1<<8); + case 1: + return reg & (1<<9); + case 2: + return reg & (1<<28); + } + return 0; } static inline int i810_ac97_enable_variable_rate(struct ac97_codec *codec) @@ -2577,6 +2670,7 @@ /* power it all up */ i810_ac97_set(codec, AC97_POWER_CONTROL, i810_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00); + /* wait for analog ready */ for (i=10; i && ((i810_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) != 0xf); i--) { @@ -2640,9 +2734,9 @@ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ); /* actually 600mS by the spec */ reg = inl(card->iobase + GLOB_STA); - if(reg & 0x100) + if(reg & 0x100) printk("OK\n"); - else + else printk("no response.\n"); } inw(card->ac97base); @@ -2653,6 +2747,7 @@ { int num_ac97 = 0; int total_channels = 0; + int ac97_count = 0; struct ac97_codec *codec; u16 eid; u32 reg; @@ -2678,10 +2773,14 @@ inw(card->ac97base); for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + + /* sigh, my mother board reports a tertiary codec (Intel D845GBV), + yet it's accessed as primary codec... (jsaw) */ + if (!d845gbv_bug) ac97_count = num_ac97; /* Assume codec isn't available until we go through the * gauntlet below */ - card->ac97_codec[num_ac97] = NULL; + card->ac97_codec[ac97_count] = NULL; /* The ICH programmer's reference says you should */ /* check the ready status before probing. So we chk */ @@ -2690,9 +2789,14 @@ if (!i810_ac97_exists(card,num_ac97)) { if(num_ac97 == 0) printk(KERN_ERR "i810_audio: Primary codec not ready.\n"); - break; /* I think this works, if not ready stop */ - } + /*@FIXME see comment about D845GBV */ + if (d845gbv_bug) + continue; + else + break; + } + if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) return -ENOMEM; memset(codec, 0, sizeof(struct ac97_codec)); @@ -2700,10 +2804,18 @@ /* initialize some basic codec information, other fields will be filled in ac97_probe_codec */ codec->private_data = card; - codec->id = num_ac97; - codec->codec_read = i810_ac97_get; - codec->codec_write = i810_ac97_set; + /*@FIXME see comment about D845GBV */ + codec->id = ac97_count; + + if (card->use_mmio) { + codec->codec_read = i810_ac97_get_mmio; + codec->codec_write = i810_ac97_set_mmio; + } + else { + codec->codec_read = i810_ac97_get_io; + codec->codec_write = i810_ac97_set_io; + } if(!i810_ac97_probe_and_powerup(card,codec)) { printk("i810_audio: timed out waiting for codec %d analog ready.\n", num_ac97); @@ -2728,6 +2840,8 @@ { printk(KERN_WARNING "i810_audio: codec %d is a softmodem - skipping.\n", num_ac97); kfree(codec); + /*@FIXME see comment about D845GBV */ + if (d845gbv_bug) ac97_count++; continue; } @@ -2814,13 +2928,15 @@ break; } - card->ac97_codec[num_ac97] = codec; + card->ac97_codec[ac97_count] = codec; + /*@FIXME see comment about D845GBV */ + if (d845gbv_bug) ac97_count++; } /* pick the minimum of channels supported by ICHx or codec(s) */ card->channels = (card->channels > total_channels)?total_channels:card->channels; - return num_ac97; + return ac97_count; } static void __init i810_configure_clocking (void) @@ -2915,10 +3031,29 @@ memset(card, 0, sizeof(*card)); card->initializing = 1; - card->iobase = pci_resource_start (pci_dev, 1); - card->ac97base = pci_resource_start (pci_dev, 0); card->pci_dev = pci_dev; card->pci_id = pci_id->device; + card->ac97base = pci_resource_start (pci_dev, 0); + card->iobase = pci_resource_start (pci_dev, 1); + + /* + ICH4 supports/needs direct memory access + to support more than 2 codecs + (jsaw) + */ + if ((pci_id->device == PCI_DEVICE_ID_INTEL_ICH4)) { + card->ac97base_mmio_phys = pci_resource_start (pci_dev, 2); + card->iobase_mmio_phys = pci_resource_start (pci_dev, 3); + + if ((card->ac97base_mmio_phys) && (card->iobase_mmio_phys)) { + card->use_mmio = 1; + } + else { + card->ac97base_mmio_phys = 0; + card->iobase_mmio_phys = 0; + } + } + card->irq = pci_dev->irq; card->next = devs; card->magic = I810_CARD_MAGIC; @@ -2930,8 +3065,11 @@ pci_set_master(pci_dev); - printk(KERN_INFO "i810: %s found at IO 0x%04lx and 0x%04lx, IRQ %d\n", - card_names[pci_id->driver_data], card->iobase, card->ac97base, + printk(KERN_INFO "i810: %s found at IO 0x%04lx and 0x%04lx, " + "MEM 0x%04lx and 0x%04lx, IRQ %d\n", + card_names[pci_id->driver_data], + card->iobase, card->ac97base, + card->ac97base_mmio_phys, card->iobase_mmio_phys, card->irq); card->alloc_pcm_channel = i810_alloc_pcm_channel; @@ -2961,10 +3099,45 @@ return -ENODEV; } + if (card->use_mmio) { + if (request_mem_region(card->ac97base_mmio_phys, 512, "ich_audio MMBAR")) { + if ((card->ac97base_mmio = ioremap(card->ac97base_mmio_phys, 512))) { /*@FIXME can ioremap fail? don't know (jsaw) */ + if (request_mem_region(card->iobase_mmio_phys, 256, "ich_audio MBBAR")) { + if ((card->iobase_mmio = ioremap(card->iobase_mmio_phys, 256))) { + printk(KERN_INFO "i810: %s mmio at 0x%04lx and 0x%04lx\n", + card_names[pci_id->driver_data], + (unsigned long) card->ac97base_mmio, + (unsigned long) card->iobase_mmio); + } + else { + iounmap(card->ac97base_mmio); + release_mem_region(card->ac97base_mmio_phys, 512); + release_mem_region(card->iobase_mmio_phys, 512); + card->use_mmio = 0; + } + } + else { + iounmap(card->ac97base_mmio); + release_mem_region(card->ac97base_mmio_phys, 512); + card->use_mmio = 0; + } + } + } + else { + card->use_mmio = 0; + } + } + /* initialize AC97 codec and register /dev/mixer */ if (i810_ac97_init(card) <= 0) { release_region(card->iobase, 64); release_region(card->ac97base, 256); + if (card->use_mmio) { + iounmap(card->ac97base_mmio); + iounmap(card->iobase_mmio); + release_mem_region(card->ac97base_mmio_phys, 512); + release_mem_region(card->iobase_mmio_phys, 256); + } free_irq(card->irq, card); kfree(card); return -ENODEV; @@ -2982,6 +3155,12 @@ printk(KERN_ERR "i810_audio: couldn't register DSP device!\n"); release_region(card->iobase, 64); release_region(card->ac97base, 256); + if (card->use_mmio) { + iounmap(card->ac97base_mmio); + iounmap(card->iobase_mmio); + release_mem_region(card->ac97base_mmio_phys, 512); + release_mem_region(card->iobase_mmio_phys, 256); + } free_irq(card->irq, card); for (i = 0; i < NR_AC97; i++) if (card->ac97_codec[i] != NULL) { @@ -2991,6 +3170,7 @@ kfree(card); return -ENODEV; } + card->initializing = 0; return 0; } @@ -3003,6 +3183,12 @@ free_irq(card->irq, devs); release_region(card->iobase, 64); release_region(card->ac97base, 256); + if (card->use_mmio) { + iounmap(card->ac97base_mmio); + iounmap(card->iobase_mmio); + release_mem_region(card->ac97base_mmio_phys, 512); + release_mem_region(card->iobase_mmio_phys, 256); + } /* unregister audio devices */ for (i = 0; i < NR_AC97; i++) @@ -3148,6 +3334,7 @@ MODULE_PARM(clocking, "i"); MODULE_PARM(strict_clocking, "i"); MODULE_PARM(spdif_locked, "i"); +MODULE_PARM(d845gbv_bug, "i"); #define I810_MODULE_NAME "intel810_audio"

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Thu Aug 15 2002 - 22:00:18 EST