lkml.org 
[lkml]   [2002]   [Aug]   [8]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patches in this message
/
Subject[PATCH] make i810_audio mmio aware/support for >2 codecs
From
Date
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"
\
 
 \ /
  Last update: 2005-03-22 13:27    [W:0.047 / U:0.696 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site