diff options
Diffstat (limited to 'sound/aoa/soundbus/i2sbus')
-rw-r--r-- | sound/aoa/soundbus/i2sbus/i2sbus-control.c | 79 | ||||
-rw-r--r-- | sound/aoa/soundbus/i2sbus/i2sbus-control.h | 37 | ||||
-rw-r--r-- | sound/aoa/soundbus/i2sbus/i2sbus-core.c | 129 | ||||
-rw-r--r-- | sound/aoa/soundbus/i2sbus/i2sbus.h | 18 |
4 files changed, 151 insertions, 112 deletions
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-control.c b/sound/aoa/soundbus/i2sbus/i2sbus-control.c index f50407952d3..87beb4ad4d6 100644 --- a/sound/aoa/soundbus/i2sbus/i2sbus-control.c +++ b/sound/aoa/soundbus/i2sbus/i2sbus-control.c @@ -6,12 +6,16 @@ * GPL v2, can be found in COPYING. */ -#include <asm/io.h> +#include <linux/kernel.h> #include <linux/delay.h> + +#include <asm/io.h> #include <asm/prom.h> #include <asm/macio.h> #include <asm/pmac_feature.h> #include <asm/pmac_pfunc.h> +#include <asm/keylargo.h> + #include "i2sbus.h" int i2sbus_control_init(struct macio_dev* dev, struct i2sbus_control **c) @@ -22,26 +26,12 @@ int i2sbus_control_init(struct macio_dev* dev, struct i2sbus_control **c) INIT_LIST_HEAD(&(*c)->list); - if (of_address_to_resource(dev->ofdev.node, 0, &(*c)->rsrc)) - goto err; - /* we really should be using feature calls instead of mapping - * these registers. It's safe for now since no one else is - * touching them... */ - (*c)->controlregs = ioremap((*c)->rsrc.start, - sizeof(struct i2s_control_regs)); - if (!(*c)->controlregs) - goto err; - + (*c)->macio = dev->bus->chip; return 0; - err: - kfree(*c); - *c = NULL; - return -ENODEV; } void i2sbus_control_destroy(struct i2sbus_control *c) { - iounmap(c->controlregs); kfree(c); } @@ -93,19 +83,22 @@ int i2sbus_control_enable(struct i2sbus_control *c, struct i2sbus_dev *i2sdev) { struct pmf_args args = { .count = 0 }; - int cc; + struct macio_chip *macio = c->macio; if (i2sdev->enable) return pmf_call_one(i2sdev->enable, &args); + if (macio == NULL || macio->base == NULL) + return -ENODEV; + switch (i2sdev->bus_number) { case 0: - cc = in_le32(&c->controlregs->cell_control); - out_le32(&c->controlregs->cell_control, cc | CTRL_CLOCK_INTF_0_ENABLE); + /* these need to be locked or done through + * newly created feature calls! */ + MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_ENABLE); break; case 1: - cc = in_le32(&c->controlregs->cell_control); - out_le32(&c->controlregs->cell_control, cc | CTRL_CLOCK_INTF_1_ENABLE); + MACIO_BIS(KEYLARGO_FCR1, KL1_I2S1_ENABLE); break; default: return -ENODEV; @@ -118,7 +111,7 @@ int i2sbus_control_cell(struct i2sbus_control *c, int enable) { struct pmf_args args = { .count = 0 }; - int cc; + struct macio_chip *macio = c->macio; switch (enable) { case 0: @@ -133,18 +126,22 @@ int i2sbus_control_cell(struct i2sbus_control *c, printk(KERN_ERR "i2sbus: INVALID CELL ENABLE VALUE\n"); return -ENODEV; } + + if (macio == NULL || macio->base == NULL) + return -ENODEV; + switch (i2sdev->bus_number) { case 0: - cc = in_le32(&c->controlregs->cell_control); - cc &= ~CTRL_CLOCK_CELL_0_ENABLE; - cc |= enable * CTRL_CLOCK_CELL_0_ENABLE; - out_le32(&c->controlregs->cell_control, cc); + if (enable) + MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_CELL_ENABLE); + else + MACIO_BIC(KEYLARGO_FCR1, KL1_I2S0_CELL_ENABLE); break; case 1: - cc = in_le32(&c->controlregs->cell_control); - cc &= ~CTRL_CLOCK_CELL_1_ENABLE; - cc |= enable * CTRL_CLOCK_CELL_1_ENABLE; - out_le32(&c->controlregs->cell_control, cc); + if (enable) + MACIO_BIS(KEYLARGO_FCR1, KL1_I2S1_CELL_ENABLE); + else + MACIO_BIC(KEYLARGO_FCR1, KL1_I2S1_CELL_ENABLE); break; default: return -ENODEV; @@ -157,7 +154,7 @@ int i2sbus_control_clock(struct i2sbus_control *c, int enable) { struct pmf_args args = { .count = 0 }; - int cc; + struct macio_chip *macio = c->macio; switch (enable) { case 0: @@ -172,18 +169,22 @@ int i2sbus_control_clock(struct i2sbus_control *c, printk(KERN_ERR "i2sbus: INVALID CLOCK ENABLE VALUE\n"); return -ENODEV; } + + if (macio == NULL || macio->base == NULL) + return -ENODEV; + switch (i2sdev->bus_number) { case 0: - cc = in_le32(&c->controlregs->cell_control); - cc &= ~CTRL_CLOCK_CLOCK_0_ENABLE; - cc |= enable * CTRL_CLOCK_CLOCK_0_ENABLE; - out_le32(&c->controlregs->cell_control, cc); + if (enable) + MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_CLK_ENABLE_BIT); + else + MACIO_BIC(KEYLARGO_FCR1, KL1_I2S0_CLK_ENABLE_BIT); break; case 1: - cc = in_le32(&c->controlregs->cell_control); - cc &= ~CTRL_CLOCK_CLOCK_1_ENABLE; - cc |= enable * CTRL_CLOCK_CLOCK_1_ENABLE; - out_le32(&c->controlregs->cell_control, cc); + if (enable) + MACIO_BIS(KEYLARGO_FCR1, KL1_I2S1_CLK_ENABLE_BIT); + else + MACIO_BIC(KEYLARGO_FCR1, KL1_I2S1_CLK_ENABLE_BIT); break; default: return -ENODEV; diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-control.h b/sound/aoa/soundbus/i2sbus/i2sbus-control.h deleted file mode 100644 index bb05550f730..00000000000 --- a/sound/aoa/soundbus/i2sbus/i2sbus-control.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * i2sbus driver -- bus register definitions - * - * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> - * - * GPL v2, can be found in COPYING. - */ -#ifndef __I2SBUS_CONTROLREGS_H -#define __I2SBUS_CONTROLREGS_H - -/* i2s control registers, at least what we know about them */ - -#define __PAD(m,n) u8 __pad##m[n] -#define _PAD(line, n) __PAD(line, n) -#define PAD(n) _PAD(__LINE__, (n)) -struct i2s_control_regs { - PAD(0x38); - __le32 fcr0; /* 0x38 (unknown) */ - __le32 cell_control; /* 0x3c (fcr1) */ - __le32 fcr2; /* 0x40 (unknown) */ - __le32 fcr3; /* 0x44 (fcr3) */ - __le32 clock_control; /* 0x48 (unknown) */ - PAD(4); - /* total size: 0x50 bytes */ -} __attribute__((__packed__)); - -#define CTRL_CLOCK_CELL_0_ENABLE (1<<10) -#define CTRL_CLOCK_CLOCK_0_ENABLE (1<<12) -#define CTRL_CLOCK_SWRESET_0 (1<<11) -#define CTRL_CLOCK_INTF_0_ENABLE (1<<13) - -#define CTRL_CLOCK_CELL_1_ENABLE (1<<17) -#define CTRL_CLOCK_CLOCK_1_ENABLE (1<<18) -#define CTRL_CLOCK_SWRESET_1 (1<<19) -#define CTRL_CLOCK_INTF_1_ENABLE (1<<20) - -#endif /* __I2SBUS_CONTROLREGS_H */ diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-core.c b/sound/aoa/soundbus/i2sbus/i2sbus-core.c index 01c0724335a..23190aa6bc7 100644 --- a/sound/aoa/soundbus/i2sbus/i2sbus-core.c +++ b/sound/aoa/soundbus/i2sbus/i2sbus-core.c @@ -7,13 +7,16 @@ */ #include <linux/module.h> -#include <asm/macio.h> -#include <asm/dbdma.h> #include <linux/pci.h> #include <linux/interrupt.h> +#include <linux/dma-mapping.h> + #include <sound/driver.h> #include <sound/core.h> -#include <linux/dma-mapping.h> + +#include <asm/macio.h> +#include <asm/dbdma.h> + #include "../soundbus.h" #include "i2sbus.h" @@ -24,6 +27,11 @@ MODULE_DESCRIPTION("Apple Soundbus: I2S support"); * string that macio puts into the relevant device */ MODULE_ALIAS("of:Ni2sTi2sC"); +static int force; +module_param(force, int, 0444); +MODULE_PARM_DESC(force, "Force loading i2sbus even when" + " no layout-id property is present"); + static struct of_device_id i2sbus_match[] = { { .name = "i2s" }, { } @@ -73,12 +81,12 @@ static void i2sbus_release_dev(struct device *dev) if (i2sdev->intfregs) iounmap(i2sdev->intfregs); if (i2sdev->out.dbdma) iounmap(i2sdev->out.dbdma); if (i2sdev->in.dbdma) iounmap(i2sdev->in.dbdma); - for (i=0;i<3;i++) + for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) if (i2sdev->allocated_resource[i]) release_and_free_resource(i2sdev->allocated_resource[i]); free_dbdma_descriptor_ring(i2sdev, &i2sdev->out.dbdma_ring); free_dbdma_descriptor_ring(i2sdev, &i2sdev->in.dbdma_ring); - for (i=0;i<3;i++) + for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) free_irq(i2sdev->interrupts[i], i2sdev); i2sbus_control_remove_dev(i2sdev->control, i2sdev); mutex_destroy(&i2sdev->lock); @@ -101,10 +109,49 @@ static irqreturn_t i2sbus_bus_intr(int irq, void *devid, struct pt_regs *regs) return IRQ_HANDLED; } -static int force; -module_param(force, int, 0444); -MODULE_PARM_DESC(force, "Force loading i2sbus even when" - " no layout-id property is present"); + +/* + * XXX FIXME: We test the layout_id's here to get the proper way of + * mapping in various registers, thanks to bugs in Apple device-trees. + * We could instead key off the machine model and the name of the i2s + * node (i2s-a). This we'll do when we move it all to macio_asic.c + * and have that export items for each sub-node too. + */ +static int i2sbus_get_and_fixup_rsrc(struct device_node *np, int index, + int layout, struct resource *res) +{ + struct device_node *parent; + int pindex, rc = -ENXIO; + u32 *reg; + + /* Machines with layout 76 and 36 (K2 based) have a weird device + * tree what we need to special case. + * Normal machines just fetch the resource from the i2s-X node. + * Darwin further divides normal machines into old and new layouts + * with a subtely different code path but that doesn't seem necessary + * in practice, they just bloated it. In addition, even on our K2 + * case the i2s-modem node, if we ever want to handle it, uses the + * normal layout + */ + if (layout != 76 && layout != 36) + return of_address_to_resource(np, index, res); + + parent = of_get_parent(np); + pindex = (index == aoa_resource_i2smmio) ? 0 : 1; + rc = of_address_to_resource(parent, pindex, res); + if (rc) + goto bail; + reg = (u32 *)get_property(np, "reg", NULL); + if (reg == NULL) { + rc = -ENXIO; + goto bail; + } + res->start += reg[index * 2]; + res->end = res->start + reg[index * 2 + 1] - 1; + bail: + of_node_put(parent); + return rc; +} /* FIXME: look at device node refcounting */ static int i2sbus_add_dev(struct macio_dev *macio, @@ -113,7 +160,8 @@ static int i2sbus_add_dev(struct macio_dev *macio, { struct i2sbus_dev *dev; struct device_node *child = NULL, *sound = NULL; - int i; + struct resource *r; + int i, layout = 0, rlen; static const char *rnames[] = { "i2sbus: %s (control)", "i2sbus: %s (tx)", "i2sbus: %s (rx)" }; @@ -129,9 +177,6 @@ static int i2sbus_add_dev(struct macio_dev *macio, if (strncmp(np->name, "i2s-", 4)) return 0; - if (macio_irq_count(macio) != 3) - return 0; - dev = kzalloc(sizeof(struct i2sbus_dev), GFP_KERNEL); if (!dev) return 0; @@ -147,8 +192,9 @@ static int i2sbus_add_dev(struct macio_dev *macio, u32 *layout_id; layout_id = (u32*) get_property(sound, "layout-id", NULL); if (layout_id) { + layout = *layout_id; snprintf(dev->sound.modalias, 32, - "sound-layout-%d", *layout_id); + "sound-layout-%d", layout); force = 1; } } @@ -178,23 +224,32 @@ static int i2sbus_add_dev(struct macio_dev *macio, dev->bus_number = np->name[4] - 'a'; INIT_LIST_HEAD(&dev->sound.codec_list); - for (i=0;i<3;i++) { + for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) { dev->interrupts[i] = -1; - snprintf(dev->rnames[i], sizeof(dev->rnames[i]), rnames[i], np->name); + snprintf(dev->rnames[i], sizeof(dev->rnames[i]), + rnames[i], np->name); } - for (i=0;i<3;i++) { - if (request_irq(macio_irq(macio, i), ints[i], 0, - dev->rnames[i], dev)) + for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) { + int irq = irq_of_parse_and_map(np, i); + if (request_irq(irq, ints[i], 0, dev->rnames[i], dev)) goto err; - dev->interrupts[i] = macio_irq(macio, i); + dev->interrupts[i] = irq; } - for (i=0;i<3;i++) { - if (of_address_to_resource(np, i, &dev->resources[i])) + + /* Resource handling is problematic as some device-trees contain + * useless crap (ugh ugh ugh). We work around that here by calling + * specific functions for calculating the appropriate resources. + * + * This will all be moved to macio_asic.c at one point + */ + for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) { + if (i2sbus_get_and_fixup_rsrc(np,i,layout,&dev->resources[i])) goto err; - /* if only we could use our resource dev->resources[i]... + /* If only we could use our resource dev->resources[i]... * but request_resource doesn't know about parents and - * contained resources... */ + * contained resources... + */ dev->allocated_resource[i] = request_mem_region(dev->resources[i].start, dev->resources[i].end - @@ -205,13 +260,25 @@ static int i2sbus_add_dev(struct macio_dev *macio, goto err; } } - /* should do sanity checking here about length of them */ - dev->intfregs = ioremap(dev->resources[0].start, - dev->resources[0].end-dev->resources[0].start+1); - dev->out.dbdma = ioremap(dev->resources[1].start, - dev->resources[1].end-dev->resources[1].start+1); - dev->in.dbdma = ioremap(dev->resources[2].start, - dev->resources[2].end-dev->resources[2].start+1); + + r = &dev->resources[aoa_resource_i2smmio]; + rlen = r->end - r->start + 1; + if (rlen < sizeof(struct i2s_interface_regs)) + goto err; + dev->intfregs = ioremap(r->start, rlen); + + r = &dev->resources[aoa_resource_txdbdma]; + rlen = r->end - r->start + 1; + if (rlen < sizeof(struct dbdma_regs)) + goto err; + dev->out.dbdma = ioremap(r->start, rlen); + + r = &dev->resources[aoa_resource_rxdbdma]; + rlen = r->end - r->start + 1; + if (rlen < sizeof(struct dbdma_regs)) + goto err; + dev->in.dbdma = ioremap(r->start, rlen); + if (!dev->intfregs || !dev->out.dbdma || !dev->in.dbdma) goto err; diff --git a/sound/aoa/soundbus/i2sbus/i2sbus.h b/sound/aoa/soundbus/i2sbus/i2sbus.h index cfa5162e3b0..0c69d209be5 100644 --- a/sound/aoa/soundbus/i2sbus/i2sbus.h +++ b/sound/aoa/soundbus/i2sbus/i2sbus.h @@ -7,20 +7,22 @@ */ #ifndef __I2SBUS_H #define __I2SBUS_H -#include <asm/dbdma.h> #include <linux/interrupt.h> -#include <sound/pcm.h> #include <linux/spinlock.h> #include <linux/mutex.h> + +#include <sound/pcm.h> + #include <asm/prom.h> +#include <asm/pmac_feature.h> +#include <asm/dbdma.h> + #include "i2sbus-interface.h" -#include "i2sbus-control.h" #include "../soundbus.h" struct i2sbus_control { - volatile struct i2s_control_regs __iomem *controlregs; - struct resource rsrc; struct list_head list; + struct macio_chip *macio; }; #define MAX_DBDMA_COMMANDS 32 @@ -45,6 +47,12 @@ struct pcm_info { volatile struct dbdma_regs __iomem *dbdma; }; +enum { + aoa_resource_i2smmio = 0, + aoa_resource_txdbdma, + aoa_resource_rxdbdma, +}; + struct i2sbus_dev { struct soundbus_dev sound; struct macio_dev *macio; |