diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2012-05-15 17:21:26 +1000 |
---|---|---|
committer | Herbert Xu <herbert@gondor.apana.org.au> | 2012-05-15 17:21:26 +1000 |
commit | b29e2679d0da91c60d3ac190d9c3bd65ac2f68c5 (patch) | |
tree | 518ec8f4bfd316e91bcc30f434edb78529557eba /drivers | |
parent | 8a63b1994c500d4825ee73dc71502deffe5b135b (diff) | |
parent | 4bb2d1009f671815870e8f78e826e4f9071392a7 (diff) | |
download | lwn-b29e2679d0da91c60d3ac190d9c3bd65ac2f68c5.tar.gz lwn-b29e2679d0da91c60d3ac190d9c3bd65ac2f68c5.zip |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux
Merge mainline to add prerequisite for ARM ux500 crypto support.
Diffstat (limited to 'drivers')
891 files changed, 63062 insertions, 23290 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index decf8e420856..6f0459cb745b 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -130,6 +130,10 @@ source "drivers/clocksource/Kconfig" source "drivers/iommu/Kconfig" +source "drivers/remoteproc/Kconfig" + +source "drivers/rpmsg/Kconfig" + source "drivers/virt/Kconfig" source "drivers/devfreq/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 932e8bf20356..262b19d6b627 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -125,6 +125,8 @@ obj-y += clk/ obj-$(CONFIG_HWSPINLOCK) += hwspinlock/ obj-$(CONFIG_NFC) += nfc/ obj-$(CONFIG_IOMMU_SUPPORT) += iommu/ +obj-$(CONFIG_REMOTEPROC) += remoteproc/ +obj-$(CONFIG_RPMSG) += rpmsg/ # Virtualization drivers obj-$(CONFIG_VIRT_DRIVERS) += virt/ diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 54eaf96ab217..01c2cf4efcdd 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -497,37 +497,22 @@ static void amba_device_release(struct device *dev) } /** - * amba_device_register - register an AMBA device - * @dev: AMBA device to register - * @parent: parent memory resource + * amba_device_add - add a previously allocated AMBA device structure + * @dev: AMBA device allocated by amba_device_alloc + * @parent: resource parent for this devices resources * - * Setup the AMBA device, reading the cell ID if present. - * Claim the resource, and register the AMBA device with - * the Linux device manager. + * Claim the resource, and read the device cell ID if not already + * initialized. Register the AMBA device with the Linux device + * manager. */ -int amba_device_register(struct amba_device *dev, struct resource *parent) +int amba_device_add(struct amba_device *dev, struct resource *parent) { u32 size; void __iomem *tmp; int i, ret; - device_initialize(&dev->dev); - - /* - * Copy from device_add - */ - if (dev->dev.init_name) { - dev_set_name(&dev->dev, "%s", dev->dev.init_name); - dev->dev.init_name = NULL; - } - - dev->dev.release = amba_device_release; - dev->dev.bus = &amba_bustype; - dev->dev.dma_mask = &dev->dma_mask; - dev->res.name = dev_name(&dev->dev); - - if (!dev->dev.coherent_dma_mask && dev->dma_mask) - dev_warn(&dev->dev, "coherent dma mask is unset\n"); + WARN_ON(dev->irq[0] == (unsigned int)-1); + WARN_ON(dev->irq[1] == (unsigned int)-1); ret = request_resource(parent, &dev->res); if (ret) @@ -582,9 +567,9 @@ int amba_device_register(struct amba_device *dev, struct resource *parent) if (ret) goto err_release; - if (dev->irq[0] != NO_IRQ) + if (dev->irq[0] && dev->irq[0] != NO_IRQ) ret = device_create_file(&dev->dev, &dev_attr_irq0); - if (ret == 0 && dev->irq[1] != NO_IRQ) + if (ret == 0 && dev->irq[1] && dev->irq[1] != NO_IRQ) ret = device_create_file(&dev->dev, &dev_attr_irq1); if (ret == 0) return ret; @@ -596,6 +581,74 @@ int amba_device_register(struct amba_device *dev, struct resource *parent) err_out: return ret; } +EXPORT_SYMBOL_GPL(amba_device_add); + +static void amba_device_initialize(struct amba_device *dev, const char *name) +{ + device_initialize(&dev->dev); + if (name) + dev_set_name(&dev->dev, "%s", name); + dev->dev.release = amba_device_release; + dev->dev.bus = &amba_bustype; + dev->dev.dma_mask = &dev->dma_mask; + dev->res.name = dev_name(&dev->dev); +} + +/** + * amba_device_alloc - allocate an AMBA device + * @name: sysfs name of the AMBA device + * @base: base of AMBA device + * @size: size of AMBA device + * + * Allocate and initialize an AMBA device structure. Returns %NULL + * on failure. + */ +struct amba_device *amba_device_alloc(const char *name, resource_size_t base, + size_t size) +{ + struct amba_device *dev; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev) { + amba_device_initialize(dev, name); + dev->res.start = base; + dev->res.end = base + size - 1; + dev->res.flags = IORESOURCE_MEM; + } + + return dev; +} +EXPORT_SYMBOL_GPL(amba_device_alloc); + +/** + * amba_device_register - register an AMBA device + * @dev: AMBA device to register + * @parent: parent memory resource + * + * Setup the AMBA device, reading the cell ID if present. + * Claim the resource, and register the AMBA device with + * the Linux device manager. + */ +int amba_device_register(struct amba_device *dev, struct resource *parent) +{ + amba_device_initialize(dev, dev->dev.init_name); + dev->dev.init_name = NULL; + + if (!dev->dev.coherent_dma_mask && dev->dma_mask) + dev_warn(&dev->dev, "coherent dma mask is unset\n"); + + return amba_device_add(dev, parent); +} + +/** + * amba_device_put - put an AMBA device + * @dev: AMBA device to put + */ +void amba_device_put(struct amba_device *dev) +{ + put_device(&dev->dev); +} +EXPORT_SYMBOL_GPL(amba_device_put); /** * amba_device_unregister - unregister an AMBA device diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index d07bf0366d99..79a1e9dd56d9 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -103,8 +103,6 @@ static struct ata_port_operations ahci_p5wdh_ops = { .hardreset = ahci_p5wdh_hardreset, }; -#define AHCI_HFLAGS(flags) .private_data = (void *)(flags) - static const struct ata_port_info ahci_port_info[] = { /* by features */ [board_ahci] = @@ -261,6 +259,14 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x1e06), board_ahci }, /* Panther Point RAID */ { PCI_VDEVICE(INTEL, 0x1e07), board_ahci }, /* Panther Point RAID */ { PCI_VDEVICE(INTEL, 0x1e0e), board_ahci }, /* Panther Point RAID */ + { PCI_VDEVICE(INTEL, 0x8c02), board_ahci }, /* Lynx Point AHCI */ + { PCI_VDEVICE(INTEL, 0x8c03), board_ahci }, /* Lynx Point AHCI */ + { PCI_VDEVICE(INTEL, 0x8c04), board_ahci }, /* Lynx Point RAID */ + { PCI_VDEVICE(INTEL, 0x8c05), board_ahci }, /* Lynx Point RAID */ + { PCI_VDEVICE(INTEL, 0x8c06), board_ahci }, /* Lynx Point RAID */ + { PCI_VDEVICE(INTEL, 0x8c07), board_ahci }, /* Lynx Point RAID */ + { PCI_VDEVICE(INTEL, 0x8c0e), board_ahci }, /* Lynx Point RAID */ + { PCI_VDEVICE(INTEL, 0x8c0f), board_ahci }, /* Lynx Point RAID */ /* JMicron 360/1/3/5/6, match class to avoid IDE function */ { PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index b1750007c8dc..c2594ddf25b0 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -195,6 +195,9 @@ enum { PORT_FBS_EN = (1 << 0), /* Enable FBS */ /* hpriv->flags bits */ + +#define AHCI_HFLAGS(flags) .private_data = (void *)(flags) + AHCI_HFLAG_NO_NCQ = (1 << 0), AHCI_HFLAG_IGN_IRQ_IF_ERR = (1 << 1), /* ignore IRQ_IF_ERR */ AHCI_HFLAG_IGN_SERR_INTERNAL = (1 << 2), /* ignore SERR_INTERNAL */ @@ -210,6 +213,9 @@ enum { AHCI_HFLAG_NO_SNTF = (1 << 12), /* no sntf */ AHCI_HFLAG_NO_FPDMA_AA = (1 << 13), /* no FPDMA AA */ AHCI_HFLAG_YES_FBS = (1 << 14), /* force FBS cap on */ + AHCI_HFLAG_DELAY_ENGINE = (1 << 15), /* do not start engine on + port start (wait until + error-handling stage) */ /* ap->flags bits */ diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 48be4e189163..0c86c77764bc 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -26,6 +26,7 @@ enum ahci_type { AHCI, /* standard platform ahci */ IMX53_AHCI, /* ahci on i.mx53 */ + STRICT_AHCI, /* delayed DMA engine start */ }; static struct platform_device_id ahci_devtype[] = { @@ -36,6 +37,9 @@ static struct platform_device_id ahci_devtype[] = { .name = "imx53-ahci", .driver_data = IMX53_AHCI, }, { + .name = "strict-ahci", + .driver_data = STRICT_AHCI, + }, { /* sentinel */ } }; @@ -56,6 +60,13 @@ static const struct ata_port_info ahci_port_info[] = { .udma_mask = ATA_UDMA6, .port_ops = &ahci_pmp_retry_srst_ops, }, + [STRICT_AHCI] = { + AHCI_HFLAGS (AHCI_HFLAG_DELAY_ENGINE), + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_ops, + }, }; static struct scsi_host_template ahci_platform_sht = { diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index fdf27b9fce43..68013f96729f 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -321,6 +321,14 @@ static const struct pci_device_id piix_pci_tbl[] = { { 0x8086, 0x1e08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata }, /* SATA Controller IDE (Panther Point) */ { 0x8086, 0x1e09, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata }, + /* SATA Controller IDE (Lynx Point) */ + { 0x8086, 0x8c00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_snb }, + /* SATA Controller IDE (Lynx Point) */ + { 0x8086, 0x8c01, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_snb }, + /* SATA Controller IDE (Lynx Point) */ + { 0x8086, 0x8c08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata }, + /* SATA Controller IDE (Lynx Point) */ + { 0x8086, 0x8c09, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata }, { } /* terminate list */ }; diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index a72bfd0ecfee..f9eaa82311a9 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -737,6 +737,7 @@ static void ahci_power_down(struct ata_port *ap) static void ahci_start_port(struct ata_port *ap) { + struct ahci_host_priv *hpriv = ap->host->private_data; struct ahci_port_priv *pp = ap->private_data; struct ata_link *link; struct ahci_em_priv *emp; @@ -746,6 +747,10 @@ static void ahci_start_port(struct ata_port *ap) /* enable FIS reception */ ahci_start_fis_rx(ap); + /* enable DMA */ + if (!(hpriv->flags & AHCI_HFLAG_DELAY_ENGINE)) + ahci_start_engine(ap); + /* turn on LEDs */ if (ap->flags & ATA_FLAG_EM) { ata_for_each_link(link, ap, EDGE) { diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c index 048589fad2ca..fc2db2a89a6b 100644 --- a/drivers/ata/pata_arasan_cf.c +++ b/drivers/ata/pata_arasan_cf.c @@ -925,11 +925,10 @@ static int arasan_cf_suspend(struct device *dev) struct ata_host *host = dev_get_drvdata(dev); struct arasan_cf_dev *acdev = host->ports[0]->private_data; - if (acdev->dma_chan) { + if (acdev->dma_chan) acdev->dma_chan->device->device_control(acdev->dma_chan, DMA_TERMINATE_ALL, 0); - dma_release_channel(acdev->dma_chan); - } + cf_exit(acdev); return ata_host_suspend(host, PMSG_SUSPEND); } @@ -945,10 +944,7 @@ static int arasan_cf_resume(struct device *dev) return 0; } -static const struct dev_pm_ops arasan_cf_pm_ops = { - .suspend = arasan_cf_suspend, - .resume = arasan_cf_resume, -}; +static SIMPLE_DEV_PM_OPS(arasan_cf_pm_ops, arasan_cf_suspend, arasan_cf_resume); #endif static struct platform_driver arasan_cf_driver = { @@ -958,7 +954,7 @@ static struct platform_driver arasan_cf_driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, #ifdef CONFIG_PM - .pm = &arasan_cf_pm_ops, + .pm = &arasan_cf_pm_ops, #endif }, }; diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c index e1fb39a74ce1..1c17cd1e8b2d 100644 --- a/drivers/ata/pata_cmd64x.c +++ b/drivers/ata/pata_cmd64x.c @@ -3,6 +3,7 @@ * (C) 2005 Red Hat Inc * Alan Cox <alan@lxorguk.ukuu.org.uk> * (C) 2009-2010 Bartlomiej Zolnierkiewicz + * (C) 2012 MontaVista Software, LLC <source@mvista.com> * * Based upon * linux/drivers/ide/pci/cmd64x.c Version 1.30 Sept 10, 2002 @@ -32,7 +33,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_cmd64x" -#define DRV_VERSION "0.2.5" +#define DRV_VERSION "0.2.18" /* * CMD64x specific registers definition. @@ -229,28 +230,85 @@ static void cmd64x_set_dmamode(struct ata_port *ap, struct ata_device *adev) } /** - * cmd648_dma_stop - DMA stop callback - * @qc: Command in progress + * cmd64x_sff_irq_check - check IDE interrupt + * @ap: ATA interface * - * DMA has completed. + * Check IDE interrupt in CFR/ARTTIM23 registers. */ -static void cmd648_bmdma_stop(struct ata_queued_cmd *qc) +static bool cmd64x_sff_irq_check(struct ata_port *ap) { - struct ata_port *ap = qc->ap; struct pci_dev *pdev = to_pci_dev(ap->host->dev); - u8 dma_intr; - int dma_mask = ap->port_no ? ARTTIM23_INTR_CH1 : CFR_INTR_CH0; - int dma_reg = ap->port_no ? ARTTIM23 : CFR; + int irq_mask = ap->port_no ? ARTTIM23_INTR_CH1 : CFR_INTR_CH0; + int irq_reg = ap->port_no ? ARTTIM23 : CFR; + u8 irq_stat; - ata_bmdma_stop(qc); + /* NOTE: reading the register should clear the interrupt */ + pci_read_config_byte(pdev, irq_reg, &irq_stat); + + return irq_stat & irq_mask; +} + +/** + * cmd64x_sff_irq_clear - clear IDE interrupt + * @ap: ATA interface + * + * Clear IDE interrupt in CFR/ARTTIM23 and DMA status registers. + */ + +static void cmd64x_sff_irq_clear(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + int irq_reg = ap->port_no ? ARTTIM23 : CFR; + u8 irq_stat; + + ata_bmdma_irq_clear(ap); - pci_read_config_byte(pdev, dma_reg, &dma_intr); - pci_write_config_byte(pdev, dma_reg, dma_intr | dma_mask); + /* Reading the register should be enough to clear the interrupt */ + pci_read_config_byte(pdev, irq_reg, &irq_stat); } /** - * cmd646r1_dma_stop - DMA stop callback + * cmd648_sff_irq_check - check IDE interrupt + * @ap: ATA interface + * + * Check IDE interrupt in MRDMODE register. + */ + +static bool cmd648_sff_irq_check(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + unsigned long base = pci_resource_start(pdev, 4); + int irq_mask = ap->port_no ? MRDMODE_INTR_CH1 : MRDMODE_INTR_CH0; + u8 mrdmode = inb(base + 1); + + return mrdmode & irq_mask; +} + +/** + * cmd648_sff_irq_clear - clear IDE interrupt + * @ap: ATA interface + * + * Clear IDE interrupt in MRDMODE and DMA status registers. + */ + +static void cmd648_sff_irq_clear(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + unsigned long base = pci_resource_start(pdev, 4); + int irq_mask = ap->port_no ? MRDMODE_INTR_CH1 : MRDMODE_INTR_CH0; + u8 mrdmode; + + ata_bmdma_irq_clear(ap); + + /* Clear this port's interrupt bit (leaving the other port alone) */ + mrdmode = inb(base + 1); + mrdmode &= ~(MRDMODE_INTR_CH0 | MRDMODE_INTR_CH1); + outb(mrdmode | irq_mask, base + 1); +} + +/** + * cmd646r1_bmdma_stop - DMA stop callback * @qc: Command in progress * * Stub for now while investigating the r1 quirk in the old driver. @@ -273,18 +331,30 @@ static const struct ata_port_operations cmd64x_base_ops = { static struct ata_port_operations cmd64x_port_ops = { .inherits = &cmd64x_base_ops, + .sff_irq_check = cmd64x_sff_irq_check, + .sff_irq_clear = cmd64x_sff_irq_clear, .cable_detect = ata_cable_40wire, }; static struct ata_port_operations cmd646r1_port_ops = { .inherits = &cmd64x_base_ops, + .sff_irq_check = cmd64x_sff_irq_check, + .sff_irq_clear = cmd64x_sff_irq_clear, .bmdma_stop = cmd646r1_bmdma_stop, .cable_detect = ata_cable_40wire, }; +static struct ata_port_operations cmd646r3_port_ops = { + .inherits = &cmd64x_base_ops, + .sff_irq_check = cmd648_sff_irq_check, + .sff_irq_clear = cmd648_sff_irq_clear, + .cable_detect = ata_cable_40wire, +}; + static struct ata_port_operations cmd648_port_ops = { .inherits = &cmd64x_base_ops, - .bmdma_stop = cmd648_bmdma_stop, + .sff_irq_check = cmd648_sff_irq_check, + .sff_irq_clear = cmd648_sff_irq_clear, .cable_detect = cmd648_cable_detect, }; @@ -306,7 +376,7 @@ static void cmd64x_fixup(struct pci_dev *pdev) static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) { - static const struct ata_port_info cmd_info[6] = { + static const struct ata_port_info cmd_info[7] = { { /* CMD 643 - no UDMA */ .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, @@ -319,12 +389,18 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) .mwdma_mask = ATA_MWDMA2, .port_ops = &cmd64x_port_ops }, - { /* CMD 646 with working UDMA */ + { /* CMD 646U with broken UDMA */ + .flags = ATA_FLAG_SLAVE_POSS, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .port_ops = &cmd646r3_port_ops + }, + { /* CMD 646U2 with working UDMA */ .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA2, - .port_ops = &cmd64x_port_ops + .port_ops = &cmd646r3_port_ops }, { /* CMD 646 rev 1 */ .flags = ATA_FLAG_SLAVE_POSS, @@ -368,21 +444,30 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) if (id->driver_data == 0) /* 643 */ ata_pci_bmdma_clear_simplex(pdev); - if (pdev->device == PCI_DEVICE_ID_CMD_646) { - /* Does UDMA work ? */ - if (pdev->revision > 4) { - ppi[0] = &cmd_info[2]; - ppi[1] = &cmd_info[2]; - } - /* Early rev with other problems ? */ - else if (pdev->revision == 1) { + if (pdev->device == PCI_DEVICE_ID_CMD_646) + switch (pdev->revision) { + /* UDMA works since rev 5 */ + default: ppi[0] = &cmd_info[3]; ppi[1] = &cmd_info[3]; - } - /* revs 1,2 have no CNTRL_CH0 */ - if (pdev->revision < 3) + break; + /* Interrupts in MRDMODE since rev 3 */ + case 3: + case 4: + ppi[0] = &cmd_info[2]; + ppi[1] = &cmd_info[2]; + break; + /* Rev 1 with other problems? */ + case 1: + ppi[0] = &cmd_info[4]; + ppi[1] = &cmd_info[4]; + /* FALL THRU */ + /* Early revs have no CNTRL_CH0 */ + case 2: + case 0: cntrl_ch0_ok = 0; - } + break; + } cmd64x_fixup(pdev); @@ -423,8 +508,8 @@ static int cmd64x_reinit_one(struct pci_dev *pdev) static const struct pci_device_id cmd64x[] = { { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_643), 0 }, { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_646), 1 }, - { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_648), 4 }, - { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_649), 5 }, + { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_648), 5 }, + { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_649), 6 }, { }, }; diff --git a/drivers/ata/pata_legacy.c b/drivers/ata/pata_legacy.c index 35aca7d1a3eb..4fe9d2138d48 100644 --- a/drivers/ata/pata_legacy.c +++ b/drivers/ata/pata_legacy.c @@ -401,8 +401,7 @@ static void ht6560b_set_piomode(struct ata_port *ap, struct ata_device *adev) ata_timing_compute(adev, adev->pio_mode, &t, 20000, 1000); active = clamp_val(t.active, 2, 15); - recover = clamp_val(t.recover, 2, 16); - recover &= 0x15; + recover = clamp_val(t.recover, 2, 16) & 0x0F; inb(0x3E6); inb(0x3E6); diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c index 00748ae1a016..d2c102fd4330 100644 --- a/drivers/ata/pata_mpc52xx.c +++ b/drivers/ata/pata_mpc52xx.c @@ -687,11 +687,11 @@ mpc52xx_ata_probe(struct platform_device *op) int ata_irq = 0; struct mpc52xx_ata __iomem *ata_regs; struct mpc52xx_ata_priv *priv = NULL; - int rv, ret, task_irq = 0; + int rv, task_irq; int mwdma_mask = 0, udma_mask = 0; const __be32 *prop; int proplen; - struct bcom_task *dmatsk = NULL; + struct bcom_task *dmatsk; /* Get ipb frequency */ ipb_freq = mpc5xxx_get_bus_frequency(op->dev.of_node); @@ -717,8 +717,7 @@ mpc52xx_ata_probe(struct platform_device *op) ata_regs = devm_ioremap(&op->dev, res_mem.start, sizeof(*ata_regs)); if (!ata_regs) { dev_err(&op->dev, "error mapping device registers\n"); - rv = -ENOMEM; - goto err; + return -ENOMEM; } /* @@ -753,7 +752,7 @@ mpc52xx_ata_probe(struct platform_device *op) if (!priv) { dev_err(&op->dev, "error allocating private structure\n"); rv = -ENOMEM; - goto err; + goto err1; } priv->ipb_period = 1000000000 / (ipb_freq / 1000); @@ -776,15 +775,15 @@ mpc52xx_ata_probe(struct platform_device *op) if (!dmatsk) { dev_err(&op->dev, "bestcomm initialization failed\n"); rv = -ENOMEM; - goto err; + goto err1; } task_irq = bcom_get_task_irq(dmatsk); - ret = request_irq(task_irq, &mpc52xx_ata_task_irq, 0, + rv = devm_request_irq(&op->dev, task_irq, &mpc52xx_ata_task_irq, 0, "ATA task", priv); - if (ret) { + if (rv) { dev_err(&op->dev, "error requesting DMA IRQ\n"); - goto err; + goto err2; } priv->dmatsk = dmatsk; @@ -792,7 +791,7 @@ mpc52xx_ata_probe(struct platform_device *op) rv = mpc52xx_ata_hw_init(priv); if (rv) { dev_err(&op->dev, "error initializing hardware\n"); - goto err; + goto err2; } /* Register ourselves to libata */ @@ -800,23 +799,16 @@ mpc52xx_ata_probe(struct platform_device *op) mwdma_mask, udma_mask); if (rv) { dev_err(&op->dev, "error registering with ATA layer\n"); - goto err; + goto err2; } return 0; - err: - devm_release_mem_region(&op->dev, res_mem.start, sizeof(*ata_regs)); - if (ata_irq) - irq_dispose_mapping(ata_irq); - if (task_irq) - irq_dispose_mapping(task_irq); - if (dmatsk) - bcom_ata_release(dmatsk); - if (ata_regs) - devm_iounmap(&op->dev, ata_regs); - if (priv) - devm_kfree(&op->dev, priv); + err2: + irq_dispose_mapping(task_irq); + bcom_ata_release(dmatsk); + err1: + irq_dispose_mapping(ata_irq); return rv; } @@ -835,12 +827,6 @@ mpc52xx_ata_remove(struct platform_device *op) bcom_ata_release(priv->dmatsk); irq_dispose_mapping(priv->ata_irq); - /* Clear up IO allocations */ - devm_iounmap(&op->dev, priv->ata_regs); - devm_release_mem_region(&op->dev, priv->ata_regs_pa, - sizeof(*priv->ata_regs)); - devm_kfree(&op->dev, priv); - return 0; } diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 0120b0d1e9a5..d6577b93bee3 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -6,7 +6,7 @@ * Author: Ashish Kalra <ashish.kalra@freescale.com> * Li Yang <leoli@freescale.com> * - * Copyright (c) 2006-2007, 2011 Freescale Semiconductor, Inc. + * Copyright (c) 2006-2007, 2011-2012 Freescale Semiconductor, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -26,6 +26,15 @@ #include <asm/io.h> #include <linux/of_platform.h> +static unsigned int intr_coalescing_count; +module_param(intr_coalescing_count, int, S_IRUGO); +MODULE_PARM_DESC(intr_coalescing_count, + "INT coalescing count threshold (1..31)"); + +static unsigned int intr_coalescing_ticks; +module_param(intr_coalescing_ticks, int, S_IRUGO); +MODULE_PARM_DESC(intr_coalescing_ticks, + "INT coalescing timer threshold in AHB ticks"); /* Controller information */ enum { SATA_FSL_QUEUE_DEPTH = 16, @@ -83,6 +92,16 @@ enum { }; /* + * Interrupt Coalescing Control Register bitdefs */ +enum { + ICC_MIN_INT_COUNT_THRESHOLD = 1, + ICC_MAX_INT_COUNT_THRESHOLD = ((1 << 5) - 1), + ICC_MIN_INT_TICKS_THRESHOLD = 0, + ICC_MAX_INT_TICKS_THRESHOLD = ((1 << 19) - 1), + ICC_SAFE_INT_TICKS = 1, +}; + +/* * Host Controller command register set - per port */ enum { @@ -263,8 +282,65 @@ struct sata_fsl_host_priv { void __iomem *csr_base; int irq; int data_snoop; + struct device_attribute intr_coalescing; }; +static void fsl_sata_set_irq_coalescing(struct ata_host *host, + unsigned int count, unsigned int ticks) +{ + struct sata_fsl_host_priv *host_priv = host->private_data; + void __iomem *hcr_base = host_priv->hcr_base; + + if (count > ICC_MAX_INT_COUNT_THRESHOLD) + count = ICC_MAX_INT_COUNT_THRESHOLD; + else if (count < ICC_MIN_INT_COUNT_THRESHOLD) + count = ICC_MIN_INT_COUNT_THRESHOLD; + + if (ticks > ICC_MAX_INT_TICKS_THRESHOLD) + ticks = ICC_MAX_INT_TICKS_THRESHOLD; + else if ((ICC_MIN_INT_TICKS_THRESHOLD == ticks) && + (count > ICC_MIN_INT_COUNT_THRESHOLD)) + ticks = ICC_SAFE_INT_TICKS; + + spin_lock(&host->lock); + iowrite32((count << 24 | ticks), hcr_base + ICC); + + intr_coalescing_count = count; + intr_coalescing_ticks = ticks; + spin_unlock(&host->lock); + + DPRINTK("intrrupt coalescing, count = 0x%x, ticks = %x\n", + intr_coalescing_count, intr_coalescing_ticks); + DPRINTK("ICC register status: (hcr base: 0x%x) = 0x%x\n", + hcr_base, ioread32(hcr_base + ICC)); +} + +static ssize_t fsl_sata_intr_coalescing_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d %d\n", + intr_coalescing_count, intr_coalescing_ticks); +} + +static ssize_t fsl_sata_intr_coalescing_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int coalescing_count, coalescing_ticks; + + if (sscanf(buf, "%d%d", + &coalescing_count, + &coalescing_ticks) != 2) { + printk(KERN_ERR "fsl-sata: wrong parameter format.\n"); + return -EINVAL; + } + + fsl_sata_set_irq_coalescing(dev_get_drvdata(dev), + coalescing_count, coalescing_ticks); + + return strlen(buf); +} + static inline unsigned int sata_fsl_tag(unsigned int tag, void __iomem *hcr_base) { @@ -346,10 +422,10 @@ static unsigned int sata_fsl_fill_sg(struct ata_queued_cmd *qc, void *cmd_desc, (unsigned long long)sg_addr, sg_len); /* warn if each s/g element is not dword aligned */ - if (sg_addr & 0x03) + if (unlikely(sg_addr & 0x03)) ata_port_err(qc->ap, "s/g addr unaligned : 0x%llx\n", (unsigned long long)sg_addr); - if (sg_len & 0x03) + if (unlikely(sg_len & 0x03)) ata_port_err(qc->ap, "s/g len unaligned : 0x%x\n", sg_len); @@ -1245,6 +1321,13 @@ static int sata_fsl_init_controller(struct ata_host *host) iowrite32(0x00000FFFF, hcr_base + CE); iowrite32(0x00000FFFF, hcr_base + DE); + /* + * reset the number of command complete bits which will cause the + * interrupt to be signaled + */ + fsl_sata_set_irq_coalescing(host, intr_coalescing_count, + intr_coalescing_ticks); + /* * host controller will be brought on-line, during xx_port_start() * callback, that should also initiate the OOB, COMINIT sequence @@ -1309,7 +1392,7 @@ static int sata_fsl_probe(struct platform_device *ofdev) void __iomem *csr_base = NULL; struct sata_fsl_host_priv *host_priv = NULL; int irq; - struct ata_host *host; + struct ata_host *host = NULL; u32 temp; struct ata_port_info pi = sata_fsl_port_info[0]; @@ -1356,6 +1439,10 @@ static int sata_fsl_probe(struct platform_device *ofdev) /* allocate host structure */ host = ata_host_alloc_pinfo(&ofdev->dev, ppi, SATA_FSL_MAX_PORTS); + if (!host) { + retval = -ENOMEM; + goto error_exit_with_cleanup; + } /* host->iomap is not used currently */ host->private_data = host_priv; @@ -1373,10 +1460,24 @@ static int sata_fsl_probe(struct platform_device *ofdev) dev_set_drvdata(&ofdev->dev, host); + host_priv->intr_coalescing.show = fsl_sata_intr_coalescing_show; + host_priv->intr_coalescing.store = fsl_sata_intr_coalescing_store; + sysfs_attr_init(&host_priv->intr_coalescing.attr); + host_priv->intr_coalescing.attr.name = "intr_coalescing"; + host_priv->intr_coalescing.attr.mode = S_IRUGO | S_IWUSR; + retval = device_create_file(host->dev, &host_priv->intr_coalescing); + if (retval) + goto error_exit_with_cleanup; + return 0; error_exit_with_cleanup: + if (host) { + dev_set_drvdata(&ofdev->dev, NULL); + ata_host_detach(host); + } + if (hcr_base) iounmap(hcr_base); if (host_priv) @@ -1390,6 +1491,8 @@ static int sata_fsl_remove(struct platform_device *ofdev) struct ata_host *host = dev_get_drvdata(&ofdev->dev); struct sata_fsl_host_priv *host_priv = host->private_data; + device_remove_file(&ofdev->dev, &host_priv->intr_coalescing); + ata_host_detach(host); dev_set_drvdata(&ofdev->dev, NULL); diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c index 428e55e012dc..869d7ff2227f 100644 --- a/drivers/base/power/clock_ops.c +++ b/drivers/base/power/clock_ops.c @@ -8,6 +8,7 @@ #include <linux/init.h> #include <linux/kernel.h> +#include <linux/device.h> #include <linux/io.h> #include <linux/pm.h> #include <linux/pm_clock.h> diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c index 4af7c1cbf909..a14085cc613f 100644 --- a/drivers/base/power/common.c +++ b/drivers/base/power/common.c @@ -8,6 +8,7 @@ #include <linux/init.h> #include <linux/kernel.h> +#include <linux/device.h> #include <linux/export.h> #include <linux/slab.h> #include <linux/pm_clock.h> diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 95706fa24c73..ac993eafec82 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -17,6 +17,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/cpufreq.h> +#include <linux/device.h> #include <linux/list.h> #include <linux/rculist.h> #include <linux/rcupdate.h> diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index d141b80479b5..fcafc5b2e651 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -22,6 +22,7 @@ struct regcache_ops; struct regmap_format { size_t buf_size; size_t reg_bytes; + size_t pad_bytes; size_t val_bytes; void (*format_write)(struct regmap *map, unsigned int reg, unsigned int val); @@ -65,16 +66,16 @@ struct regmap { unsigned int num_reg_defaults_raw; /* if set, only the cache is modified not the HW */ - unsigned int cache_only:1; + u32 cache_only; /* if set, only the HW is modified not the cache */ - unsigned int cache_bypass:1; + u32 cache_bypass; /* if set, remember to free reg_defaults_raw */ - unsigned int cache_free:1; + bool cache_free; struct reg_default *reg_defaults; const void *reg_defaults_raw; void *cache; - bool cache_dirty; + u32 cache_dirty; struct reg_default *patch; int patch_regs; @@ -87,7 +88,7 @@ struct regcache_ops { int (*exit)(struct regmap *map); int (*read)(struct regmap *map, unsigned int reg, unsigned int *value); int (*write)(struct regmap *map, unsigned int reg, unsigned int value); - int (*sync)(struct regmap *map); + int (*sync)(struct regmap *map, unsigned int min, unsigned int max); }; bool regmap_writeable(struct regmap *map, unsigned int reg); diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index b7d16143edeb..483b06d4a380 100644 --- a/drivers/base/regmap/regcache-lzo.c +++ b/drivers/base/regmap/regcache-lzo.c @@ -11,6 +11,7 @@ */ #include <linux/slab.h> +#include <linux/device.h> #include <linux/lzo.h> #include "internal.h" @@ -331,7 +332,8 @@ out: return ret; } -static int regcache_lzo_sync(struct regmap *map) +static int regcache_lzo_sync(struct regmap *map, unsigned int min, + unsigned int max) { struct regcache_lzo_ctx **lzo_blocks; unsigned int val; @@ -339,10 +341,21 @@ static int regcache_lzo_sync(struct regmap *map) int ret; lzo_blocks = map->cache; - for_each_set_bit(i, lzo_blocks[0]->sync_bmp, lzo_blocks[0]->sync_bmp_nbits) { + i = min; + for_each_set_bit_from(i, lzo_blocks[0]->sync_bmp, + lzo_blocks[0]->sync_bmp_nbits) { + if (i > max) + continue; + ret = regcache_read(map, i, &val); if (ret) return ret; + + /* Is this the hardware default? If so skip. */ + ret = regcache_lookup_reg(map, i); + if (ret > 0 && val == map->reg_defaults[ret].def) + continue; + map->cache_bypass = 1; ret = _regmap_write(map, i, val); map->cache_bypass = 0; diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 32620c4f1683..5157fa04c2f0 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -11,6 +11,7 @@ */ #include <linux/slab.h> +#include <linux/device.h> #include <linux/debugfs.h> #include <linux/rbtree.h> #include <linux/seq_file.h> @@ -357,7 +358,8 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, return 0; } -static int regcache_rbtree_sync(struct regmap *map) +static int regcache_rbtree_sync(struct regmap *map, unsigned int min, + unsigned int max) { struct regcache_rbtree_ctx *rbtree_ctx; struct rb_node *node; @@ -365,19 +367,37 @@ static int regcache_rbtree_sync(struct regmap *map) unsigned int regtmp; unsigned int val; int ret; - int i; + int i, base, end; rbtree_ctx = map->cache; for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { rbnode = rb_entry(node, struct regcache_rbtree_node, node); - for (i = 0; i < rbnode->blklen; i++) { + + if (rbnode->base_reg < min) + continue; + if (rbnode->base_reg > max) + break; + if (rbnode->base_reg + rbnode->blklen < min) + continue; + + if (min > rbnode->base_reg) + base = min - rbnode->base_reg; + else + base = 0; + + if (max < rbnode->base_reg + rbnode->blklen) + end = rbnode->base_reg + rbnode->blklen - max; + else + end = rbnode->blklen; + + for (i = base; i < end; i++) { regtmp = rbnode->base_reg + i; val = regcache_rbtree_get_register(rbnode, i, map->cache_word_size); /* Is this the hardware default? If so skip. */ ret = regcache_lookup_reg(map, i); - if (ret > 0 && val == map->reg_defaults[ret].def) + if (ret >= 0 && val == map->reg_defaults[ret].def) continue; map->cache_bypass = 1; diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 5cd2a37e7688..87f54dbf601b 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -12,6 +12,7 @@ #include <linux/slab.h> #include <linux/export.h> +#include <linux/device.h> #include <trace/events/regmap.h> #include <linux/bsearch.h> #include <linux/sort.h> @@ -35,12 +36,17 @@ static int regcache_hw_init(struct regmap *map) return -EINVAL; if (!map->reg_defaults_raw) { + u32 cache_bypass = map->cache_bypass; dev_warn(map->dev, "No cache defaults, reading back from HW\n"); + + /* Bypass the cache access till data read from HW*/ + map->cache_bypass = 1; tmp_buf = kmalloc(map->cache_size_raw, GFP_KERNEL); if (!tmp_buf) return -EINVAL; ret = regmap_bulk_read(map, 0, tmp_buf, map->num_reg_defaults_raw); + map->cache_bypass = cache_bypass; if (ret < 0) { kfree(tmp_buf); return ret; @@ -211,7 +217,6 @@ int regcache_read(struct regmap *map, return -EINVAL; } -EXPORT_SYMBOL_GPL(regcache_read); /** * regcache_write: Set the value of a given register in the cache. @@ -238,7 +243,6 @@ int regcache_write(struct regmap *map, return 0; } -EXPORT_SYMBOL_GPL(regcache_write); /** * regcache_sync: Sync the register cache with the hardware. @@ -254,12 +258,11 @@ EXPORT_SYMBOL_GPL(regcache_write); int regcache_sync(struct regmap *map) { int ret = 0; - unsigned int val; unsigned int i; const char *name; unsigned int bypass; - BUG_ON(!map->cache_ops); + BUG_ON(!map->cache_ops || !map->cache_ops->sync); mutex_lock(&map->lock); /* Remember the initial bypass state */ @@ -269,7 +272,11 @@ int regcache_sync(struct regmap *map) name = map->cache_ops->name; trace_regcache_sync(map->dev, name, "start"); + if (!map->cache_dirty) + goto out; + /* Apply any patch first */ + map->cache_bypass = 1; for (i = 0; i < map->patch_regs; i++) { ret = _regmap_write(map, map->patch[i].reg, map->patch[i].def); if (ret != 0) { @@ -278,27 +285,13 @@ int regcache_sync(struct regmap *map) goto out; } } + map->cache_bypass = 0; - if (!map->cache_dirty) - goto out; - if (map->cache_ops->sync) { - ret = map->cache_ops->sync(map); - } else { - for (i = 0; i < map->num_reg_defaults; i++) { - ret = regcache_read(map, i, &val); - if (ret < 0) - goto out; - map->cache_bypass = 1; - ret = _regmap_write(map, i, val); - map->cache_bypass = 0; - if (ret < 0) - goto out; - dev_dbg(map->dev, "Synced register %#x, value %#x\n", - map->reg_defaults[i].reg, - map->reg_defaults[i].def); - } + ret = map->cache_ops->sync(map, 0, map->max_register); + + if (ret == 0) + map->cache_dirty = false; - } out: trace_regcache_sync(map->dev, name, "stop"); /* Restore the bypass state */ @@ -310,6 +303,51 @@ out: EXPORT_SYMBOL_GPL(regcache_sync); /** + * regcache_sync_region: Sync part of the register cache with the hardware. + * + * @map: map to sync. + * @min: first register to sync + * @max: last register to sync + * + * Write all non-default register values in the specified region to + * the hardware. + * + * Return a negative value on failure, 0 on success. + */ +int regcache_sync_region(struct regmap *map, unsigned int min, + unsigned int max) +{ + int ret = 0; + const char *name; + unsigned int bypass; + + BUG_ON(!map->cache_ops || !map->cache_ops->sync); + + mutex_lock(&map->lock); + + /* Remember the initial bypass state */ + bypass = map->cache_bypass; + + name = map->cache_ops->name; + dev_dbg(map->dev, "Syncing %s cache from %d-%d\n", name, min, max); + + trace_regcache_sync(map->dev, name, "start region"); + + if (!map->cache_dirty) + goto out; + + ret = map->cache_ops->sync(map, min, max); + +out: + trace_regcache_sync(map->dev, name, "stop region"); + /* Restore the bypass state */ + map->cache_bypass = bypass; + mutex_unlock(&map->lock); + + return ret; +} + +/** * regcache_cache_only: Put a register map into cache only mode * * @map: map to configure @@ -326,6 +364,7 @@ void regcache_cache_only(struct regmap *map, bool enable) mutex_lock(&map->lock); WARN_ON(map->cache_bypass && enable); map->cache_only = enable; + trace_regmap_cache_only(map->dev, enable); mutex_unlock(&map->lock); } EXPORT_SYMBOL_GPL(regcache_cache_only); @@ -363,6 +402,7 @@ void regcache_cache_bypass(struct regmap *map, bool enable) mutex_lock(&map->lock); WARN_ON(map->cache_only && enable); map->cache_bypass = enable; + trace_regmap_cache_bypass(map->dev, enable); mutex_unlock(&map->lock); } EXPORT_SYMBOL_GPL(regcache_cache_bypass); @@ -385,10 +425,16 @@ bool regcache_set_val(void *base, unsigned int idx, cache[idx] = val; break; } + case 4: { + u32 *cache = base; + if (cache[idx] == val) + return true; + cache[idx] = val; + break; + } default: BUG(); } - /* unreachable */ return false; } @@ -407,6 +453,10 @@ unsigned int regcache_get_val(const void *base, unsigned int idx, const u16 *cache = base; return cache[idx]; } + case 4: { + const u32 *cache = base; + return cache[idx]; + } default: BUG(); } diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 6f397476e27c..58517a5dac13 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -11,10 +11,10 @@ */ #include <linux/slab.h> -#include <linux/module.h> #include <linux/mutex.h> #include <linux/debugfs.h> #include <linux/uaccess.h> +#include <linux/device.h> #include "internal.h" @@ -33,6 +33,35 @@ static int regmap_open_file(struct inode *inode, struct file *file) return 0; } +static ssize_t regmap_name_read_file(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct regmap *map = file->private_data; + int ret; + char *buf; + + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = snprintf(buf, PAGE_SIZE, "%s\n", map->dev->driver->name); + if (ret < 0) { + kfree(buf); + return ret; + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + kfree(buf); + return ret; +} + +static const struct file_operations regmap_name_fops = { + .open = regmap_open_file, + .read = regmap_name_read_file, + .llseek = default_llseek, +}; + static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -103,9 +132,51 @@ out: return ret; } +#undef REGMAP_ALLOW_WRITE_DEBUGFS +#ifdef REGMAP_ALLOW_WRITE_DEBUGFS +/* + * This can be dangerous especially when we have clients such as + * PMICs, therefore don't provide any real compile time configuration option + * for this feature, people who want to use this will need to modify + * the source code directly. + */ +static ssize_t regmap_map_write_file(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + char buf[32]; + size_t buf_size; + char *start = buf; + unsigned long reg, value; + struct regmap *map = file->private_data; + + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + while (*start == ' ') + start++; + reg = simple_strtoul(start, &start, 16); + while (*start == ' ') + start++; + if (strict_strtoul(start, 16, &value)) + return -EINVAL; + + /* Userspace has been fiddling around behind the kernel's back */ + add_taint(TAINT_USER); + + regmap_write(map, reg, value); + return buf_size; +} +#else +#define regmap_map_write_file NULL +#endif + static const struct file_operations regmap_map_fops = { .open = regmap_open_file, .read = regmap_map_read_file, + .write = regmap_map_write_file, .llseek = default_llseek, }; @@ -186,12 +257,24 @@ void regmap_debugfs_init(struct regmap *map) return; } + debugfs_create_file("name", 0400, map->debugfs, + map, ®map_name_fops); + if (map->max_register) { debugfs_create_file("registers", 0400, map->debugfs, map, ®map_map_fops); debugfs_create_file("access", 0400, map->debugfs, map, ®map_access_fops); } + + if (map->cache_type) { + debugfs_create_bool("cache_only", 0400, map->debugfs, + &map->cache_only); + debugfs_create_bool("cache_dirty", 0400, map->debugfs, + &map->cache_dirty); + debugfs_create_bool("cache_bypass", 0400, map->debugfs, + &map->cache_bypass); + } } void regmap_debugfs_exit(struct regmap *map) diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index 38621ec87c05..9a3a8c564389 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -111,4 +111,21 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c, } EXPORT_SYMBOL_GPL(regmap_init_i2c); +/** + * devm_regmap_init_i2c(): Initialise managed register map + * + * @i2c: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ +struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, + const struct regmap_config *config) +{ + return devm_regmap_init(&i2c->dev, ®map_i2c, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_i2c); + MODULE_LICENSE("GPL"); diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 428836fc5835..1befaa7a31cb 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -11,6 +11,7 @@ */ #include <linux/export.h> +#include <linux/device.h> #include <linux/regmap.h> #include <linux/irq.h> #include <linux/interrupt.h> diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index 2560658de344..7c0c35a39c33 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -70,4 +70,21 @@ struct regmap *regmap_init_spi(struct spi_device *spi, } EXPORT_SYMBOL_GPL(regmap_init_spi); +/** + * devm_regmap_init_spi(): Initialise register map + * + * @spi: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The map will be automatically freed by the + * device management code. + */ +struct regmap *devm_regmap_init_spi(struct spi_device *spi, + const struct regmap_config *config) +{ + return devm_regmap_init(&spi->dev, ®map_spi, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_spi); + MODULE_LICENSE("GPL"); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7ac234f0b1c5..7a3f535e481c 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -10,8 +10,9 @@ * published by the Free Software Foundation. */ +#include <linux/device.h> #include <linux/slab.h> -#include <linux/module.h> +#include <linux/export.h> #include <linux/mutex.h> #include <linux/err.h> @@ -36,6 +37,9 @@ bool regmap_readable(struct regmap *map, unsigned int reg) if (map->max_register && reg > map->max_register) return false; + if (map->format.format_write) + return false; + if (map->readable_reg) return map->readable_reg(map->dev, reg); @@ -44,7 +48,7 @@ bool regmap_readable(struct regmap *map, unsigned int reg) bool regmap_volatile(struct regmap *map, unsigned int reg) { - if (map->max_register && reg > map->max_register) + if (!regmap_readable(map, reg)) return false; if (map->volatile_reg) @@ -55,7 +59,7 @@ bool regmap_volatile(struct regmap *map, unsigned int reg) bool regmap_precious(struct regmap *map, unsigned int reg) { - if (map->max_register && reg > map->max_register) + if (!regmap_readable(map, reg)) return false; if (map->precious_reg) @@ -76,6 +80,14 @@ static bool regmap_volatile_range(struct regmap *map, unsigned int reg, return true; } +static void regmap_format_2_6_write(struct regmap *map, + unsigned int reg, unsigned int val) +{ + u8 *out = map->work_buf; + + *out = (reg << 6) | val; +} + static void regmap_format_4_12_write(struct regmap *map, unsigned int reg, unsigned int val) { @@ -114,6 +126,13 @@ static void regmap_format_16(void *buf, unsigned int val) b[0] = cpu_to_be16(val); } +static void regmap_format_32(void *buf, unsigned int val) +{ + __be32 *b = buf; + + b[0] = cpu_to_be32(val); +} + static unsigned int regmap_parse_8(void *buf) { u8 *b = buf; @@ -130,6 +149,15 @@ static unsigned int regmap_parse_16(void *buf) return b[0]; } +static unsigned int regmap_parse_32(void *buf) +{ + __be32 *b = buf; + + b[0] = be32_to_cpu(b[0]); + + return b[0]; +} + /** * regmap_init(): Initialise register map * @@ -159,8 +187,10 @@ struct regmap *regmap_init(struct device *dev, mutex_init(&map->lock); map->format.buf_size = (config->reg_bits + config->val_bits) / 8; - map->format.reg_bytes = config->reg_bits / 8; - map->format.val_bytes = config->val_bits / 8; + map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8); + map->format.pad_bytes = config->pad_bits / 8; + map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8); + map->format.buf_size += map->format.pad_bytes; map->dev = dev; map->bus = bus; map->max_register = config->max_register; @@ -178,6 +208,16 @@ struct regmap *regmap_init(struct device *dev, } switch (config->reg_bits) { + case 2: + switch (config->val_bits) { + case 6: + map->format.format_write = regmap_format_2_6_write; + break; + default: + goto err_map; + } + break; + case 4: switch (config->val_bits) { case 12: @@ -216,6 +256,10 @@ struct regmap *regmap_init(struct device *dev, map->format.format_reg = regmap_format_16; break; + case 32: + map->format.format_reg = regmap_format_32; + break; + default: goto err_map; } @@ -229,13 +273,17 @@ struct regmap *regmap_init(struct device *dev, map->format.format_val = regmap_format_16; map->format.parse_val = regmap_parse_16; break; + case 32: + map->format.format_val = regmap_format_32; + map->format.parse_val = regmap_parse_32; + break; } if (!map->format.format_write && !(map->format.format_reg && map->format.format_val)) goto err_map; - map->work_buf = kmalloc(map->format.buf_size, GFP_KERNEL); + map->work_buf = kzalloc(map->format.buf_size, GFP_KERNEL); if (map->work_buf == NULL) { ret = -ENOMEM; goto err_map; @@ -258,6 +306,45 @@ err: } EXPORT_SYMBOL_GPL(regmap_init); +static void devm_regmap_release(struct device *dev, void *res) +{ + regmap_exit(*(struct regmap **)res); +} + +/** + * devm_regmap_init(): Initialise managed register map + * + * @dev: Device that will be interacted with + * @bus: Bus-specific callbacks to use with device + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. This function should generally not be called + * directly, it should be called by bus-specific init functions. The + * map will be automatically freed by the device management code. + */ +struct regmap *devm_regmap_init(struct device *dev, + const struct regmap_bus *bus, + const struct regmap_config *config) +{ + struct regmap **ptr, *regmap; + + ptr = devres_alloc(devm_regmap_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + regmap = regmap_init(dev, bus, config); + if (!IS_ERR(regmap)) { + *ptr = regmap; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return regmap; +} +EXPORT_SYMBOL_GPL(devm_regmap_init); + /** * regmap_reinit_cache(): Reinitialise the current register cache * @@ -276,6 +363,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) mutex_lock(&map->lock); regcache_exit(map); + regmap_debugfs_exit(map); map->max_register = config->max_register; map->writeable_reg = config->writeable_reg; @@ -284,6 +372,8 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) map->precious_reg = config->precious_reg; map->cache_type = config->cache_type; + regmap_debugfs_init(map); + map->cache_bypass = false; map->cache_only = false; @@ -321,6 +411,26 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, if (!map->writeable_reg(map->dev, reg + i)) return -EINVAL; + if (!map->cache_bypass && map->format.parse_val) { + unsigned int ival; + int val_bytes = map->format.val_bytes; + for (i = 0; i < val_len / val_bytes; i++) { + memcpy(map->work_buf, val + (i * val_bytes), val_bytes); + ival = map->format.parse_val(map->work_buf); + ret = regcache_write(map, reg + i, ival); + if (ret) { + dev_err(map->dev, + "Error in caching of register: %u ret: %d\n", + reg + i, ret); + return ret; + } + } + if (map->cache_only) { + map->cache_dirty = true; + return 0; + } + } + map->format.format_reg(map->work_buf, reg); u8[0] |= map->write_flag_mask; @@ -332,23 +442,28 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, * send the work_buf directly, otherwise try to do a gather * write. */ - if (val == map->work_buf + map->format.reg_bytes) + if (val == (map->work_buf + map->format.pad_bytes + + map->format.reg_bytes)) ret = map->bus->write(map->dev, map->work_buf, - map->format.reg_bytes + val_len); + map->format.reg_bytes + + map->format.pad_bytes + + val_len); else if (map->bus->gather_write) ret = map->bus->gather_write(map->dev, map->work_buf, - map->format.reg_bytes, + map->format.reg_bytes + + map->format.pad_bytes, val, val_len); /* If that didn't work fall back on linearising by hand. */ if (ret == -ENOTSUPP) { - len = map->format.reg_bytes + val_len; - buf = kmalloc(len, GFP_KERNEL); + len = map->format.reg_bytes + map->format.pad_bytes + val_len; + buf = kzalloc(len, GFP_KERNEL); if (!buf) return -ENOMEM; memcpy(buf, map->work_buf, map->format.reg_bytes); - memcpy(buf + map->format.reg_bytes, val, val_len); + memcpy(buf + map->format.reg_bytes + map->format.pad_bytes, + val, val_len); ret = map->bus->write(map->dev, buf, len); kfree(buf); @@ -366,7 +481,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, int ret; BUG_ON(!map->format.format_write && !map->format.format_val); - if (!map->cache_bypass) { + if (!map->cache_bypass && map->format.format_write) { ret = regcache_write(map, reg, val); if (ret != 0) return ret; @@ -390,10 +505,12 @@ int _regmap_write(struct regmap *map, unsigned int reg, return ret; } else { - map->format.format_val(map->work_buf + map->format.reg_bytes, - val); + map->format.format_val(map->work_buf + map->format.reg_bytes + + map->format.pad_bytes, val); return _regmap_raw_write(map, reg, - map->work_buf + map->format.reg_bytes, + map->work_buf + + map->format.reg_bytes + + map->format.pad_bytes, map->format.val_bytes); } } @@ -441,12 +558,8 @@ EXPORT_SYMBOL_GPL(regmap_write); int regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len) { - size_t val_count = val_len / map->format.val_bytes; int ret; - WARN_ON(!regmap_volatile_range(map, reg, val_count) && - map->cache_type != REGCACHE_NONE); - mutex_lock(&map->lock); ret = _regmap_raw_write(map, reg, val, val_len); @@ -457,6 +570,56 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_raw_write); +/* + * regmap_bulk_write(): Write multiple registers to the device + * + * @map: Register map to write to + * @reg: First register to be write from + * @val: Block of data to be written, in native register size for device + * @val_count: Number of registers to write + * + * This function is intended to be used for writing a large block of + * data to be device either in single transfer or multiple transfer. + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, + size_t val_count) +{ + int ret = 0, i; + size_t val_bytes = map->format.val_bytes; + void *wval; + + if (!map->format.parse_val) + return -EINVAL; + + mutex_lock(&map->lock); + + /* No formatting is require if val_byte is 1 */ + if (val_bytes == 1) { + wval = (void *)val; + } else { + wval = kmemdup(val, val_count * val_bytes, GFP_KERNEL); + if (!wval) { + ret = -ENOMEM; + dev_err(map->dev, "Error in memory allocation\n"); + goto out; + } + for (i = 0; i < val_count * val_bytes; i += val_bytes) + map->format.parse_val(wval + i); + } + ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count); + + if (val_bytes != 1) + kfree(wval); + +out: + mutex_unlock(&map->lock); + return ret; +} +EXPORT_SYMBOL_GPL(regmap_bulk_write); + static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int val_len) { @@ -476,7 +639,8 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, trace_regmap_hw_read_start(map->dev, reg, val_len / map->format.val_bytes); - ret = map->bus->read(map->dev, map->work_buf, map->format.reg_bytes, + ret = map->bus->read(map->dev, map->work_buf, + map->format.reg_bytes + map->format.pad_bytes, val, val_len); trace_regmap_hw_read_done(map->dev, reg, @@ -549,16 +713,32 @@ EXPORT_SYMBOL_GPL(regmap_read); int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, size_t val_len) { - size_t val_count = val_len / map->format.val_bytes; - int ret; - - WARN_ON(!regmap_volatile_range(map, reg, val_count) && - map->cache_type != REGCACHE_NONE); + size_t val_bytes = map->format.val_bytes; + size_t val_count = val_len / val_bytes; + unsigned int v; + int ret, i; mutex_lock(&map->lock); - ret = _regmap_raw_read(map, reg, val, val_len); + if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass || + map->cache_type == REGCACHE_NONE) { + /* Physical block read if there's no cache involved */ + ret = _regmap_raw_read(map, reg, val, val_len); + + } else { + /* Otherwise go word by word for the cache; should be low + * cost as we expect to hit the cache. + */ + for (i = 0; i < val_count; i++) { + ret = _regmap_read(map, reg + i, &v); + if (ret != 0) + goto out; + + map->format.format_val(val + (i * val_bytes), v); + } + } + out: mutex_unlock(&map->lock); return ret; @@ -712,7 +892,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, } } - map->patch = kcalloc(sizeof(struct reg_default), num_regs, GFP_KERNEL); + map->patch = kcalloc(num_regs, sizeof(struct reg_default), GFP_KERNEL); if (map->patch != NULL) { memcpy(map->patch, regs, num_regs * sizeof(struct reg_default)); diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index e09f9cebbb20..abfaacaaf346 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -179,7 +179,7 @@ int drbd_khelper(struct drbd_conf *mdev, char *cmd) dev_info(DEV, "helper command: %s %s %s\n", usermode_helper, cmd, mb); drbd_bcast_ev_helper(mdev, cmd); - ret = call_usermodehelper(usermode_helper, argv, envp, 1); + ret = call_usermodehelper(usermode_helper, argv, envp, UMH_WAIT_PROC); if (ret) dev_warn(DEV, "helper command: %s %s %s exit code %u (0x%x)\n", usermode_helper, cmd, mb, diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index a6278e7e61a0..013c7a549fb6 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -41,19 +41,35 @@ #include "rbd_types.h" -#define DRV_NAME "rbd" -#define DRV_NAME_LONG "rbd (rados block device)" +/* + * The basic unit of block I/O is a sector. It is interpreted in a + * number of contexts in Linux (blk, bio, genhd), but the default is + * universally 512 bytes. These symbols are just slightly more + * meaningful than the bare numbers they represent. + */ +#define SECTOR_SHIFT 9 +#define SECTOR_SIZE (1ULL << SECTOR_SHIFT) + +#define RBD_DRV_NAME "rbd" +#define RBD_DRV_NAME_LONG "rbd (rados block device)" #define RBD_MINORS_PER_MAJOR 256 /* max minors per blkdev */ -#define RBD_MAX_MD_NAME_LEN (96 + sizeof(RBD_SUFFIX)) +#define RBD_MAX_MD_NAME_LEN (RBD_MAX_OBJ_NAME_LEN + sizeof(RBD_SUFFIX)) #define RBD_MAX_POOL_NAME_LEN 64 #define RBD_MAX_SNAP_NAME_LEN 32 #define RBD_MAX_OPT_LEN 1024 #define RBD_SNAP_HEAD_NAME "-" +/* + * An RBD device name will be "rbd#", where the "rbd" comes from + * RBD_DRV_NAME above, and # is a unique integer identifier. + * MAX_INT_FORMAT_WIDTH is used in ensuring DEV_NAME_LEN is big + * enough to hold all possible device names. + */ #define DEV_NAME_LEN 32 +#define MAX_INT_FORMAT_WIDTH ((5 * sizeof (int)) / 2 + 1) #define RBD_NOTIFY_TIMEOUT_DEFAULT 10 @@ -66,7 +82,6 @@ struct rbd_image_header { __u8 obj_order; __u8 crypt_type; __u8 comp_type; - struct rw_semaphore snap_rwsem; struct ceph_snap_context *snapc; size_t snap_names_len; u64 snap_seq; @@ -83,7 +98,7 @@ struct rbd_options { }; /* - * an instance of the client. multiple devices may share a client. + * an instance of the client. multiple devices may share an rbd client. */ struct rbd_client { struct ceph_client *client; @@ -92,20 +107,9 @@ struct rbd_client { struct list_head node; }; -struct rbd_req_coll; - /* - * a single io request + * a request completion status */ -struct rbd_request { - struct request *rq; /* blk layer request */ - struct bio *bio; /* cloned bio */ - struct page **pages; /* list of used pages */ - u64 len; - int coll_index; - struct rbd_req_coll *coll; -}; - struct rbd_req_status { int done; int rc; @@ -122,6 +126,18 @@ struct rbd_req_coll { struct rbd_req_status status[0]; }; +/* + * a single io request + */ +struct rbd_request { + struct request *rq; /* blk layer request */ + struct bio *bio; /* cloned bio */ + struct page **pages; /* list of used pages */ + u64 len; + int coll_index; + struct rbd_req_coll *coll; +}; + struct rbd_snap { struct device dev; const char *name; @@ -140,7 +156,6 @@ struct rbd_device { struct gendisk *disk; /* blkdev's gendisk and rq */ struct request_queue *q; - struct ceph_client *client; struct rbd_client *rbd_client; char name[DEV_NAME_LEN]; /* blkdev name, e.g. rbd3 */ @@ -157,6 +172,8 @@ struct rbd_device { struct ceph_osd_event *watch_event; struct ceph_osd_request *watch_request; + /* protects updating the header */ + struct rw_semaphore header_rwsem; char snap_name[RBD_MAX_SNAP_NAME_LEN]; u32 cur_snap; /* index+1 of current snapshot within snap context 0 - for the head */ @@ -171,15 +188,13 @@ struct rbd_device { struct device dev; }; -static struct bus_type rbd_bus_type = { - .name = "rbd", -}; - -static spinlock_t node_lock; /* protects client get/put */ - static DEFINE_MUTEX(ctl_mutex); /* Serialize open/close/setup/teardown */ + static LIST_HEAD(rbd_dev_list); /* devices */ -static LIST_HEAD(rbd_client_list); /* clients */ +static DEFINE_SPINLOCK(rbd_dev_list_lock); + +static LIST_HEAD(rbd_client_list); /* clients */ +static DEFINE_SPINLOCK(rbd_client_list_lock); static int __rbd_init_snaps_header(struct rbd_device *rbd_dev); static void rbd_dev_release(struct device *dev); @@ -190,12 +205,32 @@ static ssize_t rbd_snap_add(struct device *dev, static void __rbd_remove_snap_dev(struct rbd_device *rbd_dev, struct rbd_snap *snap); +static ssize_t rbd_add(struct bus_type *bus, const char *buf, + size_t count); +static ssize_t rbd_remove(struct bus_type *bus, const char *buf, + size_t count); -static struct rbd_device *dev_to_rbd(struct device *dev) +static struct bus_attribute rbd_bus_attrs[] = { + __ATTR(add, S_IWUSR, NULL, rbd_add), + __ATTR(remove, S_IWUSR, NULL, rbd_remove), + __ATTR_NULL +}; + +static struct bus_type rbd_bus_type = { + .name = "rbd", + .bus_attrs = rbd_bus_attrs, +}; + +static void rbd_root_dev_release(struct device *dev) { - return container_of(dev, struct rbd_device, dev); } +static struct device rbd_root_dev = { + .init_name = "rbd", + .release = rbd_root_dev_release, +}; + + static struct device *rbd_get_dev(struct rbd_device *rbd_dev) { return get_device(&rbd_dev->dev); @@ -210,8 +245,7 @@ static int __rbd_update_snaps(struct rbd_device *rbd_dev); static int rbd_open(struct block_device *bdev, fmode_t mode) { - struct gendisk *disk = bdev->bd_disk; - struct rbd_device *rbd_dev = disk->private_data; + struct rbd_device *rbd_dev = bdev->bd_disk->private_data; rbd_get_dev(rbd_dev); @@ -256,9 +290,11 @@ static struct rbd_client *rbd_client_create(struct ceph_options *opt, kref_init(&rbdc->kref); INIT_LIST_HEAD(&rbdc->node); + mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); + rbdc->client = ceph_create_client(opt, rbdc, 0, 0); if (IS_ERR(rbdc->client)) - goto out_rbdc; + goto out_mutex; opt = NULL; /* Now rbdc->client is responsible for opt */ ret = ceph_open_session(rbdc->client); @@ -267,16 +303,19 @@ static struct rbd_client *rbd_client_create(struct ceph_options *opt, rbdc->rbd_opts = rbd_opts; - spin_lock(&node_lock); + spin_lock(&rbd_client_list_lock); list_add_tail(&rbdc->node, &rbd_client_list); - spin_unlock(&node_lock); + spin_unlock(&rbd_client_list_lock); + + mutex_unlock(&ctl_mutex); dout("rbd_client_create created %p\n", rbdc); return rbdc; out_err: ceph_destroy_client(rbdc->client); -out_rbdc: +out_mutex: + mutex_unlock(&ctl_mutex); kfree(rbdc); out_opt: if (opt) @@ -324,7 +363,7 @@ static int parse_rbd_opts_token(char *c, void *private) substring_t argstr[MAX_OPT_ARGS]; int token, intval, ret; - token = match_token((char *)c, rbdopt_tokens, argstr); + token = match_token(c, rbdopt_tokens, argstr); if (token < 0) return -EINVAL; @@ -357,58 +396,54 @@ static int parse_rbd_opts_token(char *c, void *private) * Get a ceph client with specific addr and configuration, if one does * not exist create it. */ -static int rbd_get_client(struct rbd_device *rbd_dev, const char *mon_addr, - char *options) +static struct rbd_client *rbd_get_client(const char *mon_addr, + size_t mon_addr_len, + char *options) { struct rbd_client *rbdc; struct ceph_options *opt; - int ret; struct rbd_options *rbd_opts; rbd_opts = kzalloc(sizeof(*rbd_opts), GFP_KERNEL); if (!rbd_opts) - return -ENOMEM; + return ERR_PTR(-ENOMEM); rbd_opts->notify_timeout = RBD_NOTIFY_TIMEOUT_DEFAULT; - ret = ceph_parse_options(&opt, options, mon_addr, - mon_addr + strlen(mon_addr), parse_rbd_opts_token, rbd_opts); - if (ret < 0) - goto done_err; + opt = ceph_parse_options(options, mon_addr, + mon_addr + mon_addr_len, + parse_rbd_opts_token, rbd_opts); + if (IS_ERR(opt)) { + kfree(rbd_opts); + return ERR_CAST(opt); + } - spin_lock(&node_lock); + spin_lock(&rbd_client_list_lock); rbdc = __rbd_client_find(opt); if (rbdc) { + /* using an existing client */ + kref_get(&rbdc->kref); + spin_unlock(&rbd_client_list_lock); + ceph_destroy_options(opt); kfree(rbd_opts); - /* using an existing client */ - kref_get(&rbdc->kref); - rbd_dev->rbd_client = rbdc; - rbd_dev->client = rbdc->client; - spin_unlock(&node_lock); - return 0; + return rbdc; } - spin_unlock(&node_lock); + spin_unlock(&rbd_client_list_lock); rbdc = rbd_client_create(opt, rbd_opts); - if (IS_ERR(rbdc)) { - ret = PTR_ERR(rbdc); - goto done_err; - } - rbd_dev->rbd_client = rbdc; - rbd_dev->client = rbdc->client; - return 0; -done_err: - kfree(rbd_opts); - return ret; + if (IS_ERR(rbdc)) + kfree(rbd_opts); + + return rbdc; } /* * Destroy ceph client * - * Caller must hold node_lock. + * Caller must hold rbd_client_list_lock. */ static void rbd_client_release(struct kref *kref) { @@ -428,11 +463,10 @@ static void rbd_client_release(struct kref *kref) */ static void rbd_put_client(struct rbd_device *rbd_dev) { - spin_lock(&node_lock); + spin_lock(&rbd_client_list_lock); kref_put(&rbd_dev->rbd_client->kref, rbd_client_release); - spin_unlock(&node_lock); + spin_unlock(&rbd_client_list_lock); rbd_dev->rbd_client = NULL; - rbd_dev->client = NULL; } /* @@ -457,21 +491,19 @@ static int rbd_header_from_disk(struct rbd_image_header *header, gfp_t gfp_flags) { int i; - u32 snap_count = le32_to_cpu(ondisk->snap_count); - int ret = -ENOMEM; + u32 snap_count; - if (memcmp(ondisk, RBD_HEADER_TEXT, sizeof(RBD_HEADER_TEXT))) { + if (memcmp(ondisk, RBD_HEADER_TEXT, sizeof(RBD_HEADER_TEXT))) return -ENXIO; - } - init_rwsem(&header->snap_rwsem); - header->snap_names_len = le64_to_cpu(ondisk->snap_names_len); + snap_count = le32_to_cpu(ondisk->snap_count); header->snapc = kmalloc(sizeof(struct ceph_snap_context) + - snap_count * - sizeof(struct rbd_image_snap_ondisk), + snap_count * sizeof (*ondisk), gfp_flags); if (!header->snapc) return -ENOMEM; + + header->snap_names_len = le64_to_cpu(ondisk->snap_names_len); if (snap_count) { header->snap_names = kmalloc(header->snap_names_len, GFP_KERNEL); @@ -498,8 +530,7 @@ static int rbd_header_from_disk(struct rbd_image_header *header, header->snapc->num_snaps = snap_count; header->total_snaps = snap_count; - if (snap_count && - allocated_snaps == snap_count) { + if (snap_count && allocated_snaps == snap_count) { for (i = 0; i < snap_count; i++) { header->snapc->snaps[i] = le64_to_cpu(ondisk->snaps[i].id); @@ -518,7 +549,7 @@ err_names: kfree(header->snap_names); err_snapc: kfree(header->snapc); - return ret; + return -ENOMEM; } static int snap_index(struct rbd_image_header *header, int snap_num) @@ -542,35 +573,34 @@ static int snap_by_name(struct rbd_image_header *header, const char *snap_name, int i; char *p = header->snap_names; - for (i = 0; i < header->total_snaps; i++, p += strlen(p) + 1) { - if (strcmp(snap_name, p) == 0) - break; - } - if (i == header->total_snaps) - return -ENOENT; - if (seq) - *seq = header->snapc->snaps[i]; + for (i = 0; i < header->total_snaps; i++) { + if (!strcmp(snap_name, p)) { - if (size) - *size = header->snap_sizes[i]; + /* Found it. Pass back its id and/or size */ - return i; + if (seq) + *seq = header->snapc->snaps[i]; + if (size) + *size = header->snap_sizes[i]; + return i; + } + p += strlen(p) + 1; /* Skip ahead to the next name */ + } + return -ENOENT; } -static int rbd_header_set_snap(struct rbd_device *dev, - const char *snap_name, - u64 *size) +static int rbd_header_set_snap(struct rbd_device *dev, u64 *size) { struct rbd_image_header *header = &dev->header; struct ceph_snap_context *snapc = header->snapc; int ret = -ENOENT; - down_write(&header->snap_rwsem); + BUILD_BUG_ON(sizeof (dev->snap_name) < sizeof (RBD_SNAP_HEAD_NAME)); - if (!snap_name || - !*snap_name || - strcmp(snap_name, "-") == 0 || - strcmp(snap_name, RBD_SNAP_HEAD_NAME) == 0) { + down_write(&dev->header_rwsem); + + if (!memcmp(dev->snap_name, RBD_SNAP_HEAD_NAME, + sizeof (RBD_SNAP_HEAD_NAME))) { if (header->total_snaps) snapc->seq = header->snap_seq; else @@ -580,7 +610,7 @@ static int rbd_header_set_snap(struct rbd_device *dev, if (size) *size = header->image_size; } else { - ret = snap_by_name(header, snap_name, &snapc->seq, size); + ret = snap_by_name(header, dev->snap_name, &snapc->seq, size); if (ret < 0) goto done; @@ -590,7 +620,7 @@ static int rbd_header_set_snap(struct rbd_device *dev, ret = 0; done: - up_write(&header->snap_rwsem); + up_write(&dev->header_rwsem); return ret; } @@ -717,7 +747,7 @@ static struct bio *bio_chain_clone(struct bio **old, struct bio **next, /* split the bio. We'll release it either in the next call, or it will have to be released outside */ - bp = bio_split(old_chain, (len - total) / 512ULL); + bp = bio_split(old_chain, (len - total) / SECTOR_SIZE); if (!bp) goto err_out; @@ -857,7 +887,7 @@ static int rbd_do_request(struct request *rq, struct timespec mtime = CURRENT_TIME; struct rbd_request *req_data; struct ceph_osd_request_head *reqhead; - struct rbd_image_header *header = &dev->header; + struct ceph_osd_client *osdc; req_data = kzalloc(sizeof(*req_data), GFP_NOIO); if (!req_data) { @@ -874,15 +904,13 @@ static int rbd_do_request(struct request *rq, dout("rbd_do_request obj=%s ofs=%lld len=%lld\n", obj, len, ofs); - down_read(&header->snap_rwsem); + down_read(&dev->header_rwsem); - req = ceph_osdc_alloc_request(&dev->client->osdc, flags, - snapc, - ops, - false, - GFP_NOIO, pages, bio); + osdc = &dev->rbd_client->client->osdc; + req = ceph_osdc_alloc_request(osdc, flags, snapc, ops, + false, GFP_NOIO, pages, bio); if (!req) { - up_read(&header->snap_rwsem); + up_read(&dev->header_rwsem); ret = -ENOMEM; goto done_pages; } @@ -909,27 +937,27 @@ static int rbd_do_request(struct request *rq, layout->fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); layout->fl_pg_preferred = cpu_to_le32(-1); layout->fl_pg_pool = cpu_to_le32(dev->poolid); - ceph_calc_raw_layout(&dev->client->osdc, layout, snapid, - ofs, &len, &bno, req, ops); + ceph_calc_raw_layout(osdc, layout, snapid, ofs, &len, &bno, + req, ops); ceph_osdc_build_request(req, ofs, &len, ops, snapc, &mtime, req->r_oid, req->r_oid_len); - up_read(&header->snap_rwsem); + up_read(&dev->header_rwsem); if (linger_req) { - ceph_osdc_set_request_linger(&dev->client->osdc, req); + ceph_osdc_set_request_linger(osdc, req); *linger_req = req; } - ret = ceph_osdc_start_request(&dev->client->osdc, req, false); + ret = ceph_osdc_start_request(osdc, req, false); if (ret < 0) goto done_err; if (!rbd_cb) { - ret = ceph_osdc_wait_request(&dev->client->osdc, req); + ret = ceph_osdc_wait_request(osdc, req); if (ver) *ver = le64_to_cpu(req->r_reassert_version.version); dout("reassert_ver=%lld\n", @@ -1213,8 +1241,8 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) rc = __rbd_update_snaps(dev); mutex_unlock(&ctl_mutex); if (rc) - pr_warning(DRV_NAME "%d got notification but failed to update" - " snaps: %d\n", dev->major, rc); + pr_warning(RBD_DRV_NAME "%d got notification but failed to " + " update snaps: %d\n", dev->major, rc); rbd_req_sync_notify_ack(dev, ver, notify_id, dev->obj_md_name); } @@ -1227,7 +1255,7 @@ static int rbd_req_sync_watch(struct rbd_device *dev, u64 ver) { struct ceph_osd_req_op *ops; - struct ceph_osd_client *osdc = &dev->client->osdc; + struct ceph_osd_client *osdc = &dev->rbd_client->client->osdc; int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_WATCH, 0); if (ret < 0) @@ -1314,7 +1342,7 @@ static int rbd_req_sync_notify(struct rbd_device *dev, const char *obj) { struct ceph_osd_req_op *ops; - struct ceph_osd_client *osdc = &dev->client->osdc; + struct ceph_osd_client *osdc = &dev->rbd_client->client->osdc; struct ceph_osd_event *event; struct rbd_notify_info info; int payload_len = sizeof(u32) + sizeof(u32); @@ -1421,9 +1449,7 @@ static void rbd_rq_fn(struct request_queue *q) struct request *rq; struct bio_pair *bp = NULL; - rq = blk_fetch_request(q); - - while (1) { + while ((rq = blk_fetch_request(q))) { struct bio *bio; struct bio *rq_bio, *next_bio = NULL; bool do_write; @@ -1441,32 +1467,32 @@ static void rbd_rq_fn(struct request_queue *q) /* filter out block requests we don't understand */ if ((rq->cmd_type != REQ_TYPE_FS)) { __blk_end_request_all(rq, 0); - goto next; + continue; } /* deduce our operation (read, write) */ do_write = (rq_data_dir(rq) == WRITE); size = blk_rq_bytes(rq); - ofs = blk_rq_pos(rq) * 512ULL; + ofs = blk_rq_pos(rq) * SECTOR_SIZE; rq_bio = rq->bio; if (do_write && rbd_dev->read_only) { __blk_end_request_all(rq, -EROFS); - goto next; + continue; } spin_unlock_irq(q->queue_lock); dout("%s 0x%x bytes at 0x%llx\n", do_write ? "write" : "read", - size, blk_rq_pos(rq) * 512ULL); + size, blk_rq_pos(rq) * SECTOR_SIZE); num_segs = rbd_get_num_segments(&rbd_dev->header, ofs, size); coll = rbd_alloc_coll(num_segs); if (!coll) { spin_lock_irq(q->queue_lock); __blk_end_request_all(rq, -ENOMEM); - goto next; + continue; } do { @@ -1512,8 +1538,6 @@ next_seg: if (bp) bio_pair_release(bp); spin_lock_irq(q->queue_lock); -next: - rq = blk_fetch_request(q); } } @@ -1526,13 +1550,17 @@ static int rbd_merge_bvec(struct request_queue *q, struct bvec_merge_data *bmd, struct bio_vec *bvec) { struct rbd_device *rbd_dev = q->queuedata; - unsigned int chunk_sectors = 1 << (rbd_dev->header.obj_order - 9); - sector_t sector = bmd->bi_sector + get_start_sect(bmd->bi_bdev); - unsigned int bio_sectors = bmd->bi_size >> 9; + unsigned int chunk_sectors; + sector_t sector; + unsigned int bio_sectors; int max; + chunk_sectors = 1 << (rbd_dev->header.obj_order - SECTOR_SHIFT); + sector = bmd->bi_sector + get_start_sect(bmd->bi_bdev); + bio_sectors = bmd->bi_size >> SECTOR_SHIFT; + max = (chunk_sectors - ((sector & (chunk_sectors - 1)) - + bio_sectors)) << 9; + + bio_sectors)) << SECTOR_SHIFT; if (max < 0) max = 0; /* bio_add cannot handle a negative return */ if (max <= bvec->bv_len && bio_sectors == 0) @@ -1565,15 +1593,16 @@ static int rbd_read_header(struct rbd_device *rbd_dev, ssize_t rc; struct rbd_image_header_ondisk *dh; int snap_count = 0; - u64 snap_names_len = 0; u64 ver; + size_t len; + /* + * First reads the fixed-size header to determine the number + * of snapshots, then re-reads it, along with all snapshot + * records as well as their stored names. + */ + len = sizeof (*dh); while (1) { - int len = sizeof(*dh) + - snap_count * sizeof(struct rbd_image_snap_ondisk) + - snap_names_len; - - rc = -ENOMEM; dh = kmalloc(len, GFP_KERNEL); if (!dh) return -ENOMEM; @@ -1588,21 +1617,22 @@ static int rbd_read_header(struct rbd_device *rbd_dev, rc = rbd_header_from_disk(header, dh, snap_count, GFP_KERNEL); if (rc < 0) { - if (rc == -ENXIO) { + if (rc == -ENXIO) pr_warning("unrecognized header format" " for image %s", rbd_dev->obj); - } goto out_dh; } - if (snap_count != header->total_snaps) { - snap_count = header->total_snaps; - snap_names_len = header->snap_names_len; - rbd_header_free(header); - kfree(dh); - continue; - } - break; + if (snap_count == header->total_snaps) + break; + + snap_count = header->total_snaps; + len = sizeof (*dh) + + snap_count * sizeof(struct rbd_image_snap_ondisk) + + header->snap_names_len; + + rbd_header_free(header); + kfree(dh); } header->obj_version = ver; @@ -1623,13 +1653,14 @@ static int rbd_header_add_snap(struct rbd_device *dev, int ret; void *data, *p, *e; u64 ver; + struct ceph_mon_client *monc; /* we should create a snapshot only if we're pointing at the head */ if (dev->cur_snap) return -EINVAL; - ret = ceph_monc_create_snapid(&dev->client->monc, dev->poolid, - &new_snapid); + monc = &dev->rbd_client->client->monc; + ret = ceph_monc_create_snapid(monc, dev->poolid, &new_snapid); dout("created snapid=%lld\n", new_snapid); if (ret < 0) return ret; @@ -1684,9 +1715,9 @@ static int __rbd_update_snaps(struct rbd_device *rbd_dev) return ret; /* resized? */ - set_capacity(rbd_dev->disk, h.image_size / 512ULL); + set_capacity(rbd_dev->disk, h.image_size / SECTOR_SIZE); - down_write(&rbd_dev->header.snap_rwsem); + down_write(&rbd_dev->header_rwsem); snap_seq = rbd_dev->header.snapc->seq; if (rbd_dev->header.total_snaps && @@ -1711,7 +1742,7 @@ static int __rbd_update_snaps(struct rbd_device *rbd_dev) ret = __rbd_init_snaps_header(rbd_dev); - up_write(&rbd_dev->header.snap_rwsem); + up_write(&rbd_dev->header_rwsem); return ret; } @@ -1721,6 +1752,7 @@ static int rbd_init_disk(struct rbd_device *rbd_dev) struct gendisk *disk; struct request_queue *q; int rc; + u64 segment_size; u64 total_size = 0; /* contact OSD, request size info about the object being mapped */ @@ -1733,7 +1765,7 @@ static int rbd_init_disk(struct rbd_device *rbd_dev) if (rc) return rc; - rc = rbd_header_set_snap(rbd_dev, rbd_dev->snap_name, &total_size); + rc = rbd_header_set_snap(rbd_dev, &total_size); if (rc) return rc; @@ -1743,7 +1775,7 @@ static int rbd_init_disk(struct rbd_device *rbd_dev) if (!disk) goto out; - snprintf(disk->disk_name, sizeof(disk->disk_name), DRV_NAME "%d", + snprintf(disk->disk_name, sizeof(disk->disk_name), RBD_DRV_NAME "%d", rbd_dev->id); disk->major = rbd_dev->major; disk->first_minor = 0; @@ -1756,11 +1788,15 @@ static int rbd_init_disk(struct rbd_device *rbd_dev) if (!q) goto out_disk; + /* We use the default size, but let's be explicit about it. */ + blk_queue_physical_block_size(q, SECTOR_SIZE); + /* set io sizes to object size */ - blk_queue_max_hw_sectors(q, rbd_obj_bytes(&rbd_dev->header) / 512ULL); - blk_queue_max_segment_size(q, rbd_obj_bytes(&rbd_dev->header)); - blk_queue_io_min(q, rbd_obj_bytes(&rbd_dev->header)); - blk_queue_io_opt(q, rbd_obj_bytes(&rbd_dev->header)); + segment_size = rbd_obj_bytes(&rbd_dev->header); + blk_queue_max_hw_sectors(q, segment_size / SECTOR_SIZE); + blk_queue_max_segment_size(q, segment_size); + blk_queue_io_min(q, segment_size); + blk_queue_io_opt(q, segment_size); blk_queue_merge_bvec(q, rbd_merge_bvec); disk->queue = q; @@ -1771,7 +1807,7 @@ static int rbd_init_disk(struct rbd_device *rbd_dev) rbd_dev->q = q; /* finally, announce the disk to the world */ - set_capacity(disk, total_size / 512ULL); + set_capacity(disk, total_size / SECTOR_SIZE); add_disk(disk); pr_info("%s: added with size 0x%llx\n", @@ -1788,10 +1824,15 @@ out: sysfs */ +static struct rbd_device *dev_to_rbd_dev(struct device *dev) +{ + return container_of(dev, struct rbd_device, dev); +} + static ssize_t rbd_size_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct rbd_device *rbd_dev = dev_to_rbd(dev); + struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); return sprintf(buf, "%llu\n", (unsigned long long)rbd_dev->header.image_size); } @@ -1799,7 +1840,7 @@ static ssize_t rbd_size_show(struct device *dev, static ssize_t rbd_major_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct rbd_device *rbd_dev = dev_to_rbd(dev); + struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); return sprintf(buf, "%d\n", rbd_dev->major); } @@ -1807,15 +1848,16 @@ static ssize_t rbd_major_show(struct device *dev, static ssize_t rbd_client_id_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct rbd_device *rbd_dev = dev_to_rbd(dev); + struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); - return sprintf(buf, "client%lld\n", ceph_client_id(rbd_dev->client)); + return sprintf(buf, "client%lld\n", + ceph_client_id(rbd_dev->rbd_client->client)); } static ssize_t rbd_pool_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct rbd_device *rbd_dev = dev_to_rbd(dev); + struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); return sprintf(buf, "%s\n", rbd_dev->pool_name); } @@ -1823,7 +1865,7 @@ static ssize_t rbd_pool_show(struct device *dev, static ssize_t rbd_name_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct rbd_device *rbd_dev = dev_to_rbd(dev); + struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); return sprintf(buf, "%s\n", rbd_dev->obj); } @@ -1832,7 +1874,7 @@ static ssize_t rbd_snap_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct rbd_device *rbd_dev = dev_to_rbd(dev); + struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); return sprintf(buf, "%s\n", rbd_dev->snap_name); } @@ -1842,7 +1884,7 @@ static ssize_t rbd_image_refresh(struct device *dev, const char *buf, size_t size) { - struct rbd_device *rbd_dev = dev_to_rbd(dev); + struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); int rc; int ret = size; @@ -1907,7 +1949,7 @@ static ssize_t rbd_snap_size_show(struct device *dev, { struct rbd_snap *snap = container_of(dev, struct rbd_snap, dev); - return sprintf(buf, "%lld\n", (long long)snap->size); + return sprintf(buf, "%zd\n", snap->size); } static ssize_t rbd_snap_id_show(struct device *dev, @@ -1916,7 +1958,7 @@ static ssize_t rbd_snap_id_show(struct device *dev, { struct rbd_snap *snap = container_of(dev, struct rbd_snap, dev); - return sprintf(buf, "%lld\n", (long long)snap->id); + return sprintf(buf, "%llu\n", (unsigned long long) snap->id); } static DEVICE_ATTR(snap_size, S_IRUGO, rbd_snap_size_show, NULL); @@ -2088,19 +2130,9 @@ static int __rbd_init_snaps_header(struct rbd_device *rbd_dev) return 0; } - -static void rbd_root_dev_release(struct device *dev) -{ -} - -static struct device rbd_root_dev = { - .init_name = "rbd", - .release = rbd_root_dev_release, -}; - static int rbd_bus_add_dev(struct rbd_device *rbd_dev) { - int ret = -ENOMEM; + int ret; struct device *dev; struct rbd_snap *snap; @@ -2114,7 +2146,7 @@ static int rbd_bus_add_dev(struct rbd_device *rbd_dev) dev_set_name(dev, "%d", rbd_dev->id); ret = device_register(dev); if (ret < 0) - goto done_free; + goto out; list_for_each_entry(snap, &rbd_dev->snaps, node) { ret = rbd_register_snap_dev(rbd_dev, snap, @@ -2122,10 +2154,7 @@ static int rbd_bus_add_dev(struct rbd_device *rbd_dev) if (ret < 0) break; } - - mutex_unlock(&ctl_mutex); - return 0; -done_free: +out: mutex_unlock(&ctl_mutex); return ret; } @@ -2154,104 +2183,250 @@ static int rbd_init_watch_dev(struct rbd_device *rbd_dev) return ret; } +static atomic64_t rbd_id_max = ATOMIC64_INIT(0); + +/* + * Get a unique rbd identifier for the given new rbd_dev, and add + * the rbd_dev to the global list. The minimum rbd id is 1. + */ +static void rbd_id_get(struct rbd_device *rbd_dev) +{ + rbd_dev->id = atomic64_inc_return(&rbd_id_max); + + spin_lock(&rbd_dev_list_lock); + list_add_tail(&rbd_dev->node, &rbd_dev_list); + spin_unlock(&rbd_dev_list_lock); +} + +/* + * Remove an rbd_dev from the global list, and record that its + * identifier is no longer in use. + */ +static void rbd_id_put(struct rbd_device *rbd_dev) +{ + struct list_head *tmp; + int rbd_id = rbd_dev->id; + int max_id; + + BUG_ON(rbd_id < 1); + + spin_lock(&rbd_dev_list_lock); + list_del_init(&rbd_dev->node); + + /* + * If the id being "put" is not the current maximum, there + * is nothing special we need to do. + */ + if (rbd_id != atomic64_read(&rbd_id_max)) { + spin_unlock(&rbd_dev_list_lock); + return; + } + + /* + * We need to update the current maximum id. Search the + * list to find out what it is. We're more likely to find + * the maximum at the end, so search the list backward. + */ + max_id = 0; + list_for_each_prev(tmp, &rbd_dev_list) { + struct rbd_device *rbd_dev; + + rbd_dev = list_entry(tmp, struct rbd_device, node); + if (rbd_id > max_id) + max_id = rbd_id; + } + spin_unlock(&rbd_dev_list_lock); + + /* + * The max id could have been updated by rbd_id_get(), in + * which case it now accurately reflects the new maximum. + * Be careful not to overwrite the maximum value in that + * case. + */ + atomic64_cmpxchg(&rbd_id_max, rbd_id, max_id); +} + +/* + * Skips over white space at *buf, and updates *buf to point to the + * first found non-space character (if any). Returns the length of + * the token (string of non-white space characters) found. Note + * that *buf must be terminated with '\0'. + */ +static inline size_t next_token(const char **buf) +{ + /* + * These are the characters that produce nonzero for + * isspace() in the "C" and "POSIX" locales. + */ + const char *spaces = " \f\n\r\t\v"; + + *buf += strspn(*buf, spaces); /* Find start of token */ + + return strcspn(*buf, spaces); /* Return token length */ +} + +/* + * Finds the next token in *buf, and if the provided token buffer is + * big enough, copies the found token into it. The result, if + * copied, is guaranteed to be terminated with '\0'. Note that *buf + * must be terminated with '\0' on entry. + * + * Returns the length of the token found (not including the '\0'). + * Return value will be 0 if no token is found, and it will be >= + * token_size if the token would not fit. + * + * The *buf pointer will be updated to point beyond the end of the + * found token. Note that this occurs even if the token buffer is + * too small to hold it. + */ +static inline size_t copy_token(const char **buf, + char *token, + size_t token_size) +{ + size_t len; + + len = next_token(buf); + if (len < token_size) { + memcpy(token, *buf, len); + *(token + len) = '\0'; + } + *buf += len; + + return len; +} + +/* + * This fills in the pool_name, obj, obj_len, snap_name, obj_len, + * rbd_dev, rbd_md_name, and name fields of the given rbd_dev, based + * on the list of monitor addresses and other options provided via + * /sys/bus/rbd/add. + */ +static int rbd_add_parse_args(struct rbd_device *rbd_dev, + const char *buf, + const char **mon_addrs, + size_t *mon_addrs_size, + char *options, + size_t options_size) +{ + size_t len; + + /* The first four tokens are required */ + + len = next_token(&buf); + if (!len) + return -EINVAL; + *mon_addrs_size = len + 1; + *mon_addrs = buf; + + buf += len; + + len = copy_token(&buf, options, options_size); + if (!len || len >= options_size) + return -EINVAL; + + len = copy_token(&buf, rbd_dev->pool_name, sizeof (rbd_dev->pool_name)); + if (!len || len >= sizeof (rbd_dev->pool_name)) + return -EINVAL; + + len = copy_token(&buf, rbd_dev->obj, sizeof (rbd_dev->obj)); + if (!len || len >= sizeof (rbd_dev->obj)) + return -EINVAL; + + /* We have the object length in hand, save it. */ + + rbd_dev->obj_len = len; + + BUILD_BUG_ON(RBD_MAX_MD_NAME_LEN + < RBD_MAX_OBJ_NAME_LEN + sizeof (RBD_SUFFIX)); + sprintf(rbd_dev->obj_md_name, "%s%s", rbd_dev->obj, RBD_SUFFIX); + + /* + * The snapshot name is optional, but it's an error if it's + * too long. If no snapshot is supplied, fill in the default. + */ + len = copy_token(&buf, rbd_dev->snap_name, sizeof (rbd_dev->snap_name)); + if (!len) + memcpy(rbd_dev->snap_name, RBD_SNAP_HEAD_NAME, + sizeof (RBD_SNAP_HEAD_NAME)); + else if (len >= sizeof (rbd_dev->snap_name)) + return -EINVAL; + + return 0; +} + static ssize_t rbd_add(struct bus_type *bus, const char *buf, size_t count) { - struct ceph_osd_client *osdc; struct rbd_device *rbd_dev; - ssize_t rc = -ENOMEM; - int irc, new_id = 0; - struct list_head *tmp; - char *mon_dev_name; - char *options; + const char *mon_addrs = NULL; + size_t mon_addrs_size = 0; + char *options = NULL; + struct ceph_osd_client *osdc; + int rc = -ENOMEM; if (!try_module_get(THIS_MODULE)) return -ENODEV; - mon_dev_name = kmalloc(RBD_MAX_OPT_LEN, GFP_KERNEL); - if (!mon_dev_name) - goto err_out_mod; - - options = kmalloc(RBD_MAX_OPT_LEN, GFP_KERNEL); - if (!options) - goto err_mon_dev; - - /* new rbd_device object */ rbd_dev = kzalloc(sizeof(*rbd_dev), GFP_KERNEL); if (!rbd_dev) - goto err_out_opt; + goto err_nomem; + options = kmalloc(count, GFP_KERNEL); + if (!options) + goto err_nomem; /* static rbd_device initialization */ spin_lock_init(&rbd_dev->lock); INIT_LIST_HEAD(&rbd_dev->node); INIT_LIST_HEAD(&rbd_dev->snaps); + init_rwsem(&rbd_dev->header_rwsem); - init_rwsem(&rbd_dev->header.snap_rwsem); + init_rwsem(&rbd_dev->header_rwsem); /* generate unique id: find highest unique id, add one */ - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - - list_for_each(tmp, &rbd_dev_list) { - struct rbd_device *rbd_dev; + rbd_id_get(rbd_dev); - rbd_dev = list_entry(tmp, struct rbd_device, node); - if (rbd_dev->id >= new_id) - new_id = rbd_dev->id + 1; - } - - rbd_dev->id = new_id; - - /* add to global list */ - list_add_tail(&rbd_dev->node, &rbd_dev_list); + /* Fill in the device name, now that we have its id. */ + BUILD_BUG_ON(DEV_NAME_LEN + < sizeof (RBD_DRV_NAME) + MAX_INT_FORMAT_WIDTH); + sprintf(rbd_dev->name, "%s%d", RBD_DRV_NAME, rbd_dev->id); /* parse add command */ - if (sscanf(buf, "%" __stringify(RBD_MAX_OPT_LEN) "s " - "%" __stringify(RBD_MAX_OPT_LEN) "s " - "%" __stringify(RBD_MAX_POOL_NAME_LEN) "s " - "%" __stringify(RBD_MAX_OBJ_NAME_LEN) "s" - "%" __stringify(RBD_MAX_SNAP_NAME_LEN) "s", - mon_dev_name, options, rbd_dev->pool_name, - rbd_dev->obj, rbd_dev->snap_name) < 4) { - rc = -EINVAL; - goto err_out_slot; - } - - if (rbd_dev->snap_name[0] == 0) - rbd_dev->snap_name[0] = '-'; - - rbd_dev->obj_len = strlen(rbd_dev->obj); - snprintf(rbd_dev->obj_md_name, sizeof(rbd_dev->obj_md_name), "%s%s", - rbd_dev->obj, RBD_SUFFIX); - - /* initialize rest of new object */ - snprintf(rbd_dev->name, DEV_NAME_LEN, DRV_NAME "%d", rbd_dev->id); - rc = rbd_get_client(rbd_dev, mon_dev_name, options); - if (rc < 0) - goto err_out_slot; + rc = rbd_add_parse_args(rbd_dev, buf, &mon_addrs, &mon_addrs_size, + options, count); + if (rc) + goto err_put_id; - mutex_unlock(&ctl_mutex); + rbd_dev->rbd_client = rbd_get_client(mon_addrs, mon_addrs_size - 1, + options); + if (IS_ERR(rbd_dev->rbd_client)) { + rc = PTR_ERR(rbd_dev->rbd_client); + goto err_put_id; + } /* pick the pool */ - osdc = &rbd_dev->client->osdc; + osdc = &rbd_dev->rbd_client->client->osdc; rc = ceph_pg_poolid_by_name(osdc->osdmap, rbd_dev->pool_name); if (rc < 0) goto err_out_client; rbd_dev->poolid = rc; /* register our block device */ - irc = register_blkdev(0, rbd_dev->name); - if (irc < 0) { - rc = irc; + rc = register_blkdev(0, rbd_dev->name); + if (rc < 0) goto err_out_client; - } - rbd_dev->major = irc; + rbd_dev->major = rc; rc = rbd_bus_add_dev(rbd_dev); if (rc) goto err_out_blkdev; - /* set up and announce blkdev mapping */ + /* + * At this point cleanup in the event of an error is the job + * of the sysfs code (initiated by rbd_bus_del_dev()). + * + * Set up and announce blkdev mapping. + */ rc = rbd_init_disk(rbd_dev); if (rc) goto err_out_bus; @@ -2263,35 +2438,26 @@ static ssize_t rbd_add(struct bus_type *bus, return count; err_out_bus: - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - list_del_init(&rbd_dev->node); - mutex_unlock(&ctl_mutex); - /* this will also clean up rest of rbd_dev stuff */ rbd_bus_del_dev(rbd_dev); kfree(options); - kfree(mon_dev_name); return rc; err_out_blkdev: unregister_blkdev(rbd_dev->major, rbd_dev->name); err_out_client: rbd_put_client(rbd_dev); - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); -err_out_slot: - list_del_init(&rbd_dev->node); - mutex_unlock(&ctl_mutex); - - kfree(rbd_dev); -err_out_opt: +err_put_id: + rbd_id_put(rbd_dev); +err_nomem: kfree(options); -err_mon_dev: - kfree(mon_dev_name); -err_out_mod: + kfree(rbd_dev); + dout("Error adding device %s\n", buf); module_put(THIS_MODULE); - return rc; + + return (ssize_t) rc; } static struct rbd_device *__rbd_get_dev(unsigned long id) @@ -2299,22 +2465,28 @@ static struct rbd_device *__rbd_get_dev(unsigned long id) struct list_head *tmp; struct rbd_device *rbd_dev; + spin_lock(&rbd_dev_list_lock); list_for_each(tmp, &rbd_dev_list) { rbd_dev = list_entry(tmp, struct rbd_device, node); - if (rbd_dev->id == id) + if (rbd_dev->id == id) { + spin_unlock(&rbd_dev_list_lock); return rbd_dev; + } } + spin_unlock(&rbd_dev_list_lock); return NULL; } static void rbd_dev_release(struct device *dev) { - struct rbd_device *rbd_dev = - container_of(dev, struct rbd_device, dev); + struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); - if (rbd_dev->watch_request) - ceph_osdc_unregister_linger_request(&rbd_dev->client->osdc, + if (rbd_dev->watch_request) { + struct ceph_client *client = rbd_dev->rbd_client->client; + + ceph_osdc_unregister_linger_request(&client->osdc, rbd_dev->watch_request); + } if (rbd_dev->watch_event) rbd_req_sync_unwatch(rbd_dev, rbd_dev->obj_md_name); @@ -2323,6 +2495,9 @@ static void rbd_dev_release(struct device *dev) /* clean up and free blkdev */ rbd_free_disk(rbd_dev); unregister_blkdev(rbd_dev->major, rbd_dev->name); + + /* done with the id, and with the rbd_dev */ + rbd_id_put(rbd_dev); kfree(rbd_dev); /* release module ref */ @@ -2355,8 +2530,6 @@ static ssize_t rbd_remove(struct bus_type *bus, goto done; } - list_del_init(&rbd_dev->node); - __rbd_remove_all_snaps(rbd_dev); rbd_bus_del_dev(rbd_dev); @@ -2370,7 +2543,7 @@ static ssize_t rbd_snap_add(struct device *dev, const char *buf, size_t count) { - struct rbd_device *rbd_dev = dev_to_rbd(dev); + struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); int ret; char *name = kmalloc(count + 1, GFP_KERNEL); if (!name) @@ -2406,12 +2579,6 @@ err_unlock: return ret; } -static struct bus_attribute rbd_bus_attrs[] = { - __ATTR(add, S_IWUSR, NULL, rbd_add), - __ATTR(remove, S_IWUSR, NULL, rbd_remove), - __ATTR_NULL -}; - /* * create control files in sysfs * /sys/bus/rbd/... @@ -2420,21 +2587,21 @@ static int rbd_sysfs_init(void) { int ret; - rbd_bus_type.bus_attrs = rbd_bus_attrs; - - ret = bus_register(&rbd_bus_type); - if (ret < 0) + ret = device_register(&rbd_root_dev); + if (ret < 0) return ret; - ret = device_register(&rbd_root_dev); + ret = bus_register(&rbd_bus_type); + if (ret < 0) + device_unregister(&rbd_root_dev); return ret; } static void rbd_sysfs_cleanup(void) { - device_unregister(&rbd_root_dev); bus_unregister(&rbd_bus_type); + device_unregister(&rbd_root_dev); } int __init rbd_init(void) @@ -2444,8 +2611,7 @@ int __init rbd_init(void) rc = rbd_sysfs_init(); if (rc) return rc; - spin_lock_init(&node_lock); - pr_info("loaded " DRV_NAME_LONG "\n"); + pr_info("loaded " RBD_DRV_NAME_LONG "\n"); return 0; } diff --git a/drivers/block/rbd_types.h b/drivers/block/rbd_types.h index fc6c678aa2cb..950708688f17 100644 --- a/drivers/block/rbd_types.h +++ b/drivers/block/rbd_types.h @@ -41,10 +41,6 @@ #define RBD_HEADER_SIGNATURE "RBD" #define RBD_HEADER_VERSION "001.005" -struct rbd_info { - __le64 max_id; -} __attribute__ ((packed)); - struct rbd_image_snap_ondisk { __le64 id; __le64 image_size; diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 2f22874c0a37..d5e1ab956740 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -1475,6 +1475,9 @@ static int __init xlblk_init(void) if (!xen_domain()) return -ENODEV; + if (!xen_platform_pci_unplug) + return -ENODEV; + if (register_blkdev(XENVBD_MAJOR, DEV_NAME)) { printk(KERN_WARNING "xen_blk: can't get major %d with name %s\n", XENVBD_MAJOR, DEV_NAME); diff --git a/drivers/char/hw_random/nomadik-rng.c b/drivers/char/hw_random/nomadik-rng.c index 3d3c1e6703b4..96de0249e595 100644 --- a/drivers/char/hw_random/nomadik-rng.c +++ b/drivers/char/hw_random/nomadik-rng.c @@ -107,17 +107,6 @@ static struct amba_driver nmk_rng_driver = { .id_table = nmk_rng_ids, }; -static int __init nmk_rng_init(void) -{ - return amba_driver_register(&nmk_rng_driver); -} - -static void __devexit nmk_rng_exit(void) -{ - amba_driver_unregister(&nmk_rng_driver); -} - -module_init(nmk_rng_init); -module_exit(nmk_rng_exit); +module_amba_driver(nmk_rng_driver); MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c index 448ddb59438a..1412565c01af 100644 --- a/drivers/char/hw_random/omap-rng.c +++ b/drivers/char/hw_random/omap-rng.c @@ -26,6 +26,8 @@ #include <asm/io.h> +#include <plat/cpu.h> + #define RNG_OUT_REG 0x00 /* Output register */ #define RNG_STAT_REG 0x04 /* Status register [0] = STAT_BUSY */ diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c index 55d0f95f82f9..32cb929b8eb6 100644 --- a/drivers/clocksource/tcb_clksrc.c +++ b/drivers/clocksource/tcb_clksrc.c @@ -19,6 +19,8 @@ * - Two channels combine to create a free-running 32 bit counter * with a base rate of 5+ MHz, packaged as a clocksource (with * resolution better than 200 nsec). + * - Some chips support 32 bit counter. A single channel is used for + * this 32 bit free-running counter. the second channel is not used. * * - The third channel may be used to provide a 16-bit clockevent * source, used in either periodic or oneshot mode. This runs @@ -54,6 +56,11 @@ static cycle_t tc_get_cycles(struct clocksource *cs) return (upper << 16) | lower; } +static cycle_t tc_get_cycles32(struct clocksource *cs) +{ + return __raw_readl(tcaddr + ATMEL_TC_REG(0, CV)); +} + static struct clocksource clksrc = { .name = "tcb_clksrc", .rating = 200, @@ -209,6 +216,48 @@ static void __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) #endif +static void __init tcb_setup_dual_chan(struct atmel_tc *tc, int mck_divisor_idx) +{ + /* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */ + __raw_writel(mck_divisor_idx /* likely divide-by-8 */ + | ATMEL_TC_WAVE + | ATMEL_TC_WAVESEL_UP /* free-run */ + | ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */ + | ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */ + tcaddr + ATMEL_TC_REG(0, CMR)); + __raw_writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA)); + __raw_writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC)); + __raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ + __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); + + /* channel 1: waveform mode, input TIOA0 */ + __raw_writel(ATMEL_TC_XC1 /* input: TIOA0 */ + | ATMEL_TC_WAVE + | ATMEL_TC_WAVESEL_UP, /* free-run */ + tcaddr + ATMEL_TC_REG(1, CMR)); + __raw_writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */ + __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR)); + + /* chain channel 0 to channel 1*/ + __raw_writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR); + /* then reset all the timers */ + __raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); +} + +static void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_idx) +{ + /* channel 0: waveform mode, input mclk/8 */ + __raw_writel(mck_divisor_idx /* likely divide-by-8 */ + | ATMEL_TC_WAVE + | ATMEL_TC_WAVESEL_UP, /* free-run */ + tcaddr + ATMEL_TC_REG(0, CMR)); + __raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ + __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); + + /* then reset all the timers */ + __raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); +} + static int __init tcb_clksrc_init(void) { static char bootinfo[] __initdata @@ -260,34 +309,19 @@ static int __init tcb_clksrc_init(void) divided_rate / 1000000, ((divided_rate + 500000) % 1000000) / 1000); - /* tclib will give us three clocks no matter what the - * underlying platform supports. - */ - clk_enable(tc->clk[1]); - - /* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */ - __raw_writel(best_divisor_idx /* likely divide-by-8 */ - | ATMEL_TC_WAVE - | ATMEL_TC_WAVESEL_UP /* free-run */ - | ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */ - | ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */ - tcaddr + ATMEL_TC_REG(0, CMR)); - __raw_writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA)); - __raw_writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC)); - __raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ - __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); - - /* channel 1: waveform mode, input TIOA0 */ - __raw_writel(ATMEL_TC_XC1 /* input: TIOA0 */ - | ATMEL_TC_WAVE - | ATMEL_TC_WAVESEL_UP, /* free-run */ - tcaddr + ATMEL_TC_REG(1, CMR)); - __raw_writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */ - __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR)); - - /* chain channel 0 to channel 1, then reset all the timers */ - __raw_writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR); - __raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); + if (tc->tcb_config && tc->tcb_config->counter_width == 32) { + /* use apropriate function to read 32 bit counter */ + clksrc.read = tc_get_cycles32; + /* setup ony channel 0 */ + tcb_setup_single_chan(tc, best_divisor_idx); + } else { + /* tclib will give us three clocks no matter what the + * underlying platform supports. + */ + clk_enable(tc->clk[1]); + /* setup both channel 0 & 1 */ + tcb_setup_dual_chan(tc, best_divisor_idx); + } /* and away we go! */ clocksource_register_hz(&clksrc, divided_rate); diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index e0664fed018a..32d790dd8180 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -2,6 +2,33 @@ # ARM CPU Frequency scaling drivers # +config ARM_OMAP2PLUS_CPUFREQ + bool "TI OMAP2+" + default ARCH_OMAP2PLUS + select CPU_FREQ_TABLE + +config ARM_S3C2416_CPUFREQ + bool "S3C2416 CPU Frequency scaling support" + depends on CPU_S3C2416 + help + This adds the CPUFreq driver for the Samsung S3C2416 and + S3C2450 SoC. The S3C2416 supports changing the rate of the + armdiv clock source and also entering a so called dynamic + voltage scaling mode in which it is possible to reduce the + core voltage of the cpu. + + If in doubt, say N. + +config ARM_S3C2416_CPUFREQ_VCORESCALE + bool "Allow voltage scaling for S3C2416 arm core (EXPERIMENTAL)" + depends on ARM_S3C2416_CPUFREQ && REGULATOR && EXPERIMENTAL + help + Enable CPU voltage scaling when entering the dvs mode. + It uses information gathered through existing hardware and + tests but not documented in any datasheet. + + If in doubt, say N. + config ARM_S3C64XX_CPUFREQ bool "Samsung S3C64XX" depends on CPU_S3C6410 @@ -25,6 +52,8 @@ config ARM_EXYNOS_CPUFREQ bool "SAMSUNG EXYNOS SoCs" depends on ARCH_EXYNOS select ARM_EXYNOS4210_CPUFREQ if CPU_EXYNOS4210 + select ARM_EXYNOS4X12_CPUFREQ if (SOC_EXYNOS4212 || SOC_EXYNOS4412) + select ARM_EXYNOS5250_CPUFREQ if SOC_EXYNOS5250 default y help This adds the CPUFreq driver common part for Samsung @@ -34,6 +63,19 @@ config ARM_EXYNOS_CPUFREQ config ARM_EXYNOS4210_CPUFREQ bool "Samsung EXYNOS4210" + depends on ARCH_EXYNOS help This adds the CPUFreq driver for Samsung EXYNOS4210 SoC (S5PV310 or S5PC210). + +config ARM_EXYNOS4X12_CPUFREQ + bool "Samsung EXYNOS4X12" + help + This adds the CPUFreq driver for Samsung EXYNOS4X12 + SoC (EXYNOS4212 or EXYNOS4412). + +config ARM_EXYNOS5250_CPUFREQ + bool "Samsung EXYNOS5250" + help + This adds the CPUFreq driver for Samsung EXYNOS5250 + SoC. diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index ac000fa76bbb..9531fc2eda22 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -40,11 +40,14 @@ obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o ################################################################################## # ARM SoC drivers obj-$(CONFIG_UX500_SOC_DB8500) += db8500-cpufreq.o +obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o obj-$(CONFIG_ARM_EXYNOS_CPUFREQ) += exynos-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o -obj-$(CONFIG_ARCH_OMAP2PLUS) += omap-cpufreq.o +obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o +obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o +obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 622013fb7890..7f2f149ae40f 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -126,6 +126,15 @@ static int __init init_cpufreq_transition_notifier_list(void) } pure_initcall(init_cpufreq_transition_notifier_list); +static int off __read_mostly; +int cpufreq_disabled(void) +{ + return off; +} +void disable_cpufreq(void) +{ + off = 1; +} static LIST_HEAD(cpufreq_governor_list); static DEFINE_MUTEX(cpufreq_governor_mutex); @@ -1441,6 +1450,9 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, { int retval = -EINVAL; + if (cpufreq_disabled()) + return -ENODEV; + pr_debug("target for CPU %u: %u kHz, relation %u\n", policy->cpu, target_freq, relation); if (cpu_online(policy->cpu) && cpufreq_driver->target) @@ -1549,6 +1561,9 @@ int cpufreq_register_governor(struct cpufreq_governor *governor) if (!governor) return -EINVAL; + if (cpufreq_disabled()) + return -ENODEV; + mutex_lock(&cpufreq_governor_mutex); err = -EBUSY; @@ -1572,6 +1587,9 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor) if (!governor) return; + if (cpufreq_disabled()) + return; + #ifdef CONFIG_HOTPLUG_CPU for_each_present_cpu(cpu) { if (cpu_online(cpu)) @@ -1814,6 +1832,9 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) unsigned long flags; int ret; + if (cpufreq_disabled()) + return -ENODEV; + if (!driver_data || !driver_data->verify || !driver_data->init || ((!driver_data->setpolicy) && (!driver_data->target))) return -EINVAL; @@ -1901,6 +1922,9 @@ static int __init cpufreq_core_init(void) { int cpu; + if (cpufreq_disabled()) + return -ENODEV; + for_each_possible_cpu(cpu) { per_cpu(cpufreq_policy_cpu, cpu) = -1; init_rwsem(&per_cpu(cpu_policy_rwsem, cpu)); diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index c3e0652520a1..836e9b062e5e 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -257,6 +257,62 @@ show_one(sampling_down_factor, sampling_down_factor); show_one(ignore_nice_load, ignore_nice); show_one(powersave_bias, powersave_bias); +/** + * update_sampling_rate - update sampling rate effective immediately if needed. + * @new_rate: new sampling rate + * + * If new rate is smaller than the old, simply updaing + * dbs_tuners_int.sampling_rate might not be appropriate. For example, + * if the original sampling_rate was 1 second and the requested new sampling + * rate is 10 ms because the user needs immediate reaction from ondemand + * governor, but not sure if higher frequency will be required or not, + * then, the governor may change the sampling rate too late; up to 1 second + * later. Thus, if we are reducing the sampling rate, we need to make the + * new value effective immediately. + */ +static void update_sampling_rate(unsigned int new_rate) +{ + int cpu; + + dbs_tuners_ins.sampling_rate = new_rate + = max(new_rate, min_sampling_rate); + + for_each_online_cpu(cpu) { + struct cpufreq_policy *policy; + struct cpu_dbs_info_s *dbs_info; + unsigned long next_sampling, appointed_at; + + policy = cpufreq_cpu_get(cpu); + if (!policy) + continue; + dbs_info = &per_cpu(od_cpu_dbs_info, policy->cpu); + cpufreq_cpu_put(policy); + + mutex_lock(&dbs_info->timer_mutex); + + if (!delayed_work_pending(&dbs_info->work)) { + mutex_unlock(&dbs_info->timer_mutex); + continue; + } + + next_sampling = jiffies + usecs_to_jiffies(new_rate); + appointed_at = dbs_info->work.timer.expires; + + + if (time_before(next_sampling, appointed_at)) { + + mutex_unlock(&dbs_info->timer_mutex); + cancel_delayed_work_sync(&dbs_info->work); + mutex_lock(&dbs_info->timer_mutex); + + schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work, + usecs_to_jiffies(new_rate)); + + } + mutex_unlock(&dbs_info->timer_mutex); + } +} + static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, const char *buf, size_t count) { @@ -265,7 +321,7 @@ static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, ret = sscanf(buf, "%u", &input); if (ret != 1) return -EINVAL; - dbs_tuners_ins.sampling_rate = max(input, min_sampling_rate); + update_sampling_rate(input); return count; } diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index 5467879ea07d..b243a7ee01f6 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -210,6 +210,8 @@ static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy) cpufreq_frequency_table_get_attr(exynos_info->freq_table, policy->cpu); + locking_frequency = exynos_getspeed(0); + /* set the transition latency value */ policy->cpuinfo.transition_latency = 100000; @@ -252,6 +254,10 @@ static int __init exynos_cpufreq_init(void) if (soc_is_exynos4210()) ret = exynos4210_cpufreq_init(exynos_info); + else if (soc_is_exynos4212() || soc_is_exynos4412()) + ret = exynos4x12_cpufreq_init(exynos_info); + else if (soc_is_exynos5250()) + ret = exynos5250_cpufreq_init(exynos_info); else pr_err("%s: CPU type not found\n", __func__); diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c index 065da5b702f1..fb148fa27678 100644 --- a/drivers/cpufreq/exynos4210-cpufreq.c +++ b/drivers/cpufreq/exynos4210-cpufreq.c @@ -121,25 +121,25 @@ static void exynos4210_set_clkdiv(unsigned int div_index) tmp = exynos4210_clkdiv_table[div_index].clkdiv; - __raw_writel(tmp, S5P_CLKDIV_CPU); + __raw_writel(tmp, EXYNOS4_CLKDIV_CPU); do { - tmp = __raw_readl(S5P_CLKDIV_STATCPU); + tmp = __raw_readl(EXYNOS4_CLKDIV_STATCPU); } while (tmp & 0x1111111); /* Change Divider - CPU1 */ - tmp = __raw_readl(S5P_CLKDIV_CPU1); + tmp = __raw_readl(EXYNOS4_CLKDIV_CPU1); tmp &= ~((0x7 << 4) | 0x7); tmp |= ((clkdiv_cpu1[div_index][0] << 4) | (clkdiv_cpu1[div_index][1] << 0)); - __raw_writel(tmp, S5P_CLKDIV_CPU1); + __raw_writel(tmp, EXYNOS4_CLKDIV_CPU1); do { - tmp = __raw_readl(S5P_CLKDIV_STATCPU1); + tmp = __raw_readl(EXYNOS4_CLKDIV_STATCPU1); } while (tmp & 0x11); } @@ -151,32 +151,32 @@ static void exynos4210_set_apll(unsigned int index) clk_set_parent(moutcore, mout_mpll); do { - tmp = (__raw_readl(S5P_CLKMUX_STATCPU) - >> S5P_CLKSRC_CPU_MUXCORE_SHIFT); + tmp = (__raw_readl(EXYNOS4_CLKMUX_STATCPU) + >> EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT); tmp &= 0x7; } while (tmp != 0x2); /* 2. Set APLL Lock time */ - __raw_writel(S5P_APLL_LOCKTIME, S5P_APLL_LOCK); + __raw_writel(EXYNOS4_APLL_LOCKTIME, EXYNOS4_APLL_LOCK); /* 3. Change PLL PMS values */ - tmp = __raw_readl(S5P_APLL_CON0); + tmp = __raw_readl(EXYNOS4_APLL_CON0); tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0)); tmp |= exynos4210_apll_pms_table[index]; - __raw_writel(tmp, S5P_APLL_CON0); + __raw_writel(tmp, EXYNOS4_APLL_CON0); /* 4. wait_lock_time */ do { - tmp = __raw_readl(S5P_APLL_CON0); - } while (!(tmp & (0x1 << S5P_APLLCON0_LOCKED_SHIFT))); + tmp = __raw_readl(EXYNOS4_APLL_CON0); + } while (!(tmp & (0x1 << EXYNOS4_APLLCON0_LOCKED_SHIFT))); /* 5. MUX_CORE_SEL = APLL */ clk_set_parent(moutcore, mout_apll); do { - tmp = __raw_readl(S5P_CLKMUX_STATCPU); - tmp &= S5P_CLKMUX_STATCPU_MUXCORE_MASK; - } while (tmp != (0x1 << S5P_CLKSRC_CPU_MUXCORE_SHIFT)); + tmp = __raw_readl(EXYNOS4_CLKMUX_STATCPU); + tmp &= EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK; + } while (tmp != (0x1 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT)); } bool exynos4210_pms_change(unsigned int old_index, unsigned int new_index) @@ -198,10 +198,10 @@ static void exynos4210_set_frequency(unsigned int old_index, exynos4210_set_clkdiv(new_index); /* 2. Change just s value in apll m,p,s value */ - tmp = __raw_readl(S5P_APLL_CON0); + tmp = __raw_readl(EXYNOS4_APLL_CON0); tmp &= ~(0x7 << 0); tmp |= (exynos4210_apll_pms_table[new_index] & 0x7); - __raw_writel(tmp, S5P_APLL_CON0); + __raw_writel(tmp, EXYNOS4_APLL_CON0); } else { /* Clock Configuration Procedure */ /* 1. Change the system clock divider values */ @@ -212,10 +212,10 @@ static void exynos4210_set_frequency(unsigned int old_index, } else if (old_index < new_index) { if (!exynos4210_pms_change(old_index, new_index)) { /* 1. Change just s value in apll m,p,s value */ - tmp = __raw_readl(S5P_APLL_CON0); + tmp = __raw_readl(EXYNOS4_APLL_CON0); tmp &= ~(0x7 << 0); tmp |= (exynos4210_apll_pms_table[new_index] & 0x7); - __raw_writel(tmp, S5P_APLL_CON0); + __raw_writel(tmp, EXYNOS4_APLL_CON0); /* 2. Change the system clock divider values */ exynos4210_set_clkdiv(new_index); @@ -253,24 +253,24 @@ int exynos4210_cpufreq_init(struct exynos_dvfs_info *info) if (IS_ERR(mout_apll)) goto err_mout_apll; - tmp = __raw_readl(S5P_CLKDIV_CPU); + tmp = __raw_readl(EXYNOS4_CLKDIV_CPU); for (i = L0; i < CPUFREQ_LEVEL_END; i++) { - tmp &= ~(S5P_CLKDIV_CPU0_CORE_MASK | - S5P_CLKDIV_CPU0_COREM0_MASK | - S5P_CLKDIV_CPU0_COREM1_MASK | - S5P_CLKDIV_CPU0_PERIPH_MASK | - S5P_CLKDIV_CPU0_ATB_MASK | - S5P_CLKDIV_CPU0_PCLKDBG_MASK | - S5P_CLKDIV_CPU0_APLL_MASK); - - tmp |= ((clkdiv_cpu0[i][0] << S5P_CLKDIV_CPU0_CORE_SHIFT) | - (clkdiv_cpu0[i][1] << S5P_CLKDIV_CPU0_COREM0_SHIFT) | - (clkdiv_cpu0[i][2] << S5P_CLKDIV_CPU0_COREM1_SHIFT) | - (clkdiv_cpu0[i][3] << S5P_CLKDIV_CPU0_PERIPH_SHIFT) | - (clkdiv_cpu0[i][4] << S5P_CLKDIV_CPU0_ATB_SHIFT) | - (clkdiv_cpu0[i][5] << S5P_CLKDIV_CPU0_PCLKDBG_SHIFT) | - (clkdiv_cpu0[i][6] << S5P_CLKDIV_CPU0_APLL_SHIFT)); + tmp &= ~(EXYNOS4_CLKDIV_CPU0_CORE_MASK | + EXYNOS4_CLKDIV_CPU0_COREM0_MASK | + EXYNOS4_CLKDIV_CPU0_COREM1_MASK | + EXYNOS4_CLKDIV_CPU0_PERIPH_MASK | + EXYNOS4_CLKDIV_CPU0_ATB_MASK | + EXYNOS4_CLKDIV_CPU0_PCLKDBG_MASK | + EXYNOS4_CLKDIV_CPU0_APLL_MASK); + + tmp |= ((clkdiv_cpu0[i][0] << EXYNOS4_CLKDIV_CPU0_CORE_SHIFT) | + (clkdiv_cpu0[i][1] << EXYNOS4_CLKDIV_CPU0_COREM0_SHIFT) | + (clkdiv_cpu0[i][2] << EXYNOS4_CLKDIV_CPU0_COREM1_SHIFT) | + (clkdiv_cpu0[i][3] << EXYNOS4_CLKDIV_CPU0_PERIPH_SHIFT) | + (clkdiv_cpu0[i][4] << EXYNOS4_CLKDIV_CPU0_ATB_SHIFT) | + (clkdiv_cpu0[i][5] << EXYNOS4_CLKDIV_CPU0_PCLKDBG_SHIFT) | + (clkdiv_cpu0[i][6] << EXYNOS4_CLKDIV_CPU0_APLL_SHIFT)); exynos4210_clkdiv_table[i].clkdiv = tmp; } diff --git a/drivers/cpufreq/exynos4x12-cpufreq.c b/drivers/cpufreq/exynos4x12-cpufreq.c new file mode 100644 index 000000000000..8c5a7afa5b0b --- /dev/null +++ b/drivers/cpufreq/exynos4x12-cpufreq.c @@ -0,0 +1,536 @@ +/* + * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS4X12 - CPU frequency scaling support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/cpufreq.h> + +#include <mach/regs-clock.h> +#include <mach/cpufreq.h> + +#define CPUFREQ_LEVEL_END (L13 + 1) + +static int max_support_idx; +static int min_support_idx = (CPUFREQ_LEVEL_END - 1); + +static struct clk *cpu_clk; +static struct clk *moutcore; +static struct clk *mout_mpll; +static struct clk *mout_apll; + +struct cpufreq_clkdiv { + unsigned int index; + unsigned int clkdiv; + unsigned int clkdiv1; +}; + +static unsigned int exynos4x12_volt_table[CPUFREQ_LEVEL_END]; + +static struct cpufreq_frequency_table exynos4x12_freq_table[] = { + {L0, 1500 * 1000}, + {L1, 1400 * 1000}, + {L2, 1300 * 1000}, + {L3, 1200 * 1000}, + {L4, 1100 * 1000}, + {L5, 1000 * 1000}, + {L6, 900 * 1000}, + {L7, 800 * 1000}, + {L8, 700 * 1000}, + {L9, 600 * 1000}, + {L10, 500 * 1000}, + {L11, 400 * 1000}, + {L12, 300 * 1000}, + {L13, 200 * 1000}, + {0, CPUFREQ_TABLE_END}, +}; + +static struct cpufreq_clkdiv exynos4x12_clkdiv_table[CPUFREQ_LEVEL_END]; + +static unsigned int clkdiv_cpu0_4212[CPUFREQ_LEVEL_END][8] = { + /* + * Clock divider value for following + * { DIVCORE, DIVCOREM0, DIVCOREM1, DIVPERIPH, + * DIVATB, DIVPCLK_DBG, DIVAPLL, DIVCORE2 } + */ + /* ARM L0: 1500 MHz */ + { 0, 3, 7, 0, 6, 1, 2, 0 }, + + /* ARM L1: 1400 MHz */ + { 0, 3, 7, 0, 6, 1, 2, 0 }, + + /* ARM L2: 1300 MHz */ + { 0, 3, 7, 0, 5, 1, 2, 0 }, + + /* ARM L3: 1200 MHz */ + { 0, 3, 7, 0, 5, 1, 2, 0 }, + + /* ARM L4: 1100 MHz */ + { 0, 3, 6, 0, 4, 1, 2, 0 }, + + /* ARM L5: 1000 MHz */ + { 0, 2, 5, 0, 4, 1, 1, 0 }, + + /* ARM L6: 900 MHz */ + { 0, 2, 5, 0, 3, 1, 1, 0 }, + + /* ARM L7: 800 MHz */ + { 0, 2, 5, 0, 3, 1, 1, 0 }, + + /* ARM L8: 700 MHz */ + { 0, 2, 4, 0, 3, 1, 1, 0 }, + + /* ARM L9: 600 MHz */ + { 0, 2, 4, 0, 3, 1, 1, 0 }, + + /* ARM L10: 500 MHz */ + { 0, 2, 4, 0, 3, 1, 1, 0 }, + + /* ARM L11: 400 MHz */ + { 0, 2, 4, 0, 3, 1, 1, 0 }, + + /* ARM L12: 300 MHz */ + { 0, 2, 4, 0, 2, 1, 1, 0 }, + + /* ARM L13: 200 MHz */ + { 0, 1, 3, 0, 1, 1, 1, 0 }, +}; + +static unsigned int clkdiv_cpu0_4412[CPUFREQ_LEVEL_END][8] = { + /* + * Clock divider value for following + * { DIVCORE, DIVCOREM0, DIVCOREM1, DIVPERIPH, + * DIVATB, DIVPCLK_DBG, DIVAPLL, DIVCORE2 } + */ + /* ARM L0: 1500 MHz */ + { 0, 3, 7, 0, 6, 1, 2, 0 }, + + /* ARM L1: 1400 MHz */ + { 0, 3, 7, 0, 6, 1, 2, 0 }, + + /* ARM L2: 1300 MHz */ + { 0, 3, 7, 0, 5, 1, 2, 0 }, + + /* ARM L3: 1200 MHz */ + { 0, 3, 7, 0, 5, 1, 2, 0 }, + + /* ARM L4: 1100 MHz */ + { 0, 3, 6, 0, 4, 1, 2, 0 }, + + /* ARM L5: 1000 MHz */ + { 0, 2, 5, 0, 4, 1, 1, 0 }, + + /* ARM L6: 900 MHz */ + { 0, 2, 5, 0, 3, 1, 1, 0 }, + + /* ARM L7: 800 MHz */ + { 0, 2, 5, 0, 3, 1, 1, 0 }, + + /* ARM L8: 700 MHz */ + { 0, 2, 4, 0, 3, 1, 1, 0 }, + + /* ARM L9: 600 MHz */ + { 0, 2, 4, 0, 3, 1, 1, 0 }, + + /* ARM L10: 500 MHz */ + { 0, 2, 4, 0, 3, 1, 1, 0 }, + + /* ARM L11: 400 MHz */ + { 0, 2, 4, 0, 3, 1, 1, 0 }, + + /* ARM L12: 300 MHz */ + { 0, 2, 4, 0, 2, 1, 1, 0 }, + + /* ARM L13: 200 MHz */ + { 0, 1, 3, 0, 1, 1, 1, 0 }, +}; + +static unsigned int clkdiv_cpu1_4212[CPUFREQ_LEVEL_END][2] = { + /* Clock divider value for following + * { DIVCOPY, DIVHPM } + */ + /* ARM L0: 1500 MHz */ + { 6, 0 }, + + /* ARM L1: 1400 MHz */ + { 6, 0 }, + + /* ARM L2: 1300 MHz */ + { 5, 0 }, + + /* ARM L3: 1200 MHz */ + { 5, 0 }, + + /* ARM L4: 1100 MHz */ + { 4, 0 }, + + /* ARM L5: 1000 MHz */ + { 4, 0 }, + + /* ARM L6: 900 MHz */ + { 3, 0 }, + + /* ARM L7: 800 MHz */ + { 3, 0 }, + + /* ARM L8: 700 MHz */ + { 3, 0 }, + + /* ARM L9: 600 MHz */ + { 3, 0 }, + + /* ARM L10: 500 MHz */ + { 3, 0 }, + + /* ARM L11: 400 MHz */ + { 3, 0 }, + + /* ARM L12: 300 MHz */ + { 3, 0 }, + + /* ARM L13: 200 MHz */ + { 3, 0 }, +}; + +static unsigned int clkdiv_cpu1_4412[CPUFREQ_LEVEL_END][3] = { + /* Clock divider value for following + * { DIVCOPY, DIVHPM, DIVCORES } + */ + /* ARM L0: 1500 MHz */ + { 6, 0, 7 }, + + /* ARM L1: 1400 MHz */ + { 6, 0, 6 }, + + /* ARM L2: 1300 MHz */ + { 5, 0, 6 }, + + /* ARM L3: 1200 MHz */ + { 5, 0, 5 }, + + /* ARM L4: 1100 MHz */ + { 4, 0, 5 }, + + /* ARM L5: 1000 MHz */ + { 4, 0, 4 }, + + /* ARM L6: 900 MHz */ + { 3, 0, 4 }, + + /* ARM L7: 800 MHz */ + { 3, 0, 3 }, + + /* ARM L8: 700 MHz */ + { 3, 0, 3 }, + + /* ARM L9: 600 MHz */ + { 3, 0, 2 }, + + /* ARM L10: 500 MHz */ + { 3, 0, 2 }, + + /* ARM L11: 400 MHz */ + { 3, 0, 1 }, + + /* ARM L12: 300 MHz */ + { 3, 0, 1 }, + + /* ARM L13: 200 MHz */ + { 3, 0, 0 }, +}; + +static unsigned int exynos4x12_apll_pms_table[CPUFREQ_LEVEL_END] = { + /* APLL FOUT L0: 1500 MHz */ + ((250 << 16) | (4 << 8) | (0x0)), + + /* APLL FOUT L1: 1400 MHz */ + ((175 << 16) | (3 << 8) | (0x0)), + + /* APLL FOUT L2: 1300 MHz */ + ((325 << 16) | (6 << 8) | (0x0)), + + /* APLL FOUT L3: 1200 MHz */ + ((200 << 16) | (4 << 8) | (0x0)), + + /* APLL FOUT L4: 1100 MHz */ + ((275 << 16) | (6 << 8) | (0x0)), + + /* APLL FOUT L5: 1000 MHz */ + ((125 << 16) | (3 << 8) | (0x0)), + + /* APLL FOUT L6: 900 MHz */ + ((150 << 16) | (4 << 8) | (0x0)), + + /* APLL FOUT L7: 800 MHz */ + ((100 << 16) | (3 << 8) | (0x0)), + + /* APLL FOUT L8: 700 MHz */ + ((175 << 16) | (3 << 8) | (0x1)), + + /* APLL FOUT L9: 600 MHz */ + ((200 << 16) | (4 << 8) | (0x1)), + + /* APLL FOUT L10: 500 MHz */ + ((125 << 16) | (3 << 8) | (0x1)), + + /* APLL FOUT L11 400 MHz */ + ((100 << 16) | (3 << 8) | (0x1)), + + /* APLL FOUT L12: 300 MHz */ + ((200 << 16) | (4 << 8) | (0x2)), + + /* APLL FOUT L13: 200 MHz */ + ((100 << 16) | (3 << 8) | (0x2)), +}; + +static const unsigned int asv_voltage_4x12[CPUFREQ_LEVEL_END] = { + 1350000, 1287500, 1250000, 1187500, 1137500, 1087500, 1037500, + 1000000, 987500, 975000, 950000, 925000, 900000, 900000 +}; + +static void exynos4x12_set_clkdiv(unsigned int div_index) +{ + unsigned int tmp; + unsigned int stat_cpu1; + + /* Change Divider - CPU0 */ + + tmp = exynos4x12_clkdiv_table[div_index].clkdiv; + + __raw_writel(tmp, EXYNOS4_CLKDIV_CPU); + + while (__raw_readl(EXYNOS4_CLKDIV_STATCPU) & 0x11111111) + cpu_relax(); + + /* Change Divider - CPU1 */ + tmp = exynos4x12_clkdiv_table[div_index].clkdiv1; + + __raw_writel(tmp, EXYNOS4_CLKDIV_CPU1); + if (soc_is_exynos4212()) + stat_cpu1 = 0x11; + else + stat_cpu1 = 0x111; + + while (__raw_readl(EXYNOS4_CLKDIV_STATCPU1) & stat_cpu1) + cpu_relax(); +} + +static void exynos4x12_set_apll(unsigned int index) +{ + unsigned int tmp, pdiv; + + /* 1. MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */ + clk_set_parent(moutcore, mout_mpll); + + do { + cpu_relax(); + tmp = (__raw_readl(EXYNOS4_CLKMUX_STATCPU) + >> EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT); + tmp &= 0x7; + } while (tmp != 0x2); + + /* 2. Set APLL Lock time */ + pdiv = ((exynos4x12_apll_pms_table[index] >> 8) & 0x3f); + + __raw_writel((pdiv * 250), EXYNOS4_APLL_LOCK); + + /* 3. Change PLL PMS values */ + tmp = __raw_readl(EXYNOS4_APLL_CON0); + tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0)); + tmp |= exynos4x12_apll_pms_table[index]; + __raw_writel(tmp, EXYNOS4_APLL_CON0); + + /* 4. wait_lock_time */ + do { + cpu_relax(); + tmp = __raw_readl(EXYNOS4_APLL_CON0); + } while (!(tmp & (0x1 << EXYNOS4_APLLCON0_LOCKED_SHIFT))); + + /* 5. MUX_CORE_SEL = APLL */ + clk_set_parent(moutcore, mout_apll); + + do { + cpu_relax(); + tmp = __raw_readl(EXYNOS4_CLKMUX_STATCPU); + tmp &= EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK; + } while (tmp != (0x1 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT)); +} + +bool exynos4x12_pms_change(unsigned int old_index, unsigned int new_index) +{ + unsigned int old_pm = exynos4x12_apll_pms_table[old_index] >> 8; + unsigned int new_pm = exynos4x12_apll_pms_table[new_index] >> 8; + + return (old_pm == new_pm) ? 0 : 1; +} + +static void exynos4x12_set_frequency(unsigned int old_index, + unsigned int new_index) +{ + unsigned int tmp; + + if (old_index > new_index) { + if (!exynos4x12_pms_change(old_index, new_index)) { + /* 1. Change the system clock divider values */ + exynos4x12_set_clkdiv(new_index); + /* 2. Change just s value in apll m,p,s value */ + tmp = __raw_readl(EXYNOS4_APLL_CON0); + tmp &= ~(0x7 << 0); + tmp |= (exynos4x12_apll_pms_table[new_index] & 0x7); + __raw_writel(tmp, EXYNOS4_APLL_CON0); + + } else { + /* Clock Configuration Procedure */ + /* 1. Change the system clock divider values */ + exynos4x12_set_clkdiv(new_index); + /* 2. Change the apll m,p,s value */ + exynos4x12_set_apll(new_index); + } + } else if (old_index < new_index) { + if (!exynos4x12_pms_change(old_index, new_index)) { + /* 1. Change just s value in apll m,p,s value */ + tmp = __raw_readl(EXYNOS4_APLL_CON0); + tmp &= ~(0x7 << 0); + tmp |= (exynos4x12_apll_pms_table[new_index] & 0x7); + __raw_writel(tmp, EXYNOS4_APLL_CON0); + /* 2. Change the system clock divider values */ + exynos4x12_set_clkdiv(new_index); + } else { + /* Clock Configuration Procedure */ + /* 1. Change the apll m,p,s value */ + exynos4x12_set_apll(new_index); + /* 2. Change the system clock divider values */ + exynos4x12_set_clkdiv(new_index); + } + } +} + +static void __init set_volt_table(void) +{ + unsigned int i; + + max_support_idx = L1; + + /* Not supported */ + exynos4x12_freq_table[L0].frequency = CPUFREQ_ENTRY_INVALID; + + for (i = 0 ; i < CPUFREQ_LEVEL_END ; i++) + exynos4x12_volt_table[i] = asv_voltage_4x12[i]; +} + +int exynos4x12_cpufreq_init(struct exynos_dvfs_info *info) +{ + int i; + unsigned int tmp; + unsigned long rate; + + set_volt_table(); + + cpu_clk = clk_get(NULL, "armclk"); + if (IS_ERR(cpu_clk)) + return PTR_ERR(cpu_clk); + + moutcore = clk_get(NULL, "moutcore"); + if (IS_ERR(moutcore)) + goto err_moutcore; + + mout_mpll = clk_get(NULL, "mout_mpll"); + if (IS_ERR(mout_mpll)) + goto err_mout_mpll; + + rate = clk_get_rate(mout_mpll) / 1000; + + mout_apll = clk_get(NULL, "mout_apll"); + if (IS_ERR(mout_apll)) + goto err_mout_apll; + + for (i = L0; i < CPUFREQ_LEVEL_END; i++) { + + exynos4x12_clkdiv_table[i].index = i; + + tmp = __raw_readl(EXYNOS4_CLKDIV_CPU); + + tmp &= ~(EXYNOS4_CLKDIV_CPU0_CORE_MASK | + EXYNOS4_CLKDIV_CPU0_COREM0_MASK | + EXYNOS4_CLKDIV_CPU0_COREM1_MASK | + EXYNOS4_CLKDIV_CPU0_PERIPH_MASK | + EXYNOS4_CLKDIV_CPU0_ATB_MASK | + EXYNOS4_CLKDIV_CPU0_PCLKDBG_MASK | + EXYNOS4_CLKDIV_CPU0_APLL_MASK); + + if (soc_is_exynos4212()) { + tmp |= ((clkdiv_cpu0_4212[i][0] << EXYNOS4_CLKDIV_CPU0_CORE_SHIFT) | + (clkdiv_cpu0_4212[i][1] << EXYNOS4_CLKDIV_CPU0_COREM0_SHIFT) | + (clkdiv_cpu0_4212[i][2] << EXYNOS4_CLKDIV_CPU0_COREM1_SHIFT) | + (clkdiv_cpu0_4212[i][3] << EXYNOS4_CLKDIV_CPU0_PERIPH_SHIFT) | + (clkdiv_cpu0_4212[i][4] << EXYNOS4_CLKDIV_CPU0_ATB_SHIFT) | + (clkdiv_cpu0_4212[i][5] << EXYNOS4_CLKDIV_CPU0_PCLKDBG_SHIFT) | + (clkdiv_cpu0_4212[i][6] << EXYNOS4_CLKDIV_CPU0_APLL_SHIFT)); + } else { + tmp &= ~EXYNOS4_CLKDIV_CPU0_CORE2_MASK; + + tmp |= ((clkdiv_cpu0_4412[i][0] << EXYNOS4_CLKDIV_CPU0_CORE_SHIFT) | + (clkdiv_cpu0_4412[i][1] << EXYNOS4_CLKDIV_CPU0_COREM0_SHIFT) | + (clkdiv_cpu0_4412[i][2] << EXYNOS4_CLKDIV_CPU0_COREM1_SHIFT) | + (clkdiv_cpu0_4412[i][3] << EXYNOS4_CLKDIV_CPU0_PERIPH_SHIFT) | + (clkdiv_cpu0_4412[i][4] << EXYNOS4_CLKDIV_CPU0_ATB_SHIFT) | + (clkdiv_cpu0_4412[i][5] << EXYNOS4_CLKDIV_CPU0_PCLKDBG_SHIFT) | + (clkdiv_cpu0_4412[i][6] << EXYNOS4_CLKDIV_CPU0_APLL_SHIFT) | + (clkdiv_cpu0_4412[i][7] << EXYNOS4_CLKDIV_CPU0_CORE2_SHIFT)); + } + + exynos4x12_clkdiv_table[i].clkdiv = tmp; + + tmp = __raw_readl(EXYNOS4_CLKDIV_CPU1); + + if (soc_is_exynos4212()) { + tmp &= ~(EXYNOS4_CLKDIV_CPU1_COPY_MASK | + EXYNOS4_CLKDIV_CPU1_HPM_MASK); + tmp |= ((clkdiv_cpu1_4212[i][0] << EXYNOS4_CLKDIV_CPU1_COPY_SHIFT) | + (clkdiv_cpu1_4212[i][1] << EXYNOS4_CLKDIV_CPU1_HPM_SHIFT)); + } else { + tmp &= ~(EXYNOS4_CLKDIV_CPU1_COPY_MASK | + EXYNOS4_CLKDIV_CPU1_HPM_MASK | + EXYNOS4_CLKDIV_CPU1_CORES_MASK); + tmp |= ((clkdiv_cpu1_4412[i][0] << EXYNOS4_CLKDIV_CPU1_COPY_SHIFT) | + (clkdiv_cpu1_4412[i][1] << EXYNOS4_CLKDIV_CPU1_HPM_SHIFT) | + (clkdiv_cpu1_4412[i][2] << EXYNOS4_CLKDIV_CPU1_CORES_SHIFT)); + } + exynos4x12_clkdiv_table[i].clkdiv1 = tmp; + } + + info->mpll_freq_khz = rate; + info->pm_lock_idx = L5; + info->pll_safe_idx = L7; + info->max_support_idx = max_support_idx; + info->min_support_idx = min_support_idx; + info->cpu_clk = cpu_clk; + info->volt_table = exynos4x12_volt_table; + info->freq_table = exynos4x12_freq_table; + info->set_freq = exynos4x12_set_frequency; + info->need_apll_change = exynos4x12_pms_change; + + return 0; + +err_mout_apll: + clk_put(mout_mpll); +err_mout_mpll: + clk_put(moutcore); +err_moutcore: + clk_put(cpu_clk); + + pr_debug("%s: failed initialization\n", __func__); + return -EINVAL; +} +EXPORT_SYMBOL(exynos4x12_cpufreq_init); diff --git a/drivers/cpufreq/exynos5250-cpufreq.c b/drivers/cpufreq/exynos5250-cpufreq.c new file mode 100644 index 000000000000..a88331644ebf --- /dev/null +++ b/drivers/cpufreq/exynos5250-cpufreq.c @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2010-20122Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS5250 - CPU frequency scaling support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/cpufreq.h> + +#include <mach/map.h> +#include <mach/regs-clock.h> +#include <mach/cpufreq.h> + +#define CPUFREQ_LEVEL_END (L15 + 1) + +static int max_support_idx; +static int min_support_idx = (CPUFREQ_LEVEL_END - 1); +static struct clk *cpu_clk; +static struct clk *moutcore; +static struct clk *mout_mpll; +static struct clk *mout_apll; + +struct cpufreq_clkdiv { + unsigned int index; + unsigned int clkdiv; + unsigned int clkdiv1; +}; + +static unsigned int exynos5250_volt_table[CPUFREQ_LEVEL_END]; + +static struct cpufreq_frequency_table exynos5250_freq_table[] = { + {L0, 1700 * 1000}, + {L1, 1600 * 1000}, + {L2, 1500 * 1000}, + {L3, 1400 * 1000}, + {L4, 1300 * 1000}, + {L5, 1200 * 1000}, + {L6, 1100 * 1000}, + {L7, 1000 * 1000}, + {L8, 900 * 1000}, + {L9, 800 * 1000}, + {L10, 700 * 1000}, + {L11, 600 * 1000}, + {L12, 500 * 1000}, + {L13, 400 * 1000}, + {L14, 300 * 1000}, + {L15, 200 * 1000}, + {0, CPUFREQ_TABLE_END}, +}; + +static struct cpufreq_clkdiv exynos5250_clkdiv_table[CPUFREQ_LEVEL_END]; + +static unsigned int clkdiv_cpu0_5250[CPUFREQ_LEVEL_END][8] = { + /* + * Clock divider value for following + * { ARM, CPUD, ACP, PERIPH, ATB, PCLK_DBG, APLL, ARM2 } + */ + { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1700 MHz - N/A */ + { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1600 MHz - N/A */ + { 0, 3, 7, 7, 5, 1, 3, 0 }, /* 1500 MHz - N/A */ + { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1400 MHz */ + { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1300 MHz */ + { 0, 3, 7, 7, 5, 1, 3, 0 }, /* 1200 MHz */ + { 0, 2, 7, 7, 5, 1, 2, 0 }, /* 1100 MHz */ + { 0, 2, 7, 7, 4, 1, 2, 0 }, /* 1000 MHz */ + { 0, 2, 7, 7, 4, 1, 2, 0 }, /* 900 MHz */ + { 0, 2, 7, 7, 3, 1, 1, 0 }, /* 800 MHz */ + { 0, 1, 7, 7, 3, 1, 1, 0 }, /* 700 MHz */ + { 0, 1, 7, 7, 2, 1, 1, 0 }, /* 600 MHz */ + { 0, 1, 7, 7, 2, 1, 1, 0 }, /* 500 MHz */ + { 0, 1, 7, 7, 1, 1, 1, 0 }, /* 400 MHz */ + { 0, 1, 7, 7, 1, 1, 1, 0 }, /* 300 MHz */ + { 0, 1, 7, 7, 1, 1, 1, 0 }, /* 200 MHz */ +}; + +static unsigned int clkdiv_cpu1_5250[CPUFREQ_LEVEL_END][2] = { + /* Clock divider value for following + * { COPY, HPM } + */ + { 0, 2 }, /* 1700 MHz - N/A */ + { 0, 2 }, /* 1600 MHz - N/A */ + { 0, 2 }, /* 1500 MHz - N/A */ + { 0, 2 }, /* 1400 MHz */ + { 0, 2 }, /* 1300 MHz */ + { 0, 2 }, /* 1200 MHz */ + { 0, 2 }, /* 1100 MHz */ + { 0, 2 }, /* 1000 MHz */ + { 0, 2 }, /* 900 MHz */ + { 0, 2 }, /* 800 MHz */ + { 0, 2 }, /* 700 MHz */ + { 0, 2 }, /* 600 MHz */ + { 0, 2 }, /* 500 MHz */ + { 0, 2 }, /* 400 MHz */ + { 0, 2 }, /* 300 MHz */ + { 0, 2 }, /* 200 MHz */ +}; + +static unsigned int exynos5_apll_pms_table[CPUFREQ_LEVEL_END] = { + (0), /* 1700 MHz - N/A */ + (0), /* 1600 MHz - N/A */ + (0), /* 1500 MHz - N/A */ + (0), /* 1400 MHz */ + ((325 << 16) | (6 << 8) | 0), /* 1300 MHz */ + ((200 << 16) | (4 << 8) | 0), /* 1200 MHz */ + ((275 << 16) | (6 << 8) | 0), /* 1100 MHz */ + ((125 << 16) | (3 << 8) | 0), /* 1000 MHz */ + ((150 << 16) | (4 << 8) | 0), /* 900 MHz */ + ((100 << 16) | (3 << 8) | 0), /* 800 MHz */ + ((175 << 16) | (3 << 8) | 1), /* 700 MHz */ + ((200 << 16) | (4 << 8) | 1), /* 600 MHz */ + ((125 << 16) | (3 << 8) | 1), /* 500 MHz */ + ((100 << 16) | (3 << 8) | 1), /* 400 MHz */ + ((200 << 16) | (4 << 8) | 2), /* 300 MHz */ + ((100 << 16) | (3 << 8) | 2), /* 200 MHz */ +}; + +/* ASV group voltage table */ +static const unsigned int asv_voltage_5250[CPUFREQ_LEVEL_END] = { + 0, 0, 0, 0, 0, 0, 0, /* 1700 MHz ~ 1100 MHz Not supported */ + 1175000, 1125000, 1075000, 1050000, 1000000, + 950000, 925000, 925000, 900000 +}; + +static void set_clkdiv(unsigned int div_index) +{ + unsigned int tmp; + + /* Change Divider - CPU0 */ + + tmp = exynos5250_clkdiv_table[div_index].clkdiv; + + __raw_writel(tmp, EXYNOS5_CLKDIV_CPU0); + + while (__raw_readl(EXYNOS5_CLKDIV_STATCPU0) & 0x11111111) + cpu_relax(); + + /* Change Divider - CPU1 */ + tmp = exynos5250_clkdiv_table[div_index].clkdiv1; + + __raw_writel(tmp, EXYNOS5_CLKDIV_CPU1); + + while (__raw_readl(EXYNOS5_CLKDIV_STATCPU1) & 0x11) + cpu_relax(); +} + +static void set_apll(unsigned int new_index, + unsigned int old_index) +{ + unsigned int tmp, pdiv; + + /* 1. MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */ + clk_set_parent(moutcore, mout_mpll); + + do { + cpu_relax(); + tmp = (__raw_readl(EXYNOS5_CLKMUX_STATCPU) >> 16); + tmp &= 0x7; + } while (tmp != 0x2); + + /* 2. Set APLL Lock time */ + pdiv = ((exynos5_apll_pms_table[new_index] >> 8) & 0x3f); + + __raw_writel((pdiv * 250), EXYNOS5_APLL_LOCK); + + /* 3. Change PLL PMS values */ + tmp = __raw_readl(EXYNOS5_APLL_CON0); + tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0)); + tmp |= exynos5_apll_pms_table[new_index]; + __raw_writel(tmp, EXYNOS5_APLL_CON0); + + /* 4. wait_lock_time */ + do { + cpu_relax(); + tmp = __raw_readl(EXYNOS5_APLL_CON0); + } while (!(tmp & (0x1 << 29))); + + /* 5. MUX_CORE_SEL = APLL */ + clk_set_parent(moutcore, mout_apll); + + do { + cpu_relax(); + tmp = __raw_readl(EXYNOS5_CLKMUX_STATCPU); + tmp &= (0x7 << 16); + } while (tmp != (0x1 << 16)); + +} + +bool exynos5250_pms_change(unsigned int old_index, unsigned int new_index) +{ + unsigned int old_pm = (exynos5_apll_pms_table[old_index] >> 8); + unsigned int new_pm = (exynos5_apll_pms_table[new_index] >> 8); + + return (old_pm == new_pm) ? 0 : 1; +} + +static void exynos5250_set_frequency(unsigned int old_index, + unsigned int new_index) +{ + unsigned int tmp; + + if (old_index > new_index) { + if (!exynos5250_pms_change(old_index, new_index)) { + /* 1. Change the system clock divider values */ + set_clkdiv(new_index); + /* 2. Change just s value in apll m,p,s value */ + tmp = __raw_readl(EXYNOS5_APLL_CON0); + tmp &= ~(0x7 << 0); + tmp |= (exynos5_apll_pms_table[new_index] & 0x7); + __raw_writel(tmp, EXYNOS5_APLL_CON0); + + } else { + /* Clock Configuration Procedure */ + /* 1. Change the system clock divider values */ + set_clkdiv(new_index); + /* 2. Change the apll m,p,s value */ + set_apll(new_index, old_index); + } + } else if (old_index < new_index) { + if (!exynos5250_pms_change(old_index, new_index)) { + /* 1. Change just s value in apll m,p,s value */ + tmp = __raw_readl(EXYNOS5_APLL_CON0); + tmp &= ~(0x7 << 0); + tmp |= (exynos5_apll_pms_table[new_index] & 0x7); + __raw_writel(tmp, EXYNOS5_APLL_CON0); + /* 2. Change the system clock divider values */ + set_clkdiv(new_index); + } else { + /* Clock Configuration Procedure */ + /* 1. Change the apll m,p,s value */ + set_apll(new_index, old_index); + /* 2. Change the system clock divider values */ + set_clkdiv(new_index); + } + } +} + +static void __init set_volt_table(void) +{ + unsigned int i; + + exynos5250_freq_table[L0].frequency = CPUFREQ_ENTRY_INVALID; + exynos5250_freq_table[L1].frequency = CPUFREQ_ENTRY_INVALID; + exynos5250_freq_table[L2].frequency = CPUFREQ_ENTRY_INVALID; + exynos5250_freq_table[L3].frequency = CPUFREQ_ENTRY_INVALID; + exynos5250_freq_table[L4].frequency = CPUFREQ_ENTRY_INVALID; + exynos5250_freq_table[L5].frequency = CPUFREQ_ENTRY_INVALID; + exynos5250_freq_table[L6].frequency = CPUFREQ_ENTRY_INVALID; + + max_support_idx = L7; + + for (i = 0 ; i < CPUFREQ_LEVEL_END ; i++) + exynos5250_volt_table[i] = asv_voltage_5250[i]; +} + +int exynos5250_cpufreq_init(struct exynos_dvfs_info *info) +{ + int i; + unsigned int tmp; + unsigned long rate; + + set_volt_table(); + + cpu_clk = clk_get(NULL, "armclk"); + if (IS_ERR(cpu_clk)) + return PTR_ERR(cpu_clk); + + moutcore = clk_get(NULL, "mout_cpu"); + if (IS_ERR(moutcore)) + goto err_moutcore; + + mout_mpll = clk_get(NULL, "mout_mpll"); + if (IS_ERR(mout_mpll)) + goto err_mout_mpll; + + rate = clk_get_rate(mout_mpll) / 1000; + + mout_apll = clk_get(NULL, "mout_apll"); + if (IS_ERR(mout_apll)) + goto err_mout_apll; + + for (i = L0; i < CPUFREQ_LEVEL_END; i++) { + + exynos5250_clkdiv_table[i].index = i; + + tmp = __raw_readl(EXYNOS5_CLKDIV_CPU0); + + tmp &= ~((0x7 << 0) | (0x7 << 4) | (0x7 << 8) | + (0x7 << 12) | (0x7 << 16) | (0x7 << 20) | + (0x7 << 24) | (0x7 << 28)); + + tmp |= ((clkdiv_cpu0_5250[i][0] << 0) | + (clkdiv_cpu0_5250[i][1] << 4) | + (clkdiv_cpu0_5250[i][2] << 8) | + (clkdiv_cpu0_5250[i][3] << 12) | + (clkdiv_cpu0_5250[i][4] << 16) | + (clkdiv_cpu0_5250[i][5] << 20) | + (clkdiv_cpu0_5250[i][6] << 24) | + (clkdiv_cpu0_5250[i][7] << 28)); + + exynos5250_clkdiv_table[i].clkdiv = tmp; + + tmp = __raw_readl(EXYNOS5_CLKDIV_CPU1); + + tmp &= ~((0x7 << 0) | (0x7 << 4)); + + tmp |= ((clkdiv_cpu1_5250[i][0] << 0) | + (clkdiv_cpu1_5250[i][1] << 4)); + + exynos5250_clkdiv_table[i].clkdiv1 = tmp; + } + + info->mpll_freq_khz = rate; + /* 1000Mhz */ + info->pm_lock_idx = L7; + /* 800Mhz */ + info->pll_safe_idx = L9; + info->max_support_idx = max_support_idx; + info->min_support_idx = min_support_idx; + info->cpu_clk = cpu_clk; + info->volt_table = exynos5250_volt_table; + info->freq_table = exynos5250_freq_table; + info->set_freq = exynos5250_set_frequency; + info->need_apll_change = exynos5250_pms_change; + + return 0; + +err_mout_apll: + clk_put(mout_mpll); +err_mout_mpll: + clk_put(moutcore); +err_moutcore: + clk_put(cpu_clk); + + pr_err("%s: failed initialization\n", __func__); + return -EINVAL; +} +EXPORT_SYMBOL(exynos5250_cpufreq_init); diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index 5d04c57aae30..67bbb06d0460 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -25,6 +25,7 @@ #include <linux/opp.h> #include <linux/cpu.h> #include <linux/module.h> +#include <linux/regulator/consumer.h> #include <asm/system.h> #include <asm/smp_plat.h> @@ -37,6 +38,9 @@ #include <mach/hardware.h> +/* OPP tolerance in percentage */ +#define OPP_TOLERANCE 4 + #ifdef CONFIG_SMP struct lpj_info { unsigned long ref; @@ -52,6 +56,7 @@ static atomic_t freq_table_users = ATOMIC_INIT(0); static struct clk *mpu_clk; static char *mpu_clk_name; static struct device *mpu_dev; +static struct regulator *mpu_reg; static int omap_verify_speed(struct cpufreq_policy *policy) { @@ -76,8 +81,10 @@ static int omap_target(struct cpufreq_policy *policy, unsigned int relation) { unsigned int i; - int ret = 0; + int r, ret = 0; struct cpufreq_freqs freqs; + struct opp *opp; + unsigned long freq, volt = 0, volt_old = 0, tol = 0; if (!freq_table) { dev_err(mpu_dev, "%s: cpu%d: no freq table!\n", __func__, @@ -111,13 +118,50 @@ static int omap_target(struct cpufreq_policy *policy, cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); } -#ifdef CONFIG_CPU_FREQ_DEBUG - pr_info("cpufreq-omap: transition: %u --> %u\n", freqs.old, freqs.new); -#endif + freq = freqs.new * 1000; + + if (mpu_reg) { + opp = opp_find_freq_ceil(mpu_dev, &freq); + if (IS_ERR(opp)) { + dev_err(mpu_dev, "%s: unable to find MPU OPP for %d\n", + __func__, freqs.new); + return -EINVAL; + } + volt = opp_get_voltage(opp); + tol = volt * OPP_TOLERANCE / 100; + volt_old = regulator_get_voltage(mpu_reg); + } + + dev_dbg(mpu_dev, "cpufreq-omap: %u MHz, %ld mV --> %u MHz, %ld mV\n", + freqs.old / 1000, volt_old ? volt_old / 1000 : -1, + freqs.new / 1000, volt ? volt / 1000 : -1); + + /* scaling up? scale voltage before frequency */ + if (mpu_reg && (freqs.new > freqs.old)) { + r = regulator_set_voltage(mpu_reg, volt - tol, volt + tol); + if (r < 0) { + dev_warn(mpu_dev, "%s: unable to scale voltage up.\n", + __func__); + freqs.new = freqs.old; + goto done; + } + } ret = clk_set_rate(mpu_clk, freqs.new * 1000); - freqs.new = omap_getspeed(policy->cpu); + /* scaling down? scale voltage after frequency */ + if (mpu_reg && (freqs.new < freqs.old)) { + r = regulator_set_voltage(mpu_reg, volt - tol, volt + tol); + if (r < 0) { + dev_warn(mpu_dev, "%s: unable to scale voltage down.\n", + __func__); + ret = clk_set_rate(mpu_clk, freqs.old * 1000); + freqs.new = freqs.old; + goto done; + } + } + + freqs.new = omap_getspeed(policy->cpu); #ifdef CONFIG_SMP /* * Note that loops_per_jiffy is not updated on SMP systems in @@ -144,6 +188,7 @@ static int omap_target(struct cpufreq_policy *policy, freqs.new); #endif +done: /* notifiers */ for_each_cpu(i, policy->cpus) { freqs.cpu = i; @@ -260,6 +305,23 @@ static int __init omap_cpufreq_init(void) return -EINVAL; } + mpu_reg = regulator_get(mpu_dev, "vcc"); + if (IS_ERR(mpu_reg)) { + pr_warning("%s: unable to get MPU regulator\n", __func__); + mpu_reg = NULL; + } else { + /* + * Ensure physical regulator is present. + * (e.g. could be dummy regulator.) + */ + if (regulator_get_voltage(mpu_reg) < 0) { + pr_warn("%s: physical regulator not present for MPU\n", + __func__); + regulator_put(mpu_reg); + mpu_reg = NULL; + } + } + return cpufreq_register_driver(&omap_driver); } diff --git a/drivers/cpufreq/s3c2416-cpufreq.c b/drivers/cpufreq/s3c2416-cpufreq.c new file mode 100644 index 000000000000..50d2f15a3c8a --- /dev/null +++ b/drivers/cpufreq/s3c2416-cpufreq.c @@ -0,0 +1,542 @@ +/* + * S3C2416/2450 CPUfreq Support + * + * Copyright 2011 Heiko Stuebner <heiko@sntech.de> + * + * based on s3c64xx_cpufreq.c + * + * Copyright 2009 Wolfson Microelectronics plc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/cpufreq.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/regulator/consumer.h> +#include <linux/reboot.h> +#include <linux/module.h> + +static DEFINE_MUTEX(cpufreq_lock); + +struct s3c2416_data { + struct clk *armdiv; + struct clk *armclk; + struct clk *hclk; + + unsigned long regulator_latency; +#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE + struct regulator *vddarm; +#endif + + struct cpufreq_frequency_table *freq_table; + + bool is_dvs; + bool disable_dvs; +}; + +static struct s3c2416_data s3c2416_cpufreq; + +struct s3c2416_dvfs { + unsigned int vddarm_min; + unsigned int vddarm_max; +}; + +/* pseudo-frequency for dvs mode */ +#define FREQ_DVS 132333 + +/* frequency to sleep and reboot in + * it's essential to leave dvs, as some boards do not reconfigure the + * regulator on reboot + */ +#define FREQ_SLEEP 133333 + +/* Sources for the ARMCLK */ +#define SOURCE_HCLK 0 +#define SOURCE_ARMDIV 1 + +#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE +/* S3C2416 only supports changing the voltage in the dvs-mode. + * Voltages down to 1.0V seem to work, so we take what the regulator + * can get us. + */ +static struct s3c2416_dvfs s3c2416_dvfs_table[] = { + [SOURCE_HCLK] = { 950000, 1250000 }, + [SOURCE_ARMDIV] = { 1250000, 1350000 }, +}; +#endif + +static struct cpufreq_frequency_table s3c2416_freq_table[] = { + { SOURCE_HCLK, FREQ_DVS }, + { SOURCE_ARMDIV, 133333 }, + { SOURCE_ARMDIV, 266666 }, + { SOURCE_ARMDIV, 400000 }, + { 0, CPUFREQ_TABLE_END }, +}; + +static struct cpufreq_frequency_table s3c2450_freq_table[] = { + { SOURCE_HCLK, FREQ_DVS }, + { SOURCE_ARMDIV, 133500 }, + { SOURCE_ARMDIV, 267000 }, + { SOURCE_ARMDIV, 534000 }, + { 0, CPUFREQ_TABLE_END }, +}; + +static int s3c2416_cpufreq_verify_speed(struct cpufreq_policy *policy) +{ + struct s3c2416_data *s3c_freq = &s3c2416_cpufreq; + + if (policy->cpu != 0) + return -EINVAL; + + return cpufreq_frequency_table_verify(policy, s3c_freq->freq_table); +} + +static unsigned int s3c2416_cpufreq_get_speed(unsigned int cpu) +{ + struct s3c2416_data *s3c_freq = &s3c2416_cpufreq; + + if (cpu != 0) + return 0; + + /* return our pseudo-frequency when in dvs mode */ + if (s3c_freq->is_dvs) + return FREQ_DVS; + + return clk_get_rate(s3c_freq->armclk) / 1000; +} + +static int s3c2416_cpufreq_set_armdiv(struct s3c2416_data *s3c_freq, + unsigned int freq) +{ + int ret; + + if (clk_get_rate(s3c_freq->armdiv) / 1000 != freq) { + ret = clk_set_rate(s3c_freq->armdiv, freq * 1000); + if (ret < 0) { + pr_err("cpufreq: Failed to set armdiv rate %dkHz: %d\n", + freq, ret); + return ret; + } + } + + return 0; +} + +static int s3c2416_cpufreq_enter_dvs(struct s3c2416_data *s3c_freq, int idx) +{ +#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE + struct s3c2416_dvfs *dvfs; +#endif + int ret; + + if (s3c_freq->is_dvs) { + pr_debug("cpufreq: already in dvs mode, nothing to do\n"); + return 0; + } + + pr_debug("cpufreq: switching armclk to hclk (%lukHz)\n", + clk_get_rate(s3c_freq->hclk) / 1000); + ret = clk_set_parent(s3c_freq->armclk, s3c_freq->hclk); + if (ret < 0) { + pr_err("cpufreq: Failed to switch armclk to hclk: %d\n", ret); + return ret; + } + +#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE + /* changing the core voltage is only allowed when in dvs mode */ + if (s3c_freq->vddarm) { + dvfs = &s3c2416_dvfs_table[idx]; + + pr_debug("cpufreq: setting regultor to %d-%d\n", + dvfs->vddarm_min, dvfs->vddarm_max); + ret = regulator_set_voltage(s3c_freq->vddarm, + dvfs->vddarm_min, + dvfs->vddarm_max); + + /* when lowering the voltage failed, there is nothing to do */ + if (ret != 0) + pr_err("cpufreq: Failed to set VDDARM: %d\n", ret); + } +#endif + + s3c_freq->is_dvs = 1; + + return 0; +} + +static int s3c2416_cpufreq_leave_dvs(struct s3c2416_data *s3c_freq, int idx) +{ +#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE + struct s3c2416_dvfs *dvfs; +#endif + int ret; + + if (!s3c_freq->is_dvs) { + pr_debug("cpufreq: not in dvs mode, so can't leave\n"); + return 0; + } + +#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE + if (s3c_freq->vddarm) { + dvfs = &s3c2416_dvfs_table[idx]; + + pr_debug("cpufreq: setting regultor to %d-%d\n", + dvfs->vddarm_min, dvfs->vddarm_max); + ret = regulator_set_voltage(s3c_freq->vddarm, + dvfs->vddarm_min, + dvfs->vddarm_max); + if (ret != 0) { + pr_err("cpufreq: Failed to set VDDARM: %d\n", ret); + return ret; + } + } +#endif + + /* force armdiv to hclk frequency for transition from dvs*/ + if (clk_get_rate(s3c_freq->armdiv) > clk_get_rate(s3c_freq->hclk)) { + pr_debug("cpufreq: force armdiv to hclk frequency (%lukHz)\n", + clk_get_rate(s3c_freq->hclk) / 1000); + ret = s3c2416_cpufreq_set_armdiv(s3c_freq, + clk_get_rate(s3c_freq->hclk) / 1000); + if (ret < 0) { + pr_err("cpufreq: Failed to to set the armdiv to %lukHz: %d\n", + clk_get_rate(s3c_freq->hclk) / 1000, ret); + return ret; + } + } + + pr_debug("cpufreq: switching armclk parent to armdiv (%lukHz)\n", + clk_get_rate(s3c_freq->armdiv) / 1000); + + ret = clk_set_parent(s3c_freq->armclk, s3c_freq->armdiv); + if (ret < 0) { + pr_err("cpufreq: Failed to switch armclk clock parent to armdiv: %d\n", + ret); + return ret; + } + + s3c_freq->is_dvs = 0; + + return 0; +} + +static int s3c2416_cpufreq_set_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + struct s3c2416_data *s3c_freq = &s3c2416_cpufreq; + struct cpufreq_freqs freqs; + int idx, ret, to_dvs = 0; + unsigned int i; + + mutex_lock(&cpufreq_lock); + + pr_debug("cpufreq: to %dKHz, relation %d\n", target_freq, relation); + + ret = cpufreq_frequency_table_target(policy, s3c_freq->freq_table, + target_freq, relation, &i); + if (ret != 0) + goto out; + + idx = s3c_freq->freq_table[i].index; + + if (idx == SOURCE_HCLK) + to_dvs = 1; + + /* switching to dvs when it's not allowed */ + if (to_dvs && s3c_freq->disable_dvs) { + pr_debug("cpufreq: entering dvs mode not allowed\n"); + ret = -EINVAL; + goto out; + } + + freqs.cpu = 0; + freqs.flags = 0; + freqs.old = s3c_freq->is_dvs ? FREQ_DVS + : clk_get_rate(s3c_freq->armclk) / 1000; + + /* When leavin dvs mode, always switch the armdiv to the hclk rate + * The S3C2416 has stability issues when switching directly to + * higher frequencies. + */ + freqs.new = (s3c_freq->is_dvs && !to_dvs) + ? clk_get_rate(s3c_freq->hclk) / 1000 + : s3c_freq->freq_table[i].frequency; + + pr_debug("cpufreq: Transition %d-%dkHz\n", freqs.old, freqs.new); + + if (!to_dvs && freqs.old == freqs.new) + goto out; + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + if (to_dvs) { + pr_debug("cpufreq: enter dvs\n"); + ret = s3c2416_cpufreq_enter_dvs(s3c_freq, idx); + } else if (s3c_freq->is_dvs) { + pr_debug("cpufreq: leave dvs\n"); + ret = s3c2416_cpufreq_leave_dvs(s3c_freq, idx); + } else { + pr_debug("cpufreq: change armdiv to %dkHz\n", freqs.new); + ret = s3c2416_cpufreq_set_armdiv(s3c_freq, freqs.new); + } + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + +out: + mutex_unlock(&cpufreq_lock); + + return ret; +} + +#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE +static void __init s3c2416_cpufreq_cfg_regulator(struct s3c2416_data *s3c_freq) +{ + int count, v, i, found; + struct cpufreq_frequency_table *freq; + struct s3c2416_dvfs *dvfs; + + count = regulator_count_voltages(s3c_freq->vddarm); + if (count < 0) { + pr_err("cpufreq: Unable to check supported voltages\n"); + return; + } + + freq = s3c_freq->freq_table; + while (count > 0 && freq->frequency != CPUFREQ_TABLE_END) { + if (freq->frequency == CPUFREQ_ENTRY_INVALID) + continue; + + dvfs = &s3c2416_dvfs_table[freq->index]; + found = 0; + + /* Check only the min-voltage, more is always ok on S3C2416 */ + for (i = 0; i < count; i++) { + v = regulator_list_voltage(s3c_freq->vddarm, i); + if (v >= dvfs->vddarm_min) + found = 1; + } + + if (!found) { + pr_debug("cpufreq: %dkHz unsupported by regulator\n", + freq->frequency); + freq->frequency = CPUFREQ_ENTRY_INVALID; + } + + freq++; + } + + /* Guessed */ + s3c_freq->regulator_latency = 1 * 1000 * 1000; +} +#endif + +static int s3c2416_cpufreq_reboot_notifier_evt(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct s3c2416_data *s3c_freq = &s3c2416_cpufreq; + int ret; + + mutex_lock(&cpufreq_lock); + + /* disable further changes */ + s3c_freq->disable_dvs = 1; + + mutex_unlock(&cpufreq_lock); + + /* some boards don't reconfigure the regulator on reboot, which + * could lead to undervolting the cpu when the clock is reset. + * Therefore we always leave the DVS mode on reboot. + */ + if (s3c_freq->is_dvs) { + pr_debug("cpufreq: leave dvs on reboot\n"); + ret = cpufreq_driver_target(cpufreq_cpu_get(0), FREQ_SLEEP, 0); + if (ret < 0) + return NOTIFY_BAD; + } + + return NOTIFY_DONE; +} + +static struct notifier_block s3c2416_cpufreq_reboot_notifier = { + .notifier_call = s3c2416_cpufreq_reboot_notifier_evt, +}; + +static int __init s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy) +{ + struct s3c2416_data *s3c_freq = &s3c2416_cpufreq; + struct cpufreq_frequency_table *freq; + struct clk *msysclk; + unsigned long rate; + int ret; + + if (policy->cpu != 0) + return -EINVAL; + + msysclk = clk_get(NULL, "msysclk"); + if (IS_ERR(msysclk)) { + ret = PTR_ERR(msysclk); + pr_err("cpufreq: Unable to obtain msysclk: %d\n", ret); + return ret; + } + + /* + * S3C2416 and S3C2450 share the same processor-ID and also provide no + * other means to distinguish them other than through the rate of + * msysclk. On S3C2416 msysclk runs at 800MHz and on S3C2450 at 533MHz. + */ + rate = clk_get_rate(msysclk); + if (rate == 800 * 1000 * 1000) { + pr_info("cpufreq: msysclk running at %lukHz, using S3C2416 frequency table\n", + rate / 1000); + s3c_freq->freq_table = s3c2416_freq_table; + policy->cpuinfo.max_freq = 400000; + } else if (rate / 1000 == 534000) { + pr_info("cpufreq: msysclk running at %lukHz, using S3C2450 frequency table\n", + rate / 1000); + s3c_freq->freq_table = s3c2450_freq_table; + policy->cpuinfo.max_freq = 534000; + } + + /* not needed anymore */ + clk_put(msysclk); + + if (s3c_freq->freq_table == NULL) { + pr_err("cpufreq: No frequency information for this CPU, msysclk at %lukHz\n", + rate / 1000); + return -ENODEV; + } + + s3c_freq->is_dvs = 0; + + s3c_freq->armdiv = clk_get(NULL, "armdiv"); + if (IS_ERR(s3c_freq->armdiv)) { + ret = PTR_ERR(s3c_freq->armdiv); + pr_err("cpufreq: Unable to obtain ARMDIV: %d\n", ret); + return ret; + } + + s3c_freq->hclk = clk_get(NULL, "hclk"); + if (IS_ERR(s3c_freq->hclk)) { + ret = PTR_ERR(s3c_freq->hclk); + pr_err("cpufreq: Unable to obtain HCLK: %d\n", ret); + goto err_hclk; + } + + /* chech hclk rate, we only support the common 133MHz for now + * hclk could also run at 66MHz, but this not often used + */ + rate = clk_get_rate(s3c_freq->hclk); + if (rate < 133 * 1000 * 1000) { + pr_err("cpufreq: HCLK not at 133MHz\n"); + clk_put(s3c_freq->hclk); + ret = -EINVAL; + goto err_armclk; + } + + s3c_freq->armclk = clk_get(NULL, "armclk"); + if (IS_ERR(s3c_freq->armclk)) { + ret = PTR_ERR(s3c_freq->armclk); + pr_err("cpufreq: Unable to obtain ARMCLK: %d\n", ret); + goto err_armclk; + } + +#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE + s3c_freq->vddarm = regulator_get(NULL, "vddarm"); + if (IS_ERR(s3c_freq->vddarm)) { + ret = PTR_ERR(s3c_freq->vddarm); + pr_err("cpufreq: Failed to obtain VDDARM: %d\n", ret); + goto err_vddarm; + } + + s3c2416_cpufreq_cfg_regulator(s3c_freq); +#else + s3c_freq->regulator_latency = 0; +#endif + + freq = s3c_freq->freq_table; + while (freq->frequency != CPUFREQ_TABLE_END) { + /* special handling for dvs mode */ + if (freq->index == 0) { + if (!s3c_freq->hclk) { + pr_debug("cpufreq: %dkHz unsupported as it would need unavailable dvs mode\n", + freq->frequency); + freq->frequency = CPUFREQ_ENTRY_INVALID; + } else { + freq++; + continue; + } + } + + /* Check for frequencies we can generate */ + rate = clk_round_rate(s3c_freq->armdiv, + freq->frequency * 1000); + rate /= 1000; + if (rate != freq->frequency) { + pr_debug("cpufreq: %dkHz unsupported by clock (clk_round_rate return %lu)\n", + freq->frequency, rate); + freq->frequency = CPUFREQ_ENTRY_INVALID; + } + + freq++; + } + + policy->cur = clk_get_rate(s3c_freq->armclk) / 1000; + + /* Datasheet says PLL stabalisation time must be at least 300us, + * so but add some fudge. (reference in LOCKCON0 register description) + */ + policy->cpuinfo.transition_latency = (500 * 1000) + + s3c_freq->regulator_latency; + + ret = cpufreq_frequency_table_cpuinfo(policy, s3c_freq->freq_table); + if (ret) + goto err_freq_table; + + cpufreq_frequency_table_get_attr(s3c_freq->freq_table, 0); + + register_reboot_notifier(&s3c2416_cpufreq_reboot_notifier); + + return 0; + +err_freq_table: +#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE + regulator_put(s3c_freq->vddarm); +err_vddarm: +#endif + clk_put(s3c_freq->armclk); +err_armclk: + clk_put(s3c_freq->hclk); +err_hclk: + clk_put(s3c_freq->armdiv); + + return ret; +} + +static struct freq_attr *s3c2416_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver s3c2416_cpufreq_driver = { + .owner = THIS_MODULE, + .flags = 0, + .verify = s3c2416_cpufreq_verify_speed, + .target = s3c2416_cpufreq_set_target, + .get = s3c2416_cpufreq_get_speed, + .init = s3c2416_cpufreq_driver_init, + .name = "s3c2416", + .attr = s3c2416_cpufreq_attr, +}; + +static int __init s3c2416_cpufreq_init(void) +{ + return cpufreq_register_driver(&s3c2416_cpufreq_driver); +} +module_init(s3c2416_cpufreq_init); diff --git a/drivers/cpufreq/s3c64xx-cpufreq.c b/drivers/cpufreq/s3c64xx-cpufreq.c index a5e72cb5f53c..6f9490b3c356 100644 --- a/drivers/cpufreq/s3c64xx-cpufreq.c +++ b/drivers/cpufreq/s3c64xx-cpufreq.c @@ -217,13 +217,6 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy) } else { s3c64xx_cpufreq_config_regulator(); } - - vddint = regulator_get(NULL, "vddint"); - if (IS_ERR(vddint)) { - ret = PTR_ERR(vddint); - pr_err("Failed to obtain VDDINT: %d\n", ret); - vddint = NULL; - } #endif freq = s3c64xx_freq_table; diff --git a/drivers/devfreq/exynos4_bus.c b/drivers/devfreq/exynos4_bus.c index 1a361e99965a..88ddc77a9bb1 100644 --- a/drivers/devfreq/exynos4_bus.c +++ b/drivers/devfreq/exynos4_bus.c @@ -311,51 +311,51 @@ static int exynos4210_set_busclk(struct busfreq_data *data, struct opp *opp) /* Change Divider - DMC0 */ tmp = data->dmc_divtable[index]; - __raw_writel(tmp, S5P_CLKDIV_DMC0); + __raw_writel(tmp, EXYNOS4_CLKDIV_DMC0); do { - tmp = __raw_readl(S5P_CLKDIV_STAT_DMC0); + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_DMC0); } while (tmp & 0x11111111); /* Change Divider - TOP */ tmp = data->top_divtable[index]; - __raw_writel(tmp, S5P_CLKDIV_TOP); + __raw_writel(tmp, EXYNOS4_CLKDIV_TOP); do { - tmp = __raw_readl(S5P_CLKDIV_STAT_TOP); + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_TOP); } while (tmp & 0x11111); /* Change Divider - LEFTBUS */ - tmp = __raw_readl(S5P_CLKDIV_LEFTBUS); + tmp = __raw_readl(EXYNOS4_CLKDIV_LEFTBUS); - tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK); + tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK); tmp |= ((exynos4210_clkdiv_lr_bus[index][0] << - S5P_CLKDIV_BUS_GDLR_SHIFT) | + EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) | (exynos4210_clkdiv_lr_bus[index][1] << - S5P_CLKDIV_BUS_GPLR_SHIFT)); + EXYNOS4_CLKDIV_BUS_GPLR_SHIFT)); - __raw_writel(tmp, S5P_CLKDIV_LEFTBUS); + __raw_writel(tmp, EXYNOS4_CLKDIV_LEFTBUS); do { - tmp = __raw_readl(S5P_CLKDIV_STAT_LEFTBUS); + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_LEFTBUS); } while (tmp & 0x11); /* Change Divider - RIGHTBUS */ - tmp = __raw_readl(S5P_CLKDIV_RIGHTBUS); + tmp = __raw_readl(EXYNOS4_CLKDIV_RIGHTBUS); - tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK); + tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK); tmp |= ((exynos4210_clkdiv_lr_bus[index][0] << - S5P_CLKDIV_BUS_GDLR_SHIFT) | + EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) | (exynos4210_clkdiv_lr_bus[index][1] << - S5P_CLKDIV_BUS_GPLR_SHIFT)); + EXYNOS4_CLKDIV_BUS_GPLR_SHIFT)); - __raw_writel(tmp, S5P_CLKDIV_RIGHTBUS); + __raw_writel(tmp, EXYNOS4_CLKDIV_RIGHTBUS); do { - tmp = __raw_readl(S5P_CLKDIV_STAT_RIGHTBUS); + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_RIGHTBUS); } while (tmp & 0x11); return 0; @@ -376,137 +376,137 @@ static int exynos4x12_set_busclk(struct busfreq_data *data, struct opp *opp) /* Change Divider - DMC0 */ tmp = data->dmc_divtable[index]; - __raw_writel(tmp, S5P_CLKDIV_DMC0); + __raw_writel(tmp, EXYNOS4_CLKDIV_DMC0); do { - tmp = __raw_readl(S5P_CLKDIV_STAT_DMC0); + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_DMC0); } while (tmp & 0x11111111); /* Change Divider - DMC1 */ - tmp = __raw_readl(S5P_CLKDIV_DMC1); + tmp = __raw_readl(EXYNOS4_CLKDIV_DMC1); - tmp &= ~(S5P_CLKDIV_DMC1_G2D_ACP_MASK | - S5P_CLKDIV_DMC1_C2C_MASK | - S5P_CLKDIV_DMC1_C2CACLK_MASK); + tmp &= ~(EXYNOS4_CLKDIV_DMC1_G2D_ACP_MASK | + EXYNOS4_CLKDIV_DMC1_C2C_MASK | + EXYNOS4_CLKDIV_DMC1_C2CACLK_MASK); tmp |= ((exynos4x12_clkdiv_dmc1[index][0] << - S5P_CLKDIV_DMC1_G2D_ACP_SHIFT) | + EXYNOS4_CLKDIV_DMC1_G2D_ACP_SHIFT) | (exynos4x12_clkdiv_dmc1[index][1] << - S5P_CLKDIV_DMC1_C2C_SHIFT) | + EXYNOS4_CLKDIV_DMC1_C2C_SHIFT) | (exynos4x12_clkdiv_dmc1[index][2] << - S5P_CLKDIV_DMC1_C2CACLK_SHIFT)); + EXYNOS4_CLKDIV_DMC1_C2CACLK_SHIFT)); - __raw_writel(tmp, S5P_CLKDIV_DMC1); + __raw_writel(tmp, EXYNOS4_CLKDIV_DMC1); do { - tmp = __raw_readl(S5P_CLKDIV_STAT_DMC1); + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_DMC1); } while (tmp & 0x111111); /* Change Divider - TOP */ - tmp = __raw_readl(S5P_CLKDIV_TOP); + tmp = __raw_readl(EXYNOS4_CLKDIV_TOP); - tmp &= ~(S5P_CLKDIV_TOP_ACLK266_GPS_MASK | - S5P_CLKDIV_TOP_ACLK100_MASK | - S5P_CLKDIV_TOP_ACLK160_MASK | - S5P_CLKDIV_TOP_ACLK133_MASK | - S5P_CLKDIV_TOP_ONENAND_MASK); + tmp &= ~(EXYNOS4_CLKDIV_TOP_ACLK266_GPS_MASK | + EXYNOS4_CLKDIV_TOP_ACLK100_MASK | + EXYNOS4_CLKDIV_TOP_ACLK160_MASK | + EXYNOS4_CLKDIV_TOP_ACLK133_MASK | + EXYNOS4_CLKDIV_TOP_ONENAND_MASK); tmp |= ((exynos4x12_clkdiv_top[index][0] << - S5P_CLKDIV_TOP_ACLK266_GPS_SHIFT) | + EXYNOS4_CLKDIV_TOP_ACLK266_GPS_SHIFT) | (exynos4x12_clkdiv_top[index][1] << - S5P_CLKDIV_TOP_ACLK100_SHIFT) | + EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT) | (exynos4x12_clkdiv_top[index][2] << - S5P_CLKDIV_TOP_ACLK160_SHIFT) | + EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT) | (exynos4x12_clkdiv_top[index][3] << - S5P_CLKDIV_TOP_ACLK133_SHIFT) | + EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT) | (exynos4x12_clkdiv_top[index][4] << - S5P_CLKDIV_TOP_ONENAND_SHIFT)); + EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT)); - __raw_writel(tmp, S5P_CLKDIV_TOP); + __raw_writel(tmp, EXYNOS4_CLKDIV_TOP); do { - tmp = __raw_readl(S5P_CLKDIV_STAT_TOP); + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_TOP); } while (tmp & 0x11111); /* Change Divider - LEFTBUS */ - tmp = __raw_readl(S5P_CLKDIV_LEFTBUS); + tmp = __raw_readl(EXYNOS4_CLKDIV_LEFTBUS); - tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK); + tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK); tmp |= ((exynos4x12_clkdiv_lr_bus[index][0] << - S5P_CLKDIV_BUS_GDLR_SHIFT) | + EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) | (exynos4x12_clkdiv_lr_bus[index][1] << - S5P_CLKDIV_BUS_GPLR_SHIFT)); + EXYNOS4_CLKDIV_BUS_GPLR_SHIFT)); - __raw_writel(tmp, S5P_CLKDIV_LEFTBUS); + __raw_writel(tmp, EXYNOS4_CLKDIV_LEFTBUS); do { - tmp = __raw_readl(S5P_CLKDIV_STAT_LEFTBUS); + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_LEFTBUS); } while (tmp & 0x11); /* Change Divider - RIGHTBUS */ - tmp = __raw_readl(S5P_CLKDIV_RIGHTBUS); + tmp = __raw_readl(EXYNOS4_CLKDIV_RIGHTBUS); - tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK); + tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK); tmp |= ((exynos4x12_clkdiv_lr_bus[index][0] << - S5P_CLKDIV_BUS_GDLR_SHIFT) | + EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) | (exynos4x12_clkdiv_lr_bus[index][1] << - S5P_CLKDIV_BUS_GPLR_SHIFT)); + EXYNOS4_CLKDIV_BUS_GPLR_SHIFT)); - __raw_writel(tmp, S5P_CLKDIV_RIGHTBUS); + __raw_writel(tmp, EXYNOS4_CLKDIV_RIGHTBUS); do { - tmp = __raw_readl(S5P_CLKDIV_STAT_RIGHTBUS); + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_RIGHTBUS); } while (tmp & 0x11); /* Change Divider - MFC */ - tmp = __raw_readl(S5P_CLKDIV_MFC); + tmp = __raw_readl(EXYNOS4_CLKDIV_MFC); - tmp &= ~(S5P_CLKDIV_MFC_MASK); + tmp &= ~(EXYNOS4_CLKDIV_MFC_MASK); tmp |= ((exynos4x12_clkdiv_sclkip[index][0] << - S5P_CLKDIV_MFC_SHIFT)); + EXYNOS4_CLKDIV_MFC_SHIFT)); - __raw_writel(tmp, S5P_CLKDIV_MFC); + __raw_writel(tmp, EXYNOS4_CLKDIV_MFC); do { - tmp = __raw_readl(S5P_CLKDIV_STAT_MFC); + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_MFC); } while (tmp & 0x1); /* Change Divider - JPEG */ - tmp = __raw_readl(S5P_CLKDIV_CAM1); + tmp = __raw_readl(EXYNOS4_CLKDIV_CAM1); - tmp &= ~(S5P_CLKDIV_CAM1_JPEG_MASK); + tmp &= ~(EXYNOS4_CLKDIV_CAM1_JPEG_MASK); tmp |= ((exynos4x12_clkdiv_sclkip[index][1] << - S5P_CLKDIV_CAM1_JPEG_SHIFT)); + EXYNOS4_CLKDIV_CAM1_JPEG_SHIFT)); - __raw_writel(tmp, S5P_CLKDIV_CAM1); + __raw_writel(tmp, EXYNOS4_CLKDIV_CAM1); do { - tmp = __raw_readl(S5P_CLKDIV_STAT_CAM1); + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_CAM1); } while (tmp & 0x1); /* Change Divider - FIMC0~3 */ - tmp = __raw_readl(S5P_CLKDIV_CAM); + tmp = __raw_readl(EXYNOS4_CLKDIV_CAM); - tmp &= ~(S5P_CLKDIV_CAM_FIMC0_MASK | S5P_CLKDIV_CAM_FIMC1_MASK | - S5P_CLKDIV_CAM_FIMC2_MASK | S5P_CLKDIV_CAM_FIMC3_MASK); + tmp &= ~(EXYNOS4_CLKDIV_CAM_FIMC0_MASK | EXYNOS4_CLKDIV_CAM_FIMC1_MASK | + EXYNOS4_CLKDIV_CAM_FIMC2_MASK | EXYNOS4_CLKDIV_CAM_FIMC3_MASK); tmp |= ((exynos4x12_clkdiv_sclkip[index][2] << - S5P_CLKDIV_CAM_FIMC0_SHIFT) | + EXYNOS4_CLKDIV_CAM_FIMC0_SHIFT) | (exynos4x12_clkdiv_sclkip[index][2] << - S5P_CLKDIV_CAM_FIMC1_SHIFT) | + EXYNOS4_CLKDIV_CAM_FIMC1_SHIFT) | (exynos4x12_clkdiv_sclkip[index][2] << - S5P_CLKDIV_CAM_FIMC2_SHIFT) | + EXYNOS4_CLKDIV_CAM_FIMC2_SHIFT) | (exynos4x12_clkdiv_sclkip[index][2] << - S5P_CLKDIV_CAM_FIMC3_SHIFT)); + EXYNOS4_CLKDIV_CAM_FIMC3_SHIFT)); - __raw_writel(tmp, S5P_CLKDIV_CAM); + __raw_writel(tmp, EXYNOS4_CLKDIV_CAM); do { - tmp = __raw_readl(S5P_CLKDIV_STAT_CAM1); + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_CAM1); } while (tmp & 0x1111); return 0; @@ -760,55 +760,55 @@ static int exynos4210_init_tables(struct busfreq_data *data) int mgrp; int i, err = 0; - tmp = __raw_readl(S5P_CLKDIV_DMC0); + tmp = __raw_readl(EXYNOS4_CLKDIV_DMC0); for (i = LV_0; i < EX4210_LV_NUM; i++) { - tmp &= ~(S5P_CLKDIV_DMC0_ACP_MASK | - S5P_CLKDIV_DMC0_ACPPCLK_MASK | - S5P_CLKDIV_DMC0_DPHY_MASK | - S5P_CLKDIV_DMC0_DMC_MASK | - S5P_CLKDIV_DMC0_DMCD_MASK | - S5P_CLKDIV_DMC0_DMCP_MASK | - S5P_CLKDIV_DMC0_COPY2_MASK | - S5P_CLKDIV_DMC0_CORETI_MASK); + tmp &= ~(EXYNOS4_CLKDIV_DMC0_ACP_MASK | + EXYNOS4_CLKDIV_DMC0_ACPPCLK_MASK | + EXYNOS4_CLKDIV_DMC0_DPHY_MASK | + EXYNOS4_CLKDIV_DMC0_DMC_MASK | + EXYNOS4_CLKDIV_DMC0_DMCD_MASK | + EXYNOS4_CLKDIV_DMC0_DMCP_MASK | + EXYNOS4_CLKDIV_DMC0_COPY2_MASK | + EXYNOS4_CLKDIV_DMC0_CORETI_MASK); tmp |= ((exynos4210_clkdiv_dmc0[i][0] << - S5P_CLKDIV_DMC0_ACP_SHIFT) | + EXYNOS4_CLKDIV_DMC0_ACP_SHIFT) | (exynos4210_clkdiv_dmc0[i][1] << - S5P_CLKDIV_DMC0_ACPPCLK_SHIFT) | + EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT) | (exynos4210_clkdiv_dmc0[i][2] << - S5P_CLKDIV_DMC0_DPHY_SHIFT) | + EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT) | (exynos4210_clkdiv_dmc0[i][3] << - S5P_CLKDIV_DMC0_DMC_SHIFT) | + EXYNOS4_CLKDIV_DMC0_DMC_SHIFT) | (exynos4210_clkdiv_dmc0[i][4] << - S5P_CLKDIV_DMC0_DMCD_SHIFT) | + EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT) | (exynos4210_clkdiv_dmc0[i][5] << - S5P_CLKDIV_DMC0_DMCP_SHIFT) | + EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT) | (exynos4210_clkdiv_dmc0[i][6] << - S5P_CLKDIV_DMC0_COPY2_SHIFT) | + EXYNOS4_CLKDIV_DMC0_COPY2_SHIFT) | (exynos4210_clkdiv_dmc0[i][7] << - S5P_CLKDIV_DMC0_CORETI_SHIFT)); + EXYNOS4_CLKDIV_DMC0_CORETI_SHIFT)); data->dmc_divtable[i] = tmp; } - tmp = __raw_readl(S5P_CLKDIV_TOP); + tmp = __raw_readl(EXYNOS4_CLKDIV_TOP); for (i = LV_0; i < EX4210_LV_NUM; i++) { - tmp &= ~(S5P_CLKDIV_TOP_ACLK200_MASK | - S5P_CLKDIV_TOP_ACLK100_MASK | - S5P_CLKDIV_TOP_ACLK160_MASK | - S5P_CLKDIV_TOP_ACLK133_MASK | - S5P_CLKDIV_TOP_ONENAND_MASK); + tmp &= ~(EXYNOS4_CLKDIV_TOP_ACLK200_MASK | + EXYNOS4_CLKDIV_TOP_ACLK100_MASK | + EXYNOS4_CLKDIV_TOP_ACLK160_MASK | + EXYNOS4_CLKDIV_TOP_ACLK133_MASK | + EXYNOS4_CLKDIV_TOP_ONENAND_MASK); tmp |= ((exynos4210_clkdiv_top[i][0] << - S5P_CLKDIV_TOP_ACLK200_SHIFT) | + EXYNOS4_CLKDIV_TOP_ACLK200_SHIFT) | (exynos4210_clkdiv_top[i][1] << - S5P_CLKDIV_TOP_ACLK100_SHIFT) | + EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT) | (exynos4210_clkdiv_top[i][2] << - S5P_CLKDIV_TOP_ACLK160_SHIFT) | + EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT) | (exynos4210_clkdiv_top[i][3] << - S5P_CLKDIV_TOP_ACLK133_SHIFT) | + EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT) | (exynos4210_clkdiv_top[i][4] << - S5P_CLKDIV_TOP_ONENAND_SHIFT)); + EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT)); data->top_divtable[i] = tmp; } @@ -868,32 +868,32 @@ static int exynos4x12_init_tables(struct busfreq_data *data) int ret; /* Enable pause function for DREX2 DVFS */ - tmp = __raw_readl(S5P_DMC_PAUSE_CTRL); - tmp |= DMC_PAUSE_ENABLE; - __raw_writel(tmp, S5P_DMC_PAUSE_CTRL); + tmp = __raw_readl(EXYNOS4_DMC_PAUSE_CTRL); + tmp |= EXYNOS4_DMC_PAUSE_ENABLE; + __raw_writel(tmp, EXYNOS4_DMC_PAUSE_CTRL); - tmp = __raw_readl(S5P_CLKDIV_DMC0); + tmp = __raw_readl(EXYNOS4_CLKDIV_DMC0); for (i = 0; i < EX4x12_LV_NUM; i++) { - tmp &= ~(S5P_CLKDIV_DMC0_ACP_MASK | - S5P_CLKDIV_DMC0_ACPPCLK_MASK | - S5P_CLKDIV_DMC0_DPHY_MASK | - S5P_CLKDIV_DMC0_DMC_MASK | - S5P_CLKDIV_DMC0_DMCD_MASK | - S5P_CLKDIV_DMC0_DMCP_MASK); + tmp &= ~(EXYNOS4_CLKDIV_DMC0_ACP_MASK | + EXYNOS4_CLKDIV_DMC0_ACPPCLK_MASK | + EXYNOS4_CLKDIV_DMC0_DPHY_MASK | + EXYNOS4_CLKDIV_DMC0_DMC_MASK | + EXYNOS4_CLKDIV_DMC0_DMCD_MASK | + EXYNOS4_CLKDIV_DMC0_DMCP_MASK); tmp |= ((exynos4x12_clkdiv_dmc0[i][0] << - S5P_CLKDIV_DMC0_ACP_SHIFT) | + EXYNOS4_CLKDIV_DMC0_ACP_SHIFT) | (exynos4x12_clkdiv_dmc0[i][1] << - S5P_CLKDIV_DMC0_ACPPCLK_SHIFT) | + EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT) | (exynos4x12_clkdiv_dmc0[i][2] << - S5P_CLKDIV_DMC0_DPHY_SHIFT) | + EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT) | (exynos4x12_clkdiv_dmc0[i][3] << - S5P_CLKDIV_DMC0_DMC_SHIFT) | + EXYNOS4_CLKDIV_DMC0_DMC_SHIFT) | (exynos4x12_clkdiv_dmc0[i][4] << - S5P_CLKDIV_DMC0_DMCD_SHIFT) | + EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT) | (exynos4x12_clkdiv_dmc0[i][5] << - S5P_CLKDIV_DMC0_DMCP_SHIFT)); + EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT)); data->dmc_divtable[i] = tmp; } diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index f1a274994bb1..4a6c46dea8a0 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -252,6 +252,15 @@ config EP93XX_DMA help Enable support for the Cirrus Logic EP93xx M2P/M2M DMA controller. +config DMA_SA11X0 + tristate "SA-11x0 DMA support" + depends on ARCH_SA1100 + select DMA_ENGINE + help + Support the DMA engine found on Intel StrongARM SA-1100 and + SA-1110 SoCs. This DMA engine can only be used with on-chip + devices. + config DMA_ENGINE bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 009a222e8283..86b795baba98 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -27,3 +27,4 @@ obj-$(CONFIG_PL330_DMA) += pl330.o obj-$(CONFIG_PCH_DMA) += pch_dma.o obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o +obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index e4383ee2c9ac..38586ba8da91 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -14,7 +14,6 @@ * http://www.gnu.org/copyleft/gpl.html */ #include <linux/init.h> -#include <linux/module.h> #include <linux/types.h> #include <linux/mm.h> #include <linux/interrupt.h> diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 8bc5acf36ee5..63540d3e2153 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -35,7 +35,6 @@ #include <linux/dmaengine.h> #include <linux/of.h> #include <linux/of_device.h> -#include <linux/module.h> #include <asm/irq.h> #include <mach/sdma.h> diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index b8ec03ee8e22..16b66c827f19 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -1035,18 +1035,7 @@ static struct amba_driver pl330_driver = { .remove = pl330_remove, }; -static int __init pl330_init(void) -{ - return amba_driver_register(&pl330_driver); -} -module_init(pl330_init); - -static void __exit pl330_exit(void) -{ - amba_driver_unregister(&pl330_driver); - return; -} -module_exit(pl330_exit); +module_amba_driver(pl330_driver); MODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>"); MODULE_DESCRIPTION("API Driver for PL330 DMAC"); diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c new file mode 100644 index 000000000000..16a6b48883cf --- /dev/null +++ b/drivers/dma/sa11x0-dma.c @@ -0,0 +1,1109 @@ +/* + * SA11x0 DMAengine support + * + * Copyright (C) 2012 Russell King + * Derived in part from arch/arm/mach-sa1100/dma.c, + * Copyright (C) 2000, 2001 by Nicolas Pitre + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/sched.h> +#include <linux/device.h> +#include <linux/dmaengine.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/sa11x0-dma.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#define NR_PHY_CHAN 6 +#define DMA_ALIGN 3 +#define DMA_MAX_SIZE 0x1fff +#define DMA_CHUNK_SIZE 0x1000 + +#define DMA_DDAR 0x00 +#define DMA_DCSR_S 0x04 +#define DMA_DCSR_C 0x08 +#define DMA_DCSR_R 0x0c +#define DMA_DBSA 0x10 +#define DMA_DBTA 0x14 +#define DMA_DBSB 0x18 +#define DMA_DBTB 0x1c +#define DMA_SIZE 0x20 + +#define DCSR_RUN (1 << 0) +#define DCSR_IE (1 << 1) +#define DCSR_ERROR (1 << 2) +#define DCSR_DONEA (1 << 3) +#define DCSR_STRTA (1 << 4) +#define DCSR_DONEB (1 << 5) +#define DCSR_STRTB (1 << 6) +#define DCSR_BIU (1 << 7) + +#define DDAR_RW (1 << 0) /* 0 = W, 1 = R */ +#define DDAR_E (1 << 1) /* 0 = LE, 1 = BE */ +#define DDAR_BS (1 << 2) /* 0 = BS4, 1 = BS8 */ +#define DDAR_DW (1 << 3) /* 0 = 8b, 1 = 16b */ +#define DDAR_Ser0UDCTr (0x0 << 4) +#define DDAR_Ser0UDCRc (0x1 << 4) +#define DDAR_Ser1SDLCTr (0x2 << 4) +#define DDAR_Ser1SDLCRc (0x3 << 4) +#define DDAR_Ser1UARTTr (0x4 << 4) +#define DDAR_Ser1UARTRc (0x5 << 4) +#define DDAR_Ser2ICPTr (0x6 << 4) +#define DDAR_Ser2ICPRc (0x7 << 4) +#define DDAR_Ser3UARTTr (0x8 << 4) +#define DDAR_Ser3UARTRc (0x9 << 4) +#define DDAR_Ser4MCP0Tr (0xa << 4) +#define DDAR_Ser4MCP0Rc (0xb << 4) +#define DDAR_Ser4MCP1Tr (0xc << 4) +#define DDAR_Ser4MCP1Rc (0xd << 4) +#define DDAR_Ser4SSPTr (0xe << 4) +#define DDAR_Ser4SSPRc (0xf << 4) + +struct sa11x0_dma_sg { + u32 addr; + u32 len; +}; + +struct sa11x0_dma_desc { + struct dma_async_tx_descriptor tx; + u32 ddar; + size_t size; + + /* maybe protected by c->lock */ + struct list_head node; + unsigned sglen; + struct sa11x0_dma_sg sg[0]; +}; + +struct sa11x0_dma_phy; + +struct sa11x0_dma_chan { + struct dma_chan chan; + spinlock_t lock; + dma_cookie_t lc; + + /* protected by c->lock */ + struct sa11x0_dma_phy *phy; + enum dma_status status; + struct list_head desc_submitted; + struct list_head desc_issued; + + /* protected by d->lock */ + struct list_head node; + + u32 ddar; + const char *name; +}; + +struct sa11x0_dma_phy { + void __iomem *base; + struct sa11x0_dma_dev *dev; + unsigned num; + + struct sa11x0_dma_chan *vchan; + + /* Protected by c->lock */ + unsigned sg_load; + struct sa11x0_dma_desc *txd_load; + unsigned sg_done; + struct sa11x0_dma_desc *txd_done; +#ifdef CONFIG_PM_SLEEP + u32 dbs[2]; + u32 dbt[2]; + u32 dcsr; +#endif +}; + +struct sa11x0_dma_dev { + struct dma_device slave; + void __iomem *base; + spinlock_t lock; + struct tasklet_struct task; + struct list_head chan_pending; + struct list_head desc_complete; + struct sa11x0_dma_phy phy[NR_PHY_CHAN]; +}; + +static struct sa11x0_dma_chan *to_sa11x0_dma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct sa11x0_dma_chan, chan); +} + +static struct sa11x0_dma_dev *to_sa11x0_dma(struct dma_device *dmadev) +{ + return container_of(dmadev, struct sa11x0_dma_dev, slave); +} + +static struct sa11x0_dma_desc *to_sa11x0_dma_tx(struct dma_async_tx_descriptor *tx) +{ + return container_of(tx, struct sa11x0_dma_desc, tx); +} + +static struct sa11x0_dma_desc *sa11x0_dma_next_desc(struct sa11x0_dma_chan *c) +{ + if (list_empty(&c->desc_issued)) + return NULL; + + return list_first_entry(&c->desc_issued, struct sa11x0_dma_desc, node); +} + +static void sa11x0_dma_start_desc(struct sa11x0_dma_phy *p, struct sa11x0_dma_desc *txd) +{ + list_del(&txd->node); + p->txd_load = txd; + p->sg_load = 0; + + dev_vdbg(p->dev->slave.dev, "pchan %u: txd %p[%x]: starting: DDAR:%x\n", + p->num, txd, txd->tx.cookie, txd->ddar); +} + +static void noinline sa11x0_dma_start_sg(struct sa11x0_dma_phy *p, + struct sa11x0_dma_chan *c) +{ + struct sa11x0_dma_desc *txd = p->txd_load; + struct sa11x0_dma_sg *sg; + void __iomem *base = p->base; + unsigned dbsx, dbtx; + u32 dcsr; + + if (!txd) + return; + + dcsr = readl_relaxed(base + DMA_DCSR_R); + + /* Don't try to load the next transfer if both buffers are started */ + if ((dcsr & (DCSR_STRTA | DCSR_STRTB)) == (DCSR_STRTA | DCSR_STRTB)) + return; + + if (p->sg_load == txd->sglen) { + struct sa11x0_dma_desc *txn = sa11x0_dma_next_desc(c); + + /* + * We have reached the end of the current descriptor. + * Peek at the next descriptor, and if compatible with + * the current, start processing it. + */ + if (txn && txn->ddar == txd->ddar) { + txd = txn; + sa11x0_dma_start_desc(p, txn); + } else { + p->txd_load = NULL; + return; + } + } + + sg = &txd->sg[p->sg_load++]; + + /* Select buffer to load according to channel status */ + if (((dcsr & (DCSR_BIU | DCSR_STRTB)) == (DCSR_BIU | DCSR_STRTB)) || + ((dcsr & (DCSR_BIU | DCSR_STRTA)) == 0)) { + dbsx = DMA_DBSA; + dbtx = DMA_DBTA; + dcsr = DCSR_STRTA | DCSR_IE | DCSR_RUN; + } else { + dbsx = DMA_DBSB; + dbtx = DMA_DBTB; + dcsr = DCSR_STRTB | DCSR_IE | DCSR_RUN; + } + + writel_relaxed(sg->addr, base + dbsx); + writel_relaxed(sg->len, base + dbtx); + writel(dcsr, base + DMA_DCSR_S); + + dev_dbg(p->dev->slave.dev, "pchan %u: load: DCSR:%02x DBS%c:%08x DBT%c:%08x\n", + p->num, dcsr, + 'A' + (dbsx == DMA_DBSB), sg->addr, + 'A' + (dbtx == DMA_DBTB), sg->len); +} + +static void noinline sa11x0_dma_complete(struct sa11x0_dma_phy *p, + struct sa11x0_dma_chan *c) +{ + struct sa11x0_dma_desc *txd = p->txd_done; + + if (++p->sg_done == txd->sglen) { + struct sa11x0_dma_dev *d = p->dev; + + dev_vdbg(d->slave.dev, "pchan %u: txd %p[%x]: completed\n", + p->num, p->txd_done, p->txd_done->tx.cookie); + + c->lc = txd->tx.cookie; + + spin_lock(&d->lock); + list_add_tail(&txd->node, &d->desc_complete); + spin_unlock(&d->lock); + + p->sg_done = 0; + p->txd_done = p->txd_load; + + tasklet_schedule(&d->task); + } + + sa11x0_dma_start_sg(p, c); +} + +static irqreturn_t sa11x0_dma_irq(int irq, void *dev_id) +{ + struct sa11x0_dma_phy *p = dev_id; + struct sa11x0_dma_dev *d = p->dev; + struct sa11x0_dma_chan *c; + u32 dcsr; + + dcsr = readl_relaxed(p->base + DMA_DCSR_R); + if (!(dcsr & (DCSR_ERROR | DCSR_DONEA | DCSR_DONEB))) + return IRQ_NONE; + + /* Clear reported status bits */ + writel_relaxed(dcsr & (DCSR_ERROR | DCSR_DONEA | DCSR_DONEB), + p->base + DMA_DCSR_C); + + dev_dbg(d->slave.dev, "pchan %u: irq: DCSR:%02x\n", p->num, dcsr); + + if (dcsr & DCSR_ERROR) { + dev_err(d->slave.dev, "pchan %u: error. DCSR:%02x DDAR:%08x DBSA:%08x DBTA:%08x DBSB:%08x DBTB:%08x\n", + p->num, dcsr, + readl_relaxed(p->base + DMA_DDAR), + readl_relaxed(p->base + DMA_DBSA), + readl_relaxed(p->base + DMA_DBTA), + readl_relaxed(p->base + DMA_DBSB), + readl_relaxed(p->base + DMA_DBTB)); + } + + c = p->vchan; + if (c) { + unsigned long flags; + + spin_lock_irqsave(&c->lock, flags); + /* + * Now that we're holding the lock, check that the vchan + * really is associated with this pchan before touching the + * hardware. This should always succeed, because we won't + * change p->vchan or c->phy while the channel is actively + * transferring. + */ + if (c->phy == p) { + if (dcsr & DCSR_DONEA) + sa11x0_dma_complete(p, c); + if (dcsr & DCSR_DONEB) + sa11x0_dma_complete(p, c); + } + spin_unlock_irqrestore(&c->lock, flags); + } + + return IRQ_HANDLED; +} + +static void sa11x0_dma_start_txd(struct sa11x0_dma_chan *c) +{ + struct sa11x0_dma_desc *txd = sa11x0_dma_next_desc(c); + + /* If the issued list is empty, we have no further txds to process */ + if (txd) { + struct sa11x0_dma_phy *p = c->phy; + + sa11x0_dma_start_desc(p, txd); + p->txd_done = txd; + p->sg_done = 0; + + /* The channel should not have any transfers started */ + WARN_ON(readl_relaxed(p->base + DMA_DCSR_R) & + (DCSR_STRTA | DCSR_STRTB)); + + /* Clear the run and start bits before changing DDAR */ + writel_relaxed(DCSR_RUN | DCSR_STRTA | DCSR_STRTB, + p->base + DMA_DCSR_C); + writel_relaxed(txd->ddar, p->base + DMA_DDAR); + + /* Try to start both buffers */ + sa11x0_dma_start_sg(p, c); + sa11x0_dma_start_sg(p, c); + } +} + +static void sa11x0_dma_tasklet(unsigned long arg) +{ + struct sa11x0_dma_dev *d = (struct sa11x0_dma_dev *)arg; + struct sa11x0_dma_phy *p; + struct sa11x0_dma_chan *c; + struct sa11x0_dma_desc *txd, *txn; + LIST_HEAD(head); + unsigned pch, pch_alloc = 0; + + dev_dbg(d->slave.dev, "tasklet enter\n"); + + /* Get the completed tx descriptors */ + spin_lock_irq(&d->lock); + list_splice_init(&d->desc_complete, &head); + spin_unlock_irq(&d->lock); + + list_for_each_entry(txd, &head, node) { + c = to_sa11x0_dma_chan(txd->tx.chan); + + dev_dbg(d->slave.dev, "vchan %p: txd %p[%x] completed\n", + c, txd, txd->tx.cookie); + + spin_lock_irq(&c->lock); + p = c->phy; + if (p) { + if (!p->txd_done) + sa11x0_dma_start_txd(c); + if (!p->txd_done) { + /* No current txd associated with this channel */ + dev_dbg(d->slave.dev, "pchan %u: free\n", p->num); + + /* Mark this channel free */ + c->phy = NULL; + p->vchan = NULL; + } + } + spin_unlock_irq(&c->lock); + } + + spin_lock_irq(&d->lock); + for (pch = 0; pch < NR_PHY_CHAN; pch++) { + p = &d->phy[pch]; + + if (p->vchan == NULL && !list_empty(&d->chan_pending)) { + c = list_first_entry(&d->chan_pending, + struct sa11x0_dma_chan, node); + list_del_init(&c->node); + + pch_alloc |= 1 << pch; + + /* Mark this channel allocated */ + p->vchan = c; + + dev_dbg(d->slave.dev, "pchan %u: alloc vchan %p\n", pch, c); + } + } + spin_unlock_irq(&d->lock); + + for (pch = 0; pch < NR_PHY_CHAN; pch++) { + if (pch_alloc & (1 << pch)) { + p = &d->phy[pch]; + c = p->vchan; + + spin_lock_irq(&c->lock); + c->phy = p; + + sa11x0_dma_start_txd(c); + spin_unlock_irq(&c->lock); + } + } + + /* Now free the completed tx descriptor, and call their callbacks */ + list_for_each_entry_safe(txd, txn, &head, node) { + dma_async_tx_callback callback = txd->tx.callback; + void *callback_param = txd->tx.callback_param; + + dev_dbg(d->slave.dev, "txd %p[%x]: callback and free\n", + txd, txd->tx.cookie); + + kfree(txd); + + if (callback) + callback(callback_param); + } + + dev_dbg(d->slave.dev, "tasklet exit\n"); +} + + +static void sa11x0_dma_desc_free(struct sa11x0_dma_dev *d, struct list_head *head) +{ + struct sa11x0_dma_desc *txd, *txn; + + list_for_each_entry_safe(txd, txn, head, node) { + dev_dbg(d->slave.dev, "txd %p: freeing\n", txd); + kfree(txd); + } +} + +static int sa11x0_dma_alloc_chan_resources(struct dma_chan *chan) +{ + return 0; +} + +static void sa11x0_dma_free_chan_resources(struct dma_chan *chan) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&c->lock, flags); + spin_lock(&d->lock); + list_del_init(&c->node); + spin_unlock(&d->lock); + + list_splice_tail_init(&c->desc_submitted, &head); + list_splice_tail_init(&c->desc_issued, &head); + spin_unlock_irqrestore(&c->lock, flags); + + sa11x0_dma_desc_free(d, &head); +} + +static dma_addr_t sa11x0_dma_pos(struct sa11x0_dma_phy *p) +{ + unsigned reg; + u32 dcsr; + + dcsr = readl_relaxed(p->base + DMA_DCSR_R); + + if ((dcsr & (DCSR_BIU | DCSR_STRTA)) == DCSR_STRTA || + (dcsr & (DCSR_BIU | DCSR_STRTB)) == DCSR_BIU) + reg = DMA_DBSA; + else + reg = DMA_DBSB; + + return readl_relaxed(p->base + reg); +} + +static enum dma_status sa11x0_dma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *state) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); + struct sa11x0_dma_phy *p; + struct sa11x0_dma_desc *txd; + dma_cookie_t last_used, last_complete; + unsigned long flags; + enum dma_status ret; + size_t bytes = 0; + + last_used = c->chan.cookie; + last_complete = c->lc; + + ret = dma_async_is_complete(cookie, last_complete, last_used); + if (ret == DMA_SUCCESS) { + dma_set_tx_state(state, last_complete, last_used, 0); + return ret; + } + + spin_lock_irqsave(&c->lock, flags); + p = c->phy; + ret = c->status; + if (p) { + dma_addr_t addr = sa11x0_dma_pos(p); + + dev_vdbg(d->slave.dev, "tx_status: addr:%x\n", addr); + + txd = p->txd_done; + if (txd) { + unsigned i; + + for (i = 0; i < txd->sglen; i++) { + dev_vdbg(d->slave.dev, "tx_status: [%u] %x+%x\n", + i, txd->sg[i].addr, txd->sg[i].len); + if (addr >= txd->sg[i].addr && + addr < txd->sg[i].addr + txd->sg[i].len) { + unsigned len; + + len = txd->sg[i].len - + (addr - txd->sg[i].addr); + dev_vdbg(d->slave.dev, "tx_status: [%u] +%x\n", + i, len); + bytes += len; + i++; + break; + } + } + for (; i < txd->sglen; i++) { + dev_vdbg(d->slave.dev, "tx_status: [%u] %x+%x ++\n", + i, txd->sg[i].addr, txd->sg[i].len); + bytes += txd->sg[i].len; + } + } + if (txd != p->txd_load && p->txd_load) + bytes += p->txd_load->size; + } + list_for_each_entry(txd, &c->desc_issued, node) { + bytes += txd->size; + } + spin_unlock_irqrestore(&c->lock, flags); + + dma_set_tx_state(state, last_complete, last_used, bytes); + + dev_vdbg(d->slave.dev, "tx_status: bytes 0x%zx\n", bytes); + + return ret; +} + +/* + * Move pending txds to the issued list, and re-init pending list. + * If not already pending, add this channel to the list of pending + * channels and trigger the tasklet to run. + */ +static void sa11x0_dma_issue_pending(struct dma_chan *chan) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); + unsigned long flags; + + spin_lock_irqsave(&c->lock, flags); + list_splice_tail_init(&c->desc_submitted, &c->desc_issued); + if (!list_empty(&c->desc_issued)) { + spin_lock(&d->lock); + if (!c->phy && list_empty(&c->node)) { + list_add_tail(&c->node, &d->chan_pending); + tasklet_schedule(&d->task); + dev_dbg(d->slave.dev, "vchan %p: issued\n", c); + } + spin_unlock(&d->lock); + } else + dev_dbg(d->slave.dev, "vchan %p: nothing to issue\n", c); + spin_unlock_irqrestore(&c->lock, flags); +} + +static dma_cookie_t sa11x0_dma_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(tx->chan); + struct sa11x0_dma_desc *txd = to_sa11x0_dma_tx(tx); + unsigned long flags; + + spin_lock_irqsave(&c->lock, flags); + c->chan.cookie += 1; + if (c->chan.cookie < 0) + c->chan.cookie = 1; + txd->tx.cookie = c->chan.cookie; + + list_add_tail(&txd->node, &c->desc_submitted); + spin_unlock_irqrestore(&c->lock, flags); + + dev_dbg(tx->chan->device->dev, "vchan %p: txd %p[%x]: submitted\n", + c, txd, txd->tx.cookie); + + return txd->tx.cookie; +} + +static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sg, unsigned int sglen, + enum dma_transfer_direction dir, unsigned long flags) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + struct sa11x0_dma_desc *txd; + struct scatterlist *sgent; + unsigned i, j = sglen; + size_t size = 0; + + /* SA11x0 channels can only operate in their native direction */ + if (dir != (c->ddar & DDAR_RW ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV)) { + dev_err(chan->device->dev, "vchan %p: bad DMA direction: DDAR:%08x dir:%u\n", + c, c->ddar, dir); + return NULL; + } + + /* Do not allow zero-sized txds */ + if (sglen == 0) + return NULL; + + for_each_sg(sg, sgent, sglen, i) { + dma_addr_t addr = sg_dma_address(sgent); + unsigned int len = sg_dma_len(sgent); + + if (len > DMA_MAX_SIZE) + j += DIV_ROUND_UP(len, DMA_MAX_SIZE & ~DMA_ALIGN) - 1; + if (addr & DMA_ALIGN) { + dev_dbg(chan->device->dev, "vchan %p: bad buffer alignment: %08x\n", + c, addr); + return NULL; + } + } + + txd = kzalloc(sizeof(*txd) + j * sizeof(txd->sg[0]), GFP_ATOMIC); + if (!txd) { + dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", c); + return NULL; + } + + j = 0; + for_each_sg(sg, sgent, sglen, i) { + dma_addr_t addr = sg_dma_address(sgent); + unsigned len = sg_dma_len(sgent); + + size += len; + + do { + unsigned tlen = len; + + /* + * Check whether the transfer will fit. If not, try + * to split the transfer up such that we end up with + * equal chunks - but make sure that we preserve the + * alignment. This avoids small segments. + */ + if (tlen > DMA_MAX_SIZE) { + unsigned mult = DIV_ROUND_UP(tlen, + DMA_MAX_SIZE & ~DMA_ALIGN); + + tlen = (tlen / mult) & ~DMA_ALIGN; + } + + txd->sg[j].addr = addr; + txd->sg[j].len = tlen; + + addr += tlen; + len -= tlen; + j++; + } while (len); + } + + dma_async_tx_descriptor_init(&txd->tx, &c->chan); + txd->tx.flags = flags; + txd->tx.tx_submit = sa11x0_dma_tx_submit; + txd->ddar = c->ddar; + txd->size = size; + txd->sglen = j; + + dev_dbg(chan->device->dev, "vchan %p: txd %p: size %u nr %u\n", + c, txd, txd->size, txd->sglen); + + return &txd->tx; +} + +static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_config *cfg) +{ + u32 ddar = c->ddar & ((0xf << 4) | DDAR_RW); + dma_addr_t addr; + enum dma_slave_buswidth width; + u32 maxburst; + + if (ddar & DDAR_RW) { + addr = cfg->src_addr; + width = cfg->src_addr_width; + maxburst = cfg->src_maxburst; + } else { + addr = cfg->dst_addr; + width = cfg->dst_addr_width; + maxburst = cfg->dst_maxburst; + } + + if ((width != DMA_SLAVE_BUSWIDTH_1_BYTE && + width != DMA_SLAVE_BUSWIDTH_2_BYTES) || + (maxburst != 4 && maxburst != 8)) + return -EINVAL; + + if (width == DMA_SLAVE_BUSWIDTH_2_BYTES) + ddar |= DDAR_DW; + if (maxburst == 8) + ddar |= DDAR_BS; + + dev_dbg(c->chan.device->dev, "vchan %p: dma_slave_config addr %x width %u burst %u\n", + c, addr, width, maxburst); + + c->ddar = ddar | (addr & 0xf0000000) | (addr & 0x003ffffc) << 6; + + return 0; +} + +static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); + struct sa11x0_dma_phy *p; + LIST_HEAD(head); + unsigned long flags; + int ret; + + switch (cmd) { + case DMA_SLAVE_CONFIG: + return sa11x0_dma_slave_config(c, (struct dma_slave_config *)arg); + + case DMA_TERMINATE_ALL: + dev_dbg(d->slave.dev, "vchan %p: terminate all\n", c); + /* Clear the tx descriptor lists */ + spin_lock_irqsave(&c->lock, flags); + list_splice_tail_init(&c->desc_submitted, &head); + list_splice_tail_init(&c->desc_issued, &head); + + p = c->phy; + if (p) { + struct sa11x0_dma_desc *txd, *txn; + + dev_dbg(d->slave.dev, "pchan %u: terminating\n", p->num); + /* vchan is assigned to a pchan - stop the channel */ + writel(DCSR_RUN | DCSR_IE | + DCSR_STRTA | DCSR_DONEA | + DCSR_STRTB | DCSR_DONEB, + p->base + DMA_DCSR_C); + + list_for_each_entry_safe(txd, txn, &d->desc_complete, node) + if (txd->tx.chan == &c->chan) + list_move(&txd->node, &head); + + if (p->txd_load) { + if (p->txd_load != p->txd_done) + list_add_tail(&p->txd_load->node, &head); + p->txd_load = NULL; + } + if (p->txd_done) { + list_add_tail(&p->txd_done->node, &head); + p->txd_done = NULL; + } + c->phy = NULL; + spin_lock(&d->lock); + p->vchan = NULL; + spin_unlock(&d->lock); + tasklet_schedule(&d->task); + } + spin_unlock_irqrestore(&c->lock, flags); + sa11x0_dma_desc_free(d, &head); + ret = 0; + break; + + case DMA_PAUSE: + dev_dbg(d->slave.dev, "vchan %p: pause\n", c); + spin_lock_irqsave(&c->lock, flags); + if (c->status == DMA_IN_PROGRESS) { + c->status = DMA_PAUSED; + + p = c->phy; + if (p) { + writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_C); + } else { + spin_lock(&d->lock); + list_del_init(&c->node); + spin_unlock(&d->lock); + } + } + spin_unlock_irqrestore(&c->lock, flags); + ret = 0; + break; + + case DMA_RESUME: + dev_dbg(d->slave.dev, "vchan %p: resume\n", c); + spin_lock_irqsave(&c->lock, flags); + if (c->status == DMA_PAUSED) { + c->status = DMA_IN_PROGRESS; + + p = c->phy; + if (p) { + writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_S); + } else if (!list_empty(&c->desc_issued)) { + spin_lock(&d->lock); + list_add_tail(&c->node, &d->chan_pending); + spin_unlock(&d->lock); + } + } + spin_unlock_irqrestore(&c->lock, flags); + ret = 0; + break; + + default: + ret = -ENXIO; + break; + } + + return ret; +} + +struct sa11x0_dma_channel_desc { + u32 ddar; + const char *name; +}; + +#define CD(d1, d2) { .ddar = DDAR_##d1 | d2, .name = #d1 } +static const struct sa11x0_dma_channel_desc chan_desc[] = { + CD(Ser0UDCTr, 0), + CD(Ser0UDCRc, DDAR_RW), + CD(Ser1SDLCTr, 0), + CD(Ser1SDLCRc, DDAR_RW), + CD(Ser1UARTTr, 0), + CD(Ser1UARTRc, DDAR_RW), + CD(Ser2ICPTr, 0), + CD(Ser2ICPRc, DDAR_RW), + CD(Ser3UARTTr, 0), + CD(Ser3UARTRc, DDAR_RW), + CD(Ser4MCP0Tr, 0), + CD(Ser4MCP0Rc, DDAR_RW), + CD(Ser4MCP1Tr, 0), + CD(Ser4MCP1Rc, DDAR_RW), + CD(Ser4SSPTr, 0), + CD(Ser4SSPRc, DDAR_RW), +}; + +static int __devinit sa11x0_dma_init_dmadev(struct dma_device *dmadev, + struct device *dev) +{ + unsigned i; + + dmadev->chancnt = ARRAY_SIZE(chan_desc); + INIT_LIST_HEAD(&dmadev->channels); + dmadev->dev = dev; + dmadev->device_alloc_chan_resources = sa11x0_dma_alloc_chan_resources; + dmadev->device_free_chan_resources = sa11x0_dma_free_chan_resources; + dmadev->device_control = sa11x0_dma_control; + dmadev->device_tx_status = sa11x0_dma_tx_status; + dmadev->device_issue_pending = sa11x0_dma_issue_pending; + + for (i = 0; i < dmadev->chancnt; i++) { + struct sa11x0_dma_chan *c; + + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) { + dev_err(dev, "no memory for channel %u\n", i); + return -ENOMEM; + } + + c->chan.device = dmadev; + c->status = DMA_IN_PROGRESS; + c->ddar = chan_desc[i].ddar; + c->name = chan_desc[i].name; + spin_lock_init(&c->lock); + INIT_LIST_HEAD(&c->desc_submitted); + INIT_LIST_HEAD(&c->desc_issued); + INIT_LIST_HEAD(&c->node); + list_add_tail(&c->chan.device_node, &dmadev->channels); + } + + return dma_async_device_register(dmadev); +} + +static int sa11x0_dma_request_irq(struct platform_device *pdev, int nr, + void *data) +{ + int irq = platform_get_irq(pdev, nr); + + if (irq <= 0) + return -ENXIO; + + return request_irq(irq, sa11x0_dma_irq, 0, dev_name(&pdev->dev), data); +} + +static void sa11x0_dma_free_irq(struct platform_device *pdev, int nr, + void *data) +{ + int irq = platform_get_irq(pdev, nr); + if (irq > 0) + free_irq(irq, data); +} + +static void sa11x0_dma_free_channels(struct dma_device *dmadev) +{ + struct sa11x0_dma_chan *c, *cn; + + list_for_each_entry_safe(c, cn, &dmadev->channels, chan.device_node) { + list_del(&c->chan.device_node); + kfree(c); + } +} + +static int __devinit sa11x0_dma_probe(struct platform_device *pdev) +{ + struct sa11x0_dma_dev *d; + struct resource *res; + unsigned i; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + d = kzalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + ret = -ENOMEM; + goto err_alloc; + } + + spin_lock_init(&d->lock); + INIT_LIST_HEAD(&d->chan_pending); + INIT_LIST_HEAD(&d->desc_complete); + + d->base = ioremap(res->start, resource_size(res)); + if (!d->base) { + ret = -ENOMEM; + goto err_ioremap; + } + + tasklet_init(&d->task, sa11x0_dma_tasklet, (unsigned long)d); + + for (i = 0; i < NR_PHY_CHAN; i++) { + struct sa11x0_dma_phy *p = &d->phy[i]; + + p->dev = d; + p->num = i; + p->base = d->base + i * DMA_SIZE; + writel_relaxed(DCSR_RUN | DCSR_IE | DCSR_ERROR | + DCSR_DONEA | DCSR_STRTA | DCSR_DONEB | DCSR_STRTB, + p->base + DMA_DCSR_C); + writel_relaxed(0, p->base + DMA_DDAR); + + ret = sa11x0_dma_request_irq(pdev, i, p); + if (ret) { + while (i) { + i--; + sa11x0_dma_free_irq(pdev, i, &d->phy[i]); + } + goto err_irq; + } + } + + dma_cap_set(DMA_SLAVE, d->slave.cap_mask); + d->slave.device_prep_slave_sg = sa11x0_dma_prep_slave_sg; + ret = sa11x0_dma_init_dmadev(&d->slave, &pdev->dev); + if (ret) { + dev_warn(d->slave.dev, "failed to register slave async device: %d\n", + ret); + goto err_slave_reg; + } + + platform_set_drvdata(pdev, d); + return 0; + + err_slave_reg: + sa11x0_dma_free_channels(&d->slave); + for (i = 0; i < NR_PHY_CHAN; i++) + sa11x0_dma_free_irq(pdev, i, &d->phy[i]); + err_irq: + tasklet_kill(&d->task); + iounmap(d->base); + err_ioremap: + kfree(d); + err_alloc: + return ret; +} + +static int __devexit sa11x0_dma_remove(struct platform_device *pdev) +{ + struct sa11x0_dma_dev *d = platform_get_drvdata(pdev); + unsigned pch; + + dma_async_device_unregister(&d->slave); + + sa11x0_dma_free_channels(&d->slave); + for (pch = 0; pch < NR_PHY_CHAN; pch++) + sa11x0_dma_free_irq(pdev, pch, &d->phy[pch]); + tasklet_kill(&d->task); + iounmap(d->base); + kfree(d); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int sa11x0_dma_suspend(struct device *dev) +{ + struct sa11x0_dma_dev *d = dev_get_drvdata(dev); + unsigned pch; + + for (pch = 0; pch < NR_PHY_CHAN; pch++) { + struct sa11x0_dma_phy *p = &d->phy[pch]; + u32 dcsr, saved_dcsr; + + dcsr = saved_dcsr = readl_relaxed(p->base + DMA_DCSR_R); + if (dcsr & DCSR_RUN) { + writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_C); + dcsr = readl_relaxed(p->base + DMA_DCSR_R); + } + + saved_dcsr &= DCSR_RUN | DCSR_IE; + if (dcsr & DCSR_BIU) { + p->dbs[0] = readl_relaxed(p->base + DMA_DBSB); + p->dbt[0] = readl_relaxed(p->base + DMA_DBTB); + p->dbs[1] = readl_relaxed(p->base + DMA_DBSA); + p->dbt[1] = readl_relaxed(p->base + DMA_DBTA); + saved_dcsr |= (dcsr & DCSR_STRTA ? DCSR_STRTB : 0) | + (dcsr & DCSR_STRTB ? DCSR_STRTA : 0); + } else { + p->dbs[0] = readl_relaxed(p->base + DMA_DBSA); + p->dbt[0] = readl_relaxed(p->base + DMA_DBTA); + p->dbs[1] = readl_relaxed(p->base + DMA_DBSB); + p->dbt[1] = readl_relaxed(p->base + DMA_DBTB); + saved_dcsr |= dcsr & (DCSR_STRTA | DCSR_STRTB); + } + p->dcsr = saved_dcsr; + + writel(DCSR_STRTA | DCSR_STRTB, p->base + DMA_DCSR_C); + } + + return 0; +} + +static int sa11x0_dma_resume(struct device *dev) +{ + struct sa11x0_dma_dev *d = dev_get_drvdata(dev); + unsigned pch; + + for (pch = 0; pch < NR_PHY_CHAN; pch++) { + struct sa11x0_dma_phy *p = &d->phy[pch]; + struct sa11x0_dma_desc *txd = NULL; + u32 dcsr = readl_relaxed(p->base + DMA_DCSR_R); + + WARN_ON(dcsr & (DCSR_BIU | DCSR_STRTA | DCSR_STRTB | DCSR_RUN)); + + if (p->txd_done) + txd = p->txd_done; + else if (p->txd_load) + txd = p->txd_load; + + if (!txd) + continue; + + writel_relaxed(txd->ddar, p->base + DMA_DDAR); + + writel_relaxed(p->dbs[0], p->base + DMA_DBSA); + writel_relaxed(p->dbt[0], p->base + DMA_DBTA); + writel_relaxed(p->dbs[1], p->base + DMA_DBSB); + writel_relaxed(p->dbt[1], p->base + DMA_DBTB); + writel_relaxed(p->dcsr, p->base + DMA_DCSR_S); + } + + return 0; +} +#endif + +static const struct dev_pm_ops sa11x0_dma_pm_ops = { + .suspend_noirq = sa11x0_dma_suspend, + .resume_noirq = sa11x0_dma_resume, + .freeze_noirq = sa11x0_dma_suspend, + .thaw_noirq = sa11x0_dma_resume, + .poweroff_noirq = sa11x0_dma_suspend, + .restore_noirq = sa11x0_dma_resume, +}; + +static struct platform_driver sa11x0_dma_driver = { + .driver = { + .name = "sa11x0-dma", + .owner = THIS_MODULE, + .pm = &sa11x0_dma_pm_ops, + }, + .probe = sa11x0_dma_probe, + .remove = __devexit_p(sa11x0_dma_remove), +}; + +bool sa11x0_dma_filter_fn(struct dma_chan *chan, void *param) +{ + if (chan->device->dev->driver == &sa11x0_dma_driver.driver) { + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + const char *p = param; + + return !strcmp(c->name, p); + } + return false; +} +EXPORT_SYMBOL(sa11x0_dma_filter_fn); + +static int __init sa11x0_dma_init(void) +{ + return platform_driver_register(&sa11x0_dma_driver); +} +subsys_initcall(sa11x0_dma_init); + +static void __exit sa11x0_dma_exit(void) +{ + platform_driver_unregister(&sa11x0_dma_driver); +} +module_exit(sa11x0_dma_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("SA-11x0 DMA driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sa11x0-dma"); diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index c9eee6d33e9a..7ef73c919c5d 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -1132,12 +1132,36 @@ static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, return ddr2_cs_size(cs_mode, dclr & WIDTH_128); } else if (pvt->ext_model >= K8_REV_D) { + unsigned diff; WARN_ON(cs_mode > 10); - if (cs_mode == 3 || cs_mode == 8) - return 32 << (cs_mode - 1); - else - return 32 << cs_mode; + /* + * the below calculation, besides trying to win an obfuscated C + * contest, maps cs_mode values to DIMM chip select sizes. The + * mappings are: + * + * cs_mode CS size (mb) + * ======= ============ + * 0 32 + * 1 64 + * 2 128 + * 3 128 + * 4 256 + * 5 512 + * 6 256 + * 7 512 + * 8 1024 + * 9 1024 + * 10 2048 + * + * Basically, it calculates a value with which to shift the + * smallest CS size of 32MB. + * + * ddr[23]_cs_size have a similar purpose. + */ + diff = cs_mode/3 + (unsigned)(cs_mode > 5); + + return 32 << (cs_mode - diff); } else { WARN_ON(cs_mode > 6); @@ -2133,6 +2157,7 @@ static void read_mc_regs(struct amd64_pvt *pvt) static u32 amd64_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr) { u32 cs_mode, nr_pages; + u32 dbam = dct ? pvt->dbam1 : pvt->dbam0; /* * The math on this doesn't look right on the surface because x/2*4 can @@ -2141,16 +2166,10 @@ static u32 amd64_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr) * number of bits to shift the DBAM register to extract the proper CSROW * field. */ - cs_mode = (pvt->dbam0 >> ((csrow_nr / 2) * 4)) & 0xF; + cs_mode = (dbam >> ((csrow_nr / 2) * 4)) & 0xF; nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode) << (20 - PAGE_SHIFT); - /* - * If dual channel then double the memory size of single channel. - * Channel count is 1 or 2 - */ - nr_pages <<= (pvt->channel_count - 1); - debugf0(" (csrow=%d) DBAM map index= %d\n", csrow_nr, cs_mode); debugf0(" nr_pages= %u channel-count = %d\n", nr_pages, pvt->channel_count); @@ -2181,7 +2200,7 @@ static int init_csrows(struct mem_ctl_info *mci) for_each_chip_select(i, 0, pvt) { csrow = &mci->csrows[i]; - if (!csrow_enabled(i, 0, pvt)) { + if (!csrow_enabled(i, 0, pvt) && !csrow_enabled(i, 1, pvt)) { debugf1("----CSROW %d EMPTY for node %d\n", i, pvt->mc_node_id); continue; @@ -2191,7 +2210,10 @@ static int init_csrows(struct mem_ctl_info *mci) i, pvt->mc_node_id); empty = 0; - csrow->nr_pages = amd64_csrow_nr_pages(pvt, 0, i); + if (csrow_enabled(i, 0, pvt)) + csrow->nr_pages = amd64_csrow_nr_pages(pvt, 0, i); + if (csrow_enabled(i, 1, pvt)) + csrow->nr_pages += amd64_csrow_nr_pages(pvt, 1, i); find_csrow_limits(mci, i, &input_addr_min, &input_addr_max); sys_addr = input_addr_to_sys_addr(mci, input_addr_min); csrow->first_page = (u32) (sys_addr >> PAGE_SHIFT); @@ -2685,7 +2707,7 @@ static void __devexit amd64_remove_one_instance(struct pci_dev *pdev) * PCI core identifies what devices are on a system during boot, and then * inquiry this table to see if this driver is for a given device found. */ -static const struct pci_device_id amd64_pci_table[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(amd64_pci_table) = { { .vendor = PCI_VENDOR_ID_AMD, .device = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL, diff --git a/drivers/edac/amd76x_edac.c b/drivers/edac/amd76x_edac.c index e47e73bbbcc5..f8fd3c807bde 100644 --- a/drivers/edac/amd76x_edac.c +++ b/drivers/edac/amd76x_edac.c @@ -321,7 +321,7 @@ static void __devexit amd76x_remove_one(struct pci_dev *pdev) edac_mc_free(mci); } -static const struct pci_device_id amd76x_pci_tbl[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(amd76x_pci_tbl) = { { PCI_VEND_DEV(AMD, FE_GATE_700C), PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD762}, diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c index 1af531a11d21..41223261ede9 100644 --- a/drivers/edac/e752x_edac.c +++ b/drivers/edac/e752x_edac.c @@ -1380,7 +1380,7 @@ static void __devexit e752x_remove_one(struct pci_dev *pdev) edac_mc_free(mci); } -static const struct pci_device_id e752x_pci_tbl[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(e752x_pci_tbl) = { { PCI_VEND_DEV(INTEL, 7520_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0, E7520}, diff --git a/drivers/edac/e7xxx_edac.c b/drivers/edac/e7xxx_edac.c index 6ffb6d23281f..68dea87b72e6 100644 --- a/drivers/edac/e7xxx_edac.c +++ b/drivers/edac/e7xxx_edac.c @@ -525,7 +525,7 @@ static void __devexit e7xxx_remove_one(struct pci_dev *pdev) edac_mc_free(mci); } -static const struct pci_device_id e7xxx_pci_tbl[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(e7xxx_pci_tbl) = { { PCI_VEND_DEV(INTEL, 7205_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0, E7205}, diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index d56e63477d5c..e9a28f576d14 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -452,7 +452,7 @@ static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci, int new_bw = 0; if (!mci->set_sdram_scrub_rate) - return -EINVAL; + return -ENODEV; if (strict_strtoul(data, 10, &bandwidth) < 0) return -EINVAL; @@ -475,7 +475,7 @@ static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data) int bandwidth = 0; if (!mci->get_sdram_scrub_rate) - return -EINVAL; + return -ENODEV; bandwidth = mci->get_sdram_scrub_rate(mci); if (bandwidth < 0) { diff --git a/drivers/edac/edac_stub.c b/drivers/edac/edac_stub.c index 670c4481453b..6c86f6e54558 100644 --- a/drivers/edac/edac_stub.c +++ b/drivers/edac/edac_stub.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/edac.h> #include <linux/atomic.h> +#include <linux/device.h> #include <asm/edac.h> int edac_op_state = EDAC_OPSTATE_INVAL; diff --git a/drivers/edac/i3000_edac.c b/drivers/edac/i3000_edac.c index c0510b3d7035..277689a68841 100644 --- a/drivers/edac/i3000_edac.c +++ b/drivers/edac/i3000_edac.c @@ -470,7 +470,7 @@ static void __devexit i3000_remove_one(struct pci_dev *pdev) edac_mc_free(mci); } -static const struct pci_device_id i3000_pci_tbl[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(i3000_pci_tbl) = { { PCI_VEND_DEV(INTEL, 3000_HB), PCI_ANY_ID, PCI_ANY_ID, 0, 0, I3000}, diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c index 73f55e2008c2..046808c6357d 100644 --- a/drivers/edac/i3200_edac.c +++ b/drivers/edac/i3200_edac.c @@ -445,7 +445,7 @@ static void __devexit i3200_remove_one(struct pci_dev *pdev) edac_mc_free(mci); } -static const struct pci_device_id i3200_pci_tbl[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(i3200_pci_tbl) = { { PCI_VEND_DEV(INTEL, 3200_HB), PCI_ANY_ID, PCI_ANY_ID, 0, 0, I3200}, diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c index 4dc3ac25a422..a2680d8e744b 100644 --- a/drivers/edac/i5000_edac.c +++ b/drivers/edac/i5000_edac.c @@ -1516,7 +1516,7 @@ static void __devexit i5000_remove_one(struct pci_dev *pdev) * * The "E500P" device is the first device supported. */ -static const struct pci_device_id i5000_pci_tbl[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(i5000_pci_tbl) = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I5000_DEV16), .driver_data = I5000P}, diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c index bcbdeeca48b8..2e23547b2f24 100644 --- a/drivers/edac/i5100_edac.c +++ b/drivers/edac/i5100_edac.c @@ -1051,7 +1051,7 @@ static void __devexit i5100_remove_one(struct pci_dev *pdev) edac_mc_free(mci); } -static const struct pci_device_id i5100_pci_tbl[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(i5100_pci_tbl) = { /* Device 16, Function 0, Channel 0 Memory Map, Error Flag/Mask, ... */ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5100_16) }, { 0, } diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c index 74d6ec342afb..67ec9626a330 100644 --- a/drivers/edac/i5400_edac.c +++ b/drivers/edac/i5400_edac.c @@ -1383,7 +1383,7 @@ static void __devexit i5400_remove_one(struct pci_dev *pdev) * * The "E500P" device is the first device supported. */ -static const struct pci_device_id i5400_pci_tbl[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(i5400_pci_tbl) = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5400_ERR)}, {0,} /* 0 terminated list. */ }; diff --git a/drivers/edac/i7300_edac.c b/drivers/edac/i7300_edac.c index 6104dba380b6..3bafa3bca148 100644 --- a/drivers/edac/i7300_edac.c +++ b/drivers/edac/i7300_edac.c @@ -1192,7 +1192,7 @@ static void __devexit i7300_remove_one(struct pci_dev *pdev) * * Has only 8086:360c PCI ID */ -static const struct pci_device_id i7300_pci_tbl[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(i7300_pci_tbl) = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7300_MCH_ERR)}, {0,} /* 0 terminated list. */ }; diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 8568d9b61875..85226ccf5290 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -391,7 +391,7 @@ static const struct pci_id_table pci_dev_table[] = { /* * pci_device_id table for which devices we are looking for */ -static const struct pci_device_id i7core_pci_tbl[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(i7core_pci_tbl) = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0)}, {0,} /* 0 terminated list. */ diff --git a/drivers/edac/i82443bxgx_edac.c b/drivers/edac/i82443bxgx_edac.c index 4329d39f902c..3bf2b2f490e7 100644 --- a/drivers/edac/i82443bxgx_edac.c +++ b/drivers/edac/i82443bxgx_edac.c @@ -380,7 +380,7 @@ static void __devexit i82443bxgx_edacmc_remove_one(struct pci_dev *pdev) EXPORT_SYMBOL_GPL(i82443bxgx_edacmc_remove_one); -static const struct pci_device_id i82443bxgx_pci_tbl[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(i82443bxgx_pci_tbl) = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_0)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_2)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443GX_0)}, diff --git a/drivers/edac/i82860_edac.c b/drivers/edac/i82860_edac.c index 931a05775049..c779092d18d1 100644 --- a/drivers/edac/i82860_edac.c +++ b/drivers/edac/i82860_edac.c @@ -270,7 +270,7 @@ static void __devexit i82860_remove_one(struct pci_dev *pdev) edac_mc_free(mci); } -static const struct pci_device_id i82860_pci_tbl[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(i82860_pci_tbl) = { { PCI_VEND_DEV(INTEL, 82860_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0, I82860}, diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c index 33864c63c684..10f15d85fb5e 100644 --- a/drivers/edac/i82875p_edac.c +++ b/drivers/edac/i82875p_edac.c @@ -511,7 +511,7 @@ static void __devexit i82875p_remove_one(struct pci_dev *pdev) edac_mc_free(mci); } -static const struct pci_device_id i82875p_pci_tbl[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(i82875p_pci_tbl) = { { PCI_VEND_DEV(INTEL, 82875_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0, I82875P}, diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c index 4184e0171f00..0cd8368f88f8 100644 --- a/drivers/edac/i82975x_edac.c +++ b/drivers/edac/i82975x_edac.c @@ -612,7 +612,7 @@ static void __devexit i82975x_remove_one(struct pci_dev *pdev) edac_mc_free(mci); } -static const struct pci_device_id i82975x_pci_tbl[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(i82975x_pci_tbl) = { { PCI_VEND_DEV(INTEL, 82975_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0, I82975X diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index bd926ea2e00c..36e1486eb9aa 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c @@ -39,42 +39,31 @@ EXPORT_SYMBOL_GPL(amd_unregister_ecc_decoder); */ /* transaction type */ -const char *tt_msgs[] = { "INSN", "DATA", "GEN", "RESV" }; +const char * const tt_msgs[] = { "INSN", "DATA", "GEN", "RESV" }; EXPORT_SYMBOL_GPL(tt_msgs); /* cache level */ -const char *ll_msgs[] = { "RESV", "L1", "L2", "L3/GEN" }; +const char * const ll_msgs[] = { "RESV", "L1", "L2", "L3/GEN" }; EXPORT_SYMBOL_GPL(ll_msgs); /* memory transaction type */ -const char *rrrr_msgs[] = { +const char * const rrrr_msgs[] = { "GEN", "RD", "WR", "DRD", "DWR", "IRD", "PRF", "EV", "SNP" }; EXPORT_SYMBOL_GPL(rrrr_msgs); /* participating processor */ -const char *pp_msgs[] = { "SRC", "RES", "OBS", "GEN" }; +const char * const pp_msgs[] = { "SRC", "RES", "OBS", "GEN" }; EXPORT_SYMBOL_GPL(pp_msgs); /* request timeout */ -const char *to_msgs[] = { "no timeout", "timed out" }; +const char * const to_msgs[] = { "no timeout", "timed out" }; EXPORT_SYMBOL_GPL(to_msgs); /* memory or i/o */ -const char *ii_msgs[] = { "MEM", "RESV", "IO", "GEN" }; +const char * const ii_msgs[] = { "MEM", "RESV", "IO", "GEN" }; EXPORT_SYMBOL_GPL(ii_msgs); -static const char *f10h_nb_mce_desc[] = { - "HT link data error", - "Protocol error (link, L3, probe filter, etc.)", - "Parity error in NB-internal arrays", - "Link Retry due to IO link transmission error", - "L3 ECC data cache error", - "ECC error in L3 cache tag", - "L3 LRU parity bits error", - "ECC Error in the Probe Filter directory" -}; - static const char * const f15h_ic_mce_desc[] = { "UC during a demand linefill from L2", "Parity error during data load from IC", @@ -88,7 +77,7 @@ static const char * const f15h_ic_mce_desc[] = { "Parity error for IC probe tag valid bit", "PFB non-cacheable bit parity error", "PFB valid bit parity error", /* xec = 0xd */ - "patch RAM", /* xec = 010 */ + "Microcode Patch Buffer", /* xec = 010 */ "uop queue", "insn buffer", "predecode buffer", @@ -104,7 +93,7 @@ static const char * const f15h_cu_mce_desc[] = { "WCC Tag ECC error", "WCC Data ECC error", "WCB Data parity error", - "VB Data/ECC error", + "VB Data ECC or parity error", "L2 Tag ECC error", /* xec = 0x10 */ "Hard L2 Tag ECC error", "Multiple hits on L2 tag", @@ -112,6 +101,28 @@ static const char * const f15h_cu_mce_desc[] = { "PRB address parity error" }; +static const char * const nb_mce_desc[] = { + "DRAM ECC error detected on the NB", + "CRC error detected on HT link", + "Link-defined sync error packets detected on HT link", + "HT Master abort", + "HT Target abort", + "Invalid GART PTE entry during GART table walk", + "Unsupported atomic RMW received from an IO link", + "Watchdog timeout due to lack of progress", + "DRAM ECC error detected on the NB", + "SVM DMA Exclusion Vector error", + "HT data error detected on link", + "Protocol error (link, L3, probe filter)", + "NB internal arrays parity error", + "DRAM addr/ctl signals parity error", + "IO link transmission error", + "L3 data cache ECC error", /* xec = 0x1c */ + "L3 cache tag error", + "L3 LRU parity bits error", + "ECC Error in the Probe Filter directory" +}; + static const char * const fr_ex_mce_desc[] = { "CPU Watchdog timer expire", "Wakeup array dest tag", @@ -125,7 +136,7 @@ static const char * const fr_ex_mce_desc[] = { "Physical register file AG0 port", "Physical register file AG1 port", "Flag register file", - "DE correctable error could not be corrected" + "DE error occurred" }; static bool f12h_dc_mce(u16 ec, u8 xec) @@ -255,10 +266,9 @@ static bool f15h_dc_mce(u16 ec, u8 xec) } else if (BUS_ERROR(ec)) { if (!xec) - pr_cont("during system linefill.\n"); + pr_cont("System Read Data Error.\n"); else - pr_cont(" Internal %s condition.\n", - ((xec == 1) ? "livelock" : "deadlock")); + pr_cont(" Internal error condition type %d.\n", xec); } else ret = false; @@ -355,7 +365,11 @@ static bool f15h_ic_mce(u16 ec, u8 xec) pr_cont("%s.\n", f15h_ic_mce_desc[xec-2]); break; - case 0x10 ... 0x14: + case 0x10: + pr_cont("%s.\n", f15h_ic_mce_desc[xec-4]); + break; + + case 0x11 ... 0x14: pr_cont("Decoder %s parity error.\n", f15h_ic_mce_desc[xec-4]); break; @@ -496,58 +510,31 @@ wrong_ls_mce: pr_emerg(HW_ERR "Corrupted LS MCE info?\n"); } -static bool k8_nb_mce(u16 ec, u8 xec) +void amd_decode_nb_mce(struct mce *m) { - bool ret = true; - - switch (xec) { - case 0x1: - pr_cont("CRC error detected on HT link.\n"); - break; - - case 0x5: - pr_cont("Invalid GART PTE entry during GART table walk.\n"); - break; - - case 0x6: - pr_cont("Unsupported atomic RMW received from an IO link.\n"); - break; - - case 0x0: - case 0x8: - if (boot_cpu_data.x86 == 0x11) - return false; - - pr_cont("DRAM ECC error detected on the NB.\n"); - break; - - case 0xd: - pr_cont("Parity error on the DRAM addr/ctl signals.\n"); - break; - - default: - ret = false; - break; - } + struct cpuinfo_x86 *c = &boot_cpu_data; + int node_id = amd_get_nb_id(m->extcpu); + u16 ec = EC(m->status); + u8 xec = XEC(m->status, 0x1f); + u8 offset = 0; - return ret; -} + pr_emerg(HW_ERR "Northbridge Error (node %d): ", node_id); -static bool f10h_nb_mce(u16 ec, u8 xec) -{ - bool ret = true; - u8 offset = 0; + switch (xec) { + case 0x0 ... 0xe: - if (k8_nb_mce(ec, xec)) - return true; + /* special handling for DRAM ECCs */ + if (xec == 0x0 || xec == 0x8) { + /* no ECCs on F11h */ + if (c->x86 == 0x11) + goto wrong_nb_mce; - switch(xec) { - case 0xa ... 0xc: - offset = 10; - break; + pr_cont("%s.\n", nb_mce_desc[xec]); - case 0xe: - offset = 11; + if (nb_bus_decoder) + nb_bus_decoder(node_id, m); + return; + } break; case 0xf: @@ -556,83 +543,25 @@ static bool f10h_nb_mce(u16 ec, u8 xec) else if (BUS_ERROR(ec)) pr_cont("DMA Exclusion Vector Table Walk error.\n"); else - ret = false; - - goto out; - break; + goto wrong_nb_mce; + return; case 0x19: if (boot_cpu_data.x86 == 0x15) pr_cont("Compute Unit Data Error.\n"); else - ret = false; - - goto out; - break; + goto wrong_nb_mce; + return; case 0x1c ... 0x1f: - offset = 24; + offset = 13; break; default: - ret = false; - - goto out; - break; - } - - pr_cont("%s.\n", f10h_nb_mce_desc[xec - offset]); - -out: - return ret; -} - -static bool nb_noop_mce(u16 ec, u8 xec) -{ - return false; -} - -void amd_decode_nb_mce(struct mce *m) -{ - struct cpuinfo_x86 *c = &boot_cpu_data; - int node_id = amd_get_nb_id(m->extcpu); - u16 ec = EC(m->status); - u8 xec = XEC(m->status, 0x1f); - - pr_emerg(HW_ERR "Northbridge Error (node %d): ", node_id); - - switch (xec) { - case 0x2: - pr_cont("Sync error (sync packets on HT link detected).\n"); - return; - - case 0x3: - pr_cont("HT Master abort.\n"); - return; - - case 0x4: - pr_cont("HT Target abort.\n"); - return; - - case 0x7: - pr_cont("NB Watchdog timeout.\n"); - return; - - case 0x9: - pr_cont("SVM DMA Exclusion Vector error.\n"); - return; - - default: - break; - } - - if (!fam_ops->nb_mce(ec, xec)) goto wrong_nb_mce; + } - if (c->x86 == 0xf || c->x86 == 0x10 || c->x86 == 0x15) - if ((xec == 0x8 || xec == 0x0) && nb_bus_decoder) - nb_bus_decoder(node_id, m); - + pr_cont("%s.\n", nb_mce_desc[xec - offset]); return; wrong_nb_mce: @@ -648,9 +577,6 @@ static void amd_decode_fr_mce(struct mce *m) if (c->x86 == 0xf || c->x86 == 0x11) goto wrong_fr_mce; - if (c->x86 != 0x15 && xec != 0x0) - goto wrong_fr_mce; - pr_emerg(HW_ERR "%s Error: ", (c->x86 == 0x15 ? "Execution Unit" : "FIROB")); @@ -841,39 +767,33 @@ static int __init mce_amd_init(void) case 0xf: fam_ops->dc_mce = k8_dc_mce; fam_ops->ic_mce = k8_ic_mce; - fam_ops->nb_mce = k8_nb_mce; break; case 0x10: fam_ops->dc_mce = f10h_dc_mce; fam_ops->ic_mce = k8_ic_mce; - fam_ops->nb_mce = f10h_nb_mce; break; case 0x11: fam_ops->dc_mce = k8_dc_mce; fam_ops->ic_mce = k8_ic_mce; - fam_ops->nb_mce = f10h_nb_mce; break; case 0x12: fam_ops->dc_mce = f12h_dc_mce; fam_ops->ic_mce = k8_ic_mce; - fam_ops->nb_mce = nb_noop_mce; break; case 0x14: nb_err_cpumask = 0x3; fam_ops->dc_mce = f14h_dc_mce; fam_ops->ic_mce = f14h_ic_mce; - fam_ops->nb_mce = nb_noop_mce; break; case 0x15: xec_mask = 0x1f; fam_ops->dc_mce = f15h_dc_mce; fam_ops->ic_mce = f15h_ic_mce; - fam_ops->nb_mce = f10h_nb_mce; break; default: diff --git a/drivers/edac/mce_amd.h b/drivers/edac/mce_amd.h index 0106747e240c..c6074c5cd1ef 100644 --- a/drivers/edac/mce_amd.h +++ b/drivers/edac/mce_amd.h @@ -69,12 +69,12 @@ enum rrrr_ids { R4_SNOOP, }; -extern const char *tt_msgs[]; -extern const char *ll_msgs[]; -extern const char *rrrr_msgs[]; -extern const char *pp_msgs[]; -extern const char *to_msgs[]; -extern const char *ii_msgs[]; +extern const char * const tt_msgs[]; +extern const char * const ll_msgs[]; +extern const char * const rrrr_msgs[]; +extern const char * const pp_msgs[]; +extern const char * const to_msgs[]; +extern const char * const ii_msgs[]; /* * per-family decoder ops @@ -82,7 +82,6 @@ extern const char *ii_msgs[]; struct amd_decoder_ops { bool (*dc_mce)(u16, u8); bool (*ic_mce)(u16, u8); - bool (*nb_mce)(u16, u8); }; void amd_report_gart_errors(bool); diff --git a/drivers/edac/mce_amd_inj.c b/drivers/edac/mce_amd_inj.c index 885e8ad8fdcf..66b5151c1080 100644 --- a/drivers/edac/mce_amd_inj.c +++ b/drivers/edac/mce_amd_inj.c @@ -11,6 +11,7 @@ */ #include <linux/kobject.h> +#include <linux/device.h> #include <linux/edac.h> #include <linux/module.h> #include <asm/mce.h> diff --git a/drivers/edac/r82600_edac.c b/drivers/edac/r82600_edac.c index e294e1b3616c..6d908ad72d64 100644 --- a/drivers/edac/r82600_edac.c +++ b/drivers/edac/r82600_edac.c @@ -373,7 +373,7 @@ static void __devexit r82600_remove_one(struct pci_dev *pdev) edac_mc_free(mci); } -static const struct pci_device_id r82600_pci_tbl[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(r82600_pci_tbl) = { { PCI_DEVICE(PCI_VENDOR_ID_RADISYS, R82600_BRIDGE_ID) }, diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c index 1dc118d83cc6..3a605f777712 100644 --- a/drivers/edac/sb_edac.c +++ b/drivers/edac/sb_edac.c @@ -367,7 +367,7 @@ static const struct pci_id_table pci_dev_descr_sbridge_table[] = { /* * pci_device_id table for which devices we are looking for */ -static const struct pci_device_id sbridge_pci_tbl[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(sbridge_pci_tbl) = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA)}, {0,} /* 0 terminated list. */ }; diff --git a/drivers/edac/x38_edac.c b/drivers/edac/x38_edac.c index b6f47de152f3..a438297389e5 100644 --- a/drivers/edac/x38_edac.c +++ b/drivers/edac/x38_edac.c @@ -440,7 +440,7 @@ static void __devexit x38_remove_one(struct pci_dev *pdev) edac_mc_free(mci); } -static const struct pci_device_id x38_pci_tbl[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(x38_pci_tbl) = { { PCI_VEND_DEV(INTEL, X38_HB), PCI_ANY_ID, PCI_ANY_ID, 0, 0, X38}, diff --git a/drivers/firewire/Kconfig b/drivers/firewire/Kconfig index 2be6f4520772..7224533e8ca6 100644 --- a/drivers/firewire/Kconfig +++ b/drivers/firewire/Kconfig @@ -28,11 +28,6 @@ config FIREWIRE_OHCI To compile this driver as a module, say M here: The module will be called firewire-ohci. -config FIREWIRE_OHCI_DEBUG - bool - depends on FIREWIRE_OHCI - default y - config FIREWIRE_SBP2 tristate "Storage devices (SBP-2 protocol)" depends on FIREWIRE && SCSI diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 85661b060ed7..cc595eba7ba9 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -37,6 +37,22 @@ #include "core.h" +#define define_fw_printk_level(func, kern_level) \ +void func(const struct fw_card *card, const char *fmt, ...) \ +{ \ + struct va_format vaf; \ + va_list args; \ + \ + va_start(args, fmt); \ + vaf.fmt = fmt; \ + vaf.va = &args; \ + printk(kern_level KBUILD_MODNAME " %s: %pV", \ + dev_name(card->device), &vaf); \ + va_end(args); \ +} +define_fw_printk_level(fw_err, KERN_ERR); +define_fw_printk_level(fw_notice, KERN_NOTICE); + int fw_compute_block_crc(__be32 *block) { int length; @@ -260,7 +276,7 @@ static void allocate_broadcast_channel(struct fw_card *card, int generation) fw_iso_resource_manage(card, generation, 1ULL << 31, &channel, &bandwidth, true); if (channel != 31) { - fw_notify("failed to allocate broadcast channel\n"); + fw_notice(card, "failed to allocate broadcast channel\n"); return; } card->broadcast_channel_allocated = true; @@ -343,14 +359,14 @@ static void bm_work(struct work_struct *work) if (!card->irm_node->link_on) { new_root_id = local_id; - fw_notify("%s, making local node (%02x) root.\n", + fw_notice(card, "%s, making local node (%02x) root\n", "IRM has link off", new_root_id); goto pick_me; } if (irm_is_1394_1995_only && !keep_this_irm) { new_root_id = local_id; - fw_notify("%s, making local node (%02x) root.\n", + fw_notice(card, "%s, making local node (%02x) root\n", "IRM is not 1394a compliant", new_root_id); goto pick_me; } @@ -405,7 +421,7 @@ static void bm_work(struct work_struct *work) * root, and thus, IRM. */ new_root_id = local_id; - fw_notify("%s, making local node (%02x) root.\n", + fw_notice(card, "%s, making local node (%02x) root\n", "BM lock failed", new_root_id); goto pick_me; } @@ -478,8 +494,8 @@ static void bm_work(struct work_struct *work) spin_unlock_irq(&card->lock); if (do_reset) { - fw_notify("phy config: card %d, new root=%x, gap_count=%d\n", - card->index, new_root_id, gap_count); + fw_notice(card, "phy config: new root=%x, gap_count=%d\n", + new_root_id, gap_count); fw_send_phy_config(card, new_root_id, generation, gap_count); reset_bus(card, true); /* Will allocate broadcast channel after the reset. */ @@ -634,6 +650,11 @@ static void dummy_flush_queue_iso(struct fw_iso_context *ctx) { } +static int dummy_flush_iso_completions(struct fw_iso_context *ctx) +{ + return -ENODEV; +} + static const struct fw_card_driver dummy_driver_template = { .read_phy_reg = dummy_read_phy_reg, .update_phy_reg = dummy_update_phy_reg, @@ -646,6 +667,7 @@ static const struct fw_card_driver dummy_driver_template = { .set_iso_channels = dummy_set_iso_channels, .queue_iso = dummy_queue_iso, .flush_queue_iso = dummy_flush_queue_iso, + .flush_iso_completions = dummy_flush_iso_completions, }; void fw_card_release(struct kref *kref) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 4799393247c8..22c6df5f136d 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -51,7 +51,7 @@ /* * ABI version history is documented in linux/firewire-cdev.h. */ -#define FW_CDEV_KERNEL_VERSION 4 +#define FW_CDEV_KERNEL_VERSION 5 #define FW_CDEV_VERSION_EVENT_REQUEST2 4 #define FW_CDEV_VERSION_ALLOCATE_REGION_END 4 @@ -389,7 +389,7 @@ static void queue_bus_reset_event(struct client *client) e = kzalloc(sizeof(*e), GFP_KERNEL); if (e == NULL) { - fw_notify("Out of memory when allocating event\n"); + fw_notice(client->device->card, "out of memory when allocating event\n"); return; } @@ -438,6 +438,7 @@ union ioctl_arg { struct fw_cdev_send_phy_packet send_phy_packet; struct fw_cdev_receive_phy_packets receive_phy_packets; struct fw_cdev_set_iso_channels set_iso_channels; + struct fw_cdev_flush_iso flush_iso; }; static int ioctl_get_info(struct client *client, union ioctl_arg *arg) @@ -691,7 +692,7 @@ static void handle_request(struct fw_card *card, struct fw_request *request, r = kmalloc(sizeof(*r), GFP_ATOMIC); e = kmalloc(sizeof(*e), GFP_ATOMIC); if (r == NULL || e == NULL) { - fw_notify("Out of memory when allocating event\n"); + fw_notice(card, "out of memory when allocating event\n"); goto failed; } r->card = card; @@ -928,7 +929,7 @@ static void iso_callback(struct fw_iso_context *context, u32 cycle, e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC); if (e == NULL) { - fw_notify("Out of memory when allocating event\n"); + fw_notice(context->card, "out of memory when allocating event\n"); return; } e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT; @@ -948,7 +949,7 @@ static void iso_mc_callback(struct fw_iso_context *context, e = kmalloc(sizeof(*e), GFP_ATOMIC); if (e == NULL) { - fw_notify("Out of memory when allocating event\n"); + fw_notice(context->card, "out of memory when allocating event\n"); return; } e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL; @@ -1168,6 +1169,16 @@ static int ioctl_stop_iso(struct client *client, union ioctl_arg *arg) return fw_iso_context_stop(client->iso_context); } +static int ioctl_flush_iso(struct client *client, union ioctl_arg *arg) +{ + struct fw_cdev_flush_iso *a = &arg->flush_iso; + + if (client->iso_context == NULL || a->handle != 0) + return -EINVAL; + + return fw_iso_context_flush_completions(client->iso_context); +} + static int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg) { struct fw_cdev_get_cycle_timer2 *a = &arg->get_cycle_timer2; @@ -1548,7 +1559,7 @@ void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p) list_for_each_entry(client, &card->phy_receiver_list, phy_receiver_link) { e = kmalloc(sizeof(*e) + 8, GFP_ATOMIC); if (e == NULL) { - fw_notify("Out of memory when allocating event\n"); + fw_notice(card, "out of memory when allocating event\n"); break; } e->phy_packet.closure = client->phy_receiver_closure; @@ -1589,6 +1600,7 @@ static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { [0x15] = ioctl_send_phy_packet, [0x16] = ioctl_receive_phy_packets, [0x17] = ioctl_set_iso_channels, + [0x18] = ioctl_flush_iso, }; static int dispatch_ioctl(struct client *client, diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index f3b890da1e87..afa7c83bd114 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -485,6 +485,7 @@ static int read_rom(struct fw_device *device, */ static int read_config_rom(struct fw_device *device, int generation) { + struct fw_card *card = device->card; const u32 *old_rom, *new_rom; u32 *rom, *stack; u32 sp, key; @@ -529,12 +530,12 @@ static int read_config_rom(struct fw_device *device, int generation) */ if ((rom[2] & 0x7) < device->max_speed || device->max_speed == SCODE_BETA || - device->card->beta_repeaters_present) { + card->beta_repeaters_present) { u32 dummy; /* for S1600 and S3200 */ if (device->max_speed == SCODE_BETA) - device->max_speed = device->card->link_speed; + device->max_speed = card->link_speed; while (device->max_speed > SCODE_100) { if (read_rom(device, generation, 0, &dummy) == @@ -576,9 +577,9 @@ static int read_config_rom(struct fw_device *device, int generation) * a firmware bug. Ignore this whole block, i.e. * simply set a fake block length of 0. */ - fw_error("skipped invalid ROM block %x at %llx\n", - rom[i], - i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM); + fw_err(card, "skipped invalid ROM block %x at %llx\n", + rom[i], + i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM); rom[i] = 0; end = i; } @@ -604,9 +605,10 @@ static int read_config_rom(struct fw_device *device, int generation) * the ROM don't have to check offsets all the time. */ if (i + (rom[i] & 0xffffff) >= MAX_CONFIG_ROM_SIZE) { - fw_error("skipped unsupported ROM entry %x at %llx\n", - rom[i], - i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM); + fw_err(card, + "skipped unsupported ROM entry %x at %llx\n", + rom[i], + i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM); rom[i] = 0; continue; } @@ -641,6 +643,7 @@ static void fw_unit_release(struct device *dev) { struct fw_unit *unit = fw_unit(dev); + fw_device_put(fw_parent_device(unit)); kfree(unit); } @@ -672,7 +675,7 @@ static void create_units(struct fw_device *device) */ unit = kzalloc(sizeof(*unit), GFP_KERNEL); if (unit == NULL) { - fw_error("failed to allocate memory for unit\n"); + fw_err(device->card, "out of memory for unit\n"); continue; } @@ -692,6 +695,7 @@ static void create_units(struct fw_device *device) if (device_register(&unit->device) < 0) goto skip_unit; + fw_device_get(device); continue; skip_unit: @@ -873,7 +877,7 @@ static int lookup_existing_device(struct device *dev, void *data) smp_wmb(); /* update node_id before generation */ old->generation = card->generation; old->config_rom_retries = 0; - fw_notify("rediscovered device %s\n", dev_name(dev)); + fw_notice(card, "rediscovered device %s\n", dev_name(dev)); PREPARE_DELAYED_WORK(&old->work, fw_device_update); fw_schedule_device_work(old, 0); @@ -954,6 +958,7 @@ static void fw_device_init(struct work_struct *work) { struct fw_device *device = container_of(work, struct fw_device, work.work); + struct fw_card *card = device->card; struct device *revived_dev; int minor, ret; @@ -970,16 +975,16 @@ static void fw_device_init(struct work_struct *work) fw_schedule_device_work(device, RETRY_DELAY); } else { if (device->node->link_on) - fw_notify("giving up on config rom for node id %x\n", + fw_notice(card, "giving up on Config ROM for node id %x\n", device->node_id); - if (device->node == device->card->root_node) - fw_schedule_bm_work(device->card, 0); + if (device->node == card->root_node) + fw_schedule_bm_work(card, 0); fw_device_release(&device->device); } return; } - revived_dev = device_find_child(device->card->device, + revived_dev = device_find_child(card->device, device, lookup_existing_device); if (revived_dev) { put_device(revived_dev); @@ -1002,7 +1007,7 @@ static void fw_device_init(struct work_struct *work) device->device.bus = &fw_bus_type; device->device.type = &fw_device_type; - device->device.parent = device->card->device; + device->device.parent = card->device; device->device.devt = MKDEV(fw_cdev_major, minor); dev_set_name(&device->device, "fw%d", minor); @@ -1014,7 +1019,7 @@ static void fw_device_init(struct work_struct *work) &device->attribute_group); if (device_add(&device->device)) { - fw_error("Failed to add device.\n"); + fw_err(card, "failed to add device\n"); goto error_with_cdev; } @@ -1035,18 +1040,10 @@ static void fw_device_init(struct work_struct *work) PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); fw_schedule_device_work(device, SHUTDOWN_DELAY); } else { - if (device->config_rom_retries) - fw_notify("created device %s: GUID %08x%08x, S%d00, " - "%d config ROM retries\n", - dev_name(&device->device), - device->config_rom[3], device->config_rom[4], - 1 << device->max_speed, - device->config_rom_retries); - else - fw_notify("created device %s: GUID %08x%08x, S%d00\n", - dev_name(&device->device), - device->config_rom[3], device->config_rom[4], - 1 << device->max_speed); + fw_notice(card, "created device %s: GUID %08x%08x, S%d00\n", + dev_name(&device->device), + device->config_rom[3], device->config_rom[4], + 1 << device->max_speed); device->config_rom_retries = 0; set_broadcast_channel(device, device->generation); @@ -1058,8 +1055,8 @@ static void fw_device_init(struct work_struct *work) * just end up running the IRM work a couple of extra times - * pretty harmless. */ - if (device->node == device->card->root_node) - fw_schedule_bm_work(device->card, 0); + if (device->node == card->root_node) + fw_schedule_bm_work(card, 0); return; @@ -1163,12 +1160,13 @@ static void fw_device_refresh(struct work_struct *work) FW_DEVICE_RUNNING) == FW_DEVICE_GONE) goto gone; - fw_notify("refreshed device %s\n", dev_name(&device->device)); + fw_notice(card, "refreshed device %s\n", dev_name(&device->device)); device->config_rom_retries = 0; goto out; give_up: - fw_notify("giving up on refresh of device %s\n", dev_name(&device->device)); + fw_notice(card, "giving up on refresh of device %s\n", + dev_name(&device->device)); gone: atomic_set(&device->state, FW_DEVICE_GONE); PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index 0f90e0071875..d1565828ae2c 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -192,6 +192,12 @@ void fw_iso_context_queue_flush(struct fw_iso_context *ctx) } EXPORT_SYMBOL(fw_iso_context_queue_flush); +int fw_iso_context_flush_completions(struct fw_iso_context *ctx) +{ + return ctx->card->driver->flush_iso_completions(ctx); +} +EXPORT_SYMBOL(fw_iso_context_flush_completions); + int fw_iso_context_stop(struct fw_iso_context *ctx) { return ctx->card->driver->stop_iso(ctx); diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index 94d3b494ddfb..255646ffc352 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -205,19 +205,19 @@ static struct fw_node *build_tree(struct fw_card *card, next_sid = count_ports(sid, &port_count, &child_port_count); if (next_sid == NULL) { - fw_error("Inconsistent extended self IDs.\n"); + fw_err(card, "inconsistent extended self IDs\n"); return NULL; } q = *sid; if (phy_id != SELF_ID_PHY_ID(q)) { - fw_error("PHY ID mismatch in self ID: %d != %d.\n", - phy_id, SELF_ID_PHY_ID(q)); + fw_err(card, "PHY ID mismatch in self ID: %d != %d\n", + phy_id, SELF_ID_PHY_ID(q)); return NULL; } if (child_port_count > stack_depth) { - fw_error("Topology stack underflow\n"); + fw_err(card, "topology stack underflow\n"); return NULL; } @@ -235,7 +235,7 @@ static struct fw_node *build_tree(struct fw_card *card, node = fw_node_create(q, port_count, card->color); if (node == NULL) { - fw_error("Out of memory while building topology.\n"); + fw_err(card, "out of memory while building topology\n"); return NULL; } @@ -284,8 +284,8 @@ static struct fw_node *build_tree(struct fw_card *card, */ if ((next_sid == end && parent_count != 0) || (next_sid < end && parent_count != 1)) { - fw_error("Parent port inconsistency for node %d: " - "parent_count=%d\n", phy_id, parent_count); + fw_err(card, "parent port inconsistency for node %d: " + "parent_count=%d\n", phy_id, parent_count); return NULL; } @@ -530,7 +530,6 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, */ if (!is_next_generation(generation, card->generation) && card->local_node != NULL) { - fw_notify("skipped bus generations, destroying all nodes\n"); fw_destroy_nodes(card); card->bm_retries = 0; } @@ -557,7 +556,7 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, card->color++; if (local_node == NULL) { - fw_error("topology build failed\n"); + fw_err(card, "topology build failed\n"); /* FIXME: We need to issue a bus reset in this case. */ } else if (card->local_node == NULL) { card->local_node = local_node; diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 855ab3f5936f..dea2dcc9310d 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -565,7 +565,6 @@ int fw_core_add_address_handler(struct fw_address_handler *handler, const struct fw_address_region *region) { struct fw_address_handler *other; - unsigned long flags; int ret = -EBUSY; if (region->start & 0xffff000000000003ULL || @@ -575,7 +574,7 @@ int fw_core_add_address_handler(struct fw_address_handler *handler, handler->length == 0) return -EINVAL; - spin_lock_irqsave(&address_handler_lock, flags); + spin_lock_bh(&address_handler_lock); handler->offset = region->start; while (handler->offset + handler->length <= region->end) { @@ -594,7 +593,7 @@ int fw_core_add_address_handler(struct fw_address_handler *handler, } } - spin_unlock_irqrestore(&address_handler_lock, flags); + spin_unlock_bh(&address_handler_lock); return ret; } @@ -602,14 +601,15 @@ EXPORT_SYMBOL(fw_core_add_address_handler); /** * fw_core_remove_address_handler() - unregister an address handler + * + * When fw_core_remove_address_handler() returns, @handler->callback() is + * guaranteed to not run on any CPU anymore. */ void fw_core_remove_address_handler(struct fw_address_handler *handler) { - unsigned long flags; - - spin_lock_irqsave(&address_handler_lock, flags); + spin_lock_bh(&address_handler_lock); list_del(&handler->link); - spin_unlock_irqrestore(&address_handler_lock, flags); + spin_unlock_bh(&address_handler_lock); } EXPORT_SYMBOL(fw_core_remove_address_handler); @@ -770,7 +770,7 @@ static struct fw_request *allocate_request(struct fw_card *card, break; default: - fw_error("ERROR - corrupt request received - %08x %08x %08x\n", + fw_notice(card, "ERROR - corrupt request received - %08x %08x %08x\n", p->header[0], p->header[1], p->header[2]); return NULL; } @@ -826,7 +826,6 @@ static void handle_exclusive_region_request(struct fw_card *card, unsigned long long offset) { struct fw_address_handler *handler; - unsigned long flags; int tcode, destination, source; destination = HEADER_GET_DESTINATION(p->header[0]); @@ -835,27 +834,19 @@ static void handle_exclusive_region_request(struct fw_card *card, if (tcode == TCODE_LOCK_REQUEST) tcode = 0x10 + HEADER_GET_EXTENDED_TCODE(p->header[3]); - spin_lock_irqsave(&address_handler_lock, flags); + spin_lock_bh(&address_handler_lock); handler = lookup_enclosing_address_handler(&address_handler_list, offset, request->length); - spin_unlock_irqrestore(&address_handler_lock, flags); - - /* - * FIXME: lookup the fw_node corresponding to the sender of - * this request and pass that to the address handler instead - * of the node ID. We may also want to move the address - * allocations to fw_node so we only do this callback if the - * upper layers registered it for this node. - */ - - if (handler == NULL) - fw_send_response(card, request, RCODE_ADDRESS_ERROR); - else + if (handler) handler->address_callback(card, request, tcode, destination, source, p->generation, offset, request->data, request->length, handler->callback_data); + spin_unlock_bh(&address_handler_lock); + + if (!handler) + fw_send_response(card, request, RCODE_ADDRESS_ERROR); } static void handle_fcp_region_request(struct fw_card *card, @@ -864,7 +855,6 @@ static void handle_fcp_region_request(struct fw_card *card, unsigned long long offset) { struct fw_address_handler *handler; - unsigned long flags; int tcode, destination, source; if ((offset != (CSR_REGISTER_BASE | CSR_FCP_COMMAND) && @@ -886,7 +876,7 @@ static void handle_fcp_region_request(struct fw_card *card, return; } - spin_lock_irqsave(&address_handler_lock, flags); + spin_lock_bh(&address_handler_lock); list_for_each_entry(handler, &address_handler_list, link) { if (is_enclosing_handler(handler, offset, request->length)) handler->address_callback(card, NULL, tcode, @@ -896,7 +886,7 @@ static void handle_fcp_region_request(struct fw_card *card, request->length, handler->callback_data); } - spin_unlock_irqrestore(&address_handler_lock, flags); + spin_unlock_bh(&address_handler_lock); fw_send_response(card, request, RCODE_COMPLETE); } @@ -960,7 +950,7 @@ void fw_core_handle_response(struct fw_card *card, struct fw_packet *p) if (&t->link == &card->transaction_list) { timed_out: - fw_notify("Unsolicited response (source %x, tlabel %x)\n", + fw_notice(card, "unsolicited response (source %x, tlabel %x)\n", source, tlabel); return; } diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index b45be5767529..9047f5547d98 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -1,6 +1,8 @@ #ifndef _FIREWIRE_CORE_H #define _FIREWIRE_CORE_H +#include <linux/compiler.h> +#include <linux/device.h> #include <linux/fs.h> #include <linux/list.h> #include <linux/idr.h> @@ -23,6 +25,11 @@ struct fw_packet; /* -card */ +extern __printf(2, 3) +void fw_err(const struct fw_card *card, const char *fmt, ...); +extern __printf(2, 3) +void fw_notice(const struct fw_card *card, const char *fmt, ...); + /* bitfields within the PHY registers */ #define PHY_LINK_ACTIVE 0x80 #define PHY_CONTENDER 0x40 @@ -99,6 +106,8 @@ struct fw_card_driver { void (*flush_queue_iso)(struct fw_iso_context *ctx); + int (*flush_iso_completions)(struct fw_iso_context *ctx); + int (*stop_iso)(struct fw_iso_context *ctx); }; @@ -141,6 +150,18 @@ extern struct rw_semaphore fw_device_rwsem; extern struct idr fw_device_idr; extern int fw_cdev_major; +static inline struct fw_device *fw_device_get(struct fw_device *device) +{ + get_device(&device->device); + + return device; +} + +static inline void fw_device_put(struct fw_device *device) +{ + put_device(&device->device); +} + struct fw_device *fw_device_get_by_devt(dev_t devt); int fw_device_set_broadcast_channel(struct device *dev, void *gen); void fw_node_event(struct fw_card *card, struct fw_node *node, int event); diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index a20f45b1e7e5..08c674957af8 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -256,8 +256,8 @@ static int fwnet_header_rebuild(struct sk_buff *skb) if (get_unaligned_be16(&h->h_proto) == ETH_P_IP) return arp_find((unsigned char *)&h->h_dest, skb); - fw_notify("%s: unable to resolve type %04x addresses\n", - skb->dev->name, be16_to_cpu(h->h_proto)); + dev_notice(&skb->dev->dev, "unable to resolve type %04x addresses\n", + be16_to_cpu(h->h_proto)); return 0; } @@ -369,7 +369,7 @@ static struct fwnet_fragment_info *fwnet_frag_new( new = kmalloc(sizeof(*new), GFP_ATOMIC); if (!new) { - fw_error("out of memory\n"); + dev_err(&pd->skb->dev->dev, "out of memory\n"); return NULL; } @@ -414,7 +414,7 @@ fail_w_fi: fail_w_new: kfree(new); fail: - fw_error("out of memory\n"); + dev_err(&net->dev, "out of memory\n"); return NULL; } @@ -554,7 +554,7 @@ static int fwnet_finish_incoming_packet(struct net_device *net, sspd = arp1394->sspd; /* Sanity check. OS X 10.3 PPC reportedly sends 131. */ if (sspd > SCODE_3200) { - fw_notify("sspd %x out of range\n", sspd); + dev_notice(&net->dev, "sspd %x out of range\n", sspd); sspd = SCODE_3200; } max_payload = fwnet_max_payload(arp1394->max_rec, sspd); @@ -574,8 +574,9 @@ static int fwnet_finish_incoming_packet(struct net_device *net, spin_unlock_irqrestore(&dev->lock, flags); if (!peer) { - fw_notify("No peer for ARP packet from %016llx\n", - (unsigned long long)peer_guid); + dev_notice(&net->dev, + "no peer for ARP packet from %016llx\n", + (unsigned long long)peer_guid); goto no_peer; } @@ -691,7 +692,7 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len, skb = dev_alloc_skb(len + net->hard_header_len + 15); if (unlikely(!skb)) { - fw_error("out of memory\n"); + dev_err(&net->dev, "out of memory\n"); net->stats.rx_dropped++; return -ENOMEM; @@ -814,7 +815,7 @@ static void fwnet_receive_packet(struct fw_card *card, struct fw_request *r, rcode = RCODE_TYPE_ERROR; else if (fwnet_incoming_packet(dev, payload, length, source, generation, false) != 0) { - fw_error("Incoming packet failure\n"); + dev_err(&dev->netdev->dev, "incoming packet failure\n"); rcode = RCODE_CONFLICT_ERROR; } else rcode = RCODE_COMPLETE; @@ -881,7 +882,7 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context, if (retval >= 0) fw_iso_context_queue_flush(dev->broadcast_rcv_context); else - fw_error("requeue failed\n"); + dev_err(&dev->netdev->dev, "requeue failed\n"); } static struct kmem_cache *fwnet_packet_task_cache; @@ -936,9 +937,10 @@ static void fwnet_transmit_packet_done(struct fwnet_packet_task *ptask) case RFC2374_HDR_LASTFRAG: case RFC2374_HDR_UNFRAG: default: - fw_error("Outstanding packet %x lf %x, header %x,%x\n", - ptask->outstanding_pkts, lf, ptask->hdr.w0, - ptask->hdr.w1); + dev_err(&dev->netdev->dev, + "outstanding packet %x lf %x, header %x,%x\n", + ptask->outstanding_pkts, lf, ptask->hdr.w0, + ptask->hdr.w1); BUG(); case RFC2374_HDR_FIRSTFRAG: @@ -1010,8 +1012,9 @@ static void fwnet_write_complete(struct fw_card *card, int rcode, fwnet_transmit_packet_failed(ptask); if (printk_timed_ratelimit(&j, 1000) || rcode != last_rcode) { - fw_error("fwnet_write_complete: " - "failed: %x (skipped %d)\n", rcode, errors_skipped); + dev_err(&ptask->dev->netdev->dev, + "fwnet_write_complete failed: %x (skipped %d)\n", + rcode, errors_skipped); errors_skipped = 0; last_rcode = rcode; @@ -1539,14 +1542,12 @@ static int fwnet_probe(struct device *_dev) put_unaligned_be64(card->guid, net->dev_addr); put_unaligned_be64(~0ULL, net->broadcast); ret = register_netdev(net); - if (ret) { - fw_error("Cannot register the driver\n"); + if (ret) goto out; - } list_add_tail(&dev->dev_link, &fwnet_device_list); - fw_notify("%s: IPv4 over FireWire on device %016llx\n", - net->name, (unsigned long long)card->guid); + dev_notice(&net->dev, "IPv4 over IEEE 1394 on card %s\n", + dev_name(card->device)); have_dev: ret = fwnet_add_peer(dev, unit, device); if (ret && allocated_netdev) { @@ -1648,7 +1649,7 @@ static const struct ieee1394_device_id fwnet_id_table[] = { static struct fw_driver fwnet_driver = { .driver = { .owner = THIS_MODULE, - .name = "net", + .name = KBUILD_MODNAME, .bus = &fw_bus_type, .probe = fwnet_probe, .remove = fwnet_remove, diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c index 763626b739d1..a7c4422a688e 100644 --- a/drivers/firewire/nosy.c +++ b/drivers/firewire/nosy.c @@ -36,7 +36,7 @@ #include <linux/timex.h> #include <linux/uaccess.h> #include <linux/wait.h> - +#include <linux/dma-mapping.h> #include <linux/atomic.h> #include <asm/byteorder.h> @@ -536,7 +536,7 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused) u32 p, end; int ret, i; - if (pci_set_dma_mask(dev, 0xffffffff)) { + if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) { dev_err(&dev->dev, "DMA address limits not supported for PCILynx hardware\n"); return -ENXIO; diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 7f5f0da726da..187b3f2e797e 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -170,10 +170,12 @@ struct context { struct iso_context { struct fw_iso_context base; struct context context; - int excess_bytes; void *header; size_t header_length; - + unsigned long flushing_completions; + u32 mc_buffer_bus; + u16 mc_completed; + u16 last_timestamp; u8 sync; u8 tags; }; @@ -338,8 +340,6 @@ MODULE_PARM_DESC(quirks, "Chip quirks (default = 0" #define OHCI_PARAM_DEBUG_IRQS 4 #define OHCI_PARAM_DEBUG_BUSRESETS 8 /* only effective before chip init */ -#ifdef CONFIG_FIREWIRE_OHCI_DEBUG - static int param_debug; module_param_named(debug, param_debug, int, 0644); MODULE_PARM_DESC(debug, "Verbose logging (default = 0" @@ -349,7 +349,7 @@ MODULE_PARM_DESC(debug, "Verbose logging (default = 0" ", busReset events = " __stringify(OHCI_PARAM_DEBUG_BUSRESETS) ", or a combination, or all = -1)"); -static void log_irqs(u32 evt) +static void log_irqs(struct fw_ohci *ohci, u32 evt) { if (likely(!(param_debug & (OHCI_PARAM_DEBUG_IRQS | OHCI_PARAM_DEBUG_BUSRESETS)))) @@ -359,7 +359,8 @@ static void log_irqs(u32 evt) !(evt & OHCI1394_busReset)) return; - fw_notify("IRQ %08x%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", evt, + dev_notice(ohci->card.device, + "IRQ %08x%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", evt, evt & OHCI1394_selfIDComplete ? " selfID" : "", evt & OHCI1394_RQPkt ? " AR_req" : "", evt & OHCI1394_RSPkt ? " AR_resp" : "", @@ -398,24 +399,29 @@ static char _p(u32 *s, int shift) return port[*s >> shift & 3]; } -static void log_selfids(int node_id, int generation, int self_id_count, u32 *s) +static void log_selfids(struct fw_ohci *ohci, int generation, int self_id_count) { + u32 *s; + if (likely(!(param_debug & OHCI_PARAM_DEBUG_SELFIDS))) return; - fw_notify("%d selfIDs, generation %d, local node ID %04x\n", - self_id_count, generation, node_id); + dev_notice(ohci->card.device, + "%d selfIDs, generation %d, local node ID %04x\n", + self_id_count, generation, ohci->node_id); - for (; self_id_count--; ++s) + for (s = ohci->self_id_buffer; self_id_count--; ++s) if ((*s & 1 << 23) == 0) - fw_notify("selfID 0: %08x, phy %d [%c%c%c] " + dev_notice(ohci->card.device, + "selfID 0: %08x, phy %d [%c%c%c] " "%s gc=%d %s %s%s%s\n", *s, *s >> 24 & 63, _p(s, 6), _p(s, 4), _p(s, 2), speed[*s >> 14 & 3], *s >> 16 & 63, power[*s >> 8 & 7], *s >> 22 & 1 ? "L" : "", *s >> 11 & 1 ? "c" : "", *s & 2 ? "i" : ""); else - fw_notify("selfID n: %08x, phy %d [%c%c%c%c%c%c%c%c]\n", + dev_notice(ohci->card.device, + "selfID n: %08x, phy %d [%c%c%c%c%c%c%c%c]\n", *s, *s >> 24 & 63, _p(s, 16), _p(s, 14), _p(s, 12), _p(s, 10), _p(s, 8), _p(s, 6), _p(s, 4), _p(s, 2)); @@ -451,7 +457,8 @@ static const char *tcodes[] = { [0xe] = "link internal", [0xf] = "-reserved-", }; -static void log_ar_at_event(char dir, int speed, u32 *header, int evt) +static void log_ar_at_event(struct fw_ohci *ohci, + char dir, int speed, u32 *header, int evt) { int tcode = header[0] >> 4 & 0xf; char specific[12]; @@ -463,8 +470,9 @@ static void log_ar_at_event(char dir, int speed, u32 *header, int evt) evt = 0x1f; if (evt == OHCI1394_evt_bus_reset) { - fw_notify("A%c evt_bus_reset, generation %d\n", - dir, (header[2] >> 16) & 0xff); + dev_notice(ohci->card.device, + "A%c evt_bus_reset, generation %d\n", + dir, (header[2] >> 16) & 0xff); return; } @@ -483,39 +491,35 @@ static void log_ar_at_event(char dir, int speed, u32 *header, int evt) switch (tcode) { case 0xa: - fw_notify("A%c %s, %s\n", dir, evts[evt], tcodes[tcode]); + dev_notice(ohci->card.device, + "A%c %s, %s\n", + dir, evts[evt], tcodes[tcode]); break; case 0xe: - fw_notify("A%c %s, PHY %08x %08x\n", - dir, evts[evt], header[1], header[2]); + dev_notice(ohci->card.device, + "A%c %s, PHY %08x %08x\n", + dir, evts[evt], header[1], header[2]); break; case 0x0: case 0x1: case 0x4: case 0x5: case 0x9: - fw_notify("A%c spd %x tl %02x, " - "%04x -> %04x, %s, " - "%s, %04x%08x%s\n", - dir, speed, header[0] >> 10 & 0x3f, - header[1] >> 16, header[0] >> 16, evts[evt], - tcodes[tcode], header[1] & 0xffff, header[2], specific); + dev_notice(ohci->card.device, + "A%c spd %x tl %02x, " + "%04x -> %04x, %s, " + "%s, %04x%08x%s\n", + dir, speed, header[0] >> 10 & 0x3f, + header[1] >> 16, header[0] >> 16, evts[evt], + tcodes[tcode], header[1] & 0xffff, header[2], specific); break; default: - fw_notify("A%c spd %x tl %02x, " - "%04x -> %04x, %s, " - "%s%s\n", - dir, speed, header[0] >> 10 & 0x3f, - header[1] >> 16, header[0] >> 16, evts[evt], - tcodes[tcode], specific); + dev_notice(ohci->card.device, + "A%c spd %x tl %02x, " + "%04x -> %04x, %s, " + "%s%s\n", + dir, speed, header[0] >> 10 & 0x3f, + header[1] >> 16, header[0] >> 16, evts[evt], + tcodes[tcode], specific); } } -#else - -#define param_debug 0 -static inline void log_irqs(u32 evt) {} -static inline void log_selfids(int node_id, int generation, int self_id_count, u32 *s) {} -static inline void log_ar_at_event(char dir, int speed, u32 *header, int evt) {} - -#endif /* CONFIG_FIREWIRE_OHCI_DEBUG */ - static inline void reg_write(const struct fw_ohci *ohci, int offset, u32 data) { writel(data, ohci->registers + offset); @@ -559,7 +563,7 @@ static int read_phy_reg(struct fw_ohci *ohci, int addr) if (i >= 3) msleep(1); } - fw_error("failed to read phy reg\n"); + dev_err(ohci->card.device, "failed to read phy reg\n"); return -EBUSY; } @@ -581,7 +585,7 @@ static int write_phy_reg(const struct fw_ohci *ohci, int addr, u32 val) if (i >= 3) msleep(1); } - fw_error("failed to write phy reg\n"); + dev_err(ohci->card.device, "failed to write phy reg\n"); return -EBUSY; } @@ -680,11 +684,14 @@ static void ar_context_release(struct ar_context *ctx) static void ar_context_abort(struct ar_context *ctx, const char *error_msg) { - if (reg_read(ctx->ohci, CONTROL_CLEAR(ctx->regs)) & CONTEXT_RUN) { - reg_write(ctx->ohci, CONTROL_CLEAR(ctx->regs), CONTEXT_RUN); - flush_writes(ctx->ohci); + struct fw_ohci *ohci = ctx->ohci; + + if (reg_read(ohci, CONTROL_CLEAR(ctx->regs)) & CONTEXT_RUN) { + reg_write(ohci, CONTROL_CLEAR(ctx->regs), CONTEXT_RUN); + flush_writes(ohci); - fw_error("AR error: %s; DMA stopped\n", error_msg); + dev_err(ohci->card.device, "AR error: %s; DMA stopped\n", + error_msg); } /* FIXME: restart? */ } @@ -854,7 +861,7 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer) p.timestamp = status & 0xffff; p.generation = ohci->request_generation; - log_ar_at_event('R', p.speed, p.header, evt); + log_ar_at_event(ohci, 'R', p.speed, p.header, evt); /* * Several controllers, notably from NEC and VIA, forget to @@ -1226,21 +1233,22 @@ static void context_append(struct context *ctx, static void context_stop(struct context *ctx) { + struct fw_ohci *ohci = ctx->ohci; u32 reg; int i; - reg_write(ctx->ohci, CONTROL_CLEAR(ctx->regs), CONTEXT_RUN); + reg_write(ohci, CONTROL_CLEAR(ctx->regs), CONTEXT_RUN); ctx->running = false; for (i = 0; i < 1000; i++) { - reg = reg_read(ctx->ohci, CONTROL_SET(ctx->regs)); + reg = reg_read(ohci, CONTROL_SET(ctx->regs)); if ((reg & CONTEXT_ACTIVE) == 0) return; if (i) udelay(10); } - fw_error("Error: DMA context still active (0x%08x)\n", reg); + dev_err(ohci->card.device, "DMA context still active (0x%08x)\n", reg); } struct driver_data { @@ -1420,7 +1428,7 @@ static int handle_at_packet(struct context *context, evt = le16_to_cpu(last->transfer_status) & 0x1f; packet->timestamp = le16_to_cpu(last->res_count); - log_ar_at_event('T', packet->speed, packet->header, evt); + log_ar_at_event(ohci, 'T', packet->speed, packet->header, evt); switch (evt) { case OHCI1394_evt_timeout: @@ -1549,7 +1557,7 @@ static void handle_local_lock(struct fw_ohci *ohci, goto out; } - fw_error("swap not done (CSR lock timeout)\n"); + dev_err(ohci->card.device, "swap not done (CSR lock timeout)\n"); fw_fill_response(&response, packet->header, RCODE_BUSY, NULL, 0); out: @@ -1623,15 +1631,10 @@ static void detect_dead_context(struct fw_ohci *ohci, u32 ctl; ctl = reg_read(ohci, CONTROL_SET(regs)); - if (ctl & CONTEXT_DEAD) { -#ifdef CONFIG_FIREWIRE_OHCI_DEBUG - fw_error("DMA context %s has stopped, error code: %s\n", - name, evts[ctl & 0x1f]); -#else - fw_error("DMA context %s has stopped, error code: %#x\n", - name, ctl & 0x1f); -#endif - } + if (ctl & CONTEXT_DEAD) + dev_err(ohci->card.device, + "DMA context %s has stopped, error code: %s\n", + name, evts[ctl & 0x1f]); } static void handle_dead_contexts(struct fw_ohci *ohci) @@ -1781,7 +1784,8 @@ static int find_and_insert_self_id(struct fw_ohci *ohci, int self_id_count) reg = reg_read(ohci, OHCI1394_NodeID); if (!(reg & OHCI1394_NodeID_idValid)) { - fw_notify("node ID not valid, new bus reset in progress\n"); + dev_notice(ohci->card.device, + "node ID not valid, new bus reset in progress\n"); return -EBUSY; } self_id |= ((reg & 0x3f) << 24); /* phy ID */ @@ -1827,11 +1831,12 @@ static void bus_reset_work(struct work_struct *work) reg = reg_read(ohci, OHCI1394_NodeID); if (!(reg & OHCI1394_NodeID_idValid)) { - fw_notify("node ID not valid, new bus reset in progress\n"); + dev_notice(ohci->card.device, + "node ID not valid, new bus reset in progress\n"); return; } if ((reg & OHCI1394_NodeID_nodeNumber) == 63) { - fw_notify("malconfigured bus\n"); + dev_notice(ohci->card.device, "malconfigured bus\n"); return; } ohci->node_id = reg & (OHCI1394_NodeID_busNumber | @@ -1845,7 +1850,7 @@ static void bus_reset_work(struct work_struct *work) reg = reg_read(ohci, OHCI1394_SelfIDCount); if (reg & OHCI1394_SelfIDCount_selfIDError) { - fw_notify("inconsistent self IDs\n"); + dev_notice(ohci->card.device, "inconsistent self IDs\n"); return; } /* @@ -1857,7 +1862,7 @@ static void bus_reset_work(struct work_struct *work) self_id_count = (reg >> 3) & 0xff; if (self_id_count > 252) { - fw_notify("inconsistent self IDs\n"); + dev_notice(ohci->card.device, "inconsistent self IDs\n"); return; } @@ -1875,11 +1880,13 @@ static void bus_reset_work(struct work_struct *work) */ if (cond_le32_to_cpu(ohci->self_id_cpu[i]) == 0xffff008f) { - fw_notify("ignoring spurious self IDs\n"); + dev_notice(ohci->card.device, + "ignoring spurious self IDs\n"); self_id_count = j; break; } else { - fw_notify("inconsistent self IDs\n"); + dev_notice(ohci->card.device, + "inconsistent self IDs\n"); return; } } @@ -1890,13 +1897,14 @@ static void bus_reset_work(struct work_struct *work) if (ohci->quirks & QUIRK_TI_SLLZ059) { self_id_count = find_and_insert_self_id(ohci, self_id_count); if (self_id_count < 0) { - fw_notify("could not construct local self ID\n"); + dev_notice(ohci->card.device, + "could not construct local self ID\n"); return; } } if (self_id_count == 0) { - fw_notify("inconsistent self IDs\n"); + dev_notice(ohci->card.device, "inconsistent self IDs\n"); return; } rmb(); @@ -1917,8 +1925,8 @@ static void bus_reset_work(struct work_struct *work) new_generation = (reg_read(ohci, OHCI1394_SelfIDCount) >> 16) & 0xff; if (new_generation != generation) { - fw_notify("recursive bus reset detected, " - "discarding self ids\n"); + dev_notice(ohci->card.device, + "new bus reset, discarding self ids\n"); return; } @@ -1989,8 +1997,7 @@ static void bus_reset_work(struct work_struct *work) dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, free_rom, free_rom_bus); - log_selfids(ohci->node_id, generation, - self_id_count, ohci->self_id_buffer); + log_selfids(ohci, generation, self_id_count); fw_core_handle_bus_reset(&ohci->card, ohci->node_id, generation, self_id_count, ohci->self_id_buffer, @@ -2015,7 +2022,7 @@ static irqreturn_t irq_handler(int irq, void *data) */ reg_write(ohci, OHCI1394_IntEventClear, event & ~(OHCI1394_busReset | OHCI1394_postedWriteErr)); - log_irqs(event); + log_irqs(ohci, event); if (event & OHCI1394_selfIDComplete) queue_work(fw_workqueue, &ohci->bus_reset_work); @@ -2057,8 +2064,7 @@ static irqreturn_t irq_handler(int irq, void *data) } if (unlikely(event & OHCI1394_regAccessFail)) - fw_error("Register access failure - " - "please notify linux1394-devel@lists.sf.net\n"); + dev_err(ohci->card.device, "register access failure\n"); if (unlikely(event & OHCI1394_postedWriteErr)) { reg_read(ohci, OHCI1394_PostedWriteAddressHi); @@ -2066,12 +2072,13 @@ static irqreturn_t irq_handler(int irq, void *data) reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_postedWriteErr); if (printk_ratelimit()) - fw_error("PCI posted write error\n"); + dev_err(ohci->card.device, "PCI posted write error\n"); } if (unlikely(event & OHCI1394_cycleTooLong)) { if (printk_ratelimit()) - fw_notify("isochronous cycle too long\n"); + dev_notice(ohci->card.device, + "isochronous cycle too long\n"); reg_write(ohci, OHCI1394_LinkControlSet, OHCI1394_LinkControl_cycleMaster); } @@ -2084,7 +2091,8 @@ static irqreturn_t irq_handler(int irq, void *data) * them at least two cycles later. (FIXME?) */ if (printk_ratelimit()) - fw_notify("isochronous cycle inconsistent\n"); + dev_notice(ohci->card.device, + "isochronous cycle inconsistent\n"); } if (unlikely(event & OHCI1394_unrecoverableError)) @@ -2211,7 +2219,7 @@ static int ohci_enable(struct fw_card *card, int i, ret; if (software_reset(ohci)) { - fw_error("Failed to reset ohci card.\n"); + dev_err(card->device, "failed to reset ohci card\n"); return -EBUSY; } @@ -2235,7 +2243,7 @@ static int ohci_enable(struct fw_card *card, } if (!lps) { - fw_error("Failed to set Link Power Status\n"); + dev_err(card->device, "failed to set Link Power Status\n"); return -EIO; } @@ -2244,7 +2252,7 @@ static int ohci_enable(struct fw_card *card, if (ret < 0) return ret; if (ret) - fw_notify("local TSB41BA3D phy\n"); + dev_notice(card->device, "local TSB41BA3D phy\n"); else ohci->quirks &= ~QUIRK_TI_SLLZ059; } @@ -2344,7 +2352,8 @@ static int ohci_enable(struct fw_card *card, if (request_irq(dev->irq, irq_handler, pci_dev_msi_enabled(dev) ? 0 : IRQF_SHARED, ohci_driver_name, ohci)) { - fw_error("Failed to allocate interrupt %d.\n", dev->irq); + dev_err(card->device, "failed to allocate interrupt %d\n", + dev->irq); pci_disable_msi(dev); if (config_rom) { @@ -2509,7 +2518,7 @@ static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet) dma_unmap_single(ohci->card.device, packet->payload_bus, packet->payload_length, DMA_TO_DEVICE); - log_ar_at_event('T', packet->speed, packet->header, 0x20); + log_ar_at_event(ohci, 'T', packet->speed, packet->header, 0x20); driver_data->packet = NULL; packet->ack = RCODE_CANCELLED; packet->callback(packet, &ohci->card, packet->ack); @@ -2674,25 +2683,35 @@ static void ohci_write_csr(struct fw_card *card, int csr_offset, u32 value) } } -static void copy_iso_headers(struct iso_context *ctx, void *p) +static void flush_iso_completions(struct iso_context *ctx) { - int i = ctx->header_length; + ctx->base.callback.sc(&ctx->base, ctx->last_timestamp, + ctx->header_length, ctx->header, + ctx->base.callback_data); + ctx->header_length = 0; +} - if (i + ctx->base.header_size > PAGE_SIZE) - return; +static void copy_iso_headers(struct iso_context *ctx, const u32 *dma_hdr) +{ + u32 *ctx_hdr; + + if (ctx->header_length + ctx->base.header_size > PAGE_SIZE) + flush_iso_completions(ctx); + + ctx_hdr = ctx->header + ctx->header_length; + ctx->last_timestamp = (u16)le32_to_cpu((__force __le32)dma_hdr[0]); /* - * The iso header is byteswapped to little endian by - * the controller, but the remaining header quadlets - * are big endian. We want to present all the headers - * as big endian, so we have to swap the first quadlet. + * The two iso header quadlets are byteswapped to little + * endian by the controller, but we want to present them + * as big endian for consistency with the bus endianness. */ if (ctx->base.header_size > 0) - *(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4)); + ctx_hdr[0] = swab32(dma_hdr[1]); /* iso packet header */ if (ctx->base.header_size > 4) - *(u32 *) (ctx->header + i + 4) = __swab32(*(u32 *) p); + ctx_hdr[1] = swab32(dma_hdr[0]); /* timestamp */ if (ctx->base.header_size > 8) - memcpy(ctx->header + i + 8, p + 8, ctx->base.header_size - 8); + memcpy(&ctx_hdr[2], &dma_hdr[2], ctx->base.header_size - 8); ctx->header_length += ctx->base.header_size; } @@ -2704,8 +2723,6 @@ static int handle_ir_packet_per_buffer(struct context *context, container_of(context, struct iso_context, context); struct descriptor *pd; u32 buffer_dma; - __le32 *ir_header; - void *p; for (pd = d; pd <= last; pd++) if (pd->transfer_status) @@ -2724,17 +2741,10 @@ static int handle_ir_packet_per_buffer(struct context *context, DMA_FROM_DEVICE); } - p = last + 1; - copy_iso_headers(ctx, p); + copy_iso_headers(ctx, (u32 *) (last + 1)); - if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) { - ir_header = (__le32 *) p; - ctx->base.callback.sc(&ctx->base, - le32_to_cpu(ir_header[0]) & 0xffff, - ctx->header_length, ctx->header, - ctx->base.callback_data); - ctx->header_length = 0; - } + if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS)) + flush_iso_completions(ctx); return 1; } @@ -2746,29 +2756,51 @@ static int handle_ir_buffer_fill(struct context *context, { struct iso_context *ctx = container_of(context, struct iso_context, context); + unsigned int req_count, res_count, completed; u32 buffer_dma; - if (!last->transfer_status) + req_count = le16_to_cpu(last->req_count); + res_count = le16_to_cpu(ACCESS_ONCE(last->res_count)); + completed = req_count - res_count; + buffer_dma = le32_to_cpu(last->data_address); + + if (completed > 0) { + ctx->mc_buffer_bus = buffer_dma; + ctx->mc_completed = completed; + } + + if (res_count != 0) /* Descriptor(s) not done yet, stop iteration */ return 0; - buffer_dma = le32_to_cpu(last->data_address); dma_sync_single_range_for_cpu(context->ohci->card.device, buffer_dma & PAGE_MASK, buffer_dma & ~PAGE_MASK, - le16_to_cpu(last->req_count), - DMA_FROM_DEVICE); + completed, DMA_FROM_DEVICE); - if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) + if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS)) { ctx->base.callback.mc(&ctx->base, - le32_to_cpu(last->data_address) + - le16_to_cpu(last->req_count) - - le16_to_cpu(last->res_count), + buffer_dma + completed, ctx->base.callback_data); + ctx->mc_completed = 0; + } return 1; } +static void flush_ir_buffer_fill(struct iso_context *ctx) +{ + dma_sync_single_range_for_cpu(ctx->context.ohci->card.device, + ctx->mc_buffer_bus & PAGE_MASK, + ctx->mc_buffer_bus & ~PAGE_MASK, + ctx->mc_completed, DMA_FROM_DEVICE); + + ctx->base.callback.mc(&ctx->base, + ctx->mc_buffer_bus + ctx->mc_completed, + ctx->base.callback_data); + ctx->mc_completed = 0; +} + static inline void sync_it_packet_for_cpu(struct context *context, struct descriptor *pd) { @@ -2812,8 +2844,8 @@ static int handle_it_packet(struct context *context, { struct iso_context *ctx = container_of(context, struct iso_context, context); - int i; struct descriptor *pd; + __be32 *ctx_hdr; for (pd = d; pd <= last; pd++) if (pd->transfer_status) @@ -2824,20 +2856,19 @@ static int handle_it_packet(struct context *context, sync_it_packet_for_cpu(context, d); - i = ctx->header_length; - if (i + 4 < PAGE_SIZE) { - /* Present this value as big-endian to match the receive code */ - *(__be32 *)(ctx->header + i) = cpu_to_be32( - ((u32)le16_to_cpu(pd->transfer_status) << 16) | - le16_to_cpu(pd->res_count)); - ctx->header_length += 4; - } - if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) { - ctx->base.callback.sc(&ctx->base, le16_to_cpu(last->res_count), - ctx->header_length, ctx->header, - ctx->base.callback_data); - ctx->header_length = 0; - } + if (ctx->header_length + 4 > PAGE_SIZE) + flush_iso_completions(ctx); + + ctx_hdr = ctx->header + ctx->header_length; + ctx->last_timestamp = le16_to_cpu(last->res_count); + /* Present this value as big-endian to match the receive code */ + *ctx_hdr = cpu_to_be32((le16_to_cpu(pd->transfer_status) << 16) | + le16_to_cpu(pd->res_count)); + ctx->header_length += 4; + + if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS)) + flush_iso_completions(ctx); + return 1; } @@ -2924,8 +2955,10 @@ static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card, if (ret < 0) goto out_with_header; - if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) + if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) { set_multichannel_mask(ohci, 0); + ctx->mc_completed = 0; + } return &ctx->base; @@ -3387,6 +3420,39 @@ static void ohci_flush_queue_iso(struct fw_iso_context *base) reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE); } +static int ohci_flush_iso_completions(struct fw_iso_context *base) +{ + struct iso_context *ctx = container_of(base, struct iso_context, base); + int ret = 0; + + tasklet_disable(&ctx->context.tasklet); + + if (!test_and_set_bit_lock(0, &ctx->flushing_completions)) { + context_tasklet((unsigned long)&ctx->context); + + switch (base->type) { + case FW_ISO_CONTEXT_TRANSMIT: + case FW_ISO_CONTEXT_RECEIVE: + if (ctx->header_length != 0) + flush_iso_completions(ctx); + break; + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + if (ctx->mc_completed != 0) + flush_ir_buffer_fill(ctx); + break; + default: + ret = -ENOSYS; + } + + clear_bit_unlock(0, &ctx->flushing_completions); + smp_mb__after_clear_bit(); + } + + tasklet_enable(&ctx->context.tasklet); + + return ret; +} + static const struct fw_card_driver ohci_driver = { .enable = ohci_enable, .read_phy_reg = ohci_read_phy_reg, @@ -3404,6 +3470,7 @@ static const struct fw_card_driver ohci_driver = { .set_iso_channels = ohci_set_iso_channels, .queue_iso = ohci_queue_iso, .flush_queue_iso = ohci_flush_queue_iso, + .flush_iso_completions = ohci_flush_iso_completions, .start_iso = ohci_start_iso, .stop_iso = ohci_stop_iso, }; @@ -3463,7 +3530,7 @@ static int __devinit pci_probe(struct pci_dev *dev, err = pci_enable_device(dev); if (err) { - fw_error("Failed to enable OHCI hardware\n"); + dev_err(&dev->dev, "failed to enable OHCI hardware\n"); goto fail_free; } @@ -3478,13 +3545,13 @@ static int __devinit pci_probe(struct pci_dev *dev, err = pci_request_region(dev, 0, ohci_driver_name); if (err) { - fw_error("MMIO resource unavailable\n"); + dev_err(&dev->dev, "MMIO resource unavailable\n"); goto fail_disable; } ohci->registers = pci_iomap(dev, 0, OHCI1394_REGISTER_SIZE); if (ohci->registers == NULL) { - fw_error("Failed to remap registers\n"); + dev_err(&dev->dev, "failed to remap registers\n"); err = -ENXIO; goto fail_iomem; } @@ -3573,9 +3640,10 @@ static int __devinit pci_probe(struct pci_dev *dev, goto fail_contexts; version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff; - fw_notify("Added fw-ohci device %s, OHCI v%x.%x, " + dev_notice(&dev->dev, + "added OHCI v%x.%x device as card %d, " "%d IR + %d IT contexts, quirks 0x%x\n", - dev_name(&dev->dev), version >> 16, version & 0xff, + version >> 16, version & 0xff, ohci->card.index, ohci->n_ir, ohci->n_it, ohci->quirks); return 0; @@ -3604,7 +3672,7 @@ static int __devinit pci_probe(struct pci_dev *dev, pmac_ohci_off(dev); fail: if (err == -ENOMEM) - fw_error("Out of memory\n"); + dev_err(&dev->dev, "out of memory\n"); return err; } @@ -3648,7 +3716,7 @@ static void pci_remove(struct pci_dev *dev) kfree(ohci); pmac_ohci_off(dev); - fw_notify("Removed fw-ohci device.\n"); + dev_notice(&dev->dev, "removed fw-ohci device\n"); } #ifdef CONFIG_PM @@ -3662,12 +3730,12 @@ static int pci_suspend(struct pci_dev *dev, pm_message_t state) pci_disable_msi(dev); err = pci_save_state(dev); if (err) { - fw_error("pci_save_state failed\n"); + dev_err(&dev->dev, "pci_save_state failed\n"); return err; } err = pci_set_power_state(dev, pci_choose_state(dev, state)); if (err) - fw_error("pci_set_power_state failed with %d\n", err); + dev_err(&dev->dev, "pci_set_power_state failed with %d\n", err); pmac_ohci_off(dev); return 0; @@ -3683,7 +3751,7 @@ static int pci_resume(struct pci_dev *dev) pci_restore_state(dev); err = pci_enable_device(dev); if (err) { - fw_error("pci_enable_device failed\n"); + dev_err(&dev->dev, "pci_enable_device failed\n"); return err; } diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c index 80e95aa3bf14..000a29ffedae 100644 --- a/drivers/firewire/sbp2.c +++ b/drivers/firewire/sbp2.c @@ -125,8 +125,6 @@ MODULE_PARM_DESC(workarounds, "Work around device bugs (default = 0" ", override internal blacklist = " __stringify(SBP2_WORKAROUND_OVERRIDE) ", or a combination)"); -static const char sbp2_driver_name[] = "sbp2"; - /* * We create one struct sbp2_logical_unit per SBP-2 Logical Unit Number Entry * and one struct scsi_device per sbp2_logical_unit. @@ -165,7 +163,6 @@ static void sbp2_queue_work(struct sbp2_logical_unit *lu, unsigned long delay) */ struct sbp2_target { struct fw_unit *unit; - const char *bus_id; struct list_head lu_list; u64 management_agent_address; @@ -181,11 +178,21 @@ struct sbp2_target { int blocked; /* ditto */ }; -static struct fw_device *target_device(struct sbp2_target *tgt) +static struct fw_device *target_parent_device(struct sbp2_target *tgt) { return fw_parent_device(tgt->unit); } +static const struct device *tgt_dev(const struct sbp2_target *tgt) +{ + return &tgt->unit->device; +} + +static const struct device *lu_dev(const struct sbp2_logical_unit *lu) +{ + return &lu->tgt->unit->device; +} + /* Impossible login_id, to detect logout attempt before successful login */ #define INVALID_LOGIN_ID 0x10000 @@ -211,6 +218,7 @@ static struct fw_device *target_device(struct sbp2_target *tgt) #define SBP2_CSR_UNIT_CHARACTERISTICS 0x3a #define SBP2_CSR_FIRMWARE_REVISION 0x3c #define SBP2_CSR_LOGICAL_UNIT_NUMBER 0x14 +#define SBP2_CSR_UNIT_UNIQUE_ID 0x8d #define SBP2_CSR_LOGICAL_UNIT_DIRECTORY 0xd4 /* Management orb opcodes */ @@ -430,7 +438,8 @@ static void sbp2_status_write(struct fw_card *card, struct fw_request *request, memcpy(status.data, payload + 8, length - 8); if (STATUS_GET_SOURCE(status) == 2 || STATUS_GET_SOURCE(status) == 3) { - fw_notify("non-orb related status write, not handled\n"); + dev_notice(lu_dev(lu), + "non-ORB related status write, not handled\n"); fw_send_response(card, request, RCODE_COMPLETE); return; } @@ -451,7 +460,7 @@ static void sbp2_status_write(struct fw_card *card, struct fw_request *request, orb->callback(orb, &status); kref_put(&orb->kref, free_orb); /* orb callback reference */ } else { - fw_error("status write for unknown orb\n"); + dev_err(lu_dev(lu), "status write for unknown ORB\n"); } fw_send_response(card, request, RCODE_COMPLETE); @@ -492,7 +501,7 @@ static void complete_transaction(struct fw_card *card, int rcode, static void sbp2_send_orb(struct sbp2_orb *orb, struct sbp2_logical_unit *lu, int node_id, int generation, u64 offset) { - struct fw_device *device = target_device(lu->tgt); + struct fw_device *device = target_parent_device(lu->tgt); struct sbp2_pointer orb_pointer; unsigned long flags; @@ -513,7 +522,7 @@ static void sbp2_send_orb(struct sbp2_orb *orb, struct sbp2_logical_unit *lu, static int sbp2_cancel_orbs(struct sbp2_logical_unit *lu) { - struct fw_device *device = target_device(lu->tgt); + struct fw_device *device = target_parent_device(lu->tgt); struct sbp2_orb *orb, *next; struct list_head list; unsigned long flags; @@ -552,7 +561,7 @@ static int sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, int generation, int function, int lun_or_login_id, void *response) { - struct fw_device *device = target_device(lu->tgt); + struct fw_device *device = target_parent_device(lu->tgt); struct sbp2_management_orb *orb; unsigned int timeout; int retval = -ENOMEM; @@ -560,7 +569,7 @@ static int sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, if (function == SBP2_LOGOUT_REQUEST && fw_device_is_shutdown(device)) return 0; - orb = kzalloc(sizeof(*orb), GFP_ATOMIC); + orb = kzalloc(sizeof(*orb), GFP_NOIO); if (orb == NULL) return -ENOMEM; @@ -612,20 +621,20 @@ static int sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, retval = -EIO; if (sbp2_cancel_orbs(lu) == 0) { - fw_error("%s: orb reply timed out, rcode=0x%02x\n", - lu->tgt->bus_id, orb->base.rcode); + dev_err(lu_dev(lu), "ORB reply timed out, rcode 0x%02x\n", + orb->base.rcode); goto out; } if (orb->base.rcode != RCODE_COMPLETE) { - fw_error("%s: management write failed, rcode 0x%02x\n", - lu->tgt->bus_id, orb->base.rcode); + dev_err(lu_dev(lu), "management write failed, rcode 0x%02x\n", + orb->base.rcode); goto out; } if (STATUS_GET_RESPONSE(orb->status) != 0 || STATUS_GET_SBP_STATUS(orb->status) != 0) { - fw_error("%s: error status: %d:%d\n", lu->tgt->bus_id, + dev_err(lu_dev(lu), "error status: %d:%d\n", STATUS_GET_RESPONSE(orb->status), STATUS_GET_SBP_STATUS(orb->status)); goto out; @@ -648,7 +657,7 @@ static int sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, static void sbp2_agent_reset(struct sbp2_logical_unit *lu) { - struct fw_device *device = target_device(lu->tgt); + struct fw_device *device = target_parent_device(lu->tgt); __be32 d = 0; fw_run_transaction(device->card, TCODE_WRITE_QUADLET_REQUEST, @@ -665,7 +674,7 @@ static void complete_agent_reset_write_no_wait(struct fw_card *card, static void sbp2_agent_reset_no_wait(struct sbp2_logical_unit *lu) { - struct fw_device *device = target_device(lu->tgt); + struct fw_device *device = target_parent_device(lu->tgt); struct fw_transaction *t; static __be32 d; @@ -704,7 +713,7 @@ static inline void sbp2_allow_block(struct sbp2_logical_unit *lu) static void sbp2_conditionally_block(struct sbp2_logical_unit *lu) { struct sbp2_target *tgt = lu->tgt; - struct fw_card *card = target_device(tgt)->card; + struct fw_card *card = target_parent_device(tgt)->card; struct Scsi_Host *shost = container_of((void *)tgt, struct Scsi_Host, hostdata[0]); unsigned long flags; @@ -728,7 +737,7 @@ static void sbp2_conditionally_block(struct sbp2_logical_unit *lu) static void sbp2_conditionally_unblock(struct sbp2_logical_unit *lu) { struct sbp2_target *tgt = lu->tgt; - struct fw_card *card = target_device(tgt)->card; + struct fw_card *card = target_parent_device(tgt)->card; struct Scsi_Host *shost = container_of((void *)tgt, struct Scsi_Host, hostdata[0]); unsigned long flags; @@ -753,7 +762,7 @@ static void sbp2_conditionally_unblock(struct sbp2_logical_unit *lu) */ static void sbp2_unblock(struct sbp2_target *tgt) { - struct fw_card *card = target_device(tgt)->card; + struct fw_card *card = target_parent_device(tgt)->card; struct Scsi_Host *shost = container_of((void *)tgt, struct Scsi_Host, hostdata[0]); unsigned long flags; @@ -794,7 +803,7 @@ static int sbp2_lun2int(u16 lun) */ static void sbp2_set_busy_timeout(struct sbp2_logical_unit *lu) { - struct fw_device *device = target_device(lu->tgt); + struct fw_device *device = target_parent_device(lu->tgt); __be32 d = cpu_to_be32(SBP2_CYCLE_LIMIT | SBP2_RETRY_LIMIT); fw_run_transaction(device->card, TCODE_WRITE_QUADLET_REQUEST, @@ -809,7 +818,7 @@ static void sbp2_login(struct work_struct *work) struct sbp2_logical_unit *lu = container_of(work, struct sbp2_logical_unit, work.work); struct sbp2_target *tgt = lu->tgt; - struct fw_device *device = target_device(tgt); + struct fw_device *device = target_parent_device(tgt); struct Scsi_Host *shost; struct scsi_device *sdev; struct sbp2_login_response response; @@ -833,8 +842,8 @@ static void sbp2_login(struct work_struct *work) if (lu->retries++ < 5) { sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5)); } else { - fw_error("%s: failed to login to LUN %04x\n", - tgt->bus_id, lu->lun); + dev_err(tgt_dev(tgt), "failed to login to LUN %04x\n", + lu->lun); /* Let any waiting I/O fail from now on. */ sbp2_unblock(lu->tgt); } @@ -851,8 +860,8 @@ static void sbp2_login(struct work_struct *work) << 32) | be32_to_cpu(response.command_block_agent.low); lu->login_id = be32_to_cpu(response.misc) & 0xffff; - fw_notify("%s: logged in to LUN %04x (%d retries)\n", - tgt->bus_id, lu->lun, lu->retries); + dev_notice(tgt_dev(tgt), "logged in to LUN %04x (%d retries)\n", + lu->lun, lu->retries); /* set appropriate retry limit(s) in BUSY_TIMEOUT register */ sbp2_set_busy_timeout(lu); @@ -919,7 +928,7 @@ static void sbp2_reconnect(struct work_struct *work) struct sbp2_logical_unit *lu = container_of(work, struct sbp2_logical_unit, work.work); struct sbp2_target *tgt = lu->tgt; - struct fw_device *device = target_device(tgt); + struct fw_device *device = target_parent_device(tgt); int generation, node_id, local_node_id; if (fw_device_is_shutdown(device)) @@ -943,7 +952,7 @@ static void sbp2_reconnect(struct work_struct *work) smp_rmb(); /* get current card generation */ if (generation == device->card->generation || lu->retries++ >= 5) { - fw_error("%s: failed to reconnect\n", tgt->bus_id); + dev_err(tgt_dev(tgt), "failed to reconnect\n"); lu->retries = 0; PREPARE_DELAYED_WORK(&lu->work, sbp2_login); } @@ -957,8 +966,8 @@ static void sbp2_reconnect(struct work_struct *work) smp_wmb(); /* node IDs must not be older than generation */ lu->generation = generation; - fw_notify("%s: reconnected to LUN %04x (%d retries)\n", - tgt->bus_id, lu->lun, lu->retries); + dev_notice(tgt_dev(tgt), "reconnected to LUN %04x (%d retries)\n", + lu->lun, lu->retries); sbp2_agent_reset(lu); sbp2_cancel_orbs(lu); @@ -997,6 +1006,13 @@ static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry) return 0; } +static void sbp2_get_unit_unique_id(struct sbp2_target *tgt, + const u32 *leaf) +{ + if ((leaf[0] & 0xffff0000) == 0x00020000) + tgt->guid = (u64)leaf[1] << 32 | leaf[2]; +} + static int sbp2_scan_logical_unit_dir(struct sbp2_target *tgt, const u32 *directory) { @@ -1048,6 +1064,10 @@ static int sbp2_scan_unit_dir(struct sbp2_target *tgt, const u32 *directory, return -ENOMEM; break; + case SBP2_CSR_UNIT_UNIQUE_ID: + sbp2_get_unit_unique_id(tgt, ci.p - 1 + value); + break; + case SBP2_CSR_LOGICAL_UNIT_DIRECTORY: /* Adjust for the increment in the iterator */ if (sbp2_scan_logical_unit_dir(tgt, ci.p - 1 + value) < 0) @@ -1068,8 +1088,8 @@ static void sbp2_clamp_management_orb_timeout(struct sbp2_target *tgt) unsigned int timeout = tgt->mgt_orb_timeout; if (timeout > 40000) - fw_notify("%s: %ds mgt_ORB_timeout limited to 40s\n", - tgt->bus_id, timeout / 1000); + dev_notice(tgt_dev(tgt), "%ds mgt_ORB_timeout limited to 40s\n", + timeout / 1000); tgt->mgt_orb_timeout = clamp_val(timeout, 5000, 40000); } @@ -1081,9 +1101,9 @@ static void sbp2_init_workarounds(struct sbp2_target *tgt, u32 model, unsigned int w = sbp2_param_workarounds; if (w) - fw_notify("Please notify linux1394-devel@lists.sourceforge.net " - "if you need the workarounds parameter for %s\n", - tgt->bus_id); + dev_notice(tgt_dev(tgt), + "Please notify linux1394-devel@lists.sf.net " + "if you need the workarounds parameter\n"); if (w & SBP2_WORKAROUND_OVERRIDE) goto out; @@ -1103,9 +1123,9 @@ static void sbp2_init_workarounds(struct sbp2_target *tgt, u32 model, } out: if (w) - fw_notify("Workarounds for %s: 0x%x " - "(firmware_revision 0x%06x, model_id 0x%06x)\n", - tgt->bus_id, w, firmware_revision, model); + dev_notice(tgt_dev(tgt), "workarounds 0x%x " + "(firmware_revision 0x%06x, model_id 0x%06x)\n", + w, firmware_revision, model); tgt->workarounds = w; } @@ -1121,6 +1141,10 @@ static int sbp2_probe(struct device *dev) struct Scsi_Host *shost; u32 model, firmware_revision; + /* cannot (or should not) handle targets on the local node */ + if (device->is_local) + return -ENODEV; + if (dma_get_max_seg_size(device->card->device) > SBP2_MAX_SEG_SIZE) BUG_ON(dma_set_max_seg_size(device->card->device, SBP2_MAX_SEG_SIZE)); @@ -1133,7 +1157,6 @@ static int sbp2_probe(struct device *dev) dev_set_drvdata(&unit->device, tgt); tgt->unit = unit; INIT_LIST_HEAD(&tgt->lu_list); - tgt->bus_id = dev_name(&unit->device); tgt->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; if (fw_device_enable_phys_dma(device) < 0) @@ -1239,7 +1262,7 @@ static int sbp2_remove(struct device *dev) kfree(lu); } scsi_remove_host(shost); - fw_notify("released %s, target %d:0:0\n", tgt->bus_id, shost->host_no); + dev_notice(dev, "released target %d:0:0\n", shost->host_no); scsi_host_put(shost); return 0; @@ -1261,7 +1284,7 @@ static const struct ieee1394_device_id sbp2_id_table[] = { static struct fw_driver sbp2_driver = { .driver = { .owner = THIS_MODULE, - .name = sbp2_driver_name, + .name = KBUILD_MODNAME, .bus = &fw_bus_type, .probe = sbp2_probe, .remove = sbp2_remove, @@ -1286,10 +1309,19 @@ static void sbp2_unmap_scatterlist(struct device *card_device, static unsigned int sbp2_status_to_sense_data(u8 *sbp2_status, u8 *sense_data) { int sam_status; + int sfmt = (sbp2_status[0] >> 6) & 0x03; + + if (sfmt == 2 || sfmt == 3) { + /* + * Reserved for future standardization (2) or + * Status block format vendor-dependent (3) + */ + return DID_ERROR << 16; + } - sense_data[0] = 0x70; + sense_data[0] = 0x70 | sfmt | (sbp2_status[1] & 0x80); sense_data[1] = 0x0; - sense_data[2] = sbp2_status[1]; + sense_data[2] = ((sbp2_status[1] << 1) & 0xe0) | (sbp2_status[1] & 0x0f); sense_data[3] = sbp2_status[4]; sense_data[4] = sbp2_status[5]; sense_data[5] = sbp2_status[6]; @@ -1325,7 +1357,7 @@ static void complete_command_orb(struct sbp2_orb *base_orb, { struct sbp2_command_orb *orb = container_of(base_orb, struct sbp2_command_orb, base); - struct fw_device *device = target_device(orb->lu->tgt); + struct fw_device *device = target_parent_device(orb->lu->tgt); int result; if (status != NULL) { @@ -1433,7 +1465,7 @@ static int sbp2_scsi_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmd) { struct sbp2_logical_unit *lu = cmd->device->hostdata; - struct fw_device *device = target_device(lu->tgt); + struct fw_device *device = target_parent_device(lu->tgt); struct sbp2_command_orb *orb; int generation, retval = SCSI_MLQUEUE_HOST_BUSY; @@ -1442,7 +1474,7 @@ static int sbp2_scsi_queuecommand(struct Scsi_Host *shost, * transfer direction not handled. */ if (cmd->sc_data_direction == DMA_BIDIRECTIONAL) { - fw_error("Can't handle DMA_BIDIRECTIONAL, rejecting command\n"); + dev_err(lu_dev(lu), "cannot handle bidirectional command\n"); cmd->result = DID_ERROR << 16; cmd->scsi_done(cmd); return 0; @@ -1450,7 +1482,7 @@ static int sbp2_scsi_queuecommand(struct Scsi_Host *shost, orb = kzalloc(sizeof(*orb), GFP_ATOMIC); if (orb == NULL) { - fw_notify("failed to alloc orb\n"); + dev_notice(lu_dev(lu), "failed to alloc ORB\n"); return SCSI_MLQUEUE_HOST_BUSY; } @@ -1550,7 +1582,7 @@ static int sbp2_scsi_abort(struct scsi_cmnd *cmd) { struct sbp2_logical_unit *lu = cmd->device->hostdata; - fw_notify("%s: sbp2_scsi_abort\n", lu->tgt->bus_id); + dev_notice(lu_dev(lu), "sbp2_scsi_abort\n"); sbp2_agent_reset(lu); sbp2_cancel_orbs(lu); @@ -1590,7 +1622,7 @@ static struct device_attribute *sbp2_scsi_sysfs_attrs[] = { static struct scsi_host_template scsi_driver_template = { .module = THIS_MODULE, .name = "SBP-2 IEEE-1394", - .proc_name = sbp2_driver_name, + .proc_name = "sbp2", .queuecommand = sbp2_scsi_queuecommand, .slave_alloc = sbp2_scsi_slave_alloc, .slave_configure = sbp2_scsi_slave_configure, diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 1c0fc3756cb1..4ca5642e9776 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -378,13 +378,6 @@ static int __devinit ep93xx_gpio_probe(struct platform_device *pdev) } ep93xx_gpio->mmio_base = mmio; - /* Default all ports to GPIO */ - ep93xx_devcfg_set_bits(EP93XX_SYSCON_DEVCFG_KEYS | - EP93XX_SYSCON_DEVCFG_GONK | - EP93XX_SYSCON_DEVCFG_EONIDE | - EP93XX_SYSCON_DEVCFG_GONIDE | - EP93XX_SYSCON_DEVCFG_HONIDE); - for (i = 0; i < ARRAY_SIZE(ep93xx_gpio_banks); i++) { struct bgpio_chip *bgc = &ep93xx_gpio->bgc[i]; struct ep93xx_gpio_bank *bank = &ep93xx_gpio_banks[i]; diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 0b0562979171..f49bd6f47a50 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -21,6 +21,7 @@ #include <linux/io.h> #include <linux/slab.h> #include <linux/pm_runtime.h> +#include <linux/pm.h> #include <mach/hardware.h> #include <asm/irq.h> @@ -28,19 +29,36 @@ #include <asm/gpio.h> #include <asm/mach/irq.h> +#define OFF_MODE 1 + +static LIST_HEAD(omap_gpio_list); + +struct gpio_regs { + u32 irqenable1; + u32 irqenable2; + u32 wake_en; + u32 ctrl; + u32 oe; + u32 leveldetect0; + u32 leveldetect1; + u32 risingdetect; + u32 fallingdetect; + u32 dataout; + u32 debounce; + u32 debounce_en; +}; + struct gpio_bank { + struct list_head node; unsigned long pbase; void __iomem *base; u16 irq; u16 virtual_irq_start; - int method; u32 suspend_wakeup; -#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2PLUS) u32 saved_wakeup; -#endif u32 non_wakeup_gpios; u32 enabled_non_wakeup_gpios; - + struct gpio_regs context; u32 saved_datain; u32 saved_fallingdetect; u32 saved_risingdetect; @@ -51,44 +69,27 @@ struct gpio_bank { struct clk *dbck; u32 mod_usage; u32 dbck_enable_mask; + bool dbck_enabled; struct device *dev; + bool is_mpuio; bool dbck_flag; + bool loses_context; int stride; u32 width; + int context_loss_count; + u16 id; + int power_mode; + bool workaround_enabled; void (*set_dataout)(struct gpio_bank *bank, int gpio, int enable); + int (*get_context_loss_count)(struct device *dev); struct omap_gpio_reg_offs *regs; }; -#ifdef CONFIG_ARCH_OMAP3 -struct omap3_gpio_regs { - u32 irqenable1; - u32 irqenable2; - u32 wake_en; - u32 ctrl; - u32 oe; - u32 leveldetect0; - u32 leveldetect1; - u32 risingdetect; - u32 fallingdetect; - u32 dataout; -}; - -static struct omap3_gpio_regs gpio_context[OMAP34XX_NR_GPIOS]; -#endif - -/* - * TODO: Cleanup gpio_bank usage as it is having information - * related to all instances of the device - */ -static struct gpio_bank *gpio_bank; - -/* TODO: Analyze removing gpio_bank_count usage from driver code */ -int gpio_bank_count; - #define GPIO_INDEX(bank, gpio) (gpio % bank->width) #define GPIO_BIT(bank, gpio) (1 << GPIO_INDEX(bank, gpio)) +#define GPIO_MOD_CTRL_BIT BIT(0) static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input) { @@ -102,6 +103,7 @@ static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input) else l &= ~(1 << gpio); __raw_writel(l, reg); + bank->context.oe = l; } @@ -132,6 +134,7 @@ static void _set_gpio_dataout_mask(struct gpio_bank *bank, int gpio, int enable) else l &= ~gpio_bit; __raw_writel(l, reg); + bank->context.dataout = l; } static int _get_gpio_datain(struct gpio_bank *bank, int gpio) @@ -160,6 +163,22 @@ static inline void _gpio_rmw(void __iomem *base, u32 reg, u32 mask, bool set) __raw_writel(l, base + reg); } +static inline void _gpio_dbck_enable(struct gpio_bank *bank) +{ + if (bank->dbck_enable_mask && !bank->dbck_enabled) { + clk_enable(bank->dbck); + bank->dbck_enabled = true; + } +} + +static inline void _gpio_dbck_disable(struct gpio_bank *bank) +{ + if (bank->dbck_enable_mask && bank->dbck_enabled) { + clk_disable(bank->dbck); + bank->dbck_enabled = false; + } +} + /** * _set_gpio_debounce - low level gpio debounce time * @bank: the gpio bank we're acting upon @@ -188,70 +207,74 @@ static void _set_gpio_debounce(struct gpio_bank *bank, unsigned gpio, l = GPIO_BIT(bank, gpio); + clk_enable(bank->dbck); reg = bank->base + bank->regs->debounce; __raw_writel(debounce, reg); reg = bank->base + bank->regs->debounce_en; val = __raw_readl(reg); - if (debounce) { + if (debounce) val |= l; - clk_enable(bank->dbck); - } else { + else val &= ~l; - clk_disable(bank->dbck); - } bank->dbck_enable_mask = val; __raw_writel(val, reg); + clk_disable(bank->dbck); + /* + * Enable debounce clock per module. + * This call is mandatory because in omap_gpio_request() when + * *_runtime_get_sync() is called, _gpio_dbck_enable() within + * runtime callbck fails to turn on dbck because dbck_enable_mask + * used within _gpio_dbck_enable() is still not initialized at + * that point. Therefore we have to enable dbck here. + */ + _gpio_dbck_enable(bank); + if (bank->dbck_enable_mask) { + bank->context.debounce = debounce; + bank->context.debounce_en = val; + } } -#ifdef CONFIG_ARCH_OMAP2PLUS -static inline void set_24xx_gpio_triggering(struct gpio_bank *bank, int gpio, +static inline void set_gpio_trigger(struct gpio_bank *bank, int gpio, int trigger) { void __iomem *base = bank->base; u32 gpio_bit = 1 << gpio; - if (cpu_is_omap44xx()) { - _gpio_rmw(base, OMAP4_GPIO_LEVELDETECT0, gpio_bit, - trigger & IRQ_TYPE_LEVEL_LOW); - _gpio_rmw(base, OMAP4_GPIO_LEVELDETECT1, gpio_bit, - trigger & IRQ_TYPE_LEVEL_HIGH); - _gpio_rmw(base, OMAP4_GPIO_RISINGDETECT, gpio_bit, - trigger & IRQ_TYPE_EDGE_RISING); - _gpio_rmw(base, OMAP4_GPIO_FALLINGDETECT, gpio_bit, - trigger & IRQ_TYPE_EDGE_FALLING); - } else { - _gpio_rmw(base, OMAP24XX_GPIO_LEVELDETECT0, gpio_bit, - trigger & IRQ_TYPE_LEVEL_LOW); - _gpio_rmw(base, OMAP24XX_GPIO_LEVELDETECT1, gpio_bit, - trigger & IRQ_TYPE_LEVEL_HIGH); - _gpio_rmw(base, OMAP24XX_GPIO_RISINGDETECT, gpio_bit, - trigger & IRQ_TYPE_EDGE_RISING); - _gpio_rmw(base, OMAP24XX_GPIO_FALLINGDETECT, gpio_bit, - trigger & IRQ_TYPE_EDGE_FALLING); - } + _gpio_rmw(base, bank->regs->leveldetect0, gpio_bit, + trigger & IRQ_TYPE_LEVEL_LOW); + _gpio_rmw(base, bank->regs->leveldetect1, gpio_bit, + trigger & IRQ_TYPE_LEVEL_HIGH); + _gpio_rmw(base, bank->regs->risingdetect, gpio_bit, + trigger & IRQ_TYPE_EDGE_RISING); + _gpio_rmw(base, bank->regs->fallingdetect, gpio_bit, + trigger & IRQ_TYPE_EDGE_FALLING); + + bank->context.leveldetect0 = + __raw_readl(bank->base + bank->regs->leveldetect0); + bank->context.leveldetect1 = + __raw_readl(bank->base + bank->regs->leveldetect1); + bank->context.risingdetect = + __raw_readl(bank->base + bank->regs->risingdetect); + bank->context.fallingdetect = + __raw_readl(bank->base + bank->regs->fallingdetect); + if (likely(!(bank->non_wakeup_gpios & gpio_bit))) { - if (cpu_is_omap44xx()) { - _gpio_rmw(base, OMAP4_GPIO_IRQWAKEN0, gpio_bit, - trigger != 0); - } else { - /* - * GPIO wakeup request can only be generated on edge - * transitions - */ - if (trigger & IRQ_TYPE_EDGE_BOTH) - __raw_writel(1 << gpio, bank->base - + OMAP24XX_GPIO_SETWKUENA); - else - __raw_writel(1 << gpio, bank->base - + OMAP24XX_GPIO_CLEARWKUENA); - } + _gpio_rmw(base, bank->regs->wkup_en, gpio_bit, trigger != 0); + bank->context.wake_en = + __raw_readl(bank->base + bank->regs->wkup_en); } + /* This part needs to be executed always for OMAP{34xx, 44xx} */ - if (cpu_is_omap34xx() || cpu_is_omap44xx() || - (bank->non_wakeup_gpios & gpio_bit)) { + if (!bank->regs->irqctrl) { + /* On omap24xx proceed only when valid GPIO bit is set */ + if (bank->non_wakeup_gpios) { + if (!(bank->non_wakeup_gpios & gpio_bit)) + goto exit; + } + /* * Log the edge gpio and manually trigger the IRQ * after resume if the input level changes @@ -264,17 +287,11 @@ static inline void set_24xx_gpio_triggering(struct gpio_bank *bank, int gpio, bank->enabled_non_wakeup_gpios &= ~gpio_bit; } - if (cpu_is_omap44xx()) { - bank->level_mask = - __raw_readl(bank->base + OMAP4_GPIO_LEVELDETECT0) | - __raw_readl(bank->base + OMAP4_GPIO_LEVELDETECT1); - } else { - bank->level_mask = - __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT0) | - __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT1); - } +exit: + bank->level_mask = + __raw_readl(bank->base + bank->regs->leveldetect0) | + __raw_readl(bank->base + bank->regs->leveldetect1); } -#endif #ifdef CONFIG_ARCH_OMAP1 /* @@ -286,23 +303,10 @@ static void _toggle_gpio_edge_triggering(struct gpio_bank *bank, int gpio) void __iomem *reg = bank->base; u32 l = 0; - switch (bank->method) { - case METHOD_MPUIO: - reg += OMAP_MPUIO_GPIO_INT_EDGE / bank->stride; - break; -#ifdef CONFIG_ARCH_OMAP15XX - case METHOD_GPIO_1510: - reg += OMAP1510_GPIO_INT_CONTROL; - break; -#endif -#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) - case METHOD_GPIO_7XX: - reg += OMAP7XX_GPIO_INT_CONTROL; - break; -#endif - default: + if (!bank->regs->irqctrl) return; - } + + reg += bank->regs->irqctrl; l = __raw_readl(reg); if ((l >> gpio) & 1) @@ -312,31 +316,21 @@ static void _toggle_gpio_edge_triggering(struct gpio_bank *bank, int gpio) __raw_writel(l, reg); } +#else +static void _toggle_gpio_edge_triggering(struct gpio_bank *bank, int gpio) {} #endif static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) { void __iomem *reg = bank->base; + void __iomem *base = bank->base; u32 l = 0; - switch (bank->method) { -#ifdef CONFIG_ARCH_OMAP1 - case METHOD_MPUIO: - reg += OMAP_MPUIO_GPIO_INT_EDGE / bank->stride; - l = __raw_readl(reg); - if ((trigger & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) - bank->toggle_mask |= 1 << gpio; - if (trigger & IRQ_TYPE_EDGE_RISING) - l |= 1 << gpio; - else if (trigger & IRQ_TYPE_EDGE_FALLING) - l &= ~(1 << gpio); - else - goto bad; - break; -#endif -#ifdef CONFIG_ARCH_OMAP15XX - case METHOD_GPIO_1510: - reg += OMAP1510_GPIO_INT_CONTROL; + if (bank->regs->leveldetect0 && bank->regs->wkup_en) { + set_gpio_trigger(bank, gpio, trigger); + } else if (bank->regs->irqctrl) { + reg += bank->regs->irqctrl; + l = __raw_readl(reg); if ((trigger & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) bank->toggle_mask |= 1 << gpio; @@ -345,15 +339,15 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) else if (trigger & IRQ_TYPE_EDGE_FALLING) l &= ~(1 << gpio); else - goto bad; - break; -#endif -#ifdef CONFIG_ARCH_OMAP16XX - case METHOD_GPIO_1610: + return -EINVAL; + + __raw_writel(l, reg); + } else if (bank->regs->edgectrl1) { if (gpio & 0x08) - reg += OMAP1610_GPIO_EDGE_CTRL2; + reg += bank->regs->edgectrl2; else - reg += OMAP1610_GPIO_EDGE_CTRL1; + reg += bank->regs->edgectrl1; + gpio &= 0x07; l = __raw_readl(reg); l &= ~(3 << (gpio << 1)); @@ -361,40 +355,14 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) l |= 2 << (gpio << 1); if (trigger & IRQ_TYPE_EDGE_FALLING) l |= 1 << (gpio << 1); - if (trigger) - /* Enable wake-up during idle for dynamic tick */ - __raw_writel(1 << gpio, bank->base + OMAP1610_GPIO_SET_WAKEUPENA); - else - __raw_writel(1 << gpio, bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA); - break; -#endif -#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) - case METHOD_GPIO_7XX: - reg += OMAP7XX_GPIO_INT_CONTROL; - l = __raw_readl(reg); - if ((trigger & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) - bank->toggle_mask |= 1 << gpio; - if (trigger & IRQ_TYPE_EDGE_RISING) - l |= 1 << gpio; - else if (trigger & IRQ_TYPE_EDGE_FALLING) - l &= ~(1 << gpio); - else - goto bad; - break; -#endif -#ifdef CONFIG_ARCH_OMAP2PLUS - case METHOD_GPIO_24XX: - case METHOD_GPIO_44XX: - set_24xx_gpio_triggering(bank, gpio, trigger); - return 0; -#endif - default: - goto bad; + + /* Enable wake-up during idle for dynamic tick */ + _gpio_rmw(base, bank->regs->wkup_en, 1 << gpio, trigger); + bank->context.wake_en = + __raw_readl(bank->base + bank->regs->wkup_en); + __raw_writel(l, reg); } - __raw_writel(l, reg); return 0; -bad: - return -EINVAL; } static int gpio_irq_type(struct irq_data *d, unsigned type) @@ -412,12 +380,12 @@ static int gpio_irq_type(struct irq_data *d, unsigned type) if (type & ~IRQ_TYPE_SENSE_MASK) return -EINVAL; - /* OMAP1 allows only only edge triggering */ - if (!cpu_class_is_omap2() - && (type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH))) + bank = irq_data_get_irq_chip_data(d); + + if (!bank->regs->leveldetect0 && + (type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH))) return -EINVAL; - bank = irq_data_get_irq_chip_data(d); spin_lock_irqsave(&bank->lock, flags); retval = _set_gpio_triggering(bank, GPIO_INDEX(bank, gpio), type); spin_unlock_irqrestore(&bank->lock, flags); @@ -484,6 +452,7 @@ static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask) } __raw_writel(l, reg); + bank->context.irqenable1 = l; } static void _disable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask) @@ -504,6 +473,7 @@ static void _disable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask) } __raw_writel(l, reg); + bank->context.irqenable1 = l; } static inline void _set_gpio_irqenable(struct gpio_bank *bank, int gpio, int enable) @@ -567,38 +537,39 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset) struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip); unsigned long flags; - spin_lock_irqsave(&bank->lock, flags); + /* + * If this is the first gpio_request for the bank, + * enable the bank module. + */ + if (!bank->mod_usage) + pm_runtime_get_sync(bank->dev); + spin_lock_irqsave(&bank->lock, flags); /* Set trigger to none. You need to enable the desired trigger with * request_irq() or set_irq_type(). */ _set_gpio_triggering(bank, offset, IRQ_TYPE_NONE); -#ifdef CONFIG_ARCH_OMAP15XX - if (bank->method == METHOD_GPIO_1510) { - void __iomem *reg; + if (bank->regs->pinctrl) { + void __iomem *reg = bank->base + bank->regs->pinctrl; /* Claim the pin for MPU */ - reg = bank->base + OMAP1510_GPIO_PIN_CONTROL; __raw_writel(__raw_readl(reg) | (1 << offset), reg); } -#endif - if (!cpu_class_is_omap1()) { - if (!bank->mod_usage) { - void __iomem *reg = bank->base; - u32 ctrl; - - if (cpu_is_omap24xx() || cpu_is_omap34xx()) - reg += OMAP24XX_GPIO_CTRL; - else if (cpu_is_omap44xx()) - reg += OMAP4_GPIO_CTRL; - ctrl = __raw_readl(reg); - /* Module is enabled, clocks are not gated */ - ctrl &= 0xFFFFFFFE; - __raw_writel(ctrl, reg); - } - bank->mod_usage |= 1 << offset; + + if (bank->regs->ctrl && !bank->mod_usage) { + void __iomem *reg = bank->base + bank->regs->ctrl; + u32 ctrl; + + ctrl = __raw_readl(reg); + /* Module is enabled, clocks are not gated */ + ctrl &= ~GPIO_MOD_CTRL_BIT; + __raw_writel(ctrl, reg); + bank->context.ctrl = ctrl; } + + bank->mod_usage |= 1 << offset; + spin_unlock_irqrestore(&bank->lock, flags); return 0; @@ -607,48 +578,40 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset) static void omap_gpio_free(struct gpio_chip *chip, unsigned offset) { struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip); + void __iomem *base = bank->base; unsigned long flags; spin_lock_irqsave(&bank->lock, flags); -#ifdef CONFIG_ARCH_OMAP16XX - if (bank->method == METHOD_GPIO_1610) { - /* Disable wake-up during idle for dynamic tick */ - void __iomem *reg = bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA; - __raw_writel(1 << offset, reg); - } -#endif -#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) - if (bank->method == METHOD_GPIO_24XX) { - /* Disable wake-up during idle for dynamic tick */ - void __iomem *reg = bank->base + OMAP24XX_GPIO_CLEARWKUENA; - __raw_writel(1 << offset, reg); - } -#endif -#ifdef CONFIG_ARCH_OMAP4 - if (bank->method == METHOD_GPIO_44XX) { + + if (bank->regs->wkup_en) { /* Disable wake-up during idle for dynamic tick */ - void __iomem *reg = bank->base + OMAP4_GPIO_IRQWAKEN0; - __raw_writel(1 << offset, reg); + _gpio_rmw(base, bank->regs->wkup_en, 1 << offset, 0); + bank->context.wake_en = + __raw_readl(bank->base + bank->regs->wkup_en); } -#endif - if (!cpu_class_is_omap1()) { - bank->mod_usage &= ~(1 << offset); - if (!bank->mod_usage) { - void __iomem *reg = bank->base; - u32 ctrl; - - if (cpu_is_omap24xx() || cpu_is_omap34xx()) - reg += OMAP24XX_GPIO_CTRL; - else if (cpu_is_omap44xx()) - reg += OMAP4_GPIO_CTRL; - ctrl = __raw_readl(reg); - /* Module is disabled, clocks are gated */ - ctrl |= 1; - __raw_writel(ctrl, reg); - } + + bank->mod_usage &= ~(1 << offset); + + if (bank->regs->ctrl && !bank->mod_usage) { + void __iomem *reg = bank->base + bank->regs->ctrl; + u32 ctrl; + + ctrl = __raw_readl(reg); + /* Module is disabled, clocks are gated */ + ctrl |= GPIO_MOD_CTRL_BIT; + __raw_writel(ctrl, reg); + bank->context.ctrl = ctrl; } + _reset_gpio(bank, bank->chip.base + offset); spin_unlock_irqrestore(&bank->lock, flags); + + /* + * If this is the last gpio to be freed in the bank, + * disable the bank module. + */ + if (!bank->mod_usage) + pm_runtime_put(bank->dev); } /* @@ -674,6 +637,7 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) bank = irq_get_handler_data(irq); isr_reg = bank->base + bank->regs->irqstatus; + pm_runtime_get_sync(bank->dev); if (WARN_ON(!isr_reg)) goto exit; @@ -685,12 +649,8 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) enabled = _get_gpio_irqbank_mask(bank); isr_saved = isr = __raw_readl(isr_reg) & enabled; - if (cpu_is_omap15xx() && (bank->method == METHOD_MPUIO)) - isr &= 0x0000ffff; - - if (cpu_class_is_omap2()) { + if (bank->level_mask) level_mask = bank->level_mask & enabled; - } /* clear edge sensitive interrupts before handler(s) are called so that we don't miss any interrupt occurred while @@ -718,7 +678,6 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) if (!(isr & 1)) continue; -#ifdef CONFIG_ARCH_OMAP1 /* * Some chips can't respond to both rising and falling * at the same time. If this irq was requested with @@ -728,7 +687,6 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) */ if (bank->toggle_mask & (1 << gpio_index)) _toggle_gpio_edge_triggering(bank, gpio_index); -#endif generic_handle_irq(gpio_irq); } @@ -740,6 +698,7 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) exit: if (!unmasked) chained_irq_exit(chip, desc); + pm_runtime_put(bank->dev); } static void gpio_irq_shutdown(struct irq_data *d) @@ -808,14 +767,6 @@ static struct irq_chip gpio_irq_chip = { /*---------------------------------------------------------------------*/ -#ifdef CONFIG_ARCH_OMAP1 - -#define bank_is_mpuio(bank) ((bank)->method == METHOD_MPUIO) - -#ifdef CONFIG_ARCH_OMAP16XX - -#include <linux/platform_device.h> - static int omap_mpuio_suspend_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -869,32 +820,16 @@ static struct platform_device omap_mpuio_device = { /* could list the /proc/iomem resources */ }; -static inline void mpuio_init(void) +static inline void mpuio_init(struct gpio_bank *bank) { - struct gpio_bank *bank = &gpio_bank[0]; platform_set_drvdata(&omap_mpuio_device, bank); if (platform_driver_register(&omap_mpuio_driver) == 0) (void) platform_device_register(&omap_mpuio_device); } -#else -static inline void mpuio_init(void) {} -#endif /* 16xx */ - -#else - -#define bank_is_mpuio(bank) 0 -static inline void mpuio_init(void) {} - -#endif - /*---------------------------------------------------------------------*/ -/* REVISIT these are stupid implementations! replace by ones that - * don't switch on METHOD_* and which mostly avoid spinlocks - */ - static int gpio_input(struct gpio_chip *chip, unsigned offset) { struct gpio_bank *bank; @@ -1007,78 +942,32 @@ static void __init omap_gpio_show_rev(struct gpio_bank *bank) */ static struct lock_class_key gpio_lock_class; -static inline int init_gpio_info(struct platform_device *pdev) +static void omap_gpio_mod_init(struct gpio_bank *bank) { - /* TODO: Analyze removing gpio_bank_count usage from driver code */ - gpio_bank = kzalloc(gpio_bank_count * sizeof(struct gpio_bank), - GFP_KERNEL); - if (!gpio_bank) { - dev_err(&pdev->dev, "Memory alloc failed for gpio_bank\n"); - return -ENOMEM; - } - return 0; -} + void __iomem *base = bank->base; + u32 l = 0xffffffff; -/* TODO: Cleanup cpu_is_* checks */ -static void omap_gpio_mod_init(struct gpio_bank *bank, int id) -{ - if (cpu_class_is_omap2()) { - if (cpu_is_omap44xx()) { - __raw_writel(0xffffffff, bank->base + - OMAP4_GPIO_IRQSTATUSCLR0); - __raw_writel(0x00000000, bank->base + - OMAP4_GPIO_DEBOUNCENABLE); - /* Initialize interface clk ungated, module enabled */ - __raw_writel(0, bank->base + OMAP4_GPIO_CTRL); - } else if (cpu_is_omap34xx()) { - __raw_writel(0x00000000, bank->base + - OMAP24XX_GPIO_IRQENABLE1); - __raw_writel(0xffffffff, bank->base + - OMAP24XX_GPIO_IRQSTATUS1); - __raw_writel(0x00000000, bank->base + - OMAP24XX_GPIO_DEBOUNCE_EN); - - /* Initialize interface clk ungated, module enabled */ - __raw_writel(0, bank->base + OMAP24XX_GPIO_CTRL); - } else if (cpu_is_omap24xx()) { - static const u32 non_wakeup_gpios[] = { - 0xe203ffc0, 0x08700040 - }; - if (id < ARRAY_SIZE(non_wakeup_gpios)) - bank->non_wakeup_gpios = non_wakeup_gpios[id]; - } - } else if (cpu_class_is_omap1()) { - if (bank_is_mpuio(bank)) - __raw_writew(0xffff, bank->base + - OMAP_MPUIO_GPIO_MASKIT / bank->stride); - if (cpu_is_omap15xx() && bank->method == METHOD_GPIO_1510) { - __raw_writew(0xffff, bank->base - + OMAP1510_GPIO_INT_MASK); - __raw_writew(0x0000, bank->base - + OMAP1510_GPIO_INT_STATUS); - } - if (cpu_is_omap16xx() && bank->method == METHOD_GPIO_1610) { - __raw_writew(0x0000, bank->base - + OMAP1610_GPIO_IRQENABLE1); - __raw_writew(0xffff, bank->base - + OMAP1610_GPIO_IRQSTATUS1); - __raw_writew(0x0014, bank->base - + OMAP1610_GPIO_SYSCONFIG); + if (bank->width == 16) + l = 0xffff; - /* - * Enable system clock for GPIO module. - * The CAM_CLK_CTRL *is* really the right place. - */ - omap_writel(omap_readl(ULPD_CAM_CLK_CTRL) | 0x04, - ULPD_CAM_CLK_CTRL); - } - if (cpu_is_omap7xx() && bank->method == METHOD_GPIO_7XX) { - __raw_writel(0xffffffff, bank->base - + OMAP7XX_GPIO_INT_MASK); - __raw_writel(0x00000000, bank->base - + OMAP7XX_GPIO_INT_STATUS); - } + if (bank->is_mpuio) { + __raw_writel(l, bank->base + bank->regs->irqenable); + return; } + + _gpio_rmw(base, bank->regs->irqenable, l, bank->regs->irqenable_inv); + _gpio_rmw(base, bank->regs->irqstatus, l, + bank->regs->irqenable_inv == false); + _gpio_rmw(base, bank->regs->irqenable, l, bank->regs->debounce_en != 0); + _gpio_rmw(base, bank->regs->irqenable, l, bank->regs->ctrl != 0); + if (bank->regs->debounce_en) + _gpio_rmw(base, bank->regs->debounce_en, 0, 1); + + /* Save OE default value (0xffffffff) in the context */ + bank->context.oe = __raw_readl(bank->base + bank->regs->direction); + /* Initialize interface clk ungated, module enabled */ + if (bank->regs->ctrl) + _gpio_rmw(base, bank->regs->ctrl, 0, 1); } static __init void @@ -1101,8 +990,8 @@ omap_mpuio_alloc_gc(struct gpio_bank *bank, unsigned int irq_start, ct->chip.irq_mask = irq_gc_mask_set_bit; ct->chip.irq_unmask = irq_gc_mask_clr_bit; ct->chip.irq_set_type = gpio_irq_type; - /* REVISIT: assuming only 16xx supports MPUIO wake events */ - if (cpu_is_omap16xx()) + + if (bank->regs->wkup_en) ct->chip.irq_set_wake = gpio_wake_enable, ct->regs.mask = OMAP_MPUIO_GPIO_INT / bank->stride; @@ -1115,7 +1004,6 @@ static void __devinit omap_gpio_chip_init(struct gpio_bank *bank) int j; static int gpio; - bank->mod_usage = 0; /* * REVISIT eventually switch from OMAP-specific gpio structs * over to the generic ones @@ -1128,11 +1016,10 @@ static void __devinit omap_gpio_chip_init(struct gpio_bank *bank) bank->chip.set_debounce = gpio_debounce; bank->chip.set = gpio_set; bank->chip.to_irq = gpio_2irq; - if (bank_is_mpuio(bank)) { + if (bank->is_mpuio) { bank->chip.label = "mpuio"; -#ifdef CONFIG_ARCH_OMAP16XX - bank->chip.dev = &omap_mpuio_device.dev; -#endif + if (bank->regs->wkup_en) + bank->chip.dev = &omap_mpuio_device.dev; bank->chip.base = OMAP_MPUIO(0); } else { bank->chip.label = "gpio"; @@ -1147,7 +1034,7 @@ static void __devinit omap_gpio_chip_init(struct gpio_bank *bank) j < bank->virtual_irq_start + bank->width; j++) { irq_set_lockdep_class(j, &gpio_lock_class); irq_set_chip_data(j, bank); - if (bank_is_mpuio(bank)) { + if (bank->is_mpuio) { omap_mpuio_alloc_gc(bank, j, bank->width); } else { irq_set_chip(j, &gpio_irq_chip); @@ -1161,42 +1048,44 @@ static void __devinit omap_gpio_chip_init(struct gpio_bank *bank) static int __devinit omap_gpio_probe(struct platform_device *pdev) { - static int gpio_init_done; struct omap_gpio_platform_data *pdata; struct resource *res; - int id; struct gpio_bank *bank; + int ret = 0; - if (!pdev->dev.platform_data) - return -EINVAL; - - pdata = pdev->dev.platform_data; - - if (!gpio_init_done) { - int ret; - - ret = init_gpio_info(pdev); - if (ret) - return ret; + if (!pdev->dev.platform_data) { + ret = -EINVAL; + goto err_exit; } - id = pdev->id; - bank = &gpio_bank[id]; + bank = kzalloc(sizeof(struct gpio_bank), GFP_KERNEL); + if (!bank) { + dev_err(&pdev->dev, "Memory alloc failed for gpio_bank\n"); + ret = -ENOMEM; + goto err_exit; + } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (unlikely(!res)) { - dev_err(&pdev->dev, "GPIO Bank %i Invalid IRQ resource\n", id); - return -ENODEV; + dev_err(&pdev->dev, "GPIO Bank %i Invalid IRQ resource\n", + pdev->id); + ret = -ENODEV; + goto err_free; } bank->irq = res->start; + bank->id = pdev->id; + + pdata = pdev->dev.platform_data; bank->virtual_irq_start = pdata->virtual_irq_start; - bank->method = pdata->bank_type; bank->dev = &pdev->dev; bank->dbck_flag = pdata->dbck_flag; bank->stride = pdata->bank_stride; bank->width = pdata->bank_width; - + bank->is_mpuio = pdata->is_mpuio; + bank->non_wakeup_gpios = pdata->non_wakeup_gpios; + bank->loses_context = pdata->loses_context; + bank->get_context_loss_count = pdata->get_context_loss_count; bank->regs = pdata->regs; if (bank->regs->set_dataout && bank->regs->clr_dataout) @@ -1209,369 +1098,310 @@ static int __devinit omap_gpio_probe(struct platform_device *pdev) /* Static mapping, never released */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (unlikely(!res)) { - dev_err(&pdev->dev, "GPIO Bank %i Invalid mem resource\n", id); - return -ENODEV; + dev_err(&pdev->dev, "GPIO Bank %i Invalid mem resource\n", + pdev->id); + ret = -ENODEV; + goto err_free; } bank->base = ioremap(res->start, resource_size(res)); if (!bank->base) { - dev_err(&pdev->dev, "Could not ioremap gpio bank%i\n", id); - return -ENOMEM; + dev_err(&pdev->dev, "Could not ioremap gpio bank%i\n", + pdev->id); + ret = -ENOMEM; + goto err_free; } + platform_set_drvdata(pdev, bank); + pm_runtime_enable(bank->dev); + pm_runtime_irq_safe(bank->dev); pm_runtime_get_sync(bank->dev); - omap_gpio_mod_init(bank, id); + if (bank->is_mpuio) + mpuio_init(bank); + + omap_gpio_mod_init(bank); omap_gpio_chip_init(bank); omap_gpio_show_rev(bank); - if (!gpio_init_done) - gpio_init_done = 1; + pm_runtime_put(bank->dev); - return 0; + list_add_tail(&bank->node, &omap_gpio_list); + + return ret; + +err_free: + kfree(bank); +err_exit: + return ret; } -#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2PLUS) -static int omap_gpio_suspend(void) +#ifdef CONFIG_ARCH_OMAP2PLUS + +#if defined(CONFIG_PM_SLEEP) +static int omap_gpio_suspend(struct device *dev) { - int i; + struct platform_device *pdev = to_platform_device(dev); + struct gpio_bank *bank = platform_get_drvdata(pdev); + void __iomem *base = bank->base; + void __iomem *wakeup_enable; + unsigned long flags; - if (!cpu_class_is_omap2() && !cpu_is_omap16xx()) + if (!bank->mod_usage || !bank->loses_context) return 0; - for (i = 0; i < gpio_bank_count; i++) { - struct gpio_bank *bank = &gpio_bank[i]; - void __iomem *wake_status; - void __iomem *wake_clear; - void __iomem *wake_set; - unsigned long flags; - - switch (bank->method) { -#ifdef CONFIG_ARCH_OMAP16XX - case METHOD_GPIO_1610: - wake_status = bank->base + OMAP1610_GPIO_WAKEUPENABLE; - wake_clear = bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA; - wake_set = bank->base + OMAP1610_GPIO_SET_WAKEUPENA; - break; -#endif -#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) - case METHOD_GPIO_24XX: - wake_status = bank->base + OMAP24XX_GPIO_WAKE_EN; - wake_clear = bank->base + OMAP24XX_GPIO_CLEARWKUENA; - wake_set = bank->base + OMAP24XX_GPIO_SETWKUENA; - break; -#endif -#ifdef CONFIG_ARCH_OMAP4 - case METHOD_GPIO_44XX: - wake_status = bank->base + OMAP4_GPIO_IRQWAKEN0; - wake_clear = bank->base + OMAP4_GPIO_IRQWAKEN0; - wake_set = bank->base + OMAP4_GPIO_IRQWAKEN0; - break; -#endif - default: - continue; - } + if (!bank->regs->wkup_en || !bank->suspend_wakeup) + return 0; - spin_lock_irqsave(&bank->lock, flags); - bank->saved_wakeup = __raw_readl(wake_status); - __raw_writel(0xffffffff, wake_clear); - __raw_writel(bank->suspend_wakeup, wake_set); - spin_unlock_irqrestore(&bank->lock, flags); - } + wakeup_enable = bank->base + bank->regs->wkup_en; + + spin_lock_irqsave(&bank->lock, flags); + bank->saved_wakeup = __raw_readl(wakeup_enable); + _gpio_rmw(base, bank->regs->wkup_en, 0xffffffff, 0); + _gpio_rmw(base, bank->regs->wkup_en, bank->suspend_wakeup, 1); + spin_unlock_irqrestore(&bank->lock, flags); return 0; } -static void omap_gpio_resume(void) +static int omap_gpio_resume(struct device *dev) { - int i; + struct platform_device *pdev = to_platform_device(dev); + struct gpio_bank *bank = platform_get_drvdata(pdev); + void __iomem *base = bank->base; + unsigned long flags; - if (!cpu_class_is_omap2() && !cpu_is_omap16xx()) - return; + if (!bank->mod_usage || !bank->loses_context) + return 0; - for (i = 0; i < gpio_bank_count; i++) { - struct gpio_bank *bank = &gpio_bank[i]; - void __iomem *wake_clear; - void __iomem *wake_set; - unsigned long flags; - - switch (bank->method) { -#ifdef CONFIG_ARCH_OMAP16XX - case METHOD_GPIO_1610: - wake_clear = bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA; - wake_set = bank->base + OMAP1610_GPIO_SET_WAKEUPENA; - break; -#endif -#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) - case METHOD_GPIO_24XX: - wake_clear = bank->base + OMAP24XX_GPIO_CLEARWKUENA; - wake_set = bank->base + OMAP24XX_GPIO_SETWKUENA; - break; -#endif -#ifdef CONFIG_ARCH_OMAP4 - case METHOD_GPIO_44XX: - wake_clear = bank->base + OMAP4_GPIO_IRQWAKEN0; - wake_set = bank->base + OMAP4_GPIO_IRQWAKEN0; - break; -#endif - default: - continue; - } + if (!bank->regs->wkup_en || !bank->saved_wakeup) + return 0; - spin_lock_irqsave(&bank->lock, flags); - __raw_writel(0xffffffff, wake_clear); - __raw_writel(bank->saved_wakeup, wake_set); - spin_unlock_irqrestore(&bank->lock, flags); - } -} + spin_lock_irqsave(&bank->lock, flags); + _gpio_rmw(base, bank->regs->wkup_en, 0xffffffff, 0); + _gpio_rmw(base, bank->regs->wkup_en, bank->saved_wakeup, 1); + spin_unlock_irqrestore(&bank->lock, flags); -static struct syscore_ops omap_gpio_syscore_ops = { - .suspend = omap_gpio_suspend, - .resume = omap_gpio_resume, -}; + return 0; +} +#endif /* CONFIG_PM_SLEEP */ -#endif +#if defined(CONFIG_PM_RUNTIME) +static void omap_gpio_restore_context(struct gpio_bank *bank); -#ifdef CONFIG_ARCH_OMAP2PLUS +static int omap_gpio_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gpio_bank *bank = platform_get_drvdata(pdev); + u32 l1 = 0, l2 = 0; + unsigned long flags; -static int workaround_enabled; + spin_lock_irqsave(&bank->lock, flags); + if (bank->power_mode != OFF_MODE) { + bank->power_mode = 0; + goto update_gpio_context_count; + } + /* + * If going to OFF, remove triggering for all + * non-wakeup GPIOs. Otherwise spurious IRQs will be + * generated. See OMAP2420 Errata item 1.101. + */ + if (!(bank->enabled_non_wakeup_gpios)) + goto update_gpio_context_count; -void omap2_gpio_prepare_for_idle(int off_mode) -{ - int i, c = 0; - int min = 0; + bank->saved_datain = __raw_readl(bank->base + + bank->regs->datain); + l1 = __raw_readl(bank->base + bank->regs->fallingdetect); + l2 = __raw_readl(bank->base + bank->regs->risingdetect); - if (cpu_is_omap34xx()) - min = 1; + bank->saved_fallingdetect = l1; + bank->saved_risingdetect = l2; + l1 &= ~bank->enabled_non_wakeup_gpios; + l2 &= ~bank->enabled_non_wakeup_gpios; - for (i = min; i < gpio_bank_count; i++) { - struct gpio_bank *bank = &gpio_bank[i]; - u32 l1 = 0, l2 = 0; - int j; + __raw_writel(l1, bank->base + bank->regs->fallingdetect); + __raw_writel(l2, bank->base + bank->regs->risingdetect); - for (j = 0; j < hweight_long(bank->dbck_enable_mask); j++) - clk_disable(bank->dbck); + bank->workaround_enabled = true; - if (!off_mode) - continue; +update_gpio_context_count: + if (bank->get_context_loss_count) + bank->context_loss_count = + bank->get_context_loss_count(bank->dev); - /* If going to OFF, remove triggering for all - * non-wakeup GPIOs. Otherwise spurious IRQs will be - * generated. See OMAP2420 Errata item 1.101. */ - if (!(bank->enabled_non_wakeup_gpios)) - continue; + _gpio_dbck_disable(bank); + spin_unlock_irqrestore(&bank->lock, flags); - if (cpu_is_omap24xx() || cpu_is_omap34xx()) { - bank->saved_datain = __raw_readl(bank->base + - OMAP24XX_GPIO_DATAIN); - l1 = __raw_readl(bank->base + - OMAP24XX_GPIO_FALLINGDETECT); - l2 = __raw_readl(bank->base + - OMAP24XX_GPIO_RISINGDETECT); - } + return 0; +} - if (cpu_is_omap44xx()) { - bank->saved_datain = __raw_readl(bank->base + - OMAP4_GPIO_DATAIN); - l1 = __raw_readl(bank->base + - OMAP4_GPIO_FALLINGDETECT); - l2 = __raw_readl(bank->base + - OMAP4_GPIO_RISINGDETECT); - } +static int omap_gpio_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gpio_bank *bank = platform_get_drvdata(pdev); + int context_lost_cnt_after; + u32 l = 0, gen, gen0, gen1; + unsigned long flags; - bank->saved_fallingdetect = l1; - bank->saved_risingdetect = l2; - l1 &= ~bank->enabled_non_wakeup_gpios; - l2 &= ~bank->enabled_non_wakeup_gpios; + spin_lock_irqsave(&bank->lock, flags); + _gpio_dbck_enable(bank); + if (!bank->enabled_non_wakeup_gpios || !bank->workaround_enabled) { + spin_unlock_irqrestore(&bank->lock, flags); + return 0; + } - if (cpu_is_omap24xx() || cpu_is_omap34xx()) { - __raw_writel(l1, bank->base + - OMAP24XX_GPIO_FALLINGDETECT); - __raw_writel(l2, bank->base + - OMAP24XX_GPIO_RISINGDETECT); + if (bank->get_context_loss_count) { + context_lost_cnt_after = + bank->get_context_loss_count(bank->dev); + if (context_lost_cnt_after != bank->context_loss_count || + !context_lost_cnt_after) { + omap_gpio_restore_context(bank); + } else { + spin_unlock_irqrestore(&bank->lock, flags); + return 0; } + } - if (cpu_is_omap44xx()) { - __raw_writel(l1, bank->base + OMAP4_GPIO_FALLINGDETECT); - __raw_writel(l2, bank->base + OMAP4_GPIO_RISINGDETECT); - } + __raw_writel(bank->saved_fallingdetect, + bank->base + bank->regs->fallingdetect); + __raw_writel(bank->saved_risingdetect, + bank->base + bank->regs->risingdetect); + l = __raw_readl(bank->base + bank->regs->datain); - c++; - } - if (!c) { - workaround_enabled = 0; - return; - } - workaround_enabled = 1; -} + /* + * Check if any of the non-wakeup interrupt GPIOs have changed + * state. If so, generate an IRQ by software. This is + * horribly racy, but it's the best we can do to work around + * this silicon bug. + */ + l ^= bank->saved_datain; + l &= bank->enabled_non_wakeup_gpios; -void omap2_gpio_resume_after_idle(void) -{ - int i; - int min = 0; + /* + * No need to generate IRQs for the rising edge for gpio IRQs + * configured with falling edge only; and vice versa. + */ + gen0 = l & bank->saved_fallingdetect; + gen0 &= bank->saved_datain; - if (cpu_is_omap34xx()) - min = 1; - for (i = min; i < gpio_bank_count; i++) { - struct gpio_bank *bank = &gpio_bank[i]; - u32 l = 0, gen, gen0, gen1; - int j; + gen1 = l & bank->saved_risingdetect; + gen1 &= ~(bank->saved_datain); - for (j = 0; j < hweight_long(bank->dbck_enable_mask); j++) - clk_enable(bank->dbck); + /* FIXME: Consider GPIO IRQs with level detections properly! */ + gen = l & (~(bank->saved_fallingdetect) & ~(bank->saved_risingdetect)); + /* Consider all GPIO IRQs needed to be updated */ + gen |= gen0 | gen1; - if (!workaround_enabled) - continue; + if (gen) { + u32 old0, old1; - if (!(bank->enabled_non_wakeup_gpios)) - continue; + old0 = __raw_readl(bank->base + bank->regs->leveldetect0); + old1 = __raw_readl(bank->base + bank->regs->leveldetect1); if (cpu_is_omap24xx() || cpu_is_omap34xx()) { - __raw_writel(bank->saved_fallingdetect, - bank->base + OMAP24XX_GPIO_FALLINGDETECT); - __raw_writel(bank->saved_risingdetect, - bank->base + OMAP24XX_GPIO_RISINGDETECT); - l = __raw_readl(bank->base + OMAP24XX_GPIO_DATAIN); + __raw_writel(old0 | gen, bank->base + + bank->regs->leveldetect0); + __raw_writel(old1 | gen, bank->base + + bank->regs->leveldetect1); } if (cpu_is_omap44xx()) { - __raw_writel(bank->saved_fallingdetect, - bank->base + OMAP4_GPIO_FALLINGDETECT); - __raw_writel(bank->saved_risingdetect, - bank->base + OMAP4_GPIO_RISINGDETECT); - l = __raw_readl(bank->base + OMAP4_GPIO_DATAIN); - } - - /* Check if any of the non-wakeup interrupt GPIOs have changed - * state. If so, generate an IRQ by software. This is - * horribly racy, but it's the best we can do to work around - * this silicon bug. */ - l ^= bank->saved_datain; - l &= bank->enabled_non_wakeup_gpios; - - /* - * No need to generate IRQs for the rising edge for gpio IRQs - * configured with falling edge only; and vice versa. - */ - gen0 = l & bank->saved_fallingdetect; - gen0 &= bank->saved_datain; - - gen1 = l & bank->saved_risingdetect; - gen1 &= ~(bank->saved_datain); - - /* FIXME: Consider GPIO IRQs with level detections properly! */ - gen = l & (~(bank->saved_fallingdetect) & - ~(bank->saved_risingdetect)); - /* Consider all GPIO IRQs needed to be updated */ - gen |= gen0 | gen1; - - if (gen) { - u32 old0, old1; - - if (cpu_is_omap24xx() || cpu_is_omap34xx()) { - old0 = __raw_readl(bank->base + - OMAP24XX_GPIO_LEVELDETECT0); - old1 = __raw_readl(bank->base + - OMAP24XX_GPIO_LEVELDETECT1); - __raw_writel(old0 | gen, bank->base + - OMAP24XX_GPIO_LEVELDETECT0); - __raw_writel(old1 | gen, bank->base + - OMAP24XX_GPIO_LEVELDETECT1); - __raw_writel(old0, bank->base + - OMAP24XX_GPIO_LEVELDETECT0); - __raw_writel(old1, bank->base + - OMAP24XX_GPIO_LEVELDETECT1); - } - - if (cpu_is_omap44xx()) { - old0 = __raw_readl(bank->base + - OMAP4_GPIO_LEVELDETECT0); - old1 = __raw_readl(bank->base + - OMAP4_GPIO_LEVELDETECT1); - __raw_writel(old0 | l, bank->base + - OMAP4_GPIO_LEVELDETECT0); - __raw_writel(old1 | l, bank->base + - OMAP4_GPIO_LEVELDETECT1); - __raw_writel(old0, bank->base + - OMAP4_GPIO_LEVELDETECT0); - __raw_writel(old1, bank->base + - OMAP4_GPIO_LEVELDETECT1); - } + __raw_writel(old0 | l, bank->base + + bank->regs->leveldetect0); + __raw_writel(old1 | l, bank->base + + bank->regs->leveldetect1); } + __raw_writel(old0, bank->base + bank->regs->leveldetect0); + __raw_writel(old1, bank->base + bank->regs->leveldetect1); } + bank->workaround_enabled = false; + spin_unlock_irqrestore(&bank->lock, flags); + + return 0; } +#endif /* CONFIG_PM_RUNTIME */ -#endif +void omap2_gpio_prepare_for_idle(int pwr_mode) +{ + struct gpio_bank *bank; + + list_for_each_entry(bank, &omap_gpio_list, node) { + if (!bank->mod_usage || !bank->loses_context) + continue; + + bank->power_mode = pwr_mode; + + pm_runtime_put_sync_suspend(bank->dev); + } +} -#ifdef CONFIG_ARCH_OMAP3 -/* save the registers of bank 2-6 */ -void omap_gpio_save_context(void) +void omap2_gpio_resume_after_idle(void) { - int i; - - /* saving banks from 2-6 only since GPIO1 is in WKUP */ - for (i = 1; i < gpio_bank_count; i++) { - struct gpio_bank *bank = &gpio_bank[i]; - gpio_context[i].irqenable1 = - __raw_readl(bank->base + OMAP24XX_GPIO_IRQENABLE1); - gpio_context[i].irqenable2 = - __raw_readl(bank->base + OMAP24XX_GPIO_IRQENABLE2); - gpio_context[i].wake_en = - __raw_readl(bank->base + OMAP24XX_GPIO_WAKE_EN); - gpio_context[i].ctrl = - __raw_readl(bank->base + OMAP24XX_GPIO_CTRL); - gpio_context[i].oe = - __raw_readl(bank->base + OMAP24XX_GPIO_OE); - gpio_context[i].leveldetect0 = - __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT0); - gpio_context[i].leveldetect1 = - __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT1); - gpio_context[i].risingdetect = - __raw_readl(bank->base + OMAP24XX_GPIO_RISINGDETECT); - gpio_context[i].fallingdetect = - __raw_readl(bank->base + OMAP24XX_GPIO_FALLINGDETECT); - gpio_context[i].dataout = - __raw_readl(bank->base + OMAP24XX_GPIO_DATAOUT); + struct gpio_bank *bank; + + list_for_each_entry(bank, &omap_gpio_list, node) { + if (!bank->mod_usage || !bank->loses_context) + continue; + + pm_runtime_get_sync(bank->dev); } } -/* restore the required registers of bank 2-6 */ -void omap_gpio_restore_context(void) +#if defined(CONFIG_PM_RUNTIME) +static void omap_gpio_restore_context(struct gpio_bank *bank) { - int i; - - for (i = 1; i < gpio_bank_count; i++) { - struct gpio_bank *bank = &gpio_bank[i]; - __raw_writel(gpio_context[i].irqenable1, - bank->base + OMAP24XX_GPIO_IRQENABLE1); - __raw_writel(gpio_context[i].irqenable2, - bank->base + OMAP24XX_GPIO_IRQENABLE2); - __raw_writel(gpio_context[i].wake_en, - bank->base + OMAP24XX_GPIO_WAKE_EN); - __raw_writel(gpio_context[i].ctrl, - bank->base + OMAP24XX_GPIO_CTRL); - __raw_writel(gpio_context[i].oe, - bank->base + OMAP24XX_GPIO_OE); - __raw_writel(gpio_context[i].leveldetect0, - bank->base + OMAP24XX_GPIO_LEVELDETECT0); - __raw_writel(gpio_context[i].leveldetect1, - bank->base + OMAP24XX_GPIO_LEVELDETECT1); - __raw_writel(gpio_context[i].risingdetect, - bank->base + OMAP24XX_GPIO_RISINGDETECT); - __raw_writel(gpio_context[i].fallingdetect, - bank->base + OMAP24XX_GPIO_FALLINGDETECT); - __raw_writel(gpio_context[i].dataout, - bank->base + OMAP24XX_GPIO_DATAOUT); + __raw_writel(bank->context.wake_en, + bank->base + bank->regs->wkup_en); + __raw_writel(bank->context.ctrl, bank->base + bank->regs->ctrl); + __raw_writel(bank->context.leveldetect0, + bank->base + bank->regs->leveldetect0); + __raw_writel(bank->context.leveldetect1, + bank->base + bank->regs->leveldetect1); + __raw_writel(bank->context.risingdetect, + bank->base + bank->regs->risingdetect); + __raw_writel(bank->context.fallingdetect, + bank->base + bank->regs->fallingdetect); + if (bank->regs->set_dataout && bank->regs->clr_dataout) + __raw_writel(bank->context.dataout, + bank->base + bank->regs->set_dataout); + else + __raw_writel(bank->context.dataout, + bank->base + bank->regs->dataout); + __raw_writel(bank->context.oe, bank->base + bank->regs->direction); + + if (bank->dbck_enable_mask) { + __raw_writel(bank->context.debounce, bank->base + + bank->regs->debounce); + __raw_writel(bank->context.debounce_en, + bank->base + bank->regs->debounce_en); } + + __raw_writel(bank->context.irqenable1, + bank->base + bank->regs->irqenable); + __raw_writel(bank->context.irqenable2, + bank->base + bank->regs->irqenable2); } +#endif /* CONFIG_PM_RUNTIME */ +#else +#define omap_gpio_suspend NULL +#define omap_gpio_resume NULL +#define omap_gpio_runtime_suspend NULL +#define omap_gpio_runtime_resume NULL #endif +static const struct dev_pm_ops gpio_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(omap_gpio_suspend, omap_gpio_resume) + SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume, + NULL) +}; + static struct platform_driver omap_gpio_driver = { .probe = omap_gpio_probe, .driver = { .name = "omap_gpio", + .pm = &gpio_pm_ops, }, }; @@ -1585,17 +1415,3 @@ static int __init omap_gpio_drv_reg(void) return platform_driver_register(&omap_gpio_driver); } postcore_initcall(omap_gpio_drv_reg); - -static int __init omap_gpio_sysinit(void) -{ - mpuio_init(); - -#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2PLUS) - if (cpu_is_omap16xx() || cpu_class_is_omap2()) - register_syscore_ops(&omap_gpio_syscore_ops); -#endif - - return 0; -} - -arch_initcall(omap_gpio_sysinit); diff --git a/drivers/gpio/gpio-sa1100.c b/drivers/gpio/gpio-sa1100.c index 7eecf69362ee..8ea3b33d4b40 100644 --- a/drivers/gpio/gpio-sa1100.c +++ b/drivers/gpio/gpio-sa1100.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <mach/hardware.h> +#include <mach/irqs.h> static int sa1100_gpio_get(struct gpio_chip *chip, unsigned offset) { diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index 0a79a1167a25..46277877b7ec 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -169,7 +169,7 @@ int s3c24xx_gpio_setpull_1down(struct samsung_gpio_chip *chip, return s3c24xx_gpio_setpull_1(chip, off, pull, S3C_GPIO_PULL_DOWN); } -static int exynos4_gpio_setpull(struct samsung_gpio_chip *chip, +static int exynos_gpio_setpull(struct samsung_gpio_chip *chip, unsigned int off, samsung_gpio_pull_t pull) { if (pull == S3C_GPIO_PULL_UP) @@ -178,7 +178,7 @@ static int exynos4_gpio_setpull(struct samsung_gpio_chip *chip, return samsung_gpio_setpull_updown(chip, off, pull); } -static samsung_gpio_pull_t exynos4_gpio_getpull(struct samsung_gpio_chip *chip, +static samsung_gpio_pull_t exynos_gpio_getpull(struct samsung_gpio_chip *chip, unsigned int off) { samsung_gpio_pull_t pull; @@ -452,9 +452,9 @@ static struct samsung_gpio_cfg s3c24xx_gpiocfg_banka = { }; #endif -static struct samsung_gpio_cfg exynos4_gpio_cfg = { - .set_pull = exynos4_gpio_setpull, - .get_pull = exynos4_gpio_getpull, +static struct samsung_gpio_cfg exynos_gpio_cfg = { + .set_pull = exynos_gpio_setpull, + .get_pull = exynos_gpio_getpull, .set_config = samsung_gpio_setcfg_4bit, .get_config = samsung_gpio_getcfg_4bit, }; @@ -502,13 +502,13 @@ static struct samsung_gpio_cfg samsung_gpio_cfgs[] = { .get_config = samsung_gpio_getcfg_2bit, }, [8] = { - .set_pull = exynos4_gpio_setpull, - .get_pull = exynos4_gpio_getpull, + .set_pull = exynos_gpio_setpull, + .get_pull = exynos_gpio_getpull, }, [9] = { .cfg_eint = 0x3, - .set_pull = exynos4_gpio_setpull, - .get_pull = exynos4_gpio_getpull, + .set_pull = exynos_gpio_setpull, + .get_pull = exynos_gpio_getpull, } }; @@ -2113,10 +2113,10 @@ static struct samsung_gpio_chip s5pv210_gpios_4bit[] = { }; /* - * Followings are the gpio banks in EXYNOS4210 + * Followings are the gpio banks in EXYNOS SoCs * * The 'config' member when left to NULL, is initialized to the default - * structure samsung_gpio_cfgs[3] in the init function below. + * structure exynos_gpio_cfg in the init function below. * * The 'base' member is also initialized in the init function below. * Note: The initialization of 'base' member of samsung_gpio_chip structure @@ -2331,7 +2331,6 @@ static struct samsung_gpio_chip exynos4_gpios_2[] = { .label = "GPY6", }, }, { - .base = (S5P_VA_GPIO2 + 0xC00), .config = &samsung_gpio_cfgs[9], .irq_base = IRQ_EINT(0), .chip = { @@ -2341,7 +2340,6 @@ static struct samsung_gpio_chip exynos4_gpios_2[] = { .to_irq = samsung_gpiolib_to_irq, }, }, { - .base = (S5P_VA_GPIO2 + 0xC20), .config = &samsung_gpio_cfgs[9], .irq_base = IRQ_EINT(8), .chip = { @@ -2351,7 +2349,6 @@ static struct samsung_gpio_chip exynos4_gpios_2[] = { .to_irq = samsung_gpiolib_to_irq, }, }, { - .base = (S5P_VA_GPIO2 + 0xC40), .config = &samsung_gpio_cfgs[9], .irq_base = IRQ_EINT(16), .chip = { @@ -2361,7 +2358,6 @@ static struct samsung_gpio_chip exynos4_gpios_2[] = { .to_irq = samsung_gpiolib_to_irq, }, }, { - .base = (S5P_VA_GPIO2 + 0xC60), .config = &samsung_gpio_cfgs[9], .irq_base = IRQ_EINT(24), .chip = { @@ -2386,8 +2382,280 @@ static struct samsung_gpio_chip exynos4_gpios_3[] = { #endif }; -#if defined(CONFIG_ARCH_EXYNOS4) && defined(CONFIG_OF) -static int exynos4_gpio_xlate(struct gpio_chip *gc, +static struct samsung_gpio_chip exynos5_gpios_1[] = { +#ifdef CONFIG_ARCH_EXYNOS5 + { + .chip = { + .base = EXYNOS5_GPA0(0), + .ngpio = EXYNOS5_GPIO_A0_NR, + .label = "GPA0", + }, + }, { + .chip = { + .base = EXYNOS5_GPA1(0), + .ngpio = EXYNOS5_GPIO_A1_NR, + .label = "GPA1", + }, + }, { + .chip = { + .base = EXYNOS5_GPA2(0), + .ngpio = EXYNOS5_GPIO_A2_NR, + .label = "GPA2", + }, + }, { + .chip = { + .base = EXYNOS5_GPB0(0), + .ngpio = EXYNOS5_GPIO_B0_NR, + .label = "GPB0", + }, + }, { + .chip = { + .base = EXYNOS5_GPB1(0), + .ngpio = EXYNOS5_GPIO_B1_NR, + .label = "GPB1", + }, + }, { + .chip = { + .base = EXYNOS5_GPB2(0), + .ngpio = EXYNOS5_GPIO_B2_NR, + .label = "GPB2", + }, + }, { + .chip = { + .base = EXYNOS5_GPB3(0), + .ngpio = EXYNOS5_GPIO_B3_NR, + .label = "GPB3", + }, + }, { + .chip = { + .base = EXYNOS5_GPC0(0), + .ngpio = EXYNOS5_GPIO_C0_NR, + .label = "GPC0", + }, + }, { + .chip = { + .base = EXYNOS5_GPC1(0), + .ngpio = EXYNOS5_GPIO_C1_NR, + .label = "GPC1", + }, + }, { + .chip = { + .base = EXYNOS5_GPC2(0), + .ngpio = EXYNOS5_GPIO_C2_NR, + .label = "GPC2", + }, + }, { + .chip = { + .base = EXYNOS5_GPC3(0), + .ngpio = EXYNOS5_GPIO_C3_NR, + .label = "GPC3", + }, + }, { + .chip = { + .base = EXYNOS5_GPD0(0), + .ngpio = EXYNOS5_GPIO_D0_NR, + .label = "GPD0", + }, + }, { + .chip = { + .base = EXYNOS5_GPD1(0), + .ngpio = EXYNOS5_GPIO_D1_NR, + .label = "GPD1", + }, + }, { + .chip = { + .base = EXYNOS5_GPY0(0), + .ngpio = EXYNOS5_GPIO_Y0_NR, + .label = "GPY0", + }, + }, { + .chip = { + .base = EXYNOS5_GPY1(0), + .ngpio = EXYNOS5_GPIO_Y1_NR, + .label = "GPY1", + }, + }, { + .chip = { + .base = EXYNOS5_GPY2(0), + .ngpio = EXYNOS5_GPIO_Y2_NR, + .label = "GPY2", + }, + }, { + .chip = { + .base = EXYNOS5_GPY3(0), + .ngpio = EXYNOS5_GPIO_Y3_NR, + .label = "GPY3", + }, + }, { + .chip = { + .base = EXYNOS5_GPY4(0), + .ngpio = EXYNOS5_GPIO_Y4_NR, + .label = "GPY4", + }, + }, { + .chip = { + .base = EXYNOS5_GPY5(0), + .ngpio = EXYNOS5_GPIO_Y5_NR, + .label = "GPY5", + }, + }, { + .chip = { + .base = EXYNOS5_GPY6(0), + .ngpio = EXYNOS5_GPIO_Y6_NR, + .label = "GPY6", + }, + }, { + .config = &samsung_gpio_cfgs[9], + .irq_base = IRQ_EINT(0), + .chip = { + .base = EXYNOS5_GPX0(0), + .ngpio = EXYNOS5_GPIO_X0_NR, + .label = "GPX0", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .config = &samsung_gpio_cfgs[9], + .irq_base = IRQ_EINT(8), + .chip = { + .base = EXYNOS5_GPX1(0), + .ngpio = EXYNOS5_GPIO_X1_NR, + .label = "GPX1", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .config = &samsung_gpio_cfgs[9], + .irq_base = IRQ_EINT(16), + .chip = { + .base = EXYNOS5_GPX2(0), + .ngpio = EXYNOS5_GPIO_X2_NR, + .label = "GPX2", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .config = &samsung_gpio_cfgs[9], + .irq_base = IRQ_EINT(24), + .chip = { + .base = EXYNOS5_GPX3(0), + .ngpio = EXYNOS5_GPIO_X3_NR, + .label = "GPX3", + .to_irq = samsung_gpiolib_to_irq, + }, + }, +#endif +}; + +static struct samsung_gpio_chip exynos5_gpios_2[] = { +#ifdef CONFIG_ARCH_EXYNOS5 + { + .chip = { + .base = EXYNOS5_GPE0(0), + .ngpio = EXYNOS5_GPIO_E0_NR, + .label = "GPE0", + }, + }, { + .chip = { + .base = EXYNOS5_GPE1(0), + .ngpio = EXYNOS5_GPIO_E1_NR, + .label = "GPE1", + }, + }, { + .chip = { + .base = EXYNOS5_GPF0(0), + .ngpio = EXYNOS5_GPIO_F0_NR, + .label = "GPF0", + }, + }, { + .chip = { + .base = EXYNOS5_GPF1(0), + .ngpio = EXYNOS5_GPIO_F1_NR, + .label = "GPF1", + }, + }, { + .chip = { + .base = EXYNOS5_GPG0(0), + .ngpio = EXYNOS5_GPIO_G0_NR, + .label = "GPG0", + }, + }, { + .chip = { + .base = EXYNOS5_GPG1(0), + .ngpio = EXYNOS5_GPIO_G1_NR, + .label = "GPG1", + }, + }, { + .chip = { + .base = EXYNOS5_GPG2(0), + .ngpio = EXYNOS5_GPIO_G2_NR, + .label = "GPG2", + }, + }, { + .chip = { + .base = EXYNOS5_GPH0(0), + .ngpio = EXYNOS5_GPIO_H0_NR, + .label = "GPH0", + }, + }, { + .chip = { + .base = EXYNOS5_GPH1(0), + .ngpio = EXYNOS5_GPIO_H1_NR, + .label = "GPH1", + + }, + }, +#endif +}; + +static struct samsung_gpio_chip exynos5_gpios_3[] = { +#ifdef CONFIG_ARCH_EXYNOS5 + { + .chip = { + .base = EXYNOS5_GPV0(0), + .ngpio = EXYNOS5_GPIO_V0_NR, + .label = "GPV0", + }, + }, { + .chip = { + .base = EXYNOS5_GPV1(0), + .ngpio = EXYNOS5_GPIO_V1_NR, + .label = "GPV1", + }, + }, { + .chip = { + .base = EXYNOS5_GPV2(0), + .ngpio = EXYNOS5_GPIO_V2_NR, + .label = "GPV2", + }, + }, { + .chip = { + .base = EXYNOS5_GPV3(0), + .ngpio = EXYNOS5_GPIO_V3_NR, + .label = "GPV3", + }, + }, { + .chip = { + .base = EXYNOS5_GPV4(0), + .ngpio = EXYNOS5_GPIO_V4_NR, + .label = "GPV4", + }, + }, +#endif +}; + +static struct samsung_gpio_chip exynos5_gpios_4[] = { +#ifdef CONFIG_ARCH_EXYNOS5 + { + .chip = { + .base = EXYNOS5_GPZ(0), + .ngpio = EXYNOS5_GPIO_Z_NR, + .label = "GPZ", + }, + }, +#endif +}; + + +#if defined(CONFIG_ARCH_EXYNOS) && defined(CONFIG_OF) +static int exynos_gpio_xlate(struct gpio_chip *gc, const struct of_phandle_args *gpiospec, u32 *flags) { unsigned int pin; @@ -2413,13 +2681,13 @@ static int exynos4_gpio_xlate(struct gpio_chip *gc, return gpiospec->args[0]; } -static const struct of_device_id exynos4_gpio_dt_match[] __initdata = { +static const struct of_device_id exynos_gpio_dt_match[] __initdata = { { .compatible = "samsung,exynos4-gpio", }, {} }; -static __init void exynos4_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip, - u64 base, u64 offset) +static __init void exynos_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip, + u64 base, u64 offset) { struct gpio_chip *gc = &chip->chip; u64 address; @@ -2429,28 +2697,29 @@ static __init void exynos4_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip, address = chip->base ? base + ((u32)chip->base & 0xfff) : base + offset; gc->of_node = of_find_matching_node_by_address(NULL, - exynos4_gpio_dt_match, address); + exynos_gpio_dt_match, address); if (!gc->of_node) { pr_info("gpio: device tree node not found for gpio controller" " with base address %08llx\n", address); return; } gc->of_gpio_n_cells = 4; - gc->of_xlate = exynos4_gpio_xlate; + gc->of_xlate = exynos_gpio_xlate; } -#elif defined(CONFIG_ARCH_EXYNOS4) -static __init void exynos4_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip, - u64 base, u64 offset) +#elif defined(CONFIG_ARCH_EXYNOS) +static __init void exynos_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip, + u64 base, u64 offset) { return; } -#endif /* defined(CONFIG_ARCH_EXYNOS4) && defined(CONFIG_OF) */ +#endif /* defined(CONFIG_ARCH_EXYNOS) && defined(CONFIG_OF) */ /* TODO: cleanup soc_is_* */ static __init int samsung_gpiolib_init(void) { struct samsung_gpio_chip *chip; int i, nr_chips; + void __iomem *gpio_base1, *gpio_base2, *gpio_base3, *gpio_base4; int group = 0; samsung_gpiolib_set_cfg(samsung_gpio_cfgs, ARRAY_SIZE(samsung_gpio_cfgs)); @@ -2516,66 +2785,200 @@ static __init int samsung_gpiolib_init(void) s5p_register_gpioint_bank(IRQ_GPIOINT, 0, S5P_GPIOINT_GROUP_MAXNR); #endif } else if (soc_is_exynos4210()) { - group = 0; +#ifdef CONFIG_CPU_EXYNOS4210 + void __iomem *gpx_base; /* gpio part1 */ + gpio_base1 = ioremap(EXYNOS4_PA_GPIO1, SZ_4K); + if (gpio_base1 == NULL) { + pr_err("unable to ioremap for gpio_base1\n"); + goto err_ioremap1; + } + chip = exynos4_gpios_1; nr_chips = ARRAY_SIZE(exynos4_gpios_1); for (i = 0; i < nr_chips; i++, chip++) { if (!chip->config) { - chip->config = &exynos4_gpio_cfg; + chip->config = &exynos_gpio_cfg; chip->group = group++; } -#ifdef CONFIG_CPU_EXYNOS4210 - exynos4_gpiolib_attach_ofnode(chip, + exynos_gpiolib_attach_ofnode(chip, EXYNOS4_PA_GPIO1, i * 0x20); -#endif } - samsung_gpiolib_add_4bit_chips(exynos4_gpios_1, nr_chips, S5P_VA_GPIO1); + samsung_gpiolib_add_4bit_chips(exynos4_gpios_1, + nr_chips, gpio_base1); /* gpio part2 */ + gpio_base2 = ioremap(EXYNOS4_PA_GPIO2, SZ_4K); + if (gpio_base2 == NULL) { + pr_err("unable to ioremap for gpio_base2\n"); + goto err_ioremap2; + } + + /* need to set base address for gpx */ + chip = &exynos4_gpios_2[16]; + gpx_base = gpio_base2 + 0xC00; + for (i = 0; i < 4; i++, chip++, gpx_base += 0x20) + chip->base = gpx_base; + chip = exynos4_gpios_2; nr_chips = ARRAY_SIZE(exynos4_gpios_2); for (i = 0; i < nr_chips; i++, chip++) { if (!chip->config) { - chip->config = &exynos4_gpio_cfg; + chip->config = &exynos_gpio_cfg; chip->group = group++; } -#ifdef CONFIG_CPU_EXYNOS4210 - exynos4_gpiolib_attach_ofnode(chip, + exynos_gpiolib_attach_ofnode(chip, EXYNOS4_PA_GPIO2, i * 0x20); -#endif } - samsung_gpiolib_add_4bit_chips(exynos4_gpios_2, nr_chips, S5P_VA_GPIO2); + samsung_gpiolib_add_4bit_chips(exynos4_gpios_2, + nr_chips, gpio_base2); /* gpio part3 */ + gpio_base3 = ioremap(EXYNOS4_PA_GPIO3, SZ_256); + if (gpio_base3 == NULL) { + pr_err("unable to ioremap for gpio_base3\n"); + goto err_ioremap3; + } + chip = exynos4_gpios_3; nr_chips = ARRAY_SIZE(exynos4_gpios_3); for (i = 0; i < nr_chips; i++, chip++) { if (!chip->config) { - chip->config = &exynos4_gpio_cfg; + chip->config = &exynos_gpio_cfg; chip->group = group++; } -#ifdef CONFIG_CPU_EXYNOS4210 - exynos4_gpiolib_attach_ofnode(chip, + exynos_gpiolib_attach_ofnode(chip, EXYNOS4_PA_GPIO3, i * 0x20); -#endif } - samsung_gpiolib_add_4bit_chips(exynos4_gpios_3, nr_chips, S5P_VA_GPIO3); + samsung_gpiolib_add_4bit_chips(exynos4_gpios_3, + nr_chips, gpio_base3); #if defined(CONFIG_CPU_EXYNOS4210) && defined(CONFIG_S5P_GPIO_INT) s5p_register_gpioint_bank(IRQ_GPIO_XA, 0, IRQ_GPIO1_NR_GROUPS); s5p_register_gpioint_bank(IRQ_GPIO_XB, IRQ_GPIO1_NR_GROUPS, IRQ_GPIO2_NR_GROUPS); #endif + +#endif /* CONFIG_CPU_EXYNOS4210 */ + } else if (soc_is_exynos5250()) { +#ifdef CONFIG_SOC_EXYNOS5250 + void __iomem *gpx_base; + + /* gpio part1 */ + gpio_base1 = ioremap(EXYNOS5_PA_GPIO1, SZ_4K); + if (gpio_base1 == NULL) { + pr_err("unable to ioremap for gpio_base1\n"); + goto err_ioremap1; + } + + /* need to set base address for gpx */ + chip = &exynos5_gpios_1[20]; + gpx_base = gpio_base1 + 0xC00; + for (i = 0; i < 4; i++, chip++, gpx_base += 0x20) + chip->base = gpx_base; + + chip = exynos5_gpios_1; + nr_chips = ARRAY_SIZE(exynos5_gpios_1); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &exynos_gpio_cfg; + chip->group = group++; + } + exynos_gpiolib_attach_ofnode(chip, + EXYNOS5_PA_GPIO1, i * 0x20); + } + samsung_gpiolib_add_4bit_chips(exynos5_gpios_1, + nr_chips, gpio_base1); + + /* gpio part2 */ + gpio_base2 = ioremap(EXYNOS5_PA_GPIO2, SZ_4K); + if (gpio_base2 == NULL) { + pr_err("unable to ioremap for gpio_base2\n"); + goto err_ioremap2; + } + + chip = exynos5_gpios_2; + nr_chips = ARRAY_SIZE(exynos5_gpios_2); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &exynos_gpio_cfg; + chip->group = group++; + } + exynos_gpiolib_attach_ofnode(chip, + EXYNOS5_PA_GPIO2, i * 0x20); + } + samsung_gpiolib_add_4bit_chips(exynos5_gpios_2, + nr_chips, gpio_base2); + + /* gpio part3 */ + gpio_base3 = ioremap(EXYNOS5_PA_GPIO3, SZ_4K); + if (gpio_base3 == NULL) { + pr_err("unable to ioremap for gpio_base3\n"); + goto err_ioremap3; + } + + /* need to set base address for gpv */ + exynos5_gpios_3[0].base = gpio_base3; + exynos5_gpios_3[1].base = gpio_base3 + 0x20; + exynos5_gpios_3[2].base = gpio_base3 + 0x60; + exynos5_gpios_3[3].base = gpio_base3 + 0x80; + exynos5_gpios_3[4].base = gpio_base3 + 0xC0; + + chip = exynos5_gpios_3; + nr_chips = ARRAY_SIZE(exynos5_gpios_3); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &exynos_gpio_cfg; + chip->group = group++; + } + exynos_gpiolib_attach_ofnode(chip, + EXYNOS5_PA_GPIO3, i * 0x20); + } + samsung_gpiolib_add_4bit_chips(exynos5_gpios_3, + nr_chips, gpio_base3); + + /* gpio part4 */ + gpio_base4 = ioremap(EXYNOS5_PA_GPIO4, SZ_4K); + if (gpio_base4 == NULL) { + pr_err("unable to ioremap for gpio_base4\n"); + goto err_ioremap4; + } + + chip = exynos5_gpios_4; + nr_chips = ARRAY_SIZE(exynos5_gpios_4); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &exynos_gpio_cfg; + chip->group = group++; + } + exynos_gpiolib_attach_ofnode(chip, + EXYNOS5_PA_GPIO4, i * 0x20); + } + samsung_gpiolib_add_4bit_chips(exynos5_gpios_4, + nr_chips, gpio_base4); +#endif /* CONFIG_SOC_EXYNOS5250 */ } else { WARN(1, "Unknown SoC in gpio-samsung, no GPIOs added\n"); return -ENODEV; } return 0; + +err_ioremap4: + iounmap(gpio_base3); +err_ioremap3: + iounmap(gpio_base2); +err_ioremap2: + iounmap(gpio_base1); +err_ioremap1: + return -ENOMEM; } core_initcall(samsung_gpiolib_init); diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index bdc293791590..6f17671260e1 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -25,6 +25,7 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/module.h> +#include <linux/irqdomain.h> #include <asm/mach/irq.h> @@ -74,9 +75,10 @@ struct tegra_gpio_bank { #endif }; - +static struct irq_domain *irq_domain; static void __iomem *regs; -static struct tegra_gpio_bank tegra_gpio_banks[7]; +static u32 tegra_gpio_bank_count; +static struct tegra_gpio_bank *tegra_gpio_banks; static inline void tegra_gpio_writel(u32 val, u32 reg) { @@ -139,7 +141,7 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip, unsigned offset, static int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { - return TEGRA_GPIO_TO_IRQ(offset); + return irq_find_mapping(irq_domain, offset); } static struct gpio_chip tegra_gpio_chip = { @@ -155,28 +157,28 @@ static struct gpio_chip tegra_gpio_chip = { static void tegra_gpio_irq_ack(struct irq_data *d) { - int gpio = d->irq - INT_GPIO_BASE; + int gpio = d->hwirq; tegra_gpio_writel(1 << GPIO_BIT(gpio), GPIO_INT_CLR(gpio)); } static void tegra_gpio_irq_mask(struct irq_data *d) { - int gpio = d->irq - INT_GPIO_BASE; + int gpio = d->hwirq; tegra_gpio_mask_write(GPIO_MSK_INT_ENB(gpio), gpio, 0); } static void tegra_gpio_irq_unmask(struct irq_data *d) { - int gpio = d->irq - INT_GPIO_BASE; + int gpio = d->hwirq; tegra_gpio_mask_write(GPIO_MSK_INT_ENB(gpio), gpio, 1); } static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) { - int gpio = d->irq - INT_GPIO_BASE; + int gpio = d->hwirq; struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); int port = GPIO_PORT(gpio); int lvl_type; @@ -273,7 +275,7 @@ void tegra_gpio_resume(void) local_irq_save(flags); - for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) { + for (b = 0; b < tegra_gpio_bank_count; b++) { struct tegra_gpio_bank *bank = &tegra_gpio_banks[b]; for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { @@ -296,7 +298,7 @@ void tegra_gpio_suspend(void) int p; local_irq_save(flags); - for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) { + for (b = 0; b < tegra_gpio_bank_count; b++) { struct tegra_gpio_bank *bank = &tegra_gpio_banks[b]; for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { @@ -337,13 +339,44 @@ static struct lock_class_key gpio_lock_class; static int __devinit tegra_gpio_probe(struct platform_device *pdev) { + int irq_base; struct resource *res; struct tegra_gpio_bank *bank; int gpio; int i; int j; - for (i = 0; i < ARRAY_SIZE(tegra_gpio_banks); i++) { + for (;;) { + res = platform_get_resource(pdev, IORESOURCE_IRQ, tegra_gpio_bank_count); + if (!res) + break; + tegra_gpio_bank_count++; + } + if (!tegra_gpio_bank_count) { + dev_err(&pdev->dev, "Missing IRQ resource\n"); + return -ENODEV; + } + + tegra_gpio_chip.ngpio = tegra_gpio_bank_count * 32; + + tegra_gpio_banks = devm_kzalloc(&pdev->dev, + tegra_gpio_bank_count * sizeof(*tegra_gpio_banks), + GFP_KERNEL); + if (!tegra_gpio_banks) { + dev_err(&pdev->dev, "Couldn't allocate bank structure\n"); + return -ENODEV; + } + + irq_base = irq_alloc_descs(-1, 0, tegra_gpio_chip.ngpio, 0); + if (irq_base < 0) { + dev_err(&pdev->dev, "Couldn't allocate IRQ numbers\n"); + return -ENODEV; + } + irq_domain = irq_domain_add_legacy(pdev->dev.of_node, + tegra_gpio_chip.ngpio, irq_base, 0, + &irq_domain_simple_ops, NULL); + + for (i = 0; i < tegra_gpio_bank_count; i++) { res = platform_get_resource(pdev, IORESOURCE_IRQ, i); if (!res) { dev_err(&pdev->dev, "Missing IRQ resource\n"); @@ -380,8 +413,8 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev) gpiochip_add(&tegra_gpio_chip); - for (gpio = 0; gpio < TEGRA_NR_GPIOS; gpio++) { - int irq = TEGRA_GPIO_TO_IRQ(gpio); + for (gpio = 0; gpio < tegra_gpio_chip.ngpio; gpio++) { + int irq = irq_find_mapping(irq_domain, gpio); /* No validity check; all Tegra GPIOs are valid IRQs */ bank = &tegra_gpio_banks[GPIO_BANK(gpio)]; @@ -393,7 +426,7 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev) set_irq_flags(irq, IRQF_VALID); } - for (i = 0; i < ARRAY_SIZE(tegra_gpio_banks); i++) { + for (i = 0; i < tegra_gpio_bank_count; i++) { bank = &tegra_gpio_banks[i]; irq_set_chained_handler(bank->irq, tegra_gpio_irq_handler); diff --git a/drivers/gpu/drm/radeon/cayman_blit_shaders.c b/drivers/gpu/drm/radeon/cayman_blit_shaders.c index 7b4eeb7b4a8c..19a0114d2e3b 100644 --- a/drivers/gpu/drm/radeon/cayman_blit_shaders.c +++ b/drivers/gpu/drm/radeon/cayman_blit_shaders.c @@ -24,6 +24,7 @@ * Alex Deucher <alexander.deucher@amd.com> */ +#include <linux/bug.h> #include <linux/types.h> #include <linux/kernel.h> diff --git a/drivers/gpu/drm/radeon/evergreen_blit_shaders.c b/drivers/gpu/drm/radeon/evergreen_blit_shaders.c index 3a10399e0066..f85c0af115b5 100644 --- a/drivers/gpu/drm/radeon/evergreen_blit_shaders.c +++ b/drivers/gpu/drm/radeon/evergreen_blit_shaders.c @@ -24,6 +24,7 @@ * Alex Deucher <alexander.deucher@amd.com> */ +#include <linux/bug.h> #include <linux/types.h> #include <linux/kernel.h> diff --git a/drivers/gpu/drm/radeon/r600_blit_shaders.c b/drivers/gpu/drm/radeon/r600_blit_shaders.c index 73e2c7c6edbc..34c8b2340f33 100644 --- a/drivers/gpu/drm/radeon/r600_blit_shaders.c +++ b/drivers/gpu/drm/radeon/r600_blit_shaders.c @@ -24,6 +24,7 @@ * Alex Deucher <alexander.deucher@amd.com> */ +#include <linux/bug.h> #include <linux/types.h> #include <linux/kernel.h> diff --git a/drivers/gpu/drm/radeon/si_blit_shaders.c b/drivers/gpu/drm/radeon/si_blit_shaders.c index a7124b483adf..ec415e7dfa4b 100644 --- a/drivers/gpu/drm/radeon/si_blit_shaders.c +++ b/drivers/gpu/drm/radeon/si_blit_shaders.c @@ -25,6 +25,7 @@ */ #include <linux/types.h> +#include <linux/bug.h> #include <linux/kernel.h> const u32 si_default_state[] = diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 990fe19330e6..4da66b4b977c 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1935,6 +1935,16 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) }, { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) }, { HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) }, +#if defined(CONFIG_MOUSE_SYNAPTICS_USB) || defined(CONFIG_MOUSE_SYNAPTICS_USB_MODULE) + { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_INT_TP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_CPAD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_STICK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_WP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_COMP_TP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_WTP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_DPAD) }, +#endif { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) }, @@ -2016,6 +2026,16 @@ static bool hid_ignore(struct hid_device *hdev) if (hdev->product >= USB_DEVICE_ID_LOGITECH_HARMONY_FIRST && hdev->product <= USB_DEVICE_ID_LOGITECH_HARMONY_LAST) return true; + /* + * The Keene FM transmitter USB device has the same USB ID as + * the Logitech AudioHub Speaker, but it should ignore the hid. + * Check if the name is that of the Keene device. + * For reference: the name of the AudioHub is + * "HOLTEK AudioHub Speaker". + */ + if (hdev->product == USB_DEVICE_ID_LOGITECH_AUDIOHUB && + !strcmp(hdev->name, "HOLTEK B-LINK USB Audio ")) + return true; break; case USB_VENDOR_ID_SOUNDGRAPH: if (hdev->product >= USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST && diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 3eb00902ca40..e39aecb1f9f2 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -471,6 +471,7 @@ #define USB_DEVICE_ID_LG_MULTITOUCH 0x0064 #define USB_VENDOR_ID_LOGITECH 0x046d +#define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f @@ -677,6 +678,17 @@ #define USB_DEVICE_ID_SYMBOL_SCANNER_1 0x0800 #define USB_DEVICE_ID_SYMBOL_SCANNER_2 0x1300 +#define USB_VENDOR_ID_SYNAPTICS 0x06cb +#define USB_DEVICE_ID_SYNAPTICS_TP 0x0001 +#define USB_DEVICE_ID_SYNAPTICS_INT_TP 0x0002 +#define USB_DEVICE_ID_SYNAPTICS_CPAD 0x0003 +#define USB_DEVICE_ID_SYNAPTICS_TS 0x0006 +#define USB_DEVICE_ID_SYNAPTICS_STICK 0x0007 +#define USB_DEVICE_ID_SYNAPTICS_WP 0x0008 +#define USB_DEVICE_ID_SYNAPTICS_COMP_TP 0x0009 +#define USB_DEVICE_ID_SYNAPTICS_WTP 0x0010 +#define USB_DEVICE_ID_SYNAPTICS_DPAD 0x0013 + #define USB_VENDOR_ID_THRUSTMASTER 0x044f #define USB_VENDOR_ID_TIVO 0x150a diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 811e6c47e7e6..5b32d56dbb4d 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -648,7 +648,8 @@ config SENSORS_LM90 LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A, Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008, - Winbond/Nuvoton W83L771W/G/AWG/ASG and Philips SA56004 sensor chips. + Winbond/Nuvoton W83L771W/G/AWG/ASG, Philips SA56004, and GMT G781 + sensor chips. This driver can also be built as a module. If so, the module will be called lm90. @@ -812,6 +813,16 @@ config SENSORS_MAX6650 This driver can also be built as a module. If so, the module will be called max6650. +config SENSORS_MCP3021 + tristate "Microchip MCP3021" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for the MCP3021 chip + that is a A/D converter (ADC) with 10-bit resolution. + + This driver can also be built as a module. If so, the module + will be called mcp3021. + config SENSORS_NTC_THERMISTOR tristate "NTC thermistor support" depends on EXPERIMENTAL @@ -1229,18 +1240,19 @@ config SENSORS_W83795 depends on I2C && EXPERIMENTAL help If you say yes here you get support for the Winbond W83795G and - W83795ADG hardware monitoring chip. + W83795ADG hardware monitoring chip, including manual fan speed + control. This driver can also be built as a module. If so, the module will be called w83795. config SENSORS_W83795_FANCTRL - boolean "Include fan control support (DANGEROUS)" + boolean "Include automatic fan control support (DANGEROUS)" depends on SENSORS_W83795 && EXPERIMENTAL default n help - If you say yes here, support for the both manual and automatic - fan control features will be included in the driver. + If you say yes here, support for automatic fan speed control + will be included in the driver. This part of the code wasn't carefully reviewed and tested yet, so enabling this option is strongly discouraged on production @@ -1358,10 +1370,10 @@ config SENSORS_APPLESMC the awesome power of applesmc. config SENSORS_MC13783_ADC - tristate "Freescale MC13783 ADC" - depends on MFD_MC13783 + tristate "Freescale MC13783/MC13892 ADC" + depends on MFD_MC13XXX help - Support for the A/D converter on MC13783 PMIC. + Support for the A/D converter on MC13783 and MC13892 PMIC. if ACPI diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 8251ce8cd035..6d3f11f71815 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -95,6 +95,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o obj-$(CONFIG_SENSORS_MAX6642) += max6642.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o +obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c index 523f8fb9e7d9..b7494af1e4a9 100644 --- a/drivers/hwmon/fam15h_power.c +++ b/drivers/hwmon/fam15h_power.c @@ -60,15 +60,15 @@ static ssize_t show_power(struct device *dev, pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5), REG_TDP_RUNNING_AVERAGE, &val); running_avg_capture = (val >> 4) & 0x3fffff; - running_avg_capture = sign_extend32(running_avg_capture, 22); - running_avg_range = val & 0xf; + running_avg_capture = sign_extend32(running_avg_capture, 21); + running_avg_range = (val & 0xf) + 1; pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5), REG_TDP_LIMIT3, &val); tdp_limit = val >> 16; - curr_pwr_watts = tdp_limit + data->base_tdp - - (s32)(running_avg_capture >> (running_avg_range + 1)); + curr_pwr_watts = (tdp_limit + data->base_tdp) << running_avg_range; + curr_pwr_watts -= running_avg_capture; curr_pwr_watts *= data->tdp_to_watts; /* @@ -78,7 +78,7 @@ static ssize_t show_power(struct device *dev, * scaling factor 1/(2^16). For conversion we use * (10^6)/(2^16) = 15625/(2^10) */ - curr_pwr_watts = (curr_pwr_watts * 15625) >> 10; + curr_pwr_watts = (curr_pwr_watts * 15625) >> (10 + running_avg_range); return sprintf(buf, "%u\n", (unsigned int) curr_pwr_watts); } static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL); diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index 15c05cc83e2c..602a0f0b0de8 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -148,46 +148,9 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END }; #define UPDATE_INTERVAL(max, rate) \ ((1000 << (LM63_MAX_CONVRATE - (rate))) / (max)) -/* - * Functions declaration - */ - -static int lm63_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int lm63_remove(struct i2c_client *client); - -static struct lm63_data *lm63_update_device(struct device *dev); - -static int lm63_detect(struct i2c_client *client, struct i2c_board_info *info); -static void lm63_init_client(struct i2c_client *client); - enum chips { lm63, lm64, lm96163 }; /* - * Driver data (common to all clients) - */ - -static const struct i2c_device_id lm63_id[] = { - { "lm63", lm63 }, - { "lm64", lm64 }, - { "lm96163", lm96163 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, lm63_id); - -static struct i2c_driver lm63_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "lm63", - }, - .probe = lm63_probe, - .remove = lm63_remove, - .id_table = lm63_id, - .detect = lm63_detect, - .address_list = normal_i2c, -}; - -/* * Client data (each client gets its own) */ @@ -242,6 +205,145 @@ static inline int lut_temp_from_reg(struct lm63_data *data, int nr) return data->temp8[nr] * (data->lut_temp_highres ? 500 : 1000); } +static inline int lut_temp_to_reg(struct lm63_data *data, long val) +{ + val -= data->temp2_offset; + if (data->lut_temp_highres) + return DIV_ROUND_CLOSEST(SENSORS_LIMIT(val, 0, 127500), 500); + else + return DIV_ROUND_CLOSEST(SENSORS_LIMIT(val, 0, 127000), 1000); +} + +/* + * Update the lookup table register cache. + * client->update_lock must be held when calling this function. + */ +static void lm63_update_lut(struct i2c_client *client) +{ + struct lm63_data *data = i2c_get_clientdata(client); + int i; + + if (time_after(jiffies, data->lut_last_updated + 5 * HZ) || + !data->lut_valid) { + for (i = 0; i < data->lut_size; i++) { + data->pwm1[1 + i] = i2c_smbus_read_byte_data(client, + LM63_REG_LUT_PWM(i)); + data->temp8[3 + i] = i2c_smbus_read_byte_data(client, + LM63_REG_LUT_TEMP(i)); + } + data->lut_temp_hyst = i2c_smbus_read_byte_data(client, + LM63_REG_LUT_TEMP_HYST); + + data->lut_last_updated = jiffies; + data->lut_valid = 1; + } +} + +static struct lm63_data *lm63_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm63_data *data = i2c_get_clientdata(client); + unsigned long next_update; + + mutex_lock(&data->update_lock); + + next_update = data->last_updated + + msecs_to_jiffies(data->update_interval) + 1; + + if (time_after(jiffies, next_update) || !data->valid) { + if (data->config & 0x04) { /* tachometer enabled */ + /* order matters for fan1_input */ + data->fan[0] = i2c_smbus_read_byte_data(client, + LM63_REG_TACH_COUNT_LSB) & 0xFC; + data->fan[0] |= i2c_smbus_read_byte_data(client, + LM63_REG_TACH_COUNT_MSB) << 8; + data->fan[1] = (i2c_smbus_read_byte_data(client, + LM63_REG_TACH_LIMIT_LSB) & 0xFC) + | (i2c_smbus_read_byte_data(client, + LM63_REG_TACH_LIMIT_MSB) << 8); + } + + data->pwm1_freq = i2c_smbus_read_byte_data(client, + LM63_REG_PWM_FREQ); + if (data->pwm1_freq == 0) + data->pwm1_freq = 1; + data->pwm1[0] = i2c_smbus_read_byte_data(client, + LM63_REG_PWM_VALUE); + + data->temp8[0] = i2c_smbus_read_byte_data(client, + LM63_REG_LOCAL_TEMP); + data->temp8[1] = i2c_smbus_read_byte_data(client, + LM63_REG_LOCAL_HIGH); + + /* order matters for temp2_input */ + data->temp11[0] = i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TEMP_MSB) << 8; + data->temp11[0] |= i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TEMP_LSB); + data->temp11[1] = (i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_LOW_MSB) << 8) + | i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_LOW_LSB); + data->temp11[2] = (i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_HIGH_MSB) << 8) + | i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_HIGH_LSB); + data->temp11[3] = (i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_OFFSET_MSB) << 8) + | i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_OFFSET_LSB); + + if (data->kind == lm96163) + data->temp11u = (i2c_smbus_read_byte_data(client, + LM96163_REG_REMOTE_TEMP_U_MSB) << 8) + | i2c_smbus_read_byte_data(client, + LM96163_REG_REMOTE_TEMP_U_LSB); + + data->temp8[2] = i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TCRIT); + data->temp2_crit_hyst = i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TCRIT_HYST); + + data->alarms = i2c_smbus_read_byte_data(client, + LM63_REG_ALERT_STATUS) & 0x7F; + + data->last_updated = jiffies; + data->valid = 1; + } + + lm63_update_lut(client); + + mutex_unlock(&data->update_lock); + + return data; +} + +/* + * Trip points in the lookup table should be in ascending order for both + * temperatures and PWM output values. + */ +static int lm63_lut_looks_bad(struct i2c_client *client) +{ + struct lm63_data *data = i2c_get_clientdata(client); + int i; + + mutex_lock(&data->update_lock); + lm63_update_lut(client); + + for (i = 1; i < data->lut_size; i++) { + if (data->pwm1[1 + i - 1] > data->pwm1[1 + i] + || data->temp8[3 + i - 1] > data->temp8[3 + i]) { + dev_warn(&client->dev, + "Lookup table doesn't look sane (check entries %d and %d)\n", + i, i + 1); + break; + } + } + mutex_unlock(&data->update_lock); + + return i == data->lut_size ? 0 : 1; +} + /* * Sysfs callback functions and files */ @@ -294,13 +396,16 @@ static ssize_t show_pwm1(struct device *dev, struct device_attribute *devattr, return sprintf(buf, "%d\n", pwm); } -static ssize_t set_pwm1(struct device *dev, struct device_attribute *dummy, +static ssize_t set_pwm1(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct lm63_data *data = i2c_get_clientdata(client); + int nr = attr->index; unsigned long val; int err; + u8 reg; if (!(data->config_fan & 0x20)) /* register is read-only */ return -EPERM; @@ -309,11 +414,13 @@ static ssize_t set_pwm1(struct device *dev, struct device_attribute *dummy, if (err) return err; + reg = nr ? LM63_REG_LUT_PWM(nr - 1) : LM63_REG_PWM_VALUE; val = SENSORS_LIMIT(val, 0, 255); + mutex_lock(&data->update_lock); - data->pwm1[0] = data->pwm_highres ? val : + data->pwm1[nr] = data->pwm_highres ? val : (val * data->pwm1_freq * 2 + 127) / 255; - i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1[0]); + i2c_smbus_write_byte_data(client, reg, data->pwm1[nr]); mutex_unlock(&data->update_lock); return count; } @@ -325,6 +432,41 @@ static ssize_t show_pwm1_enable(struct device *dev, return sprintf(buf, "%d\n", data->config_fan & 0x20 ? 1 : 2); } +static ssize_t set_pwm1_enable(struct device *dev, + struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm63_data *data = i2c_get_clientdata(client); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + if (val < 1 || val > 2) + return -EINVAL; + + /* + * Only let the user switch to automatic mode if the lookup table + * looks sane. + */ + if (val == 2 && lm63_lut_looks_bad(client)) + return -EPERM; + + mutex_lock(&data->update_lock); + data->config_fan = i2c_smbus_read_byte_data(client, + LM63_REG_CONFIG_FAN); + if (val == 1) + data->config_fan |= 0x20; + else + data->config_fan &= ~0x20; + i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN, + data->config_fan); + mutex_unlock(&data->update_lock); + return count; +} + /* * There are 8bit registers for both local(temp1) and remote(temp2) sensor. * For remote sensor registers temp2_offset has to be considered, @@ -367,23 +509,31 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, struct i2c_client *client = to_i2c_client(dev); struct lm63_data *data = i2c_get_clientdata(client); int nr = attr->index; - int reg = nr == 2 ? LM63_REG_REMOTE_TCRIT : LM63_REG_LOCAL_HIGH; long val; int err; int temp; + u8 reg; err = kstrtol(buf, 10, &val); if (err) return err; mutex_lock(&data->update_lock); - if (nr == 2) { + switch (nr) { + case 2: + reg = LM63_REG_REMOTE_TCRIT; if (data->remote_unsigned) temp = TEMP8U_TO_REG(val - data->temp2_offset); else temp = TEMP8_TO_REG(val - data->temp2_offset); - } else { + break; + case 1: + reg = LM63_REG_LOCAL_HIGH; temp = TEMP8_TO_REG(val); + break; + default: /* lookup table */ + reg = LM63_REG_LUT_TEMP(nr - 3); + temp = lut_temp_to_reg(data, val); } data->temp8[nr] = temp; i2c_smbus_write_byte_data(client, reg, temp); @@ -613,65 +763,78 @@ static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan, set_fan, 1); static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0); -static DEVICE_ATTR(pwm1_enable, S_IRUGO, show_pwm1_enable, NULL); -static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IRUGO, show_pwm1, NULL, 1); -static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IRUGO, - show_lut_temp, NULL, 3); +static DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + show_pwm1_enable, set_pwm1_enable); +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 1); +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 3); static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 3); -static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IRUGO, show_pwm1, NULL, 2); -static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp, S_IRUGO, - show_lut_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 2); +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 4); static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 4); -static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IRUGO, show_pwm1, NULL, 3); -static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp, S_IRUGO, - show_lut_temp, NULL, 5); +static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 3); +static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 5); static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 5); -static SENSOR_DEVICE_ATTR(pwm1_auto_point4_pwm, S_IRUGO, show_pwm1, NULL, 4); -static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp, S_IRUGO, - show_lut_temp, NULL, 6); +static SENSOR_DEVICE_ATTR(pwm1_auto_point4_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 4); +static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 6); static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 6); -static SENSOR_DEVICE_ATTR(pwm1_auto_point5_pwm, S_IRUGO, show_pwm1, NULL, 5); -static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp, S_IRUGO, - show_lut_temp, NULL, 7); +static SENSOR_DEVICE_ATTR(pwm1_auto_point5_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 5); +static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 7); static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 7); -static SENSOR_DEVICE_ATTR(pwm1_auto_point6_pwm, S_IRUGO, show_pwm1, NULL, 6); -static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp, S_IRUGO, - show_lut_temp, NULL, 8); +static SENSOR_DEVICE_ATTR(pwm1_auto_point6_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 6); +static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 8); static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 8); -static SENSOR_DEVICE_ATTR(pwm1_auto_point7_pwm, S_IRUGO, show_pwm1, NULL, 7); -static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp, S_IRUGO, - show_lut_temp, NULL, 9); +static SENSOR_DEVICE_ATTR(pwm1_auto_point7_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 7); +static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 9); static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 9); -static SENSOR_DEVICE_ATTR(pwm1_auto_point8_pwm, S_IRUGO, show_pwm1, NULL, 8); -static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp, S_IRUGO, - show_lut_temp, NULL, 10); +static SENSOR_DEVICE_ATTR(pwm1_auto_point8_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 8); +static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 10); static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 10); -static SENSOR_DEVICE_ATTR(pwm1_auto_point9_pwm, S_IRUGO, show_pwm1, NULL, 9); -static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp, S_IRUGO, - show_lut_temp, NULL, 11); +static SENSOR_DEVICE_ATTR(pwm1_auto_point9_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 9); +static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 11); static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 11); -static SENSOR_DEVICE_ATTR(pwm1_auto_point10_pwm, S_IRUGO, show_pwm1, NULL, 10); -static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp, S_IRUGO, - show_lut_temp, NULL, 12); +static SENSOR_DEVICE_ATTR(pwm1_auto_point10_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 10); +static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 12); static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 12); -static SENSOR_DEVICE_ATTR(pwm1_auto_point11_pwm, S_IRUGO, show_pwm1, NULL, 11); -static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp, S_IRUGO, - show_lut_temp, NULL, 13); +static SENSOR_DEVICE_ATTR(pwm1_auto_point11_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 11); +static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 13); static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 13); -static SENSOR_DEVICE_ATTR(pwm1_auto_point12_pwm, S_IRUGO, show_pwm1, NULL, 12); -static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp, S_IRUGO, - show_lut_temp, NULL, 14); +static SENSOR_DEVICE_ATTR(pwm1_auto_point12_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 12); +static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 14); static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 14); @@ -817,28 +980,25 @@ static const struct attribute_group lm63_group_fan1 = { */ /* Return 0 if detection is successful, -ENODEV otherwise */ -static int lm63_detect(struct i2c_client *new_client, +static int lm63_detect(struct i2c_client *client, struct i2c_board_info *info) { - struct i2c_adapter *adapter = new_client->adapter; + struct i2c_adapter *adapter = client->adapter; u8 man_id, chip_id, reg_config1, reg_config2; u8 reg_alert_status, reg_alert_mask; - int address = new_client->addr; + int address = client->addr; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - man_id = i2c_smbus_read_byte_data(new_client, LM63_REG_MAN_ID); - chip_id = i2c_smbus_read_byte_data(new_client, LM63_REG_CHIP_ID); + man_id = i2c_smbus_read_byte_data(client, LM63_REG_MAN_ID); + chip_id = i2c_smbus_read_byte_data(client, LM63_REG_CHIP_ID); - reg_config1 = i2c_smbus_read_byte_data(new_client, - LM63_REG_CONFIG1); - reg_config2 = i2c_smbus_read_byte_data(new_client, - LM63_REG_CONFIG2); - reg_alert_status = i2c_smbus_read_byte_data(new_client, + reg_config1 = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1); + reg_config2 = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG2); + reg_alert_status = i2c_smbus_read_byte_data(client, LM63_REG_ALERT_STATUS); - reg_alert_mask = i2c_smbus_read_byte_data(new_client, - LM63_REG_ALERT_MASK); + reg_alert_mask = i2c_smbus_read_byte_data(client, LM63_REG_ALERT_MASK); if (man_id != 0x01 /* National Semiconductor */ || (reg_config1 & 0x18) != 0x00 @@ -863,74 +1023,6 @@ static int lm63_detect(struct i2c_client *new_client, return 0; } -static int lm63_probe(struct i2c_client *new_client, - const struct i2c_device_id *id) -{ - struct lm63_data *data; - int err; - - data = kzalloc(sizeof(struct lm63_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } - - i2c_set_clientdata(new_client, data); - data->valid = 0; - mutex_init(&data->update_lock); - - /* Set the device type */ - data->kind = id->driver_data; - if (data->kind == lm64) - data->temp2_offset = 16000; - - /* Initialize chip */ - lm63_init_client(new_client); - - /* Register sysfs hooks */ - err = sysfs_create_group(&new_client->dev.kobj, &lm63_group); - if (err) - goto exit_free; - if (data->config & 0x04) { /* tachometer enabled */ - err = sysfs_create_group(&new_client->dev.kobj, - &lm63_group_fan1); - if (err) - goto exit_remove_files; - } - if (data->kind == lm96163) { - err = device_create_file(&new_client->dev, - &dev_attr_temp2_type); - if (err) - goto exit_remove_files; - - err = sysfs_create_group(&new_client->dev.kobj, - &lm63_group_extra_lut); - if (err) - goto exit_remove_files; - } - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - -exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &lm63_group); - sysfs_remove_group(&new_client->dev.kobj, &lm63_group_fan1); - if (data->kind == lm96163) { - device_remove_file(&new_client->dev, &dev_attr_temp2_type); - sysfs_remove_group(&new_client->dev.kobj, - &lm63_group_extra_lut); - } -exit_free: - kfree(data); -exit: - return err; -} - /* * Ideally we shouldn't have to initialize anything, since the BIOS * should have taken care of everything @@ -1010,114 +1102,110 @@ static void lm63_init_client(struct i2c_client *client) (data->config_fan & 0x20) ? "manual" : "auto"); } -static int lm63_remove(struct i2c_client *client) +static int lm63_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - struct lm63_data *data = i2c_get_clientdata(client); + struct lm63_data *data; + int err; - hwmon_device_unregister(data->hwmon_dev); + data = kzalloc(sizeof(struct lm63_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->valid = 0; + mutex_init(&data->update_lock); + + /* Set the device type */ + data->kind = id->driver_data; + if (data->kind == lm64) + data->temp2_offset = 16000; + + /* Initialize chip */ + lm63_init_client(client); + + /* Register sysfs hooks */ + err = sysfs_create_group(&client->dev.kobj, &lm63_group); + if (err) + goto exit_free; + if (data->config & 0x04) { /* tachometer enabled */ + err = sysfs_create_group(&client->dev.kobj, &lm63_group_fan1); + if (err) + goto exit_remove_files; + } + if (data->kind == lm96163) { + err = device_create_file(&client->dev, &dev_attr_temp2_type); + if (err) + goto exit_remove_files; + + err = sysfs_create_group(&client->dev.kobj, + &lm63_group_extra_lut); + if (err) + goto exit_remove_files; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove_files; + } + + return 0; + +exit_remove_files: sysfs_remove_group(&client->dev.kobj, &lm63_group); sysfs_remove_group(&client->dev.kobj, &lm63_group_fan1); if (data->kind == lm96163) { device_remove_file(&client->dev, &dev_attr_temp2_type); sysfs_remove_group(&client->dev.kobj, &lm63_group_extra_lut); } - +exit_free: kfree(data); - return 0; +exit: + return err; } -static struct lm63_data *lm63_update_device(struct device *dev) +static int lm63_remove(struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(dev); struct lm63_data *data = i2c_get_clientdata(client); - unsigned long next_update; - int i; - - mutex_lock(&data->update_lock); - - next_update = data->last_updated - + msecs_to_jiffies(data->update_interval) + 1; - - if (time_after(jiffies, next_update) || !data->valid) { - if (data->config & 0x04) { /* tachometer enabled */ - /* order matters for fan1_input */ - data->fan[0] = i2c_smbus_read_byte_data(client, - LM63_REG_TACH_COUNT_LSB) & 0xFC; - data->fan[0] |= i2c_smbus_read_byte_data(client, - LM63_REG_TACH_COUNT_MSB) << 8; - data->fan[1] = (i2c_smbus_read_byte_data(client, - LM63_REG_TACH_LIMIT_LSB) & 0xFC) - | (i2c_smbus_read_byte_data(client, - LM63_REG_TACH_LIMIT_MSB) << 8); - } - - data->pwm1_freq = i2c_smbus_read_byte_data(client, - LM63_REG_PWM_FREQ); - if (data->pwm1_freq == 0) - data->pwm1_freq = 1; - data->pwm1[0] = i2c_smbus_read_byte_data(client, - LM63_REG_PWM_VALUE); - - data->temp8[0] = i2c_smbus_read_byte_data(client, - LM63_REG_LOCAL_TEMP); - data->temp8[1] = i2c_smbus_read_byte_data(client, - LM63_REG_LOCAL_HIGH); - - /* order matters for temp2_input */ - data->temp11[0] = i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TEMP_MSB) << 8; - data->temp11[0] |= i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TEMP_LSB); - data->temp11[1] = (i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_LOW_MSB) << 8) - | i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_LOW_LSB); - data->temp11[2] = (i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_HIGH_MSB) << 8) - | i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_HIGH_LSB); - data->temp11[3] = (i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_OFFSET_MSB) << 8) - | i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_OFFSET_LSB); - - if (data->kind == lm96163) - data->temp11u = (i2c_smbus_read_byte_data(client, - LM96163_REG_REMOTE_TEMP_U_MSB) << 8) - | i2c_smbus_read_byte_data(client, - LM96163_REG_REMOTE_TEMP_U_LSB); - - data->temp8[2] = i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TCRIT); - data->temp2_crit_hyst = i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TCRIT_HYST); - - data->alarms = i2c_smbus_read_byte_data(client, - LM63_REG_ALERT_STATUS) & 0x7F; - data->last_updated = jiffies; - data->valid = 1; + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &lm63_group); + sysfs_remove_group(&client->dev.kobj, &lm63_group_fan1); + if (data->kind == lm96163) { + device_remove_file(&client->dev, &dev_attr_temp2_type); + sysfs_remove_group(&client->dev.kobj, &lm63_group_extra_lut); } - if (time_after(jiffies, data->lut_last_updated + 5 * HZ) || - !data->lut_valid) { - for (i = 0; i < data->lut_size; i++) { - data->pwm1[1 + i] = i2c_smbus_read_byte_data(client, - LM63_REG_LUT_PWM(i)); - data->temp8[3 + i] = i2c_smbus_read_byte_data(client, - LM63_REG_LUT_TEMP(i)); - } - data->lut_temp_hyst = i2c_smbus_read_byte_data(client, - LM63_REG_LUT_TEMP_HYST); + kfree(data); + return 0; +} - data->lut_last_updated = jiffies; - data->lut_valid = 1; - } +/* + * Driver data (common to all clients) + */ - mutex_unlock(&data->update_lock); +static const struct i2c_device_id lm63_id[] = { + { "lm63", lm63 }, + { "lm64", lm64 }, + { "lm96163", lm96163 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm63_id); - return data; -} +static struct i2c_driver lm63_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "lm63", + }, + .probe = lm63_probe, + .remove = lm63_remove, + .id_table = lm63_id, + .detect = lm63_detect, + .address_list = normal_i2c, +}; module_i2c_driver(lm63_driver); diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 248f2b40dfaf..22b14a68e35e 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -57,6 +57,9 @@ * This driver also supports the SA56004 from Philips. This device is * pin-compatible with the LM86, the ED/EDP parts are also address-compatible. * + * This driver also supports the G781 from GMT. This device is compatible + * with the ADM1032. + * * Since the LM90 was the first chipset supported by this driver, most * comments will refer to this chipset, but are actually general and * concern all supported chipsets, unless mentioned otherwise. @@ -107,7 +110,7 @@ static const unsigned short normal_i2c[] = { 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, - max6646, w83l771, max6696, sa56004 }; + max6646, w83l771, max6696, sa56004, g781 }; /* * The LM90 registers @@ -184,6 +187,7 @@ static const struct i2c_device_id lm90_id[] = { { "adm1032", adm1032 }, { "adt7461", adt7461 }, { "adt7461a", adt7461 }, + { "g781", g781 }, { "lm90", lm90 }, { "lm86", lm86 }, { "lm89", lm86 }, @@ -229,6 +233,12 @@ static const struct lm90_params lm90_params[] = { .alert_alarms = 0x7c, .max_convrate = 10, }, + [g781] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_BROKEN_ALERT, + .alert_alarms = 0x7c, + .max_convrate = 8, + }, [lm86] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, .alert_alarms = 0x7b, @@ -308,22 +318,24 @@ struct lm90_data { /* registers values */ s8 temp8[8]; /* 0: local low limit - 1: local high limit - 2: local critical limit - 3: remote critical limit - 4: local emergency limit (max6659 and max6695/96) - 5: remote emergency limit (max6659 and max6695/96) - 6: remote 2 critical limit (max6695/96 only) - 7: remote 2 emergency limit (max6695/96 only) */ + * 1: local high limit + * 2: local critical limit + * 3: remote critical limit + * 4: local emergency limit (max6659 and max6695/96) + * 5: remote emergency limit (max6659 and max6695/96) + * 6: remote 2 critical limit (max6695/96 only) + * 7: remote 2 emergency limit (max6695/96 only) + */ s16 temp11[8]; /* 0: remote input - 1: remote low limit - 2: remote high limit - 3: remote offset (except max6646, max6657/58/59, - and max6695/96) - 4: local input - 5: remote 2 input (max6695/96 only) - 6: remote 2 low limit (max6695/96 only) - 7: remote 2 high limit (ma6695/96 only) */ + * 1: remote low limit + * 2: remote high limit + * 3: remote offset (except max6646, max6657/58/59, + * and max6695/96) + * 4: local input + * 5: remote 2 input (max6695/96 only) + * 6: remote 2 low limit (max6695/96 only) + * 7: remote 2 high limit (max6695/96 only) + */ u8 temp_hyst; u16 alarms; /* bitvector (upper 8 bits for max6695/96) */ }; @@ -533,8 +545,10 @@ static struct lm90_data *lm90_update_device(struct device *dev) data->alarms |= alarms << 8; } - /* Re-enable ALERT# output if it was originally enabled and - * relevant alarms are all clear */ + /* + * Re-enable ALERT# output if it was originally enabled and + * relevant alarms are all clear + */ if ((data->config_orig & 0x80) == 0 && (data->alarms & data->alert_alarms) == 0) { u8 config; @@ -1162,8 +1176,10 @@ static int lm90_detect(struct i2c_client *client, && (config1 & 0x3F) == 0x00 && convrate <= 0x0A) { name = "adm1032"; - /* The ADM1032 supports PEC, but only if combined - transactions are not used. */ + /* + * The ADM1032 supports PEC, but only if combined + * transactions are not used. + */ if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) info->flags |= I2C_CLIENT_PEC; @@ -1283,6 +1299,13 @@ static int lm90_detect(struct i2c_client *client, && convrate <= 0x09) { name = "sa56004"; } + } else + if ((address == 0x4C || address == 0x4D) + && man_id == 0x47) { /* GMT */ + if (chip_id == 0x01 /* G781 */ + && (config1 & 0x3F) == 0x00 + && convrate <= 0x08) + name = "g781"; } if (!name) { /* identification failed */ @@ -1313,6 +1336,15 @@ static void lm90_remove_files(struct i2c_client *client, struct lm90_data *data) sysfs_remove_group(&dev->kobj, &lm90_group); } +static void lm90_restore_conf(struct i2c_client *client, struct lm90_data *data) +{ + /* Restore initial configuration */ + i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, + data->convrate_orig); + i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, + data->config_orig); +} + static void lm90_init_client(struct i2c_client *client) { u8 config, convrate; @@ -1382,8 +1414,10 @@ static int lm90_probe(struct i2c_client *client, client->flags &= ~I2C_CLIENT_PEC; } - /* Different devices have different alarm bits triggering the - * ALERT# output */ + /* + * Different devices have different alarm bits triggering the + * ALERT# output + */ data->alert_alarms = lm90_params[data->kind].alert_alarms; /* Set chip capabilities */ @@ -1399,7 +1433,7 @@ static int lm90_probe(struct i2c_client *client, /* Register sysfs hooks */ err = sysfs_create_group(&dev->kobj, &lm90_group); if (err) - goto exit_free; + goto exit_restore; if (client->flags & I2C_CLIENT_PEC) { err = device_create_file(dev, &dev_attr_pec); if (err) @@ -1438,7 +1472,8 @@ static int lm90_probe(struct i2c_client *client, exit_remove_files: lm90_remove_files(client, data); -exit_free: +exit_restore: + lm90_restore_conf(client, data); kfree(data); exit: return err; @@ -1450,12 +1485,7 @@ static int lm90_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); lm90_remove_files(client, data); - - /* Restore initial configuration */ - i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, - data->convrate_orig); - i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, - data->config_orig); + lm90_restore_conf(client, data); kfree(data); return 0; @@ -1488,9 +1518,11 @@ static void lm90_alert(struct i2c_client *client, unsigned int flag) dev_warn(&client->dev, "temp%d out of range, please check!\n", 3); - /* Disable ALERT# output, because these chips don't implement - SMBus alert correctly; they should only hold the alert line - low briefly. */ + /* + * Disable ALERT# output, because these chips don't implement + * SMBus alert correctly; they should only hold the alert line + * low briefly. + */ if ((data->flags & LM90_HAVE_BROKEN_ALERT) && (alarms & data->alert_alarms)) { dev_dbg(&client->dev, "Disabling ALERT#\n"); diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c index ef65ab56b094..6c6b240a782e 100644 --- a/drivers/hwmon/mc13783-adc.c +++ b/drivers/hwmon/mc13783-adc.c @@ -1,5 +1,5 @@ /* - * Driver for the Freescale Semiconductor MC13783 adc. + * Driver for the ADC on Freescale Semiconductor MC13783 and MC13892 PMICs. * * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright (C) 2009 Sascha Hauer, Pengutronix @@ -18,7 +18,7 @@ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <linux/mfd/mc13783.h> +#include <linux/mfd/mc13xxx.h> #include <linux/platform_device.h> #include <linux/hwmon-sysfs.h> #include <linux/kernel.h> @@ -28,24 +28,30 @@ #include <linux/init.h> #include <linux/err.h> -#define MC13783_ADC_NAME "mc13783-adc" +#define DRIVER_NAME "mc13783-adc" + +/* platform device id driver data */ +#define MC13783_ADC_16CHANS 1 +#define MC13783_ADC_BPDIV2 2 struct mc13783_adc_priv { struct mc13xxx *mc13xxx; struct device *hwmon_dev; + char name[10]; }; static ssize_t mc13783_adc_show_name(struct device *dev, struct device_attribute *devattr, char *buf) { - return sprintf(buf, "mc13783_adc\n"); + struct mc13783_adc_priv *priv = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", priv->name); } static int mc13783_adc_read(struct device *dev, struct device_attribute *devattr, unsigned int *val) { - struct platform_device *pdev = to_platform_device(dev); - struct mc13783_adc_priv *priv = platform_get_drvdata(pdev); + struct mc13783_adc_priv *priv = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); unsigned int channel = attr->index; unsigned int sample[4]; @@ -68,16 +74,21 @@ static ssize_t mc13783_adc_read_bp(struct device *dev, struct device_attribute *devattr, char *buf) { unsigned val; + struct platform_device *pdev = to_platform_device(dev); + kernel_ulong_t driver_data = platform_get_device_id(pdev)->driver_data; int ret = mc13783_adc_read(dev, devattr, &val); if (ret) return ret; - /* - * BP (channel 2) reports with offset 2.4V to the actual value to fit - * the input range of the ADC. unit = 2.25mV = 9/4 mV. - */ - val = DIV_ROUND_CLOSEST(val * 9, 4) + 2400; + if (driver_data & MC13783_ADC_BPDIV2) + val = DIV_ROUND_CLOSEST(val * 9, 2); + else + /* + * BP (channel 2) reports with offset 2.4V to the actual value + * to fit the input range of the ADC. unit = 2.25mV = 9/4 mV. + */ + val = DIV_ROUND_CLOSEST(val * 9, 4) + 2400; return sprintf(buf, "%u\n", val); } @@ -114,12 +125,21 @@ static SENSOR_DEVICE_ATTR(in13_input, S_IRUGO, mc13783_adc_read_gp, NULL, 13); static SENSOR_DEVICE_ATTR(in14_input, S_IRUGO, mc13783_adc_read_gp, NULL, 14); static SENSOR_DEVICE_ATTR(in15_input, S_IRUGO, mc13783_adc_read_gp, NULL, 15); -static struct attribute *mc13783_attr[] = { +static struct attribute *mc13783_attr_base[] = { &dev_attr_name.attr, &sensor_dev_attr_in2_input.dev_attr.attr, &sensor_dev_attr_in5_input.dev_attr.attr, &sensor_dev_attr_in6_input.dev_attr.attr, &sensor_dev_attr_in7_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group mc13783_group_base = { + .attrs = mc13783_attr_base, +}; + +/* these are only used if MC13783_ADC_16CHANS is provided in driver data */ +static struct attribute *mc13783_attr_16chans[] = { &sensor_dev_attr_in8_input.dev_attr.attr, &sensor_dev_attr_in9_input.dev_attr.attr, &sensor_dev_attr_in10_input.dev_attr.attr, @@ -127,8 +147,8 @@ static struct attribute *mc13783_attr[] = { NULL }; -static const struct attribute_group mc13783_group = { - .attrs = mc13783_attr, +static const struct attribute_group mc13783_group_16chans = { + .attrs = mc13783_attr_16chans, }; /* last four channels may be occupied by the touchscreen */ @@ -156,24 +176,37 @@ static int __init mc13783_adc_probe(struct platform_device *pdev) { struct mc13783_adc_priv *priv; int ret; + const struct platform_device_id *id = platform_get_device_id(pdev); + char *dash; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->mc13xxx = dev_get_drvdata(pdev->dev.parent); + snprintf(priv->name, ARRAY_SIZE(priv->name), "%s", id->name); + dash = strchr(priv->name, '-'); + if (dash) + *dash = '\0'; platform_set_drvdata(pdev, priv); /* Register sysfs hooks */ - ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group); + ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group_base); if (ret) - goto out_err_create1; + goto out_err_create_base; + + if (id->driver_data & MC13783_ADC_16CHANS) { + ret = sysfs_create_group(&pdev->dev.kobj, + &mc13783_group_16chans); + if (ret) + goto out_err_create_16chans; + } if (!mc13783_adc_use_touchscreen(pdev)) { ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group_ts); if (ret) - goto out_err_create2; + goto out_err_create_ts; } priv->hwmon_dev = hwmon_device_register(&pdev->dev); @@ -184,17 +217,20 @@ static int __init mc13783_adc_probe(struct platform_device *pdev) goto out_err_register; } - return 0; out_err_register: if (!mc13783_adc_use_touchscreen(pdev)) sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts); -out_err_create2: +out_err_create_ts: - sysfs_remove_group(&pdev->dev.kobj, &mc13783_group); -out_err_create1: + if (id->driver_data & MC13783_ADC_16CHANS) + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_16chans); +out_err_create_16chans: + + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_base); +out_err_create_base: platform_set_drvdata(pdev, NULL); kfree(priv); @@ -205,13 +241,17 @@ out_err_create1: static int __devexit mc13783_adc_remove(struct platform_device *pdev) { struct mc13783_adc_priv *priv = platform_get_drvdata(pdev); + kernel_ulong_t driver_data = platform_get_device_id(pdev)->driver_data; hwmon_device_unregister(priv->hwmon_dev); if (!mc13783_adc_use_touchscreen(pdev)) sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts); - sysfs_remove_group(&pdev->dev.kobj, &mc13783_group); + if (driver_data & MC13783_ADC_16CHANS) + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_16chans); + + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_base); platform_set_drvdata(pdev, NULL); kfree(priv); @@ -219,12 +259,26 @@ static int __devexit mc13783_adc_remove(struct platform_device *pdev) return 0; } +static const struct platform_device_id mc13783_adc_idtable[] = { + { + .name = "mc13783-adc", + .driver_data = MC13783_ADC_16CHANS, + }, { + .name = "mc13892-adc", + .driver_data = MC13783_ADC_BPDIV2, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, mc13783_adc_idtable); + static struct platform_driver mc13783_adc_driver = { - .remove = __devexit_p(mc13783_adc_remove), + .remove = __devexit_p(mc13783_adc_remove), .driver = { .owner = THIS_MODULE, - .name = MC13783_ADC_NAME, + .name = DRIVER_NAME, }, + .id_table = mc13783_adc_idtable, }; static int __init mc13783_adc_init(void) @@ -243,4 +297,3 @@ module_exit(mc13783_adc_exit); MODULE_DESCRIPTION("MC13783 ADC driver"); MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" MC13783_ADC_NAME); diff --git a/drivers/hwmon/mcp3021.c b/drivers/hwmon/mcp3021.c new file mode 100644 index 000000000000..d0afc0cd3ff4 --- /dev/null +++ b/drivers/hwmon/mcp3021.c @@ -0,0 +1,171 @@ +/* + * mcp3021.c - driver for the Microchip MCP3021 chip + * + * Copyright (C) 2008-2009, 2012 Freescale Semiconductor, Inc. + * Author: Mingkai Hu <Mingkai.hu@freescale.com> + * + * This driver export the value of analog input voltage to sysfs, the + * voltage unit is mV. Through the sysfs interface, lm-sensors tool + * can also display the input voltage. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/hwmon.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/device.h> + +/* Vdd info */ +#define MCP3021_VDD_MAX 5500 +#define MCP3021_VDD_MIN 2700 +#define MCP3021_VDD_REF 3300 + +/* output format */ +#define MCP3021_SAR_SHIFT 2 +#define MCP3021_SAR_MASK 0x3ff + +#define MCP3021_OUTPUT_RES 10 /* 10-bit resolution */ +#define MCP3021_OUTPUT_SCALE 4 + +/* + * Client data (each client gets its own) + */ +struct mcp3021_data { + struct device *hwmon_dev; + u32 vdd; /* device power supply */ +}; + +static int mcp3021_read16(struct i2c_client *client) +{ + int ret; + u16 reg; + __be16 buf; + + ret = i2c_master_recv(client, (char *)&buf, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + + /* The output code of the MCP3021 is transmitted with MSB first. */ + reg = be16_to_cpu(buf); + + /* + * The ten-bit output code is composed of the lower 4-bit of the + * first byte and the upper 6-bit of the second byte. + */ + reg = (reg >> MCP3021_SAR_SHIFT) & MCP3021_SAR_MASK; + + return reg; +} + +static inline u16 volts_from_reg(u16 vdd, u16 val) +{ + if (val == 0) + return 0; + + val = val * MCP3021_OUTPUT_SCALE - MCP3021_OUTPUT_SCALE / 2; + + return val * DIV_ROUND_CLOSEST(vdd, + (1 << MCP3021_OUTPUT_RES) * MCP3021_OUTPUT_SCALE); +} + +static ssize_t show_in_input(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mcp3021_data *data = i2c_get_clientdata(client); + int reg, in_input; + + reg = mcp3021_read16(client); + if (reg < 0) + return reg; + + in_input = volts_from_reg(data->vdd, reg); + return sprintf(buf, "%d\n", in_input); +} + +static DEVICE_ATTR(in0_input, S_IRUGO, show_in_input, NULL); + +static int mcp3021_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err; + struct mcp3021_data *data = NULL; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + data = kzalloc(sizeof(struct mcp3021_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + + if (client->dev.platform_data) { + data->vdd = *(u32 *)client->dev.platform_data; + if (data->vdd > MCP3021_VDD_MAX || + data->vdd < MCP3021_VDD_MIN) { + err = -EINVAL; + goto exit_free; + } + } else + data->vdd = MCP3021_VDD_REF; + + err = sysfs_create_file(&client->dev.kobj, &dev_attr_in0_input.attr); + if (err) + goto exit_free; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + return 0; + +exit_remove: + sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr); +exit_free: + kfree(data); + return err; +} + +static int mcp3021_remove(struct i2c_client *client) +{ + struct mcp3021_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr); + kfree(data); + + return 0; +} + +static const struct i2c_device_id mcp3021_id[] = { + { "mcp3021", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcp3021_id); + +static struct i2c_driver mcp3021_driver = { + .driver = { + .name = "mcp3021", + }, + .probe = mcp3021_probe, + .remove = mcp3021_remove, + .id_table = mcp3021_id, +}; + +module_i2c_driver(mcp3021_driver); + +MODULE_AUTHOR("Mingkai Hu <Mingkai.hu@freescale.com>"); +MODULE_DESCRIPTION("Microchip MCP3021 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index deb12c982800..d887cb3b72e8 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -72,8 +72,10 @@ MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended"); #define TEMP_CRIT_HYST 2 #define TEMP_WARN 3 #define TEMP_WARN_HYST 4 -/* only crit and crit_hyst affect real-time alarm status - * current crit crit_hyst warn warn_hyst */ +/* + * only crit and crit_hyst affect real-time alarm status + * current crit crit_hyst warn warn_hyst + */ static const u16 W83795_REG_TEMP[][5] = { {0x21, 0x96, 0x97, 0x98, 0x99}, /* TD1/TR1 */ {0x22, 0x9a, 0x9b, 0x9c, 0x9d}, /* TD2/TR2 */ @@ -354,26 +356,34 @@ struct w83795_data { u8 temp_mode; /* Bit vector, 0 = TR, 1 = TD */ u8 temp_src[3]; /* Register value */ - u8 enable_dts; /* Enable PECI and SB-TSI, + u8 enable_dts; /* + * Enable PECI and SB-TSI, * bit 0: =1 enable, =0 disable, - * bit 1: =1 AMD SB-TSI, =0 Intel PECI */ + * bit 1: =1 AMD SB-TSI, =0 Intel PECI + */ u8 has_dts; /* Enable monitor DTS temp */ s8 dts[8]; /* Register value */ u8 dts_read_vrlsb[8]; /* Register value */ s8 dts_ext[4]; /* Register value */ - u8 has_pwm; /* 795g supports 8 pwm, 795adg only supports 2, + u8 has_pwm; /* + * 795g supports 8 pwm, 795adg only supports 2, * no config register, only affected by chip - * type */ - u8 pwm[8][5]; /* Register value, output, freq, start, - * non stop, stop time */ + * type + */ + u8 pwm[8][5]; /* + * Register value, output, freq, start, + * non stop, stop time + */ u16 clkin; /* CLKIN frequency in kHz */ u8 pwm_fcms[2]; /* Register value */ u8 pwm_tfmr[6]; /* Register value */ u8 pwm_fomc; /* Register value */ - u16 target_speed[8]; /* Register value, target speed for speed - * cruise */ + u16 target_speed[8]; /* + * Register value, target speed for speed + * cruise + */ u8 tol_speed; /* tolerance of target speed */ u8 pwm_temp[6][4]; /* TTTI, CTFS, HCT, HOT */ u8 sf4_reg[6][2][7]; /* 6 temp, temp/dcpwm, 7 registers */ @@ -482,8 +492,10 @@ static void w83795_update_limits(struct i2c_client *client) /* Read the fan limits */ lsb = 0; /* Silent false gcc warning */ for (i = 0; i < ARRAY_SIZE(data->fan); i++) { - /* Each register contains LSB for 2 fans, but we want to - * read it only once to save time */ + /* + * Each register contains LSB for 2 fans, but we want to + * read it only once to save time + */ if ((i & 1) == 0 && (data->has_fan & (3 << i))) lsb = w83795_read(client, W83795_REG_FAN_MIN_LSB(i)); @@ -665,9 +677,11 @@ static struct w83795_data *w83795_update_device(struct device *dev) w83795_read(client, W83795_REG_PWM(i, PWM_OUTPUT)); } - /* Update intrusion and alarms + /* + * Update intrusion and alarms * It is important to read intrusion first, because reading from - * register SMI STS6 clears the interrupt status temporarily. */ + * register SMI STS6 clears the interrupt status temporarily. + */ tmp = w83795_read(client, W83795_REG_ALARM_CTRL); /* Switch to interrupt status for intrusion if needed */ if (tmp & ALARM_CTRL_RTSACS) @@ -929,6 +943,14 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr, if (val < 1 || val > 2) return -EINVAL; +#ifndef CONFIG_SENSORS_W83795_FANCTRL + if (val > 1) { + dev_warn(dev, "Automatic fan speed control support disabled\n"); + dev_warn(dev, "Build with CONFIG_SENSORS_W83795_FANCTRL=y if you want it\n"); + return -EOPNOTSUPP; + } +#endif + mutex_lock(&data->update_lock); switch (val) { case 1: @@ -1595,8 +1617,10 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, #define NOT_USED -1 -/* Don't change the attribute order, _max, _min and _beep are accessed by index - * somewhere else in the code */ +/* + * Don't change the attribute order, _max, _min and _beep are accessed by index + * somewhere else in the code + */ #define SENSOR_ATTR_IN(index) { \ SENSOR_ATTR_2(in##index##_input, S_IRUGO, show_in, NULL, \ IN_READ, index), \ @@ -1610,8 +1634,10 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, show_alarm_beep, store_beep, BEEP_ENABLE, \ index + ((index > 14) ? 1 : 0)) } -/* Don't change the attribute order, _beep is accessed by index - * somewhere else in the code */ +/* + * Don't change the attribute order, _beep is accessed by index + * somewhere else in the code + */ #define SENSOR_ATTR_FAN(index) { \ SENSOR_ATTR_2(fan##index##_input, S_IRUGO, show_fan, \ NULL, FAN_INPUT, index - 1), \ @@ -1625,23 +1651,25 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, #define SENSOR_ATTR_PWM(index) { \ SENSOR_ATTR_2(pwm##index, S_IWUSR | S_IRUGO, show_pwm, \ store_pwm, PWM_OUTPUT, index - 1), \ + SENSOR_ATTR_2(pwm##index##_enable, S_IWUSR | S_IRUGO, \ + show_pwm_enable, store_pwm_enable, NOT_USED, index - 1), \ + SENSOR_ATTR_2(pwm##index##_mode, S_IRUGO, \ + show_pwm_mode, NULL, NOT_USED, index - 1), \ + SENSOR_ATTR_2(pwm##index##_freq, S_IWUSR | S_IRUGO, \ + show_pwm, store_pwm, PWM_FREQ, index - 1), \ SENSOR_ATTR_2(pwm##index##_nonstop, S_IWUSR | S_IRUGO, \ show_pwm, store_pwm, PWM_NONSTOP, index - 1), \ SENSOR_ATTR_2(pwm##index##_start, S_IWUSR | S_IRUGO, \ show_pwm, store_pwm, PWM_START, index - 1), \ SENSOR_ATTR_2(pwm##index##_stop_time, S_IWUSR | S_IRUGO, \ show_pwm, store_pwm, PWM_STOP_TIME, index - 1), \ - SENSOR_ATTR_2(pwm##index##_freq, S_IWUSR | S_IRUGO, \ - show_pwm, store_pwm, PWM_FREQ, index - 1), \ - SENSOR_ATTR_2(pwm##index##_enable, S_IWUSR | S_IRUGO, \ - show_pwm_enable, store_pwm_enable, NOT_USED, index - 1), \ - SENSOR_ATTR_2(pwm##index##_mode, S_IRUGO, \ - show_pwm_mode, NULL, NOT_USED, index - 1), \ SENSOR_ATTR_2(fan##index##_target, S_IWUSR | S_IRUGO, \ show_fanin, store_fanin, FANIN_TARGET, index - 1) } -/* Don't change the attribute order, _beep is accessed by index - * somewhere else in the code */ +/* + * Don't change the attribute order, _beep is accessed by index + * somewhere else in the code + */ #define SENSOR_ATTR_DTS(index) { \ SENSOR_ATTR_2(temp##index##_type, S_IRUGO , \ show_dts_mode, NULL, NOT_USED, index - 7), \ @@ -1660,8 +1688,10 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, SENSOR_ATTR_2(temp##index##_beep, S_IWUSR | S_IRUGO, \ show_alarm_beep, store_beep, BEEP_ENABLE, index + 17) } -/* Don't change the attribute order, _beep is accessed by index - * somewhere else in the code */ +/* + * Don't change the attribute order, _beep is accessed by index + * somewhere else in the code + */ #define SENSOR_ATTR_TEMP(index) { \ SENSOR_ATTR_2(temp##index##_type, S_IRUGO | (index < 4 ? S_IWUSR : 0), \ show_temp_mode, store_temp_mode, NOT_USED, index - 1), \ @@ -1867,8 +1897,10 @@ static int w83795_get_device_id(struct i2c_client *client) device_id = i2c_smbus_read_byte_data(client, W83795_REG_DEVICEID); - /* Special case for rev. A chips; can't be checked first because later - revisions emulate this for compatibility */ + /* + * Special case for rev. A chips; can't be checked first because later + * revisions emulate this for compatibility + */ if (device_id < 0 || (device_id & 0xf0) != 0x50) { int alt_id; @@ -1920,8 +1952,10 @@ static int w83795_detect(struct i2c_client *client, return -ENODEV; } - /* If Nuvoton chip, address of chip and W83795_REG_I2C_ADDR - should match */ + /* + * If Nuvoton chip, address of chip and W83795_REG_I2C_ADDR + * should match + */ if ((bank & 0x07) == 0) { i2c_addr = i2c_smbus_read_byte_data(client, W83795_REG_I2C_ADDR); @@ -1933,10 +1967,12 @@ static int w83795_detect(struct i2c_client *client, } } - /* Check 795 chip type: 795G or 795ADG - Usually we don't write to chips during detection, but here we don't - quite have the choice; hopefully it's OK, we are about to return - success anyway */ + /* + * Check 795 chip type: 795G or 795ADG + * Usually we don't write to chips during detection, but here we don't + * quite have the choice; hopefully it's OK, we are about to return + * success anyway + */ if ((bank & 0x07) != 0) i2c_smbus_write_byte_data(client, W83795_REG_BANKSEL, bank & ~0x07); @@ -1953,6 +1989,14 @@ static int w83795_detect(struct i2c_client *client, return 0; } +#ifdef CONFIG_SENSORS_W83795_FANCTRL +#define NUM_PWM_ATTRIBUTES ARRAY_SIZE(w83795_pwm[0]) +#define NUM_TEMP_ATTRIBUTES ARRAY_SIZE(w83795_temp[0]) +#else +#define NUM_PWM_ATTRIBUTES 4 +#define NUM_TEMP_ATTRIBUTES 8 +#endif + static int w83795_handle_files(struct device *dev, int (*fn)(struct device *, const struct device_attribute *)) { @@ -2006,24 +2050,18 @@ static int w83795_handle_files(struct device *dev, int (*fn)(struct device *, } } -#ifdef CONFIG_SENSORS_W83795_FANCTRL for (i = 0; i < data->has_pwm; i++) { - for (j = 0; j < ARRAY_SIZE(w83795_pwm[0]); j++) { + for (j = 0; j < NUM_PWM_ATTRIBUTES; j++) { err = fn(dev, &w83795_pwm[i][j].dev_attr); if (err) return err; } } -#endif for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) { if (!(data->has_temp & (1 << i))) continue; -#ifdef CONFIG_SENSORS_W83795_FANCTRL - for (j = 0; j < ARRAY_SIZE(w83795_temp[0]); j++) { -#else - for (j = 0; j < 8; j++) { -#endif + for (j = 0; j < NUM_TEMP_ATTRIBUTES; j++) { if (j == 7 && !data->enable_beep) continue; err = fn(dev, &w83795_temp[i][j].dev_attr); @@ -2183,8 +2221,10 @@ static int w83795_probe(struct i2c_client *client, /* The W83795G has a dedicated BEEP pin */ data->enable_beep = 1; } else { - /* The W83795ADG has a shared pin for OVT# and BEEP, so you - * can't have both */ + /* + * The W83795ADG has a shared pin for OVT# and BEEP, so you + * can't have both + */ tmp = w83795_read(client, W83795_REG_OVT_CFG); if ((tmp & OVT_CFG_SEL) == 0) data->enable_beep = 1; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 3101dd59e379..71c1b0a7535c 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -369,6 +369,21 @@ config I2C_DESIGNWARE_PCI This driver can also be built as a module. If so, the module will be called i2c-designware-pci. +config I2C_EG20T + tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C" + depends on PCI + help + This driver is for PCH(Platform controller Hub) I2C of EG20T which + is an IOH(Input/Output Hub) for x86 embedded processor. + This driver can access PCH I2C bus device. + + This driver also can be used for LAPIS Semiconductor IOH(Input/ + Output Hub), ML7213, ML7223 and ML7831. + ML7213 IOH is for IVI(In-Vehicle Infotainment) use, ML7223 IOH is + for MP(Media Phone) use and ML7831 IOH is for general purpose use. + ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series. + ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH. + config I2C_GPIO tristate "GPIO-based bitbanging I2C" depends on GENERIC_GPIO @@ -630,6 +645,16 @@ config I2C_SIMTEC This driver can also be built as a module. If so, the module will be called i2c-simtec. +config I2C_SIRF + tristate "CSR SiRFprimaII I2C interface" + depends on ARCH_PRIMA2 + help + If you say yes to this option, support will be included for the + CSR SiRFprimaII I2C interface. + + This driver can also be built as a module. If so, the module + will be called i2c-sirf. + config I2C_STU300 tristate "ST Microelectronics DDC I2C interface" depends on MACH_U300 @@ -681,20 +706,15 @@ config I2C_XILINX This driver can also be built as a module. If so, the module will be called xilinx_i2c. -config I2C_EG20T - tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C" - depends on PCI +config I2C_XLR + tristate "XLR I2C support" + depends on CPU_XLR help - This driver is for PCH(Platform controller Hub) I2C of EG20T which - is an IOH(Input/Output Hub) for x86 embedded processor. - This driver can access PCH I2C bus device. + This driver enables support for the on-chip I2C interface of + the Netlogic XLR/XLS MIPS processors. - This driver also can be used for LAPIS Semiconductor IOH(Input/ - Output Hub), ML7213, ML7223 and ML7831. - ML7213 IOH is for IVI(In-Vehicle Infotainment) use, ML7223 IOH is - for MP(Media Phone) use and ML7831 IOH is for general purpose use. - ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series. - ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH. + This driver can also be built as a module. If so, the module + will be called i2c-xlr. comment "External I2C/SMBus adapter drivers" diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index fba6da60aa0e..569567b0d027 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o i2c-designware-platform-objs := i2c-designware-platdrv.o i2c-designware-core.o obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o i2c-designware-pci-objs := i2c-designware-pcidrv.o i2c-designware-core.o +obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o @@ -63,12 +64,13 @@ obj-$(CONFIG_I2C_S6000) += i2c-s6000.o obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o +obj-$(CONFIG_I2C_SIRF) += i2c-sirf.o obj-$(CONFIG_I2C_STU300) += i2c-stu300.o obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o -obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o +obj-$(CONFIG_I2C_XLR) += i2c-xlr.o # External I2C/SMBus adapter drivers obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 5244c4724df7..4ba589ab8614 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -214,7 +214,7 @@ static int __init dw_i2c_init_driver(void) { return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe); } -module_init(dw_i2c_init_driver); +subsys_initcall(dw_i2c_init_driver); static void __exit dw_i2c_exit_driver(void) { diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c index ca8877641040..f086131cb1c7 100644 --- a/drivers/i2c/busses/i2c-eg20t.c +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -271,30 +271,36 @@ static inline bool ktime_lt(const ktime_t cmp1, const ktime_t cmp2) /** * pch_i2c_wait_for_bus_idle() - check the status of bus. * @adap: Pointer to struct i2c_algo_pch_data. - * @timeout: waiting time counter (us). + * @timeout: waiting time counter (ms). */ static s32 pch_i2c_wait_for_bus_idle(struct i2c_algo_pch_data *adap, s32 timeout) { void __iomem *p = adap->pch_base_address; - ktime_t ns_val; + int schedule = 0; + unsigned long end = jiffies + msecs_to_jiffies(timeout); + + while (ioread32(p + PCH_I2CSR) & I2CMBB_BIT) { + if (time_after(jiffies, end)) { + pch_dbg(adap, "I2CSR = %x\n", ioread32(p + PCH_I2CSR)); + pch_err(adap, "%s: Timeout Error.return%d\n", + __func__, -ETIME); + pch_i2c_init(adap); - if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0) - return 0; + return -ETIME; + } - /* MAX timeout value is timeout*1000*1000nsec */ - ns_val = ktime_add_ns(ktime_get(), timeout*1000*1000); - do { - msleep(20); - if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0) - return 0; - } while (ktime_lt(ktime_get(), ns_val)); + if (!schedule) + /* Retry after some usecs */ + udelay(5); + else + /* Wait a bit more without consuming CPU */ + usleep_range(20, 1000); - pch_dbg(adap, "I2CSR = %x\n", ioread32(p + PCH_I2CSR)); - pch_err(adap, "%s: Timeout Error.return%d\n", __func__, -ETIME); - pch_i2c_init(adap); + schedule = 1; + } - return -ETIME; + return 0; } /** @@ -778,8 +784,6 @@ static s32 pch_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *pmsg; u32 i = 0; u32 status; - u32 msglen; - u32 subaddrlen; s32 ret; struct i2c_algo_pch_data *adap = i2c_adap->algo_data; @@ -804,12 +808,6 @@ static s32 pch_i2c_xfer(struct i2c_adapter *i2c_adap, status = pmsg->flags; pch_dbg(adap, "After invoking I2C_MODE_SEL :flag= 0x%x\n", status); - /* calculate sub address length and message length */ - /* these are applicable only for buffer mode */ - subaddrlen = pmsg->buf[0]; - /* calculate actual message length excluding - * the sub address fields */ - msglen = (pmsg->len) - (subaddrlen + 1); if ((status & (I2C_M_RD)) != false) { ret = pch_i2c_readbytes(i2c_adap, pmsg, (i + 1 == num), diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 58832e578fff..dfb84b7ee550 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -149,11 +149,6 @@ static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy) break; if (!for_busy && !(temp & I2SR_IBB)) break; - if (signal_pending(current)) { - dev_dbg(&i2c_imx->adapter.dev, - "<%s> I2C Interrupted\n", __func__); - return -EINTR; - } if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) { dev_dbg(&i2c_imx->adapter.dev, "<%s> I2C bus is busy\n", __func__); @@ -196,7 +191,7 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx) dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); - clk_enable(i2c_imx->clk); + clk_prepare_enable(i2c_imx->clk); writeb(i2c_imx->ifdr, i2c_imx->base + IMX_I2C_IFDR); /* Enable I2C controller */ writeb(0, i2c_imx->base + IMX_I2C_I2SR); @@ -245,7 +240,7 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx) /* Disable I2C controller */ writeb(0, i2c_imx->base + IMX_I2C_I2CR); - clk_disable(i2c_imx->clk); + clk_disable_unprepare(i2c_imx->clk); } static void __init i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx, diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index a8ebb84e23f9..206caacd30d7 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -454,7 +454,7 @@ static int mpc_write(struct mpc_i2c *i2c, int target, } static int mpc_read(struct mpc_i2c *i2c, int target, - u8 *data, int length, int restart) + u8 *data, int length, int restart, bool recv_len) { unsigned timeout = i2c->adap.timeout; int i, result; @@ -470,7 +470,7 @@ static int mpc_read(struct mpc_i2c *i2c, int target, return result; if (length) { - if (length == 1) + if (length == 1 && !recv_len) writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK); else writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA); @@ -479,17 +479,46 @@ static int mpc_read(struct mpc_i2c *i2c, int target, } for (i = 0; i < length; i++) { + u8 byte; + result = i2c_wait(i2c, timeout, 0); if (result < 0) return result; - /* Generate txack on next to last byte */ - if (i == length - 2) - writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK); - /* Do not generate stop on last byte */ - if (i == length - 1) - writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX); - data[i] = readb(i2c->base + MPC_I2C_DR); + /* + * For block reads, we have to know the total length (1st byte) + * before we can determine if we are done. + */ + if (i || !recv_len) { + /* Generate txack on next to last byte */ + if (i == length - 2) + writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA + | CCR_TXAK); + /* Do not generate stop on last byte */ + if (i == length - 1) + writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA + | CCR_MTX); + } + + byte = readb(i2c->base + MPC_I2C_DR); + + /* + * Adjust length if first received byte is length. + * The length is 1 length byte plus actually data length + */ + if (i == 0 && recv_len) { + if (byte == 0 || byte > I2C_SMBUS_BLOCK_MAX) + return -EPROTO; + length += byte; + /* + * For block reads, generate txack here if data length + * is 1 byte (total length is 2 bytes). + */ + if (length == 2) + writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA + | CCR_TXAK); + } + data[i] = byte; } return length; @@ -532,12 +561,17 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) "Doing %s %d bytes to 0x%02x - %d of %d messages\n", pmsg->flags & I2C_M_RD ? "read" : "write", pmsg->len, pmsg->addr, i + 1, num); - if (pmsg->flags & I2C_M_RD) - ret = - mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i); - else + if (pmsg->flags & I2C_M_RD) { + bool recv_len = pmsg->flags & I2C_M_RECV_LEN; + + ret = mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i, + recv_len); + if (recv_len && ret > 0) + pmsg->len = ret; + } else { ret = mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i); + } } mpc_i2c_stop(i2c); return (ret < 0) ? ret : num; @@ -545,7 +579,8 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) static u32 mpc_functionality(struct i2c_adapter *adap) { - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL + | I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL; } static const struct i2c_algorithm mpc_algo = { diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index d60364650990..f6733267fa9c 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -29,6 +29,8 @@ #include <linux/errno.h> #include <linux/interrupt.h> #include <linux/i2c-pxa.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <linux/of_i2c.h> #include <linux/platform_device.h> #include <linux/err.h> @@ -1044,23 +1046,60 @@ static const struct i2c_algorithm i2c_pxa_pio_algorithm = { .functionality = i2c_pxa_functionality, }; -static int i2c_pxa_probe(struct platform_device *dev) +static struct of_device_id i2c_pxa_dt_ids[] = { + { .compatible = "mrvl,pxa-i2c", .data = (void *)REGS_PXA2XX }, + { .compatible = "mrvl,pwri2c", .data = (void *)REGS_PXA3XX }, + { .compatible = "mrvl,mmp-twsi", .data = (void *)REGS_PXA2XX }, + {} +}; +MODULE_DEVICE_TABLE(of, i2c_pxa_dt_ids); + +static int i2c_pxa_probe_dt(struct platform_device *pdev, struct pxa_i2c *i2c, + enum pxa_i2c_types *i2c_types) { - struct pxa_i2c *i2c; - struct resource *res; - struct i2c_pxa_platform_data *plat = dev->dev.platform_data; - const struct platform_device_id *id = platform_get_device_id(dev); - enum pxa_i2c_types i2c_type = id->driver_data; + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_id = + of_match_device(i2c_pxa_dt_ids, &pdev->dev); int ret; - int irq; - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - irq = platform_get_irq(dev, 0); - if (res == NULL || irq < 0) - return -ENODEV; + if (!of_id) + return 1; + ret = of_alias_get_id(np, "i2c"); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); + return ret; + } + pdev->id = ret; + if (of_get_property(np, "mrvl,i2c-polling", NULL)) + i2c->use_pio = 1; + if (of_get_property(np, "mrvl,i2c-fast-mode", NULL)) + i2c->fast_mode = 1; + *i2c_types = (u32)(of_id->data); + return 0; +} - if (!request_mem_region(res->start, resource_size(res), res->name)) - return -ENOMEM; +static int i2c_pxa_probe_pdata(struct platform_device *pdev, + struct pxa_i2c *i2c, + enum pxa_i2c_types *i2c_types) +{ + struct i2c_pxa_platform_data *plat = pdev->dev.platform_data; + const struct platform_device_id *id = platform_get_device_id(pdev); + + *i2c_types = id->driver_data; + if (plat) { + i2c->use_pio = plat->use_pio; + i2c->fast_mode = plat->fast_mode; + } + return 0; +} + +static int i2c_pxa_probe(struct platform_device *dev) +{ + struct i2c_pxa_platform_data *plat = dev->dev.platform_data; + enum pxa_i2c_types i2c_type; + struct pxa_i2c *i2c; + struct resource *res = NULL; + int ret, irq; i2c = kzalloc(sizeof(struct pxa_i2c), GFP_KERNEL); if (!i2c) { @@ -1068,6 +1107,24 @@ static int i2c_pxa_probe(struct platform_device *dev) goto emalloc; } + ret = i2c_pxa_probe_dt(dev, i2c, &i2c_type); + if (ret > 0) + ret = i2c_pxa_probe_pdata(dev, i2c, &i2c_type); + if (ret < 0) + goto eclk; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + irq = platform_get_irq(dev, 0); + if (res == NULL || irq < 0) { + ret = -ENODEV; + goto eclk; + } + + if (!request_mem_region(res->start, resource_size(res), res->name)) { + ret = -ENOMEM; + goto eclk; + } + i2c->adap.owner = THIS_MODULE; i2c->adap.retries = 5; @@ -1109,21 +1166,16 @@ static int i2c_pxa_probe(struct platform_device *dev) i2c->slave_addr = I2C_PXA_SLAVE_ADDR; -#ifdef CONFIG_I2C_PXA_SLAVE if (plat) { +#ifdef CONFIG_I2C_PXA_SLAVE i2c->slave_addr = plat->slave_addr; i2c->slave = plat->slave; - } #endif - - clk_enable(i2c->clk); - - if (plat) { i2c->adap.class = plat->class; - i2c->use_pio = plat->use_pio; - i2c->fast_mode = plat->fast_mode; } + clk_enable(i2c->clk); + if (i2c->use_pio) { i2c->adap.algo = &i2c_pxa_pio_algorithm; } else { @@ -1234,6 +1286,7 @@ static struct platform_driver i2c_pxa_driver = { .name = "pxa2xx-i2c", .owner = THIS_MODULE, .pm = I2C_PXA_DEV_PM_OPS, + .of_match_table = i2c_pxa_dt_ids, }, .id_table = i2c_pxa_id_table, }; diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 4c1718081685..737f7218a32c 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -31,6 +31,7 @@ #include <linux/errno.h> #include <linux/err.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/clk.h> #include <linux/cpufreq.h> #include <linux/slab.h> @@ -564,6 +565,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, int retry; int ret; + pm_runtime_get_sync(&adap->dev); clk_enable(i2c->clk); for (retry = 0; retry < adap->retries; retry++) { @@ -572,6 +574,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, if (ret != -EAGAIN) { clk_disable(i2c->clk); + pm_runtime_put_sync(&adap->dev); return ret; } @@ -581,6 +584,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, } clk_disable(i2c->clk); + pm_runtime_put_sync(&adap->dev); return -EREMOTEIO; } @@ -890,7 +894,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) } } - i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL); + i2c = devm_kzalloc(&pdev->dev, sizeof(struct s3c24xx_i2c), GFP_KERNEL); if (!i2c) { dev_err(&pdev->dev, "no memory for state\n"); return -ENOMEM; @@ -1013,6 +1017,9 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) of_i2c_register_devices(&i2c->adap); platform_set_drvdata(pdev, i2c); + pm_runtime_enable(&pdev->dev); + pm_runtime_enable(&i2c->adap.dev); + dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev)); clk_disable(i2c->clk); return 0; @@ -1035,7 +1042,6 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) clk_put(i2c->clk); err_noclk: - kfree(i2c); return ret; } @@ -1048,6 +1054,9 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev) { struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); + pm_runtime_disable(&i2c->adap.dev); + pm_runtime_disable(&pdev->dev); + s3c24xx_i2c_deregister_cpufreq(i2c); i2c_del_adapter(&i2c->adap); @@ -1061,7 +1070,6 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev) release_resource(i2c->ioarea); s3c24xx_i2c_dt_gpio_free(i2c); kfree(i2c->ioarea); - kfree(i2c); return 0; } diff --git a/drivers/i2c/busses/i2c-sirf.c b/drivers/i2c/busses/i2c-sirf.c new file mode 100644 index 000000000000..5574a47792fb --- /dev/null +++ b/drivers/i2c/busses/i2c-sirf.c @@ -0,0 +1,459 @@ +/* + * I2C bus driver for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> + +#define SIRFSOC_I2C_CLK_CTRL 0x00 +#define SIRFSOC_I2C_STATUS 0x0C +#define SIRFSOC_I2C_CTRL 0x10 +#define SIRFSOC_I2C_IO_CTRL 0x14 +#define SIRFSOC_I2C_SDA_DELAY 0x18 +#define SIRFSOC_I2C_CMD_START 0x1C +#define SIRFSOC_I2C_CMD_BUF 0x30 +#define SIRFSOC_I2C_DATA_BUF 0x80 + +#define SIRFSOC_I2C_CMD_BUF_MAX 16 +#define SIRFSOC_I2C_DATA_BUF_MAX 16 + +#define SIRFSOC_I2C_CMD(x) (SIRFSOC_I2C_CMD_BUF + (x)*0x04) +#define SIRFSOC_I2C_DATA_MASK(x) (0xFF<<(((x)&3)*8)) +#define SIRFSOC_I2C_DATA_SHIFT(x) (((x)&3)*8) + +#define SIRFSOC_I2C_DIV_MASK (0xFFFF) + +/* I2C status flags */ +#define SIRFSOC_I2C_STAT_BUSY BIT(0) +#define SIRFSOC_I2C_STAT_TIP BIT(1) +#define SIRFSOC_I2C_STAT_NACK BIT(2) +#define SIRFSOC_I2C_STAT_TR_INT BIT(4) +#define SIRFSOC_I2C_STAT_STOP BIT(6) +#define SIRFSOC_I2C_STAT_CMD_DONE BIT(8) +#define SIRFSOC_I2C_STAT_ERR BIT(9) +#define SIRFSOC_I2C_CMD_INDEX (0x1F<<16) + +/* I2C control flags */ +#define SIRFSOC_I2C_RESET BIT(0) +#define SIRFSOC_I2C_CORE_EN BIT(1) +#define SIRFSOC_I2C_MASTER_MODE BIT(2) +#define SIRFSOC_I2C_CMD_DONE_EN BIT(11) +#define SIRFSOC_I2C_ERR_INT_EN BIT(12) + +#define SIRFSOC_I2C_SDA_DELAY_MASK (0xFF) +#define SIRFSOC_I2C_SCLF_FILTER (3<<8) + +#define SIRFSOC_I2C_START_CMD BIT(0) + +#define SIRFSOC_I2C_CMD_RP(x) ((x)&0x7) +#define SIRFSOC_I2C_NACK BIT(3) +#define SIRFSOC_I2C_WRITE BIT(4) +#define SIRFSOC_I2C_READ BIT(5) +#define SIRFSOC_I2C_STOP BIT(6) +#define SIRFSOC_I2C_START BIT(7) + +#define SIRFSOC_I2C_DEFAULT_SPEED 100000 + +struct sirfsoc_i2c { + void __iomem *base; + struct clk *clk; + u32 cmd_ptr; /* Current position in CMD buffer */ + u8 *buf; /* Buffer passed by user */ + u32 msg_len; /* Message length */ + u32 finished_len; /* number of bytes read/written */ + u32 read_cmd_len; /* number of read cmd sent */ + int msg_read; /* 1 indicates a read message */ + int err_status; /* 1 indicates an error on bus */ + + u32 sda_delay; /* For suspend/resume */ + u32 clk_div; + int last; /* Last message in transfer, STOP cmd can be sent */ + + struct completion done; /* indicates completion of message transfer */ + struct i2c_adapter adapter; +}; + +static void i2c_sirfsoc_read_data(struct sirfsoc_i2c *siic) +{ + u32 data = 0; + int i; + + for (i = 0; i < siic->read_cmd_len; i++) { + if (!(i & 0x3)) + data = readl(siic->base + SIRFSOC_I2C_DATA_BUF + i); + siic->buf[siic->finished_len++] = + (u8)((data & SIRFSOC_I2C_DATA_MASK(i)) >> + SIRFSOC_I2C_DATA_SHIFT(i)); + } +} + +static void i2c_sirfsoc_queue_cmd(struct sirfsoc_i2c *siic) +{ + u32 regval; + int i = 0; + + if (siic->msg_read) { + while (((siic->finished_len + i) < siic->msg_len) + && (siic->cmd_ptr < SIRFSOC_I2C_CMD_BUF_MAX)) { + regval = SIRFSOC_I2C_READ | SIRFSOC_I2C_CMD_RP(0); + if (((siic->finished_len + i) == + (siic->msg_len - 1)) && siic->last) + regval |= SIRFSOC_I2C_STOP | SIRFSOC_I2C_NACK; + writel(regval, + siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++)); + i++; + } + + siic->read_cmd_len = i; + } else { + while ((siic->cmd_ptr < SIRFSOC_I2C_CMD_BUF_MAX - 1) + && (siic->finished_len < siic->msg_len)) { + regval = SIRFSOC_I2C_WRITE | SIRFSOC_I2C_CMD_RP(0); + if ((siic->finished_len == (siic->msg_len - 1)) + && siic->last) + regval |= SIRFSOC_I2C_STOP; + writel(regval, + siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++)); + writel(siic->buf[siic->finished_len++], + siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++)); + } + } + siic->cmd_ptr = 0; + + /* Trigger the transfer */ + writel(SIRFSOC_I2C_START_CMD, siic->base + SIRFSOC_I2C_CMD_START); +} + +static irqreturn_t i2c_sirfsoc_irq(int irq, void *dev_id) +{ + struct sirfsoc_i2c *siic = (struct sirfsoc_i2c *)dev_id; + u32 i2c_stat = readl(siic->base + SIRFSOC_I2C_STATUS); + + if (i2c_stat & SIRFSOC_I2C_STAT_ERR) { + /* Error conditions */ + siic->err_status = 1; + writel(SIRFSOC_I2C_STAT_ERR, siic->base + SIRFSOC_I2C_STATUS); + + if (i2c_stat & SIRFSOC_I2C_STAT_NACK) + dev_err(&siic->adapter.dev, "ACK not received\n"); + else + dev_err(&siic->adapter.dev, "I2C error\n"); + + complete(&siic->done); + } else if (i2c_stat & SIRFSOC_I2C_STAT_CMD_DONE) { + /* CMD buffer execution complete */ + if (siic->msg_read) + i2c_sirfsoc_read_data(siic); + if (siic->finished_len == siic->msg_len) + complete(&siic->done); + else /* Fill a new CMD buffer for left data */ + i2c_sirfsoc_queue_cmd(siic); + + writel(SIRFSOC_I2C_STAT_CMD_DONE, siic->base + SIRFSOC_I2C_STATUS); + } + + return IRQ_HANDLED; +} + +static void i2c_sirfsoc_set_address(struct sirfsoc_i2c *siic, + struct i2c_msg *msg) +{ + unsigned char addr; + u32 regval = SIRFSOC_I2C_START | SIRFSOC_I2C_CMD_RP(0) | SIRFSOC_I2C_WRITE; + + /* no data and last message -> add STOP */ + if (siic->last && (msg->len == 0)) + regval |= SIRFSOC_I2C_STOP; + + writel(regval, siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++)); + + addr = msg->addr << 1; /* Generate address */ + if (msg->flags & I2C_M_RD) + addr |= 1; + + writel(addr, siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++)); +} + +static int i2c_sirfsoc_xfer_msg(struct sirfsoc_i2c *siic, struct i2c_msg *msg) +{ + u32 regval = readl(siic->base + SIRFSOC_I2C_CTRL); + /* timeout waiting for the xfer to finish or fail */ + int timeout = msecs_to_jiffies((msg->len + 1) * 50); + int ret = 0; + + i2c_sirfsoc_set_address(siic, msg); + + writel(regval | SIRFSOC_I2C_CMD_DONE_EN | SIRFSOC_I2C_ERR_INT_EN, + siic->base + SIRFSOC_I2C_CTRL); + i2c_sirfsoc_queue_cmd(siic); + + if (wait_for_completion_timeout(&siic->done, timeout) == 0) { + siic->err_status = 1; + dev_err(&siic->adapter.dev, "Transfer timeout\n"); + } + + writel(regval & ~(SIRFSOC_I2C_CMD_DONE_EN | SIRFSOC_I2C_ERR_INT_EN), + siic->base + SIRFSOC_I2C_CTRL); + writel(0, siic->base + SIRFSOC_I2C_CMD_START); + + if (siic->err_status) { + writel(readl(siic->base + SIRFSOC_I2C_CTRL) | SIRFSOC_I2C_RESET, + siic->base + SIRFSOC_I2C_CTRL); + while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET) + cpu_relax(); + + ret = -EIO; + } + + return ret; +} + +static u32 i2c_sirfsoc_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static int i2c_sirfsoc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct sirfsoc_i2c *siic = adap->algo_data; + int i, ret; + + clk_enable(siic->clk); + + for (i = 0; i < num; i++) { + siic->buf = msgs[i].buf; + siic->msg_len = msgs[i].len; + siic->msg_read = !!(msgs[i].flags & I2C_M_RD); + siic->err_status = 0; + siic->cmd_ptr = 0; + siic->finished_len = 0; + siic->last = (i == (num - 1)); + + ret = i2c_sirfsoc_xfer_msg(siic, &msgs[i]); + if (ret) { + clk_disable(siic->clk); + return ret; + } + } + + clk_disable(siic->clk); + return num; +} + +/* I2C algorithms associated with this master controller driver */ +static const struct i2c_algorithm i2c_sirfsoc_algo = { + .master_xfer = i2c_sirfsoc_xfer, + .functionality = i2c_sirfsoc_func, +}; + +static int __devinit i2c_sirfsoc_probe(struct platform_device *pdev) +{ + struct sirfsoc_i2c *siic; + struct i2c_adapter *adap; + struct resource *mem_res; + struct clk *clk; + int bitrate; + int ctrl_speed; + int irq; + + int err; + u32 regval; + + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + err = PTR_ERR(clk); + dev_err(&pdev->dev, "Clock get failed\n"); + goto err_get_clk; + } + + err = clk_prepare(clk); + if (err) { + dev_err(&pdev->dev, "Clock prepare failed\n"); + goto err_clk_prep; + } + + err = clk_enable(clk); + if (err) { + dev_err(&pdev->dev, "Clock enable failed\n"); + goto err_clk_en; + } + + ctrl_speed = clk_get_rate(clk); + + siic = devm_kzalloc(&pdev->dev, sizeof(*siic), GFP_KERNEL); + if (!siic) { + dev_err(&pdev->dev, "Can't allocate driver data\n"); + err = -ENOMEM; + goto out; + } + adap = &siic->adapter; + adap->class = I2C_CLASS_HWMON; + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (mem_res == NULL) { + dev_err(&pdev->dev, "Unable to get MEM resource\n"); + err = -EINVAL; + goto out; + } + + siic->base = devm_request_and_ioremap(&pdev->dev, mem_res); + if (siic->base == NULL) { + dev_err(&pdev->dev, "IO remap failed!\n"); + err = -ENOMEM; + goto out; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + err = irq; + goto out; + } + err = devm_request_irq(&pdev->dev, irq, i2c_sirfsoc_irq, 0, + dev_name(&pdev->dev), siic); + if (err) + goto out; + + adap->algo = &i2c_sirfsoc_algo; + adap->algo_data = siic; + + adap->dev.parent = &pdev->dev; + adap->nr = pdev->id; + + strlcpy(adap->name, "sirfsoc-i2c", sizeof(adap->name)); + + platform_set_drvdata(pdev, adap); + init_completion(&siic->done); + + /* Controller Initalisation */ + + writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL); + while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET) + cpu_relax(); + writel(SIRFSOC_I2C_CORE_EN | SIRFSOC_I2C_MASTER_MODE, + siic->base + SIRFSOC_I2C_CTRL); + + siic->clk = clk; + + err = of_property_read_u32(pdev->dev.of_node, + "clock-frequency", &bitrate); + if (err < 0) + bitrate = SIRFSOC_I2C_DEFAULT_SPEED; + + if (bitrate < 100000) + regval = + (2 * ctrl_speed) / (2 * bitrate * 11); + else + regval = ctrl_speed / (bitrate * 5); + + writel(regval, siic->base + SIRFSOC_I2C_CLK_CTRL); + if (regval > 0xFF) + writel(0xFF, siic->base + SIRFSOC_I2C_SDA_DELAY); + else + writel(regval, siic->base + SIRFSOC_I2C_SDA_DELAY); + + err = i2c_add_numbered_adapter(adap); + if (err < 0) { + dev_err(&pdev->dev, "Can't add new i2c adapter\n"); + goto out; + } + + clk_disable(clk); + + dev_info(&pdev->dev, " I2C adapter ready to operate\n"); + + return 0; + +out: + clk_disable(clk); +err_clk_en: + clk_unprepare(clk); +err_clk_prep: + clk_put(clk); +err_get_clk: + return err; +} + +static int __devexit i2c_sirfsoc_remove(struct platform_device *pdev) +{ + struct i2c_adapter *adapter = platform_get_drvdata(pdev); + struct sirfsoc_i2c *siic = adapter->algo_data; + + writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL); + i2c_del_adapter(adapter); + clk_unprepare(siic->clk); + clk_put(siic->clk); + return 0; +} + +#ifdef CONFIG_PM +static int i2c_sirfsoc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct i2c_adapter *adapter = platform_get_drvdata(pdev); + struct sirfsoc_i2c *siic = adapter->algo_data; + + clk_enable(siic->clk); + siic->sda_delay = readl(siic->base + SIRFSOC_I2C_SDA_DELAY); + siic->clk_div = readl(siic->base + SIRFSOC_I2C_CLK_CTRL); + clk_disable(siic->clk); + return 0; +} + +static int i2c_sirfsoc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct i2c_adapter *adapter = platform_get_drvdata(pdev); + struct sirfsoc_i2c *siic = adapter->algo_data; + + clk_enable(siic->clk); + writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL); + writel(SIRFSOC_I2C_CORE_EN | SIRFSOC_I2C_MASTER_MODE, + siic->base + SIRFSOC_I2C_CTRL); + writel(siic->clk_div, siic->base + SIRFSOC_I2C_CLK_CTRL); + writel(siic->sda_delay, siic->base + SIRFSOC_I2C_SDA_DELAY); + clk_disable(siic->clk); + return 0; +} + +static const struct dev_pm_ops i2c_sirfsoc_pm_ops = { + .suspend = i2c_sirfsoc_suspend, + .resume = i2c_sirfsoc_resume, +}; +#endif + +static const struct of_device_id sirfsoc_i2c_of_match[] __devinitconst = { + { .compatible = "sirf,prima2-i2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, sirfsoc_i2c_of_match); + +static struct platform_driver i2c_sirfsoc_driver = { + .driver = { + .name = "sirfsoc_i2c", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &i2c_sirfsoc_pm_ops, +#endif + .of_match_table = sirfsoc_i2c_of_match, + }, + .probe = i2c_sirfsoc_probe, + .remove = __devexit_p(i2c_sirfsoc_remove), +}; +module_platform_driver(i2c_sirfsoc_driver); + +MODULE_DESCRIPTION("SiRF SoC I2C master controller driver"); +MODULE_AUTHOR("Zhiwu Song <Zhiwu.Song@csr.com>, " + "Xiangzhen Ye <Xiangzhen.Ye@csr.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 0ab4a9548745..e978635e60f0 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -457,7 +457,6 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, int ret; tegra_i2c_flush_fifos(i2c_dev); - i2c_writel(i2c_dev, 0xFF, I2C_INT_STATUS); if (msg->len == 0) return -EINVAL; diff --git a/drivers/i2c/busses/i2c-versatile.c b/drivers/i2c/busses/i2c-versatile.c index 60556012312f..f585aead50cc 100644 --- a/drivers/i2c/busses/i2c-versatile.c +++ b/drivers/i2c/busses/i2c-versatile.c @@ -16,6 +16,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/io.h> +#include <linux/of_i2c.h> #define I2C_CONTROL 0x00 #define I2C_CONTROLS 0x00 @@ -99,6 +100,7 @@ static int i2c_versatile_probe(struct platform_device *dev) strlcpy(i2c->adap.name, "Versatile I2C adapter", sizeof(i2c->adap.name)); i2c->adap.algo_data = &i2c->algo; i2c->adap.dev.parent = &dev->dev; + i2c->adap.dev.of_node = dev->dev.of_node; i2c->algo = i2c_versatile_algo; i2c->algo.data = i2c; @@ -111,6 +113,7 @@ static int i2c_versatile_probe(struct platform_device *dev) ret = i2c_bit_add_bus(&i2c->adap); if (ret >= 0) { platform_set_drvdata(dev, i2c); + of_i2c_register_devices(&i2c->adap); return 0; } @@ -133,12 +136,19 @@ static int i2c_versatile_remove(struct platform_device *dev) return 0; } +static const struct of_device_id i2c_versatile_match[] = { + { .compatible = "arm,versatile-i2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_versatile_match); + static struct platform_driver i2c_versatile_driver = { .probe = i2c_versatile_probe, .remove = i2c_versatile_remove, .driver = { .name = "versatile-i2c", .owner = THIS_MODULE, + .of_match_table = i2c_versatile_match, }, }; diff --git a/drivers/i2c/busses/i2c-xlr.c b/drivers/i2c/busses/i2c-xlr.c new file mode 100644 index 000000000000..96d3fabd8883 --- /dev/null +++ b/drivers/i2c/busses/i2c-xlr.c @@ -0,0 +1,278 @@ +/* + * Copyright 2011, Netlogic Microsystems Inc. + * Copyright 2004, Matt Porter <mporter@kernel.crashing.org> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/platform_device.h> + +/* XLR I2C REGISTERS */ +#define XLR_I2C_CFG 0x00 +#define XLR_I2C_CLKDIV 0x01 +#define XLR_I2C_DEVADDR 0x02 +#define XLR_I2C_ADDR 0x03 +#define XLR_I2C_DATAOUT 0x04 +#define XLR_I2C_DATAIN 0x05 +#define XLR_I2C_STATUS 0x06 +#define XLR_I2C_STARTXFR 0x07 +#define XLR_I2C_BYTECNT 0x08 +#define XLR_I2C_HDSTATIM 0x09 + +/* XLR I2C REGISTERS FLAGS */ +#define XLR_I2C_BUS_BUSY 0x01 +#define XLR_I2C_SDOEMPTY 0x02 +#define XLR_I2C_RXRDY 0x04 +#define XLR_I2C_ACK_ERR 0x08 +#define XLR_I2C_ARB_STARTERR 0x30 + +/* Register Values */ +#define XLR_I2C_CFG_ADDR 0xF8 +#define XLR_I2C_CFG_NOADDR 0xFA +#define XLR_I2C_STARTXFR_ND 0x02 /* No Data */ +#define XLR_I2C_STARTXFR_RD 0x01 /* Read */ +#define XLR_I2C_STARTXFR_WR 0x00 /* Write */ + +#define XLR_I2C_TIMEOUT 10 /* timeout per byte in msec */ + +/* + * On XLR/XLS, we need to use __raw_ IO to read the I2C registers + * because they are in the big-endian MMIO area on the SoC. + * + * The readl/writel implementation on XLR/XLS byteswaps, because + * those are for its little-endian PCI space (see arch/mips/Kconfig). + */ +static inline void xlr_i2c_wreg(u32 __iomem *base, unsigned int reg, u32 val) +{ + __raw_writel(val, base + reg); +} + +static inline u32 xlr_i2c_rdreg(u32 __iomem *base, unsigned int reg) +{ + return __raw_readl(base + reg); +} + +struct xlr_i2c_private { + struct i2c_adapter adap; + u32 __iomem *iobase; +}; + +static int xlr_i2c_tx(struct xlr_i2c_private *priv, u16 len, + u8 *buf, u16 addr) +{ + struct i2c_adapter *adap = &priv->adap; + unsigned long timeout, stoptime, checktime; + u32 i2c_status; + int pos, timedout; + u8 offset, byte; + + offset = buf[0]; + xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset); + xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr); + xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG, XLR_I2C_CFG_ADDR); + xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1); + + timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT); + stoptime = jiffies + timeout; + timedout = 0; + pos = 1; +retry: + if (len == 1) { + xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, + XLR_I2C_STARTXFR_ND); + } else { + xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[pos]); + xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, + XLR_I2C_STARTXFR_WR); + } + + while (!timedout) { + checktime = jiffies; + i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS); + + if (i2c_status & XLR_I2C_SDOEMPTY) { + pos++; + /* need to do a empty dataout after the last byte */ + byte = (pos < len) ? buf[pos] : 0; + xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, byte); + + /* reset timeout on successful xmit */ + stoptime = jiffies + timeout; + } + timedout = time_after(checktime, stoptime); + + if (i2c_status & XLR_I2C_ARB_STARTERR) { + if (timedout) + break; + goto retry; + } + + if (i2c_status & XLR_I2C_ACK_ERR) + return -EIO; + + if ((i2c_status & XLR_I2C_BUS_BUSY) == 0 && pos >= len) + return 0; + } + dev_err(&adap->dev, "I2C transmit timeout\n"); + return -ETIMEDOUT; +} + +static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 len, u8 *buf, u16 addr) +{ + struct i2c_adapter *adap = &priv->adap; + u32 i2c_status; + unsigned long timeout, stoptime, checktime; + int nbytes, timedout; + u8 byte; + + xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG, XLR_I2C_CFG_NOADDR); + xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len); + xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr); + + timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT); + stoptime = jiffies + timeout; + timedout = 0; + nbytes = 0; +retry: + xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, XLR_I2C_STARTXFR_RD); + + while (!timedout) { + checktime = jiffies; + i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS); + if (i2c_status & XLR_I2C_RXRDY) { + if (nbytes > len) + return -EIO; /* should not happen */ + + /* we need to do a dummy datain when nbytes == len */ + byte = xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN); + if (nbytes < len) + buf[nbytes] = byte; + nbytes++; + + /* reset timeout on successful read */ + stoptime = jiffies + timeout; + } + + timedout = time_after(checktime, stoptime); + if (i2c_status & XLR_I2C_ARB_STARTERR) { + if (timedout) + break; + goto retry; + } + + if (i2c_status & XLR_I2C_ACK_ERR) + return -EIO; + + if ((i2c_status & XLR_I2C_BUS_BUSY) == 0) + return 0; + } + + dev_err(&adap->dev, "I2C receive timeout\n"); + return -ETIMEDOUT; +} + +static int xlr_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct i2c_msg *msg; + int i; + int ret = 0; + struct xlr_i2c_private *priv = i2c_get_adapdata(adap); + + for (i = 0; ret == 0 && i < num; i++) { + msg = &msgs[i]; + if (msg->flags & I2C_M_RD) + ret = xlr_i2c_rx(priv, msg->len, &msg->buf[0], + msg->addr); + else + ret = xlr_i2c_tx(priv, msg->len, &msg->buf[0], + msg->addr); + } + + return (ret != 0) ? ret : num; +} + +static u32 xlr_func(struct i2c_adapter *adap) +{ + /* Emulate SMBUS over I2C */ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; +} + +static struct i2c_algorithm xlr_i2c_algo = { + .master_xfer = xlr_i2c_xfer, + .functionality = xlr_func, +}; + +static int __devinit xlr_i2c_probe(struct platform_device *pdev) +{ + struct xlr_i2c_private *priv; + struct resource *res; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->iobase = devm_request_and_ioremap(&pdev->dev, res); + if (!priv->iobase) { + dev_err(&pdev->dev, "devm_request_and_ioremap failed\n"); + return -EBUSY; + } + + priv->adap.dev.parent = &pdev->dev; + priv->adap.owner = THIS_MODULE; + priv->adap.algo_data = priv; + priv->adap.algo = &xlr_i2c_algo; + priv->adap.nr = pdev->id; + priv->adap.class = I2C_CLASS_HWMON; + snprintf(priv->adap.name, sizeof(priv->adap.name), "xlr-i2c"); + + i2c_set_adapdata(&priv->adap, priv); + ret = i2c_add_numbered_adapter(&priv->adap); + if (ret < 0) { + dev_err(&priv->adap.dev, "Failed to add i2c bus.\n"); + return ret; + } + + platform_set_drvdata(pdev, priv); + dev_info(&priv->adap.dev, "Added I2C Bus.\n"); + return 0; +} + +static int __devexit xlr_i2c_remove(struct platform_device *pdev) +{ + struct xlr_i2c_private *priv; + + priv = platform_get_drvdata(pdev); + i2c_del_adapter(&priv->adap); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver xlr_i2c_driver = { + .probe = xlr_i2c_probe, + .remove = __devexit_p(xlr_i2c_remove), + .driver = { + .name = "xlr-i2cbus", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(xlr_i2c_driver); + +MODULE_AUTHOR("Ganesan Ramalingam <ganesanr@netlogicmicro.com>"); +MODULE_DESCRIPTION("XLR/XLS SoC I2C Controller driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:xlr-i2cbus"); diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 001b147c7f95..332597980817 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -25,6 +25,10 @@ config INPUT if INPUT +config INPUT_OF_MATRIX_KEYMAP + depends on USE_OF + bool + config INPUT_FF_MEMLESS tristate "Support for memoryless force-feedback devices" help diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 0c789490e0b3..b173a13a73ca 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o +obj-$(CONFIG_INPUT_OF_MATRIX_KEYMAP) += of_keymap.o diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 7df5bfef2624..4b2e10d5d641 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -20,7 +20,7 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/init.h> -#include <linux/input.h> +#include <linux/input/mt.h> #include <linux/major.h> #include <linux/device.h> #include "input-compat.h" @@ -46,6 +46,7 @@ struct evdev_client { struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; + int clkid; unsigned int bufsize; struct input_event buffer[]; }; @@ -54,8 +55,12 @@ static struct evdev *evdev_table[EVDEV_MINORS]; static DEFINE_MUTEX(evdev_table_mutex); static void evdev_pass_event(struct evdev_client *client, - struct input_event *event) + struct input_event *event, + ktime_t mono, ktime_t real) { + event->time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ? + mono : real); + /* Interrupts are disabled, just acquire the lock. */ spin_lock(&client->buffer_lock); @@ -94,8 +99,11 @@ static void evdev_event(struct input_handle *handle, struct evdev *evdev = handle->private; struct evdev_client *client; struct input_event event; + ktime_t time_mono, time_real; + + time_mono = ktime_get(); + time_real = ktime_sub(time_mono, ktime_get_monotonic_offset()); - do_gettimeofday(&event.time); event.type = type; event.code = code; event.value = value; @@ -103,11 +111,12 @@ static void evdev_event(struct input_handle *handle, rcu_read_lock(); client = rcu_dereference(evdev->grab); + if (client) - evdev_pass_event(client, &event); + evdev_pass_event(client, &event, time_mono, time_real); else list_for_each_entry_rcu(client, &evdev->client_list, node) - evdev_pass_event(client, &event); + evdev_pass_event(client, &event, time_mono, time_real); rcu_read_unlock(); @@ -623,6 +632,28 @@ static int evdev_handle_set_keycode_v2(struct input_dev *dev, void __user *p) return input_set_keycode(dev, &ke); } +static int evdev_handle_mt_request(struct input_dev *dev, + unsigned int size, + int __user *ip) +{ + const struct input_mt_slot *mt = dev->mt; + unsigned int code; + int max_slots; + int i; + + if (get_user(code, &ip[0])) + return -EFAULT; + if (!input_is_mt_value(code)) + return -EINVAL; + + max_slots = (size - sizeof(__u32)) / sizeof(__s32); + for (i = 0; i < dev->mtsize && i < max_slots; i++) + if (put_user(input_mt_get_value(&mt[i], code), &ip[1 + i])) + return -EFAULT; + + return 0; +} + static long evdev_do_ioctl(struct file *file, unsigned int cmd, void __user *p, int compat_mode) { @@ -685,6 +716,14 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, else return evdev_ungrab(evdev, client); + case EVIOCSCLOCKID: + if (copy_from_user(&i, p, sizeof(unsigned int))) + return -EFAULT; + if (i != CLOCK_MONOTONIC && i != CLOCK_REALTIME) + return -EINVAL; + client->clkid = i; + return 0; + case EVIOCGKEYCODE: return evdev_handle_get_keycode(dev, p); @@ -708,6 +747,9 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, return bits_to_user(dev->propbit, INPUT_PROP_MAX, size, p, compat_mode); + case EVIOCGMTSLOTS(0): + return evdev_handle_mt_request(dev, size, ip); + case EVIOCGKEY(0): return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode); diff --git a/drivers/input/input.c b/drivers/input/input.c index 1f78c957a75a..8921c6180c51 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -180,7 +180,7 @@ static int input_handle_abs_event(struct input_dev *dev, return INPUT_IGNORE_EVENT; } - is_mt_event = code >= ABS_MT_FIRST && code <= ABS_MT_LAST; + is_mt_event = input_is_mt_value(code); if (!is_mt_event) { pold = &dev->absinfo[code].value; diff --git a/drivers/input/joystick/as5011.c b/drivers/input/joystick/as5011.c index 6d6e7418dc21..3063464474bf 100644 --- a/drivers/input/joystick/as5011.c +++ b/drivers/input/joystick/as5011.c @@ -355,14 +355,4 @@ static struct i2c_driver as5011_driver = { .id_table = as5011_id, }; -static int __init as5011_init(void) -{ - return i2c_add_driver(&as5011_driver); -} -module_init(as5011_init); - -static void __exit as5011_exit(void) -{ - i2c_del_driver(&as5011_driver); -} -module_exit(as5011_exit); +module_i2c_driver(as5011_driver); diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index cdc385b2cf7d..f354813a13e8 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -394,6 +394,7 @@ config KEYBOARD_NOMADIK config KEYBOARD_TEGRA tristate "NVIDIA Tegra internal matrix keyboard controller support" depends on ARCH_TEGRA + select INPUT_OF_MATRIX_KEYMAP if USE_OF help Say Y here if you want to use a matrix keyboard connected directly to the internal keyboard controller on Tegra SoCs. @@ -512,7 +513,6 @@ config KEYBOARD_OMAP config KEYBOARD_OMAP4 tristate "TI OMAP4 keypad support" - depends on ARCH_OMAP4 help Say Y here if you want to use the OMAP4 keypad. diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index 4a7f534cf64b..39ebffac207e 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -653,17 +653,7 @@ static struct i2c_driver adp5588_driver = { .id_table = adp5588_id, }; -static int __init adp5588_init(void) -{ - return i2c_add_driver(&adp5588_driver); -} -module_init(adp5588_init); - -static void __exit adp5588_exit(void) -{ - i2c_del_driver(&adp5588_driver); -} -module_exit(adp5588_exit); +module_i2c_driver(adp5588_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c index 02b5d53031bf..74e603213386 100644 --- a/drivers/input/keyboard/adp5589-keys.c +++ b/drivers/input/keyboard/adp5589-keys.c @@ -1108,17 +1108,7 @@ static struct i2c_driver adp5589_driver = { .id_table = adp5589_id, }; -static int __init adp5589_init(void) -{ - return i2c_add_driver(&adp5589_driver); -} -module_init(adp5589_init); - -static void __exit adp5589_exit(void) -{ - i2c_del_driver(&adp5589_driver); -} -module_exit(adp5589_exit); +module_i2c_driver(adp5589_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); diff --git a/drivers/input/keyboard/jornada720_kbd.c b/drivers/input/keyboard/jornada720_kbd.c index eeafc30b207b..9d639fa1afbd 100644 --- a/drivers/input/keyboard/jornada720_kbd.c +++ b/drivers/input/keyboard/jornada720_kbd.c @@ -27,6 +27,7 @@ #include <mach/jornada720.h> #include <mach/hardware.h> +#include <mach/irqs.h> MODULE_AUTHOR("Kristoffer Ericson <Kristoffer.Ericson@gmail.com>"); MODULE_DESCRIPTION("HP Jornada 710/720/728 keyboard driver"); diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c index 21823bfc7911..39ac2787e275 100644 --- a/drivers/input/keyboard/lm8323.c +++ b/drivers/input/keyboard/lm8323.c @@ -851,17 +851,7 @@ static struct i2c_driver lm8323_i2c_driver = { }; MODULE_DEVICE_TABLE(i2c, lm8323_id); -static int __init lm8323_init(void) -{ - return i2c_add_driver(&lm8323_i2c_driver); -} -module_init(lm8323_init); - -static void __exit lm8323_exit(void) -{ - i2c_del_driver(&lm8323_i2c_driver); -} -module_exit(lm8323_exit); +module_i2c_driver(lm8323_i2c_driver); MODULE_AUTHOR("Timo O. Karjalainen <timo.o.karjalainen@nokia.com>"); MODULE_AUTHOR("Daniel Stone"); diff --git a/drivers/input/keyboard/max7359_keypad.c b/drivers/input/keyboard/max7359_keypad.c index 5afe35ad24d3..8edada8ae712 100644 --- a/drivers/input/keyboard/max7359_keypad.c +++ b/drivers/input/keyboard/max7359_keypad.c @@ -316,17 +316,7 @@ static struct i2c_driver max7359_i2c_driver = { .id_table = max7359_ids, }; -static int __init max7359_init(void) -{ - return i2c_add_driver(&max7359_i2c_driver); -} -module_init(max7359_init); - -static void __exit max7359_exit(void) -{ - i2c_del_driver(&max7359_i2c_driver); -} -module_exit(max7359_exit); +module_i2c_driver(max7359_i2c_driver); MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>"); MODULE_DESCRIPTION("MAX7359 Key Switch Controller Driver"); diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c index af1aab324a4c..64a0ca4c92f3 100644 --- a/drivers/input/keyboard/mcs_touchkey.c +++ b/drivers/input/keyboard/mcs_touchkey.c @@ -274,18 +274,7 @@ static struct i2c_driver mcs_touchkey_driver = { .id_table = mcs_touchkey_id, }; -static int __init mcs_touchkey_init(void) -{ - return i2c_add_driver(&mcs_touchkey_driver); -} - -static void __exit mcs_touchkey_exit(void) -{ - i2c_del_driver(&mcs_touchkey_driver); -} - -module_init(mcs_touchkey_init); -module_exit(mcs_touchkey_exit); +module_i2c_driver(mcs_touchkey_driver); /* Module information */ MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c index 1c1615d9a7f9..caa218a51b5a 100644 --- a/drivers/input/keyboard/mpr121_touchkey.c +++ b/drivers/input/keyboard/mpr121_touchkey.c @@ -330,17 +330,7 @@ static struct i2c_driver mpr_touchkey_driver = { .remove = __devexit_p(mpr_touchkey_remove), }; -static int __init mpr_touchkey_init(void) -{ - return i2c_add_driver(&mpr_touchkey_driver); -} -module_init(mpr_touchkey_init); - -static void __exit mpr_touchkey_exit(void) -{ - i2c_del_driver(&mpr_touchkey_driver); -} -module_exit(mpr_touchkey_exit); +module_i2c_driver(mpr_touchkey_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@freescale.com>"); diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index e35566aa102f..101e245944e7 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -88,7 +88,7 @@ static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, * * Enable Multi key press detection, auto scan mode */ -static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad) +static int __init ske_keypad_chip_init(struct ske_keypad *keypad) { u32 value; int timeout = 50; @@ -198,7 +198,7 @@ static irqreturn_t ske_keypad_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static int __devinit ske_keypad_probe(struct platform_device *pdev) +static int __init ske_keypad_probe(struct platform_device *pdev) { const struct ske_keypad_platform_data *plat = pdev->dev.platform_data; struct ske_keypad *keypad; @@ -344,7 +344,7 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int ske_keypad_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -372,22 +372,17 @@ static int ske_keypad_resume(struct device *dev) return 0; } - -static const struct dev_pm_ops ske_keypad_dev_pm_ops = { - .suspend = ske_keypad_suspend, - .resume = ske_keypad_resume, -}; #endif +static SIMPLE_DEV_PM_OPS(ske_keypad_dev_pm_ops, + ske_keypad_suspend, ske_keypad_resume); + static struct platform_driver ske_keypad_driver = { .driver = { .name = "nmk-ske-keypad", .owner = THIS_MODULE, -#ifdef CONFIG_PM .pm = &ske_keypad_dev_pm_ops, -#endif }, - .probe = ske_keypad_probe, .remove = __devexit_p(ske_keypad_remove), }; diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index d5c5d77f4b82..e809ac095a38 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -31,7 +31,7 @@ #include <linux/slab.h> #include <linux/pm_runtime.h> -#include <plat/omap4-keypad.h> +#include <linux/platform_data/omap4-keypad.h> /* OMAP4 registers */ #define OMAP4_KBD_REVISION 0x00 diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c index b21bf5b876bb..0b7b2f891752 100644 --- a/drivers/input/keyboard/qt1070.c +++ b/drivers/input/keyboard/qt1070.c @@ -258,17 +258,7 @@ static struct i2c_driver qt1070_driver = { .remove = __devexit_p(qt1070_remove), }; -static int __init qt1070_init(void) -{ - return i2c_add_driver(&qt1070_driver); -} -module_init(qt1070_init); - -static void __exit qt1070_exit(void) -{ - i2c_del_driver(&qt1070_driver); -} -module_exit(qt1070_exit); +module_i2c_driver(qt1070_driver); MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>"); MODULE_DESCRIPTION("Driver for AT42QT1070 QTouch sensor"); diff --git a/drivers/input/keyboard/qt2160.c b/drivers/input/keyboard/qt2160.c index fac695157e8a..e7a5e36e1203 100644 --- a/drivers/input/keyboard/qt2160.c +++ b/drivers/input/keyboard/qt2160.c @@ -379,17 +379,7 @@ static struct i2c_driver qt2160_driver = { .remove = __devexit_p(qt2160_remove), }; -static int __init qt2160_init(void) -{ - return i2c_add_driver(&qt2160_driver); -} -module_init(qt2160_init); - -static void __exit qt2160_cleanup(void) -{ - i2c_del_driver(&qt2160_driver); -} -module_exit(qt2160_cleanup); +module_i2c_driver(qt2160_driver); MODULE_AUTHOR("Raphael Derosso Pereira <raphaelpereira@gmail.com>"); MODULE_DESCRIPTION("Driver for AT42QT2160 Touch Sensor"); diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index 17ba7f9f80f3..2391ae884fee 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -175,7 +175,7 @@ static irqreturn_t samsung_keypad_irq(int irq, void *dev_id) } while (key_down && !keypad->stopped); - pm_runtime_put_sync(&keypad->pdev->dev); + pm_runtime_put(&keypad->pdev->dev); return IRQ_HANDLED; } @@ -199,7 +199,7 @@ static void samsung_keypad_start(struct samsung_keypad *keypad) /* KEYIFCOL reg clear. */ writel(0, keypad->base + SAMSUNG_KEYIFCOL); - pm_runtime_put_sync(&keypad->pdev->dev); + pm_runtime_put(&keypad->pdev->dev); } static void samsung_keypad_stop(struct samsung_keypad *keypad) @@ -229,7 +229,7 @@ static void samsung_keypad_stop(struct samsung_keypad *keypad) */ enable_irq(keypad->irq); - pm_runtime_put_sync(&keypad->pdev->dev); + pm_runtime_put(&keypad->pdev->dev); } static int samsung_keypad_open(struct input_dev *input_dev) diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index c88bd63dc9cc..3b6b528f02fd 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -50,6 +50,7 @@ #define ROW_MASK 0xF0 #define COLUMN_MASK 0x0F #define ROW_SHIFT 4 +#define KEY_MATRIX_SHIFT 6 struct spear_kbd { struct input_dev *input; @@ -57,6 +58,7 @@ struct spear_kbd { void __iomem *io_base; struct clk *clk; unsigned int irq; + unsigned int mode; unsigned short last_key; unsigned short keycodes[256]; }; @@ -106,7 +108,8 @@ static int spear_kbd_open(struct input_dev *dev) return error; /* program keyboard */ - val = SCAN_RATE_80 | MODE_KEYBOARD | PCLK_FREQ_MSK; + val = SCAN_RATE_80 | MODE_KEYBOARD | PCLK_FREQ_MSK | + (kbd->mode << KEY_MATRIX_SHIFT); writew(val, kbd->io_base + MODE_REG); writeb(1, kbd->io_base + STATUS_REG); @@ -176,6 +179,8 @@ static int __devinit spear_kbd_probe(struct platform_device *pdev) kbd->input = input_dev; kbd->irq = irq; + kbd->mode = pdata->mode; + kbd->res = request_mem_region(res->start, resource_size(res), pdev->name); if (!kbd->res) { @@ -308,22 +313,17 @@ static int spear_kbd_resume(struct device *dev) return 0; } - -static const struct dev_pm_ops spear_kbd_pm_ops = { - .suspend = spear_kbd_suspend, - .resume = spear_kbd_resume, -}; #endif +static SIMPLE_DEV_PM_OPS(spear_kbd_pm_ops, spear_kbd_suspend, spear_kbd_resume); + static struct platform_driver spear_kbd_driver = { .probe = spear_kbd_probe, .remove = __devexit_p(spear_kbd_remove), .driver = { .name = "keyboard", .owner = THIS_MODULE, -#ifdef CONFIG_PM .pm = &spear_kbd_pm_ops, -#endif }, }; module_platform_driver(spear_kbd_driver); diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index a136e2e832be..21c42f852343 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -48,6 +48,7 @@ #define KBC_FIFO_TH_CNT_SHIFT(cnt) (cnt << 14) #define KBC_DEBOUNCE_CNT_SHIFT(cnt) (cnt << 4) #define KBC_CONTROL_FIFO_CNT_INT_EN (1 << 3) +#define KBC_CONTROL_KEYPRESS_INT_EN (1 << 1) #define KBC_CONTROL_KBC_EN (1 << 0) /* KBC Interrupt Register */ @@ -356,6 +357,18 @@ static void tegra_kbc_set_fifo_interrupt(struct tegra_kbc *kbc, bool enable) writel(val, kbc->mmio + KBC_CONTROL_0); } +static void tegra_kbc_set_keypress_interrupt(struct tegra_kbc *kbc, bool enable) +{ + u32 val; + + val = readl(kbc->mmio + KBC_CONTROL_0); + if (enable) + val |= KBC_CONTROL_KEYPRESS_INT_EN; + else + val &= ~KBC_CONTROL_KEYPRESS_INT_EN; + writel(val, kbc->mmio + KBC_CONTROL_0); +} + static void tegra_kbc_keypress_timer(unsigned long data) { struct tegra_kbc *kbc = (struct tegra_kbc *)data; @@ -455,10 +468,18 @@ static void tegra_kbc_config_pins(struct tegra_kbc *kbc) row_cfg &= ~r_mask; col_cfg &= ~c_mask; - if (pdata->pin_cfg[i].is_row) + switch (pdata->pin_cfg[i].type) { + case PIN_CFG_ROW: row_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << r_shft; - else + break; + + case PIN_CFG_COL: col_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << c_shft; + break; + + case PIN_CFG_IGNORE: + break; + } writel(row_cfg, kbc->mmio + r_offs); writel(col_cfg, kbc->mmio + c_offs); @@ -563,7 +584,8 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata, for (i = 0; i < KBC_MAX_GPIO; i++) { const struct tegra_kbc_pin_cfg *pin_cfg = &pdata->pin_cfg[i]; - if (pin_cfg->is_row) { + switch (pin_cfg->type) { + case PIN_CFG_ROW: if (pin_cfg->num >= KBC_MAX_ROW) { dev_err(dev, "pin_cfg[%d]: invalid row number %d\n", @@ -571,13 +593,25 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata, return false; } (*num_rows)++; - } else { + break; + + case PIN_CFG_COL: if (pin_cfg->num >= KBC_MAX_COL) { dev_err(dev, "pin_cfg[%d]: invalid column number %d\n", i, pin_cfg->num); return false; } + break; + + case PIN_CFG_IGNORE: + break; + + default: + dev_err(dev, + "pin_cfg[%d]: invalid entry type %d\n", + pin_cfg->type, pin_cfg->num); + return false; } } @@ -590,24 +624,25 @@ tegra_kbc_dt_parse_pdata(struct platform_device *pdev) { struct tegra_kbc_platform_data *pdata; struct device_node *np = pdev->dev.of_node; + u32 prop; + int i; if (!np) return NULL; - pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); if (!pdata) return NULL; - if (!of_property_read_u32(np, "debounce-delay", &prop)) + if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop)) pdata->debounce_cnt = prop; - if (!of_property_read_u32(np, "repeat-delay", &prop)) + if (!of_property_read_u32(np, "nvidia,repeat-delay-ms", &prop)) pdata->repeat_cnt = prop; - if (of_find_property(np, "needs-ghost-filter", NULL)) + if (of_find_property(np, "nvidia,needs-ghost-filter", NULL)) pdata->use_ghost_filter = true; - if (of_find_property(np, "wakeup-source", NULL)) + if (of_find_property(np, "nvidia,wakeup-source", NULL)) pdata->wakeup = true; /* @@ -616,14 +651,18 @@ tegra_kbc_dt_parse_pdata(struct platform_device *pdev) */ for (i = 0; i < KBC_MAX_ROW; i++) { pdata->pin_cfg[i].num = i; - pdata->pin_cfg[i].is_row = true; + pdata->pin_cfg[i].type = PIN_CFG_ROW; } for (i = 0; i < KBC_MAX_COL; i++) { pdata->pin_cfg[KBC_MAX_ROW + i].num = i; - pdata->pin_cfg[KBC_MAX_ROW + i].is_row = false; + pdata->pin_cfg[KBC_MAX_ROW + i].type = PIN_CFG_COL; } + pdata->keymap_data = matrix_keyboard_of_fill_keymap(np, "linux,keymap"); + + /* FIXME: Add handling of linux,fn-keymap here */ + return pdata; } #else @@ -759,6 +798,9 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, kbc); device_init_wakeup(&pdev->dev, pdata->wakeup); + if (!pdev->dev.platform_data) + matrix_keyboard_of_free_keymap(pdata->keymap_data); + return 0; err_free_irq: @@ -773,8 +815,10 @@ err_free_mem: input_free_device(input_dev); kfree(kbc); err_free_pdata: - if (!pdev->dev.platform_data) + if (!pdev->dev.platform_data) { + matrix_keyboard_of_free_keymap(pdata->keymap_data); kfree(pdata); + } return err; } @@ -831,6 +875,8 @@ static int tegra_kbc_suspend(struct device *dev) msleep(30); kbc->keypress_caused_wake = false; + /* Enable keypress interrupt before going into suspend. */ + tegra_kbc_set_keypress_interrupt(kbc, true); enable_irq(kbc->irq); enable_irq_wake(kbc->irq); } else { @@ -852,6 +898,8 @@ static int tegra_kbc_resume(struct device *dev) if (device_may_wakeup(&pdev->dev)) { disable_irq_wake(kbc->irq); tegra_kbc_setup_wakekeys(kbc, false); + /* We will use fifo interrupts for key detection. */ + tegra_kbc_set_keypress_interrupt(kbc, false); /* Restore the resident time of continuous polling mode. */ writel(kbc->cp_to_wkup_dly, kbc->mmio + KBC_TO_CNT_0); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 8f675ae20916..2d787796bf50 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -134,6 +134,18 @@ config INPUT_MAX8925_ONKEY To compile this driver as a module, choose M here: the module will be called max8925_onkey. +config INPUT_MAX8997_HAPTIC + tristate "MAXIM MAX8997 haptic controller support" + depends on HAVE_PWM && MFD_MAX8997 + select INPUT_FF_MEMLESS + help + This option enables device driver support for the haptic controller + on MAXIM MAX8997 chip. This driver supports ff-memless interface + from input framework. + + To compile this driver as module, choose M here: the + module will be called max8997-haptic. + config INPUT_MC13783_PWRBUTTON tristate "MC13783 ON buttons" depends on MFD_MC13783 @@ -415,7 +427,7 @@ config INPUT_PCF8574 tristate "PCF8574 Keypad input device" depends on I2C && EXPERIMENTAL help - Say Y here if you want to support a keypad connetced via I2C + Say Y here if you want to support a keypad connected via I2C with a PCF8574. To compile this driver as a module, choose M here: the @@ -455,6 +467,16 @@ config INPUT_RB532_BUTTON To compile this driver as a module, choose M here: the module will be called rb532_button. +config INPUT_DA9052_ONKEY + tristate "Dialog DA9052/DA9053 Onkey" + depends on PMIC_DA9052 + help + Support the ONKEY of Dialog DA9052 PMICs as an input device + reporting power button status. + + To compile this driver as a module, choose M here: the + module will be called da9052_onkey. + config INPUT_DM355EVM tristate "TI DaVinci DM355 EVM Keypad and IR Remote" depends on MFD_DM355EVM_MSP diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 46671a875b91..f55cdf4916fa 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_INPUT_CM109) += cm109.o obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o +obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o @@ -30,6 +31,7 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o +obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o obj-$(CONFIG_INPUT_MMA8450) += mma8450.o obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o diff --git a/drivers/input/misc/ad714x-i2c.c b/drivers/input/misc/ad714x-i2c.c index 56810fb4eadd..c8a79015472a 100644 --- a/drivers/input/misc/ad714x-i2c.c +++ b/drivers/input/misc/ad714x-i2c.c @@ -116,17 +116,7 @@ static struct i2c_driver ad714x_i2c_driver = { .id_table = ad714x_id, }; -static int __init ad714x_i2c_init(void) -{ - return i2c_add_driver(&ad714x_i2c_driver); -} -module_init(ad714x_i2c_init); - -static void __exit ad714x_i2c_exit(void) -{ - i2c_del_driver(&ad714x_i2c_driver); -} -module_exit(ad714x_i2c_exit); +module_i2c_driver(ad714x_i2c_driver); MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor I2C Bus Driver"); MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); diff --git a/drivers/input/misc/ad714x-spi.c b/drivers/input/misc/ad714x-spi.c index 875b50811361..75f6136d608e 100644 --- a/drivers/input/misc/ad714x-spi.c +++ b/drivers/input/misc/ad714x-spi.c @@ -123,17 +123,7 @@ static struct spi_driver ad714x_spi_driver = { .remove = __devexit_p(ad714x_spi_remove), }; -static __init int ad714x_spi_init(void) -{ - return spi_register_driver(&ad714x_spi_driver); -} -module_init(ad714x_spi_init); - -static __exit void ad714x_spi_exit(void) -{ - spi_unregister_driver(&ad714x_spi_driver); -} -module_exit(ad714x_spi_exit); +module_spi_driver(ad714x_spi_driver); MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor SPI Bus Driver"); MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c index ccacf2bb06a4..dd1d1c145a7f 100644 --- a/drivers/input/misc/adxl34x-i2c.c +++ b/drivers/input/misc/adxl34x-i2c.c @@ -148,17 +148,7 @@ static struct i2c_driver adxl34x_driver = { .id_table = adxl34x_id, }; -static int __init adxl34x_i2c_init(void) -{ - return i2c_add_driver(&adxl34x_driver); -} -module_init(adxl34x_i2c_init); - -static void __exit adxl34x_i2c_exit(void) -{ - i2c_del_driver(&adxl34x_driver); -} -module_exit(adxl34x_i2c_exit); +module_i2c_driver(adxl34x_driver); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer I2C Bus Driver"); diff --git a/drivers/input/misc/adxl34x-spi.c b/drivers/input/misc/adxl34x-spi.c index 34d401efd4a1..820a802a1e6e 100644 --- a/drivers/input/misc/adxl34x-spi.c +++ b/drivers/input/misc/adxl34x-spi.c @@ -129,17 +129,7 @@ static struct spi_driver adxl34x_driver = { .remove = __devexit_p(adxl34x_spi_remove), }; -static int __init adxl34x_spi_init(void) -{ - return spi_register_driver(&adxl34x_driver); -} -module_init(adxl34x_spi_init); - -static void __exit adxl34x_spi_exit(void) -{ - spi_unregister_driver(&adxl34x_driver); -} -module_exit(adxl34x_spi_exit); +module_spi_driver(adxl34x_driver); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer SPI Bus Driver"); diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c index 8f55b54352b6..e2f1e9f952b1 100644 --- a/drivers/input/misc/bma150.c +++ b/drivers/input/misc/bma150.c @@ -673,19 +673,8 @@ static struct i2c_driver bma150_driver = { .remove = __devexit_p(bma150_remove), }; -static int __init BMA150_init(void) -{ - return i2c_add_driver(&bma150_driver); -} - -static void __exit BMA150_exit(void) -{ - i2c_del_driver(&bma150_driver); -} +module_i2c_driver(bma150_driver); MODULE_AUTHOR("Albert Zhang <xu.zhang@bosch-sensortec.com>"); MODULE_DESCRIPTION("BMA150 driver"); MODULE_LICENSE("GPL"); - -module_init(BMA150_init); -module_exit(BMA150_exit); diff --git a/drivers/input/misc/cma3000_d0x_i2c.c b/drivers/input/misc/cma3000_d0x_i2c.c index d100cc5c5783..fe9b85f07792 100644 --- a/drivers/input/misc/cma3000_d0x_i2c.c +++ b/drivers/input/misc/cma3000_d0x_i2c.c @@ -125,18 +125,7 @@ static struct i2c_driver cma3000_i2c_driver = { }, }; -static int __init cma3000_i2c_init(void) -{ - return i2c_add_driver(&cma3000_i2c_driver); -} - -static void __exit cma3000_i2c_exit(void) -{ - i2c_del_driver(&cma3000_i2c_driver); -} - -module_init(cma3000_i2c_init); -module_exit(cma3000_i2c_exit); +module_i2c_driver(cma3000_i2c_driver); MODULE_DESCRIPTION("CMA3000-D0x Accelerometer I2C Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/da9052_onkey.c b/drivers/input/misc/da9052_onkey.c new file mode 100644 index 000000000000..34aebb8cd080 --- /dev/null +++ b/drivers/input/misc/da9052_onkey.c @@ -0,0 +1,169 @@ +/* + * ON pin driver for Dialog DA9052 PMICs + * + * Copyright(c) 2012 Dialog Semiconductor Ltd. + * + * Author: David Dajun Chen <dchen@diasemi.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/init.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> + +#include <linux/mfd/da9052/da9052.h> +#include <linux/mfd/da9052/reg.h> + +struct da9052_onkey { + struct da9052 *da9052; + struct input_dev *input; + struct delayed_work work; + unsigned int irq; +}; + +static void da9052_onkey_query(struct da9052_onkey *onkey) +{ + int key_stat; + + key_stat = da9052_reg_read(onkey->da9052, DA9052_EVENT_B_REG); + if (key_stat < 0) { + dev_err(onkey->da9052->dev, + "Failed to read onkey event %d\n", key_stat); + } else { + /* + * Since interrupt for deassertion of ONKEY pin is not + * generated, onkey event state determines the onkey + * button state. + */ + key_stat &= DA9052_EVENTB_ENONKEY; + input_report_key(onkey->input, KEY_POWER, key_stat); + input_sync(onkey->input); + } + + /* + * Interrupt is generated only when the ONKEY pin is asserted. + * Hence the deassertion of the pin is simulated through work queue. + */ + if (key_stat) + schedule_delayed_work(&onkey->work, msecs_to_jiffies(50)); +} + +static void da9052_onkey_work(struct work_struct *work) +{ + struct da9052_onkey *onkey = container_of(work, struct da9052_onkey, + work.work); + + da9052_onkey_query(onkey); +} + +static irqreturn_t da9052_onkey_irq(int irq, void *data) +{ + struct da9052_onkey *onkey = data; + + da9052_onkey_query(onkey); + + return IRQ_HANDLED; +} + +static int __devinit da9052_onkey_probe(struct platform_device *pdev) +{ + struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent); + struct da9052_onkey *onkey; + struct input_dev *input_dev; + int irq; + int error; + + if (!da9052) { + dev_err(&pdev->dev, "Failed to get the driver's data\n"); + return -EINVAL; + } + + irq = platform_get_irq_byname(pdev, "ONKEY"); + if (irq < 0) { + dev_err(&pdev->dev, + "Failed to get an IRQ for input device, %d\n", irq); + return -EINVAL; + } + + onkey = kzalloc(sizeof(*onkey), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!onkey || !input_dev) { + dev_err(&pdev->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + onkey->input = input_dev; + onkey->da9052 = da9052; + onkey->irq = irq; + INIT_DELAYED_WORK(&onkey->work, da9052_onkey_work); + + input_dev->name = "da9052-onkey"; + input_dev->phys = "da9052-onkey/input0"; + input_dev->dev.parent = &pdev->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY); + __set_bit(KEY_POWER, input_dev->keybit); + + error = request_threaded_irq(onkey->irq, NULL, da9052_onkey_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "ONKEY", onkey); + if (error < 0) { + dev_err(onkey->da9052->dev, + "Failed to register ONKEY IRQ %d, error = %d\n", + onkey->irq, error); + goto err_free_mem; + } + + error = input_register_device(onkey->input); + if (error) { + dev_err(&pdev->dev, "Unable to register input device, %d\n", + error); + goto err_free_irq; + } + + platform_set_drvdata(pdev, onkey); + return 0; + +err_free_irq: + free_irq(onkey->irq, onkey); + cancel_delayed_work_sync(&onkey->work); +err_free_mem: + input_free_device(input_dev); + kfree(onkey); + + return error; +} + +static int __devexit da9052_onkey_remove(struct platform_device *pdev) +{ + struct da9052_onkey *onkey = platform_get_drvdata(pdev); + + free_irq(onkey->irq, onkey); + cancel_delayed_work_sync(&onkey->work); + + input_unregister_device(onkey->input); + kfree(onkey); + + return 0; +} + +static struct platform_driver da9052_onkey_driver = { + .probe = da9052_onkey_probe, + .remove = __devexit_p(da9052_onkey_remove), + .driver = { + .name = "da9052-onkey", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(da9052_onkey_driver); + +MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); +MODULE_DESCRIPTION("Onkey driver for DA9052"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9052-onkey"); diff --git a/drivers/input/misc/gp2ap002a00f.c b/drivers/input/misc/gp2ap002a00f.c index 71fba8c2fc66..b6664cfa340a 100644 --- a/drivers/input/misc/gp2ap002a00f.c +++ b/drivers/input/misc/gp2ap002a00f.c @@ -281,18 +281,7 @@ static struct i2c_driver gp2a_i2c_driver = { .id_table = gp2a_i2c_id, }; -static int __init gp2a_init(void) -{ - return i2c_add_driver(&gp2a_i2c_driver); -} - -static void __exit gp2a_exit(void) -{ - i2c_del_driver(&gp2a_i2c_driver); -} - -module_init(gp2a_init); -module_exit(gp2a_exit); +module_i2c_driver(gp2a_i2c_driver); MODULE_AUTHOR("Courtney Cavin <courtney.cavin@sonyericsson.com>"); MODULE_DESCRIPTION("Sharp GP2AP002A00F I2C Proximity/Opto sensor driver"); diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c index 783597a9a64a..f46139f19ff1 100644 --- a/drivers/input/misc/kxtj9.c +++ b/drivers/input/misc/kxtj9.c @@ -41,6 +41,14 @@ #define PC1_ON (1 << 7) /* Data ready funtion enable bit: set during probe if using irq mode */ #define DRDYE (1 << 5) +/* DATA CONTROL REGISTER BITS */ +#define ODR12_5F 0 +#define ODR25F 1 +#define ODR50F 2 +#define ODR100F 3 +#define ODR200F 4 +#define ODR400F 5 +#define ODR800F 6 /* INTERRUPT CONTROL REGISTER 1 BITS */ /* Set these during probe if using irq mode */ #define KXTJ9_IEL (1 << 3) @@ -116,9 +124,13 @@ static void kxtj9_report_acceleration_data(struct kxtj9_data *tj9) if (err < 0) dev_err(&tj9->client->dev, "accelerometer data read failed\n"); - x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]) >> tj9->shift; - y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]) >> tj9->shift; - z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]) >> tj9->shift; + x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]); + y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]); + z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]); + + x >>= tj9->shift; + y >>= tj9->shift; + z >>= tj9->shift; input_report_abs(tj9->input_dev, ABS_X, tj9->pdata.negate_x ? -x : x); input_report_abs(tj9->input_dev, ABS_Y, tj9->pdata.negate_y ? -y : y); @@ -487,7 +499,7 @@ static int __devinit kxtj9_verify(struct kxtj9_data *tj9) goto out; } - retval = retval != 0x06 ? -EIO : 0; + retval = (retval != 0x07 && retval != 0x08) ? -EIO : 0; out: kxtj9_device_power_off(tj9); @@ -537,7 +549,7 @@ static int __devinit kxtj9_probe(struct i2c_client *client, i2c_set_clientdata(client, tj9); tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range; - tj9->data_ctrl = tj9->pdata.data_odr_init; + tj9->last_poll_interval = tj9->pdata.init_interval; if (client->irq) { /* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */ @@ -655,17 +667,7 @@ static struct i2c_driver kxtj9_driver = { .id_table = kxtj9_id, }; -static int __init kxtj9_init(void) -{ - return i2c_add_driver(&kxtj9_driver); -} -module_init(kxtj9_init); - -static void __exit kxtj9_exit(void) -{ - i2c_del_driver(&kxtj9_driver); -} -module_exit(kxtj9_exit); +module_i2c_driver(kxtj9_driver); MODULE_DESCRIPTION("KXTJ9 accelerometer driver"); MODULE_AUTHOR("Chris Hudson <chudson@kionix.com>"); diff --git a/drivers/input/misc/max8925_onkey.c b/drivers/input/misc/max8925_onkey.c index 23cf08271049..0a12b74140d3 100644 --- a/drivers/input/misc/max8925_onkey.c +++ b/drivers/input/misc/max8925_onkey.c @@ -1,5 +1,5 @@ /** - * max8925_onkey.c - MAX8925 ONKEY driver + * MAX8925 ONKEY driver * * Copyright (C) 2009 Marvell International Ltd. * Haojian Zhuang <haojian.zhuang@marvell.com> @@ -35,7 +35,7 @@ struct max8925_onkey_info { struct input_dev *idev; struct i2c_client *i2c; struct device *dev; - int irq[2]; + unsigned int irq[2]; }; /* @@ -46,17 +46,14 @@ struct max8925_onkey_info { static irqreturn_t max8925_onkey_handler(int irq, void *data) { struct max8925_onkey_info *info = data; - int ret, event; - - ret = max8925_reg_read(info->i2c, MAX8925_ON_OFF_STATUS); - if (ret & SW_INPUT) - event = 1; - else - event = 0; - input_report_key(info->idev, KEY_POWER, event); + int state; + + state = max8925_reg_read(info->i2c, MAX8925_ON_OFF_STATUS); + + input_report_key(info->idev, KEY_POWER, state & SW_INPUT); input_sync(info->idev); - dev_dbg(info->dev, "onkey event:%d\n", event); + dev_dbg(info->dev, "onkey state:%d\n", state); /* Enable hardreset to halt if system isn't shutdown on time */ max8925_set_bits(info->i2c, MAX8925_SYSENSEL, @@ -69,6 +66,7 @@ static int __devinit max8925_onkey_probe(struct platform_device *pdev) { struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); struct max8925_onkey_info *info; + struct input_dev *input; int irq[2], error; irq[0] = platform_get_irq(pdev, 0); @@ -76,6 +74,7 @@ static int __devinit max8925_onkey_probe(struct platform_device *pdev) dev_err(&pdev->dev, "No IRQ resource!\n"); return -EINVAL; } + irq[1] = platform_get_irq(pdev, 1); if (irq[1] < 0) { dev_err(&pdev->dev, "No IRQ resource!\n"); @@ -83,11 +82,24 @@ static int __devinit max8925_onkey_probe(struct platform_device *pdev) } info = kzalloc(sizeof(struct max8925_onkey_info), GFP_KERNEL); - if (!info) - return -ENOMEM; + input = input_allocate_device(); + if (!info || !input) { + error = -ENOMEM; + goto err_free_mem; + } + info->idev = input; info->i2c = chip->i2c; info->dev = &pdev->dev; + info->irq[0] = irq[0]; + info->irq[1] = irq[1]; + + input->name = "max8925_on"; + input->phys = "max8925_on/input0"; + input->id.bustype = BUS_I2C; + input->dev.parent = &pdev->dev; + input_set_capability(input, EV_KEY, KEY_POWER); + irq[0] += chip->irq_base; irq[1] += chip->irq_base; @@ -96,60 +108,46 @@ static int __devinit max8925_onkey_probe(struct platform_device *pdev) if (error < 0) { dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", irq[0], error); - goto out; + goto err_free_mem; } + error = request_threaded_irq(irq[1], NULL, max8925_onkey_handler, IRQF_ONESHOT, "onkey-up", info); if (error < 0) { dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", irq[1], error); - goto out_irq; + goto err_free_irq0; } - info->idev = input_allocate_device(); - if (!info->idev) { - dev_err(chip->dev, "Failed to allocate input dev\n"); - error = -ENOMEM; - goto out_input; - } - - info->idev->name = "max8925_on"; - info->idev->phys = "max8925_on/input0"; - info->idev->id.bustype = BUS_I2C; - info->idev->dev.parent = &pdev->dev; - info->irq[0] = irq[0]; - info->irq[1] = irq[1]; - info->idev->evbit[0] = BIT_MASK(EV_KEY); - info->idev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); - - error = input_register_device(info->idev); if (error) { dev_err(chip->dev, "Can't register input device: %d\n", error); - goto out_reg; + goto err_free_irq1; } platform_set_drvdata(pdev, info); + device_init_wakeup(&pdev->dev, 1); return 0; -out_reg: - input_free_device(info->idev); -out_input: - free_irq(info->irq[1], info); -out_irq: - free_irq(info->irq[0], info); -out: +err_free_irq1: + free_irq(irq[1], info); +err_free_irq0: + free_irq(irq[0], info); +err_free_mem: + input_free_device(input); kfree(info); + return error; } static int __devexit max8925_onkey_remove(struct platform_device *pdev) { struct max8925_onkey_info *info = platform_get_drvdata(pdev); + struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); - free_irq(info->irq[0], info); - free_irq(info->irq[1], info); + free_irq(info->irq[0] + chip->irq_base, info); + free_irq(info->irq[1] + chip->irq_base, info); input_unregister_device(info->idev); kfree(info); @@ -158,10 +156,43 @@ static int __devexit max8925_onkey_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int max8925_onkey_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max8925_onkey_info *info = platform_get_drvdata(pdev); + struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); + + if (device_may_wakeup(dev)) { + chip->wakeup_flag |= 1 << info->irq[0]; + chip->wakeup_flag |= 1 << info->irq[1]; + } + + return 0; +} + +static int max8925_onkey_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max8925_onkey_info *info = platform_get_drvdata(pdev); + struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); + + if (device_may_wakeup(dev)) { + chip->wakeup_flag &= ~(1 << info->irq[0]); + chip->wakeup_flag &= ~(1 << info->irq[1]); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(max8925_onkey_pm_ops, max8925_onkey_suspend, max8925_onkey_resume); + static struct platform_driver max8925_onkey_driver = { .driver = { .name = "max8925-onkey", .owner = THIS_MODULE, + .pm = &max8925_onkey_pm_ops, }, .probe = max8925_onkey_probe, .remove = __devexit_p(max8925_onkey_remove), diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c new file mode 100644 index 000000000000..05b7b8bfaf0a --- /dev/null +++ b/drivers/input/misc/max8997_haptic.c @@ -0,0 +1,407 @@ +/* + * MAX8997-haptic controller driver + * + * Copyright (C) 2012 Samsung Electronics + * Donggeun Kim <dg77.kim@samsung.com> + * + * This program is not provided / owned by Maxim Integrated Products. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/pwm.h> +#include <linux/input.h> +#include <linux/mfd/max8997-private.h> +#include <linux/mfd/max8997.h> +#include <linux/regulator/consumer.h> + +/* Haptic configuration 2 register */ +#define MAX8997_MOTOR_TYPE_SHIFT 7 +#define MAX8997_ENABLE_SHIFT 6 +#define MAX8997_MODE_SHIFT 5 + +/* Haptic driver configuration register */ +#define MAX8997_CYCLE_SHIFT 6 +#define MAX8997_SIG_PERIOD_SHIFT 4 +#define MAX8997_SIG_DUTY_SHIFT 2 +#define MAX8997_PWM_DUTY_SHIFT 0 + +struct max8997_haptic { + struct device *dev; + struct i2c_client *client; + struct input_dev *input_dev; + struct regulator *regulator; + + struct work_struct work; + struct mutex mutex; + + bool enabled; + unsigned int level; + + struct pwm_device *pwm; + unsigned int pwm_period; + enum max8997_haptic_pwm_divisor pwm_divisor; + + enum max8997_haptic_motor_type type; + enum max8997_haptic_pulse_mode mode; + + unsigned int internal_mode_pattern; + unsigned int pattern_cycle; + unsigned int pattern_signal_period; +}; + +static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip) +{ + int ret = 0; + + if (chip->mode == MAX8997_EXTERNAL_MODE) { + unsigned int duty = chip->pwm_period * chip->level / 100; + ret = pwm_config(chip->pwm, duty, chip->pwm_period); + } else { + int i; + u8 duty_index = 0; + + for (i = 0; i <= 64; i++) { + if (chip->level <= i * 100 / 64) { + duty_index = i; + break; + } + } + switch (chip->internal_mode_pattern) { + case 0: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC1, duty_index); + break; + case 1: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC2, duty_index); + break; + case 2: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC3, duty_index); + break; + case 3: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC4, duty_index); + break; + default: + break; + } + } + return ret; +} + +static void max8997_haptic_configure(struct max8997_haptic *chip) +{ + u8 value; + + value = chip->type << MAX8997_MOTOR_TYPE_SHIFT | + chip->enabled << MAX8997_ENABLE_SHIFT | + chip->mode << MAX8997_MODE_SHIFT | chip->pwm_divisor; + max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_CONF2, value); + + if (chip->mode == MAX8997_INTERNAL_MODE && chip->enabled) { + value = chip->internal_mode_pattern << MAX8997_CYCLE_SHIFT | + chip->internal_mode_pattern << MAX8997_SIG_PERIOD_SHIFT | + chip->internal_mode_pattern << MAX8997_SIG_DUTY_SHIFT | + chip->internal_mode_pattern << MAX8997_PWM_DUTY_SHIFT; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_DRVCONF, value); + + switch (chip->internal_mode_pattern) { + case 0: + value = chip->pattern_cycle << 4; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF1, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF1, value); + break; + + case 1: + value = chip->pattern_cycle; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF1, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF2, value); + break; + + case 2: + value = chip->pattern_cycle << 4; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF2, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF3, value); + break; + + case 3: + value = chip->pattern_cycle; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF2, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF4, value); + break; + + default: + break; + } + } +} + +static void max8997_haptic_enable(struct max8997_haptic *chip) +{ + int error; + + mutex_lock(&chip->mutex); + + error = max8997_haptic_set_duty_cycle(chip); + if (error) { + dev_err(chip->dev, "set_pwm_cycle failed, error: %d\n", error); + goto out; + } + + if (!chip->enabled) { + chip->enabled = true; + regulator_enable(chip->regulator); + max8997_haptic_configure(chip); + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_enable(chip->pwm); + } + +out: + mutex_unlock(&chip->mutex); +} + +static void max8997_haptic_disable(struct max8997_haptic *chip) +{ + mutex_lock(&chip->mutex); + + if (chip->enabled) { + chip->enabled = false; + max8997_haptic_configure(chip); + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_disable(chip->pwm); + regulator_disable(chip->regulator); + } + + mutex_unlock(&chip->mutex); +} + +static void max8997_haptic_play_effect_work(struct work_struct *work) +{ + struct max8997_haptic *chip = + container_of(work, struct max8997_haptic, work); + + if (chip->level) + max8997_haptic_enable(chip); + else + max8997_haptic_disable(chip); +} + +static int max8997_haptic_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct max8997_haptic *chip = input_get_drvdata(dev); + + chip->level = effect->u.rumble.strong_magnitude; + if (!chip->level) + chip->level = effect->u.rumble.weak_magnitude; + + schedule_work(&chip->work); + + return 0; +} + +static void max8997_haptic_close(struct input_dev *dev) +{ + struct max8997_haptic *chip = input_get_drvdata(dev); + + cancel_work_sync(&chip->work); + max8997_haptic_disable(chip); +} + +static int __devinit max8997_haptic_probe(struct platform_device *pdev) +{ + struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); + const struct max8997_platform_data *pdata = + dev_get_platdata(iodev->dev); + const struct max8997_haptic_platform_data *haptic_pdata = + pdata->haptic_pdata; + struct max8997_haptic *chip; + struct input_dev *input_dev; + int error; + + if (!haptic_pdata) { + dev_err(&pdev->dev, "no haptic platform data\n"); + return -EINVAL; + } + + chip = kzalloc(sizeof(struct max8997_haptic), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!chip || !input_dev) { + dev_err(&pdev->dev, "unable to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + INIT_WORK(&chip->work, max8997_haptic_play_effect_work); + mutex_init(&chip->mutex); + + chip->client = iodev->haptic; + chip->dev = &pdev->dev; + chip->input_dev = input_dev; + chip->pwm_period = haptic_pdata->pwm_period; + chip->type = haptic_pdata->type; + chip->mode = haptic_pdata->mode; + chip->pwm_divisor = haptic_pdata->pwm_divisor; + + switch (chip->mode) { + case MAX8997_INTERNAL_MODE: + chip->internal_mode_pattern = + haptic_pdata->internal_mode_pattern; + chip->pattern_cycle = haptic_pdata->pattern_cycle; + chip->pattern_signal_period = + haptic_pdata->pattern_signal_period; + break; + + case MAX8997_EXTERNAL_MODE: + chip->pwm = pwm_request(haptic_pdata->pwm_channel_id, + "max8997-haptic"); + if (IS_ERR(chip->pwm)) { + error = PTR_ERR(chip->pwm); + dev_err(&pdev->dev, + "unable to request PWM for haptic, error: %d\n", + error); + goto err_free_mem; + } + break; + + default: + dev_err(&pdev->dev, + "Invalid chip mode specified (%d)\n", chip->mode); + error = -EINVAL; + goto err_free_mem; + } + + chip->regulator = regulator_get(&pdev->dev, "inmotor"); + if (IS_ERR(chip->regulator)) { + error = PTR_ERR(chip->regulator); + dev_err(&pdev->dev, + "unable to get regulator, error: %d\n", + error); + goto err_free_pwm; + } + + input_dev->name = "max8997-haptic"; + input_dev->id.version = 1; + input_dev->dev.parent = &pdev->dev; + input_dev->close = max8997_haptic_close; + input_set_drvdata(input_dev, chip); + input_set_capability(input_dev, EV_FF, FF_RUMBLE); + + error = input_ff_create_memless(input_dev, NULL, + max8997_haptic_play_effect); + if (error) { + dev_err(&pdev->dev, + "unable to create FF device, error: %d\n", + error); + goto err_put_regulator; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, + "unable to register input device, error: %d\n", + error); + goto err_destroy_ff; + } + + platform_set_drvdata(pdev, chip); + return 0; + +err_destroy_ff: + input_ff_destroy(input_dev); +err_put_regulator: + regulator_put(chip->regulator); +err_free_pwm: + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_free(chip->pwm); +err_free_mem: + input_free_device(input_dev); + kfree(chip); + + return error; +} + +static int __devexit max8997_haptic_remove(struct platform_device *pdev) +{ + struct max8997_haptic *chip = platform_get_drvdata(pdev); + + input_unregister_device(chip->input_dev); + regulator_put(chip->regulator); + + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_free(chip->pwm); + + kfree(chip); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int max8997_haptic_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max8997_haptic *chip = platform_get_drvdata(pdev); + + max8997_haptic_disable(chip); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(max8997_haptic_pm_ops, max8997_haptic_suspend, NULL); + +static const struct platform_device_id max8997_haptic_id[] = { + { "max8997-haptic", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, max8997_haptic_id); + +static struct platform_driver max8997_haptic_driver = { + .driver = { + .name = "max8997-haptic", + .owner = THIS_MODULE, + .pm = &max8997_haptic_pm_ops, + }, + .probe = max8997_haptic_probe, + .remove = __devexit_p(max8997_haptic_remove), + .id_table = max8997_haptic_id, +}; +module_platform_driver(max8997_haptic_driver); + +MODULE_ALIAS("platform:max8997-haptic"); +MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); +MODULE_DESCRIPTION("max8997_haptic driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c index 4d60080bb5d5..873ebced544e 100644 --- a/drivers/input/misc/mma8450.c +++ b/drivers/input/misc/mma8450.c @@ -247,17 +247,7 @@ static struct i2c_driver mma8450_driver = { .id_table = mma8450_id, }; -static int __init mma8450_init(void) -{ - return i2c_add_driver(&mma8450_driver); -} -module_init(mma8450_init); - -static void __exit mma8450_exit(void) -{ - i2c_del_driver(&mma8450_driver); -} -module_exit(mma8450_exit); +module_i2c_driver(mma8450_driver); MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_DESCRIPTION("MMA8450 3-Axis Accelerometer Driver"); diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c index 208d1a1cc7f3..5403c571b6a5 100644 --- a/drivers/input/misc/mpu3050.c +++ b/drivers/input/misc/mpu3050.c @@ -475,17 +475,7 @@ static struct i2c_driver mpu3050_i2c_driver = { .id_table = mpu3050_ids, }; -static int __init mpu3050_init(void) -{ - return i2c_add_driver(&mpu3050_i2c_driver); -} -module_init(mpu3050_init); - -static void __exit mpu3050_exit(void) -{ - i2c_del_driver(&mpu3050_i2c_driver); -} -module_exit(mpu3050_exit); +module_i2c_driver(mpu3050_i2c_driver); MODULE_AUTHOR("Wistron Corp."); MODULE_DESCRIPTION("MPU3050 Tri-axis gyroscope driver"); diff --git a/drivers/input/misc/pcf8574_keypad.c b/drivers/input/misc/pcf8574_keypad.c index 08be1a355956..544c6635abe9 100644 --- a/drivers/input/misc/pcf8574_keypad.c +++ b/drivers/input/misc/pcf8574_keypad.c @@ -216,17 +216,7 @@ static struct i2c_driver pcf8574_kp_driver = { .id_table = pcf8574_kp_id, }; -static int __init pcf8574_kp_init(void) -{ - return i2c_add_driver(&pcf8574_kp_driver); -} -module_init(pcf8574_kp_init); - -static void __exit pcf8574_kp_exit(void) -{ - i2c_del_driver(&pcf8574_kp_driver); -} -module_exit(pcf8574_kp_exit); +module_i2c_driver(pcf8574_kp_driver); MODULE_AUTHOR("Michael Hennerich"); MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574"); diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c index f3bc4189a7ba..fc0ed9b43424 100644 --- a/drivers/input/misc/twl4030-vibra.c +++ b/drivers/input/misc/twl4030-vibra.c @@ -172,7 +172,7 @@ static void twl4030_vibra_close(struct input_dev *input) } /*** Module ***/ -#if CONFIG_PM_SLEEP +#ifdef CONFIG_PM_SLEEP static int twl4030_vibra_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 9c1e6ee83531..9b8db821d5f0 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -322,4 +322,21 @@ config MOUSE_SYNAPTICS_I2C To compile this driver as a module, choose M here: the module will be called synaptics_i2c. +config MOUSE_SYNAPTICS_USB + tristate "Synaptics USB device support" + depends on USB_ARCH_HAS_HCD + select USB + help + Say Y here if you want to use a Synaptics USB touchpad or pointing + stick. + + While these devices emulate an USB mouse by default and can be used + with standard usbhid driver, this driver, together with its X.Org + counterpart, allows you to fully utilize capabilities of the device. + More information can be found at: + <http://jan-steinhoff.de/linux/synaptics-usb.html> + + To compile this driver as a module, choose M here: the + module will be called synaptics_usb. + endif diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 570c84a4a654..4718effeb8d9 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.o obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o +obj-$(CONFIG_MOUSE_SYNAPTICS_USB) += synaptics_usb.o obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o psmouse-objs := psmouse-base.o synaptics.o diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index 927e479c2649..f9e2758b9f46 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -433,6 +433,7 @@ static void setup_events_to_report(struct input_dev *input_dev, __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit); __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); if (cfg->caps & HAS_INTEGRATED_BUTTON) __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c index 1c5d521de600..575f880727fe 100644 --- a/drivers/input/mouse/hgpk.c +++ b/drivers/input/mouse/hgpk.c @@ -640,7 +640,6 @@ static int hgpk_reset_device(struct psmouse *psmouse, bool recalibrate) static int hgpk_force_recalibrate(struct psmouse *psmouse) { - struct ps2dev *ps2dev = &psmouse->ps2dev; struct hgpk_data *priv = psmouse->private; int err; @@ -669,12 +668,9 @@ static int hgpk_force_recalibrate(struct psmouse *psmouse) * we don't have a good way to deal with it. The 2s window stuff * (below) is our best option for now. */ - - if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) + if (psmouse_activate(psmouse)) return -1; - psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); - if (tpdebug) psmouse_dbg(psmouse, "touchpad reactivated\n"); @@ -733,8 +729,7 @@ static int hgpk_toggle_powersave(struct psmouse *psmouse, int enable) } /* should be all set, enable the touchpad */ - ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE); - psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); + psmouse_activate(psmouse); psmouse_dbg(psmouse, "Touchpad powered up.\n"); } else { psmouse_dbg(psmouse, "Powering off touchpad.\n"); diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index e6c9931f02c7..22fe2547e169 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -1092,28 +1092,33 @@ static void psmouse_initialize(struct psmouse *psmouse) * psmouse_activate() enables the mouse so that we get motion reports from it. */ -static void psmouse_activate(struct psmouse *psmouse) +int psmouse_activate(struct psmouse *psmouse) { - if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) + if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { psmouse_warn(psmouse, "Failed to enable mouse on %s\n", psmouse->ps2dev.serio->phys); + return -1; + } psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); + return 0; } - /* * psmouse_deactivate() puts the mouse into poll mode so that we don't get motion * reports from it unless we explicitly request it. */ -static void psmouse_deactivate(struct psmouse *psmouse) +int psmouse_deactivate(struct psmouse *psmouse) { - if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) + if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) { psmouse_warn(psmouse, "Failed to deactivate mouse on %s\n", psmouse->ps2dev.serio->phys); + return -1; + } psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + return 0; } diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 6a417092d010..fe1df231ba4c 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -105,6 +105,8 @@ int psmouse_reset(struct psmouse *psmouse); void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state); void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution); psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse); +int psmouse_activate(struct psmouse *psmouse); +int psmouse_deactivate(struct psmouse *psmouse); struct psmouse_attribute { struct device_attribute dattr; diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c index e36847de7617..2a77a52d2e62 100644 --- a/drivers/input/mouse/sentelic.c +++ b/drivers/input/mouse/sentelic.c @@ -90,8 +90,7 @@ static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val) * to do that for writes because sysfs set helper does this for * us. */ - ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); - psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + psmouse_deactivate(psmouse); ps2_begin_command(ps2dev); @@ -128,8 +127,7 @@ static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val) out: ps2_end_command(ps2dev); - ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); - psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); + psmouse_activate(psmouse); dev_dbg(&ps2dev->serio->dev, "READ REG: 0x%02x is 0x%02x (rc = %d)\n", reg_addr, *reg_val, rc); return rc; @@ -213,8 +211,7 @@ static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val) unsigned char param[3]; int rc = -1; - ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); - psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + psmouse_deactivate(psmouse); ps2_begin_command(ps2dev); @@ -239,8 +236,7 @@ static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val) out: ps2_end_command(ps2dev); - ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); - psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); + psmouse_activate(psmouse); dev_dbg(&ps2dev->serio->dev, "READ PAGE REG: 0x%02x (rc = %d)\n", *reg_val, rc); return rc; diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c index 1c58aafa523f..f14675702c0f 100644 --- a/drivers/input/mouse/synaptics_i2c.c +++ b/drivers/input/mouse/synaptics_i2c.c @@ -672,18 +672,7 @@ static struct i2c_driver synaptics_i2c_driver = { .id_table = synaptics_i2c_id_table, }; -static int __init synaptics_i2c_init(void) -{ - return i2c_add_driver(&synaptics_i2c_driver); -} - -static void __exit synaptics_i2c_exit(void) -{ - i2c_del_driver(&synaptics_i2c_driver); -} - -module_init(synaptics_i2c_init); -module_exit(synaptics_i2c_exit); +module_i2c_driver(synaptics_i2c_driver); MODULE_DESCRIPTION("Synaptics I2C touchpad driver"); MODULE_AUTHOR("Mike Rapoport, Igor Grinberg, Compulab"); diff --git a/drivers/input/mouse/synaptics_usb.c b/drivers/input/mouse/synaptics_usb.c new file mode 100644 index 000000000000..3c5eaaa5d154 --- /dev/null +++ b/drivers/input/mouse/synaptics_usb.c @@ -0,0 +1,557 @@ +/* + * USB Synaptics device driver + * + * Copyright (c) 2002 Rob Miller (rob@inpharmatica . co . uk) + * Copyright (c) 2003 Ron Lee (ron@debian.org) + * cPad driver for kernel 2.4 + * + * Copyright (c) 2004 Jan Steinhoff (cpad@jan-steinhoff . de) + * Copyright (c) 2004 Ron Lee (ron@debian.org) + * rewritten for kernel 2.6 + * + * cPad display character device part is not included. It can be found at + * http://jan-steinhoff.de/linux/synaptics-usb.html + * + * Bases on: usb_skeleton.c v2.2 by Greg Kroah-Hartman + * drivers/hid/usbhid/usbmouse.c by Vojtech Pavlik + * drivers/input/mouse/synaptics.c by Peter Osterlund + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Trademarks are the property of their respective owners. + */ + +/* + * There are three different types of Synaptics USB devices: Touchpads, + * touchsticks (or trackpoints), and touchscreens. Touchpads are well supported + * by this driver, touchstick support has not been tested much yet, and + * touchscreens have not been tested at all. + * + * Up to three alternate settings are possible: + * setting 0: one int endpoint for relative movement (used by usbhid.ko) + * setting 1: one int endpoint for absolute finger position + * setting 2 (cPad only): one int endpoint for absolute finger position and + * two bulk endpoints for the display (in/out) + * This driver uses setting 1. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/usb.h> +#include <linux/input.h> +#include <linux/usb/input.h> + +#define USB_VENDOR_ID_SYNAPTICS 0x06cb +#define USB_DEVICE_ID_SYNAPTICS_TP 0x0001 /* Synaptics USB TouchPad */ +#define USB_DEVICE_ID_SYNAPTICS_INT_TP 0x0002 /* Integrated USB TouchPad */ +#define USB_DEVICE_ID_SYNAPTICS_CPAD 0x0003 /* Synaptics cPad */ +#define USB_DEVICE_ID_SYNAPTICS_TS 0x0006 /* Synaptics TouchScreen */ +#define USB_DEVICE_ID_SYNAPTICS_STICK 0x0007 /* Synaptics USB Styk */ +#define USB_DEVICE_ID_SYNAPTICS_WP 0x0008 /* Synaptics USB WheelPad */ +#define USB_DEVICE_ID_SYNAPTICS_COMP_TP 0x0009 /* Composite USB TouchPad */ +#define USB_DEVICE_ID_SYNAPTICS_WTP 0x0010 /* Wireless TouchPad */ +#define USB_DEVICE_ID_SYNAPTICS_DPAD 0x0013 /* DisplayPad */ + +#define SYNUSB_TOUCHPAD (1 << 0) +#define SYNUSB_STICK (1 << 1) +#define SYNUSB_TOUCHSCREEN (1 << 2) +#define SYNUSB_AUXDISPLAY (1 << 3) /* For cPad */ +#define SYNUSB_COMBO (1 << 4) /* Composite device (TP + stick) */ +#define SYNUSB_IO_ALWAYS (1 << 5) + +#define USB_DEVICE_SYNAPTICS(prod, kind) \ + USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, \ + USB_DEVICE_ID_SYNAPTICS_##prod), \ + .driver_info = (kind), + +#define SYNUSB_RECV_SIZE 8 + +#define XMIN_NOMINAL 1472 +#define XMAX_NOMINAL 5472 +#define YMIN_NOMINAL 1408 +#define YMAX_NOMINAL 4448 + +struct synusb { + struct usb_device *udev; + struct usb_interface *intf; + struct urb *urb; + unsigned char *data; + + /* input device related data structures */ + struct input_dev *input; + char name[128]; + char phys[64]; + + /* characteristics of the device */ + unsigned long flags; +}; + +static void synusb_report_buttons(struct synusb *synusb) +{ + struct input_dev *input_dev = synusb->input; + + input_report_key(input_dev, BTN_LEFT, synusb->data[1] & 0x04); + input_report_key(input_dev, BTN_RIGHT, synusb->data[1] & 0x01); + input_report_key(input_dev, BTN_MIDDLE, synusb->data[1] & 0x02); +} + +static void synusb_report_stick(struct synusb *synusb) +{ + struct input_dev *input_dev = synusb->input; + int x, y; + unsigned int pressure; + + pressure = synusb->data[6]; + x = (s16)(be16_to_cpup((__be16 *)&synusb->data[2]) << 3) >> 7; + y = (s16)(be16_to_cpup((__be16 *)&synusb->data[4]) << 3) >> 7; + + if (pressure > 0) { + input_report_rel(input_dev, REL_X, x); + input_report_rel(input_dev, REL_Y, -y); + } + + input_report_abs(input_dev, ABS_PRESSURE, pressure); + + synusb_report_buttons(synusb); + + input_sync(input_dev); +} + +static void synusb_report_touchpad(struct synusb *synusb) +{ + struct input_dev *input_dev = synusb->input; + unsigned int num_fingers, tool_width; + unsigned int x, y; + unsigned int pressure, w; + + pressure = synusb->data[6]; + x = be16_to_cpup((__be16 *)&synusb->data[2]); + y = be16_to_cpup((__be16 *)&synusb->data[4]); + w = synusb->data[0] & 0x0f; + + if (pressure > 0) { + num_fingers = 1; + tool_width = 5; + switch (w) { + case 0 ... 1: + num_fingers = 2 + w; + break; + + case 2: /* pen, pretend its a finger */ + break; + + case 4 ... 15: + tool_width = w; + break; + } + } else { + num_fingers = 0; + tool_width = 0; + } + + /* + * Post events + * BTN_TOUCH has to be first as mousedev relies on it when doing + * absolute -> relative conversion + */ + + if (pressure > 30) + input_report_key(input_dev, BTN_TOUCH, 1); + if (pressure < 25) + input_report_key(input_dev, BTN_TOUCH, 0); + + if (num_fingers > 0) { + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, + YMAX_NOMINAL + YMIN_NOMINAL - y); + } + + input_report_abs(input_dev, ABS_PRESSURE, pressure); + input_report_abs(input_dev, ABS_TOOL_WIDTH, tool_width); + + input_report_key(input_dev, BTN_TOOL_FINGER, num_fingers == 1); + input_report_key(input_dev, BTN_TOOL_DOUBLETAP, num_fingers == 2); + input_report_key(input_dev, BTN_TOOL_TRIPLETAP, num_fingers == 3); + + synusb_report_buttons(synusb); + if (synusb->flags & SYNUSB_AUXDISPLAY) + input_report_key(input_dev, BTN_MIDDLE, synusb->data[1] & 0x08); + + input_sync(input_dev); +} + +static void synusb_irq(struct urb *urb) +{ + struct synusb *synusb = urb->context; + int error; + + /* Check our status in case we need to bail out early. */ + switch (urb->status) { + case 0: + usb_mark_last_busy(synusb->udev); + break; + + /* Device went away so don't keep trying to read from it. */ + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + return; + + default: + goto resubmit; + break; + } + + if (synusb->flags & SYNUSB_STICK) + synusb_report_stick(synusb); + else + synusb_report_touchpad(synusb); + +resubmit: + error = usb_submit_urb(urb, GFP_ATOMIC); + if (error && error != -EPERM) + dev_err(&synusb->intf->dev, + "%s - usb_submit_urb failed with result: %d", + __func__, error); +} + +static struct usb_endpoint_descriptor * +synusb_get_in_endpoint(struct usb_host_interface *iface) +{ + + struct usb_endpoint_descriptor *endpoint; + int i; + + for (i = 0; i < iface->desc.bNumEndpoints; ++i) { + endpoint = &iface->endpoint[i].desc; + + if (usb_endpoint_is_int_in(endpoint)) { + /* we found our interrupt in endpoint */ + return endpoint; + } + } + + return NULL; +} + +static int synusb_open(struct input_dev *dev) +{ + struct synusb *synusb = input_get_drvdata(dev); + int retval; + + retval = usb_autopm_get_interface(synusb->intf); + if (retval) { + dev_err(&synusb->intf->dev, + "%s - usb_autopm_get_interface failed, error: %d\n", + __func__, retval); + return retval; + } + + retval = usb_submit_urb(synusb->urb, GFP_KERNEL); + if (retval) { + dev_err(&synusb->intf->dev, + "%s - usb_submit_urb failed, error: %d\n", + __func__, retval); + retval = -EIO; + goto out; + } + + synusb->intf->needs_remote_wakeup = 1; + +out: + usb_autopm_put_interface(synusb->intf); + return retval; +} + +static void synusb_close(struct input_dev *dev) +{ + struct synusb *synusb = input_get_drvdata(dev); + int autopm_error; + + autopm_error = usb_autopm_get_interface(synusb->intf); + + usb_kill_urb(synusb->urb); + synusb->intf->needs_remote_wakeup = 0; + + if (!autopm_error) + usb_autopm_put_interface(synusb->intf); +} + +static int synusb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *ep; + struct synusb *synusb; + struct input_dev *input_dev; + unsigned int intf_num = intf->cur_altsetting->desc.bInterfaceNumber; + unsigned int altsetting = min(intf->num_altsetting, 1U); + int error; + + error = usb_set_interface(udev, intf_num, altsetting); + if (error) { + dev_err(&udev->dev, + "Can not set alternate setting to %i, error: %i", + altsetting, error); + return error; + } + + ep = synusb_get_in_endpoint(intf->cur_altsetting); + if (!ep) + return -ENODEV; + + synusb = kzalloc(sizeof(*synusb), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!synusb || !input_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + synusb->udev = udev; + synusb->intf = intf; + synusb->input = input_dev; + + synusb->flags = id->driver_info; + if (synusb->flags & SYNUSB_COMBO) { + /* + * This is a combo device, we need to set proper + * capability, depending on the interface. + */ + synusb->flags |= intf_num == 1 ? + SYNUSB_STICK : SYNUSB_TOUCHPAD; + } + + synusb->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!synusb->urb) { + error = -ENOMEM; + goto err_free_mem; + } + + synusb->data = usb_alloc_coherent(udev, SYNUSB_RECV_SIZE, GFP_KERNEL, + &synusb->urb->transfer_dma); + if (!synusb->data) { + error = -ENOMEM; + goto err_free_urb; + } + + usb_fill_int_urb(synusb->urb, udev, + usb_rcvintpipe(udev, ep->bEndpointAddress), + synusb->data, SYNUSB_RECV_SIZE, + synusb_irq, synusb, + ep->bInterval); + synusb->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + if (udev->manufacturer) + strlcpy(synusb->name, udev->manufacturer, + sizeof(synusb->name)); + + if (udev->product) { + if (udev->manufacturer) + strlcat(synusb->name, " ", sizeof(synusb->name)); + strlcat(synusb->name, udev->product, sizeof(synusb->name)); + } + + if (!strlen(synusb->name)) + snprintf(synusb->name, sizeof(synusb->name), + "USB Synaptics Device %04x:%04x", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + if (synusb->flags & SYNUSB_STICK) + strlcat(synusb->name, " (Stick) ", sizeof(synusb->name)); + + usb_make_path(udev, synusb->phys, sizeof(synusb->phys)); + strlcat(synusb->phys, "/input0", sizeof(synusb->phys)); + + input_dev->name = synusb->name; + input_dev->phys = synusb->phys; + usb_to_input_id(udev, &input_dev->id); + input_dev->dev.parent = &synusb->intf->dev; + + if (!(synusb->flags & SYNUSB_IO_ALWAYS)) { + input_dev->open = synusb_open; + input_dev->close = synusb_close; + } + + input_set_drvdata(input_dev, synusb); + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + + if (synusb->flags & SYNUSB_STICK) { + __set_bit(EV_REL, input_dev->evbit); + __set_bit(REL_X, input_dev->relbit); + __set_bit(REL_Y, input_dev->relbit); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 127, 0, 0); + } else { + input_set_abs_params(input_dev, ABS_X, + XMIN_NOMINAL, XMAX_NOMINAL, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + YMIN_NOMINAL, YMAX_NOMINAL, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(BTN_TOOL_FINGER, input_dev->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); + __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); + } + + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + __set_bit(BTN_MIDDLE, input_dev->keybit); + + usb_set_intfdata(intf, synusb); + + if (synusb->flags & SYNUSB_IO_ALWAYS) { + error = synusb_open(input_dev); + if (error) + goto err_free_dma; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(&udev->dev, + "Failed to register input device, error %d\n", + error); + goto err_stop_io; + } + + return 0; + +err_stop_io: + if (synusb->flags & SYNUSB_IO_ALWAYS) + synusb_close(synusb->input); +err_free_dma: + usb_free_coherent(udev, SYNUSB_RECV_SIZE, synusb->data, + synusb->urb->transfer_dma); +err_free_urb: + usb_free_urb(synusb->urb); +err_free_mem: + input_free_device(input_dev); + kfree(synusb); + usb_set_intfdata(intf, NULL); + + return error; +} + +static void synusb_disconnect(struct usb_interface *intf) +{ + struct synusb *synusb = usb_get_intfdata(intf); + struct usb_device *udev = interface_to_usbdev(intf); + + if (synusb->flags & SYNUSB_IO_ALWAYS) + synusb_close(synusb->input); + + input_unregister_device(synusb->input); + + usb_free_coherent(udev, SYNUSB_RECV_SIZE, synusb->data, + synusb->urb->transfer_dma); + usb_free_urb(synusb->urb); + kfree(synusb); + + usb_set_intfdata(intf, NULL); +} + +static int synusb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct synusb *synusb = usb_get_intfdata(intf); + struct input_dev *input_dev = synusb->input; + + mutex_lock(&input_dev->mutex); + usb_kill_urb(synusb->urb); + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int synusb_resume(struct usb_interface *intf) +{ + struct synusb *synusb = usb_get_intfdata(intf); + struct input_dev *input_dev = synusb->input; + int retval = 0; + + mutex_lock(&input_dev->mutex); + + if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) && + usb_submit_urb(synusb->urb, GFP_NOIO) < 0) { + retval = -EIO; + } + + mutex_unlock(&input_dev->mutex); + + return retval; +} + +static int synusb_pre_reset(struct usb_interface *intf) +{ + struct synusb *synusb = usb_get_intfdata(intf); + struct input_dev *input_dev = synusb->input; + + mutex_lock(&input_dev->mutex); + usb_kill_urb(synusb->urb); + + return 0; +} + +static int synusb_post_reset(struct usb_interface *intf) +{ + struct synusb *synusb = usb_get_intfdata(intf); + struct input_dev *input_dev = synusb->input; + int retval = 0; + + if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) && + usb_submit_urb(synusb->urb, GFP_NOIO) < 0) { + retval = -EIO; + } + + mutex_unlock(&input_dev->mutex); + + return retval; +} + +static int synusb_reset_resume(struct usb_interface *intf) +{ + return synusb_resume(intf); +} + +static struct usb_device_id synusb_idtable[] = { + { USB_DEVICE_SYNAPTICS(TP, SYNUSB_TOUCHPAD) }, + { USB_DEVICE_SYNAPTICS(INT_TP, SYNUSB_TOUCHPAD) }, + { USB_DEVICE_SYNAPTICS(CPAD, + SYNUSB_TOUCHPAD | SYNUSB_AUXDISPLAY | SYNUSB_IO_ALWAYS) }, + { USB_DEVICE_SYNAPTICS(TS, SYNUSB_TOUCHSCREEN) }, + { USB_DEVICE_SYNAPTICS(STICK, SYNUSB_STICK) }, + { USB_DEVICE_SYNAPTICS(WP, SYNUSB_TOUCHPAD) }, + { USB_DEVICE_SYNAPTICS(COMP_TP, SYNUSB_COMBO) }, + { USB_DEVICE_SYNAPTICS(WTP, SYNUSB_TOUCHPAD) }, + { USB_DEVICE_SYNAPTICS(DPAD, SYNUSB_TOUCHPAD) }, + { } +}; +MODULE_DEVICE_TABLE(usb, synusb_idtable); + +static struct usb_driver synusb_driver = { + .name = "synaptics_usb", + .probe = synusb_probe, + .disconnect = synusb_disconnect, + .id_table = synusb_idtable, + .suspend = synusb_suspend, + .resume = synusb_resume, + .pre_reset = synusb_pre_reset, + .post_reset = synusb_post_reset, + .reset_resume = synusb_reset_resume, + .supports_autosuspend = 1, +}; + +module_usb_driver(synusb_driver); + +MODULE_AUTHOR("Rob Miller <rob@inpharmatica.co.uk>, " + "Ron Lee <ron@debian.org>, " + "Jan Steinhoff <cpad@jan-steinhoff.de>"); +MODULE_DESCRIPTION("Synaptics USB device driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/of_keymap.c b/drivers/input/of_keymap.c new file mode 100644 index 000000000000..061493d57682 --- /dev/null +++ b/drivers/input/of_keymap.c @@ -0,0 +1,87 @@ +/* + * Helpers for open firmware matrix keyboard bindings + * + * Copyright (C) 2012 Google, Inc + * + * Author: + * Olof Johansson <olof@lixom.net> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/input.h> +#include <linux/of.h> +#include <linux/input/matrix_keypad.h> +#include <linux/export.h> +#include <linux/gfp.h> +#include <linux/slab.h> + +struct matrix_keymap_data * +matrix_keyboard_of_fill_keymap(struct device_node *np, + const char *propname) +{ + struct matrix_keymap_data *kd; + u32 *keymap; + int proplen, i; + const __be32 *prop; + + if (!np) + return NULL; + + if (!propname) + propname = "linux,keymap"; + + prop = of_get_property(np, propname, &proplen); + if (!prop) + return NULL; + + if (proplen % sizeof(u32)) { + pr_warn("Malformed keymap property %s in %s\n", + propname, np->full_name); + return NULL; + } + + kd = kzalloc(sizeof(*kd), GFP_KERNEL); + if (!kd) + return NULL; + + kd->keymap = keymap = kzalloc(proplen, GFP_KERNEL); + if (!kd->keymap) { + kfree(kd); + return NULL; + } + + kd->keymap_size = proplen / sizeof(u32); + + for (i = 0; i < kd->keymap_size; i++) { + u32 tmp = be32_to_cpup(prop + i); + int key_code, row, col; + + row = (tmp >> 24) & 0xff; + col = (tmp >> 16) & 0xff; + key_code = tmp & 0xffff; + keymap[i] = KEY(row, col, key_code); + } + + return kd; +} +EXPORT_SYMBOL_GPL(matrix_keyboard_of_fill_keymap); + +void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd) +{ + if (kd) { + kfree(kd->keymap); + kfree(kd); + } +} +EXPORT_SYMBOL_GPL(matrix_keyboard_of_free_keymap); diff --git a/drivers/input/serio/altera_ps2.c b/drivers/input/serio/altera_ps2.c index 35864c6130bb..cc11f4efe119 100644 --- a/drivers/input/serio/altera_ps2.c +++ b/drivers/input/serio/altera_ps2.c @@ -180,8 +180,6 @@ static const struct of_device_id altera_ps2_match[] = { {}, }; MODULE_DEVICE_TABLE(of, altera_ps2_match); -#else /* CONFIG_OF */ -#define altera_ps2_match NULL #endif /* CONFIG_OF */ /* @@ -193,7 +191,7 @@ static struct platform_driver altera_ps2_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, - .of_match_table = altera_ps2_match, + .of_match_table = of_match_ptr(altera_ps2_match), }, }; module_platform_driver(altera_ps2_driver); diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c index 8407d5b0ced8..2ffd110bd5bc 100644 --- a/drivers/input/serio/ambakmi.c +++ b/drivers/input/serio/ambakmi.c @@ -208,18 +208,7 @@ static struct amba_driver ambakmi_driver = { .resume = amba_kmi_resume, }; -static int __init amba_kmi_init(void) -{ - return amba_driver_register(&ambakmi_driver); -} - -static void __exit amba_kmi_exit(void) -{ - amba_driver_unregister(&ambakmi_driver); -} - -module_init(amba_kmi_init); -module_exit(amba_kmi_exit); +module_amba_driver(ambakmi_driver); MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); MODULE_DESCRIPTION("AMBA KMI controller driver"); diff --git a/drivers/input/serio/ams_delta_serio.c b/drivers/input/serio/ams_delta_serio.c index d4d08bd9205b..bd5b10eeeb40 100644 --- a/drivers/input/serio/ams_delta_serio.c +++ b/drivers/input/serio/ams_delta_serio.c @@ -92,8 +92,7 @@ static irqreturn_t ams_delta_serio_interrupt(int irq, void *dev_id) static int ams_delta_serio_open(struct serio *serio) { /* enable keyboard */ - ams_delta_latch2_write(AMD_DELTA_LATCH2_KEYBRD_PWR, - AMD_DELTA_LATCH2_KEYBRD_PWR); + gpio_set_value(AMS_DELTA_GPIO_PIN_KEYBRD_PWR, 1); return 0; } @@ -101,9 +100,32 @@ static int ams_delta_serio_open(struct serio *serio) static void ams_delta_serio_close(struct serio *serio) { /* disable keyboard */ - ams_delta_latch2_write(AMD_DELTA_LATCH2_KEYBRD_PWR, 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_KEYBRD_PWR, 0); } +static const struct gpio ams_delta_gpios[] __initconst_or_module = { + { + .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_DATA, + .flags = GPIOF_DIR_IN, + .label = "serio-data", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_CLK, + .flags = GPIOF_DIR_IN, + .label = "serio-clock", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_PWR, + .flags = GPIOF_OUT_INIT_LOW, + .label = "serio-power", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_DATAOUT, + .flags = GPIOF_OUT_INIT_LOW, + .label = "serio-dataout", + }, +}; + static int __init ams_delta_serio_init(void) { int err; @@ -123,19 +145,12 @@ static int __init ams_delta_serio_init(void) strlcpy(ams_delta_serio->phys, "GPIO/serio0", sizeof(ams_delta_serio->phys)); - err = gpio_request(AMS_DELTA_GPIO_PIN_KEYBRD_DATA, "serio-data"); + err = gpio_request_array(ams_delta_gpios, + ARRAY_SIZE(ams_delta_gpios)); if (err) { - pr_err("ams_delta_serio: Couldn't request gpio pin for data\n"); + pr_err("ams_delta_serio: Couldn't request gpio pins\n"); goto serio; } - gpio_direction_input(AMS_DELTA_GPIO_PIN_KEYBRD_DATA); - - err = gpio_request(AMS_DELTA_GPIO_PIN_KEYBRD_CLK, "serio-clock"); - if (err) { - pr_err("ams_delta_serio: couldn't request gpio pin for clock\n"); - goto gpio_data; - } - gpio_direction_input(AMS_DELTA_GPIO_PIN_KEYBRD_CLK); err = request_irq(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), ams_delta_serio_interrupt, IRQ_TYPE_EDGE_RISING, @@ -143,7 +158,7 @@ static int __init ams_delta_serio_init(void) if (err < 0) { pr_err("ams_delta_serio: couldn't request gpio interrupt %d\n", gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK)); - goto gpio_clk; + goto gpio; } /* * Since GPIO register handling for keyboard clock pin is performed @@ -157,10 +172,9 @@ static int __init ams_delta_serio_init(void) dev_info(&ams_delta_serio->dev, "%s\n", ams_delta_serio->name); return 0; -gpio_clk: - gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_CLK); -gpio_data: - gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_DATA); +gpio: + gpio_free_array(ams_delta_gpios, + ARRAY_SIZE(ams_delta_gpios)); serio: kfree(ams_delta_serio); return err; @@ -171,7 +185,7 @@ static void __exit ams_delta_serio_exit(void) { serio_unregister_port(ams_delta_serio); free_irq(OMAP_GPIO_IRQ(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0); - gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_CLK); - gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_DATA); + gpio_free_array(ams_delta_gpios, + ARRAY_SIZE(ams_delta_gpios)); } module_exit(ams_delta_serio_exit); diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c index 95280f9207e1..36e799c31f5e 100644 --- a/drivers/input/serio/at32psif.c +++ b/drivers/input/serio/at32psif.c @@ -98,9 +98,9 @@ struct psif { struct serio *io; void __iomem *regs; unsigned int irq; - unsigned int open; /* Prevent concurrent writes to PSIF THR. */ spinlock_t lock; + bool open; }; static irqreturn_t psif_interrupt(int irq, void *_ptr) @@ -164,7 +164,7 @@ static int psif_open(struct serio *io) psif_writel(psif, CR, PSIF_BIT(CR_TXEN) | PSIF_BIT(CR_RXEN)); psif_writel(psif, IER, PSIF_BIT(RXRDY)); - psif->open = 1; + psif->open = true; out: return retval; } @@ -173,7 +173,7 @@ static void psif_close(struct serio *io) { struct psif *psif = io->port_data; - psif->open = 0; + psif->open = false; psif_writel(psif, IDR, ~0UL); psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS)); @@ -319,9 +319,10 @@ static int __exit psif_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int psif_suspend(struct platform_device *pdev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int psif_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct psif *psif = platform_get_drvdata(pdev); if (psif->open) { @@ -332,8 +333,9 @@ static int psif_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int psif_resume(struct platform_device *pdev) +static int psif_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct psif *psif = platform_get_drvdata(pdev); if (psif->open) { @@ -344,19 +346,17 @@ static int psif_resume(struct platform_device *pdev) return 0; } -#else -#define psif_suspend NULL -#define psif_resume NULL #endif +static SIMPLE_DEV_PM_OPS(psif_pm_ops, psif_suspend, psif_resume); + static struct platform_driver psif_driver = { .remove = __exit_p(psif_remove), .driver = { .name = "atmel_psif", .owner = THIS_MODULE, + .pm = &psif_pm_ops, }, - .suspend = psif_suspend, - .resume = psif_resume, }; static int __init psif_init(void) diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c index 5eb84b3b67fb..0c0df7f73802 100644 --- a/drivers/input/serio/q40kbd.c +++ b/drivers/input/serio/q40kbd.c @@ -44,26 +44,31 @@ #include <asm/irq.h> #include <asm/q40ints.h> +#define DRV_NAME "q40kbd" + MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION("Q40 PS/2 keyboard controller driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); -static DEFINE_SPINLOCK(q40kbd_lock); -static struct serio *q40kbd_port; -static struct platform_device *q40kbd_device; +struct q40kbd { + struct serio *port; + spinlock_t lock; +}; static irqreturn_t q40kbd_interrupt(int irq, void *dev_id) { + struct q40kbd *q40kbd = dev_id; unsigned long flags; - spin_lock_irqsave(&q40kbd_lock, flags); + spin_lock_irqsave(&q40kbd->lock, flags); if (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG)) - serio_interrupt(q40kbd_port, master_inb(KEYCODE_REG), 0); + serio_interrupt(q40kbd->port, master_inb(KEYCODE_REG), 0); master_outb(-1, KEYBOARD_UNLOCK_REG); - spin_unlock_irqrestore(&q40kbd_lock, flags); + spin_unlock_irqrestore(&q40kbd->lock, flags); return IRQ_HANDLED; } @@ -72,17 +77,23 @@ static irqreturn_t q40kbd_interrupt(int irq, void *dev_id) * q40kbd_flush() flushes all data that may be in the keyboard buffers */ -static void q40kbd_flush(void) +static void q40kbd_flush(struct q40kbd *q40kbd) { int maxread = 100; unsigned long flags; - spin_lock_irqsave(&q40kbd_lock, flags); + spin_lock_irqsave(&q40kbd->lock, flags); while (maxread-- && (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG))) master_inb(KEYCODE_REG); - spin_unlock_irqrestore(&q40kbd_lock, flags); + spin_unlock_irqrestore(&q40kbd->lock, flags); +} + +static void q40kbd_stop(void) +{ + master_outb(0, KEY_IRQ_ENABLE_REG); + master_outb(-1, KEYBOARD_UNLOCK_REG); } /* @@ -92,12 +103,9 @@ static void q40kbd_flush(void) static int q40kbd_open(struct serio *port) { - q40kbd_flush(); + struct q40kbd *q40kbd = port->port_data; - if (request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0, "q40kbd", NULL)) { - printk(KERN_ERR "q40kbd.c: Can't get irq %d.\n", Q40_IRQ_KEYBOARD); - return -EBUSY; - } + q40kbd_flush(q40kbd); /* off we go */ master_outb(-1, KEYBOARD_UNLOCK_REG); @@ -108,36 +116,72 @@ static int q40kbd_open(struct serio *port) static void q40kbd_close(struct serio *port) { - master_outb(0, KEY_IRQ_ENABLE_REG); - master_outb(-1, KEYBOARD_UNLOCK_REG); - free_irq(Q40_IRQ_KEYBOARD, NULL); + struct q40kbd *q40kbd = port->port_data; - q40kbd_flush(); + q40kbd_stop(); + q40kbd_flush(q40kbd); } -static int __devinit q40kbd_probe(struct platform_device *dev) +static int __devinit q40kbd_probe(struct platform_device *pdev) { - q40kbd_port = kzalloc(sizeof(struct serio), GFP_KERNEL); - if (!q40kbd_port) - return -ENOMEM; - - q40kbd_port->id.type = SERIO_8042; - q40kbd_port->open = q40kbd_open; - q40kbd_port->close = q40kbd_close; - q40kbd_port->dev.parent = &dev->dev; - strlcpy(q40kbd_port->name, "Q40 Kbd Port", sizeof(q40kbd_port->name)); - strlcpy(q40kbd_port->phys, "Q40", sizeof(q40kbd_port->phys)); - - serio_register_port(q40kbd_port); + struct q40kbd *q40kbd; + struct serio *port; + int error; + + q40kbd = kzalloc(sizeof(struct q40kbd), GFP_KERNEL); + port = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!q40kbd || !port) { + error = -ENOMEM; + goto err_free_mem; + } + + q40kbd->port = port; + spin_lock_init(&q40kbd->lock); + + port->id.type = SERIO_8042; + port->open = q40kbd_open; + port->close = q40kbd_close; + port->port_data = q40kbd; + port->dev.parent = &pdev->dev; + strlcpy(port->name, "Q40 Kbd Port", sizeof(port->name)); + strlcpy(port->phys, "Q40", sizeof(port->phys)); + + q40kbd_stop(); + + error = request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0, + DRV_NAME, q40kbd); + if (error) { + dev_err(&pdev->dev, "Can't get irq %d.\n", Q40_IRQ_KEYBOARD); + goto err_free_mem; + } + + serio_register_port(q40kbd->port); + + platform_set_drvdata(pdev, q40kbd); printk(KERN_INFO "serio: Q40 kbd registered\n"); return 0; + +err_free_mem: + kfree(port); + kfree(q40kbd); + return error; } -static int __devexit q40kbd_remove(struct platform_device *dev) +static int __devexit q40kbd_remove(struct platform_device *pdev) { - serio_unregister_port(q40kbd_port); - + struct q40kbd *q40kbd = platform_get_drvdata(pdev); + + /* + * q40kbd_close() will be called as part of unregistering + * and will ensure that IRQ is turned off, so it is safe + * to unregister port first and free IRQ later. + */ + serio_unregister_port(q40kbd->port); + free_irq(Q40_IRQ_KEYBOARD, q40kbd); + kfree(q40kbd); + + platform_set_drvdata(pdev, NULL); return 0; } @@ -146,41 +190,16 @@ static struct platform_driver q40kbd_driver = { .name = "q40kbd", .owner = THIS_MODULE, }, - .probe = q40kbd_probe, .remove = __devexit_p(q40kbd_remove), }; static int __init q40kbd_init(void) { - int error; - - if (!MACH_IS_Q40) - return -ENODEV; - - error = platform_driver_register(&q40kbd_driver); - if (error) - return error; - - q40kbd_device = platform_device_alloc("q40kbd", -1); - if (!q40kbd_device) - goto err_unregister_driver; - - error = platform_device_add(q40kbd_device); - if (error) - goto err_free_device; - - return 0; - - err_free_device: - platform_device_put(q40kbd_device); - err_unregister_driver: - platform_driver_unregister(&q40kbd_driver); - return error; + return platform_driver_probe(&q40kbd_driver, q40kbd_probe); } static void __exit q40kbd_exit(void) { - platform_device_unregister(q40kbd_device); platform_driver_unregister(&q40kbd_driver); } diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c index 8b44ddc8041c..58b224498b35 100644 --- a/drivers/input/serio/rpckbd.c +++ b/drivers/input/serio/rpckbd.c @@ -36,7 +36,6 @@ #include <linux/io.h> #include <linux/slab.h> -#include <asm/irq.h> #include <mach/hardware.h> #include <asm/hardware/iomd.h> #include <asm/system.h> @@ -46,6 +45,11 @@ MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:kart"); +struct rpckbd_data { + int tx_irq; + int rx_irq; +}; + static int rpckbd_write(struct serio *port, unsigned char val) { while (!(iomd_readb(IOMD_KCTRL) & (1 << 7))) @@ -78,19 +82,21 @@ static irqreturn_t rpckbd_tx(int irq, void *dev_id) static int rpckbd_open(struct serio *port) { + struct rpckbd_data *rpckbd = port->port_data; + /* Reset the keyboard state machine. */ iomd_writeb(0, IOMD_KCTRL); iomd_writeb(8, IOMD_KCTRL); iomd_readb(IOMD_KARTRX); - if (request_irq(IRQ_KEYBOARDRX, rpckbd_rx, 0, "rpckbd", port) != 0) { + if (request_irq(rpckbd->rx_irq, rpckbd_rx, 0, "rpckbd", port) != 0) { printk(KERN_ERR "rpckbd.c: Could not allocate keyboard receive IRQ\n"); return -EBUSY; } - if (request_irq(IRQ_KEYBOARDTX, rpckbd_tx, 0, "rpckbd", port) != 0) { + if (request_irq(rpckbd->tx_irq, rpckbd_tx, 0, "rpckbd", port) != 0) { printk(KERN_ERR "rpckbd.c: Could not allocate keyboard transmit IRQ\n"); - free_irq(IRQ_KEYBOARDRX, port); + free_irq(rpckbd->rx_irq, port); return -EBUSY; } @@ -99,8 +105,10 @@ static int rpckbd_open(struct serio *port) static void rpckbd_close(struct serio *port) { - free_irq(IRQ_KEYBOARDRX, port); - free_irq(IRQ_KEYBOARDTX, port); + struct rpckbd_data *rpckbd = port->port_data; + + free_irq(rpckbd->rx_irq, port); + free_irq(rpckbd->tx_irq, port); } /* @@ -109,17 +117,35 @@ static void rpckbd_close(struct serio *port) */ static int __devinit rpckbd_probe(struct platform_device *dev) { + struct rpckbd_data *rpckbd; struct serio *serio; + int tx_irq, rx_irq; + + rx_irq = platform_get_irq(dev, 0); + if (rx_irq <= 0) + return rx_irq < 0 ? rx_irq : -ENXIO; + + tx_irq = platform_get_irq(dev, 1); + if (tx_irq <= 0) + return tx_irq < 0 ? tx_irq : -ENXIO; serio = kzalloc(sizeof(struct serio), GFP_KERNEL); - if (!serio) + rpckbd = kzalloc(sizeof(*rpckbd), GFP_KERNEL); + if (!serio || !rpckbd) { + kfree(rpckbd); + kfree(serio); return -ENOMEM; + } + + rpckbd->rx_irq = rx_irq; + rpckbd->tx_irq = tx_irq; serio->id.type = SERIO_8042; serio->write = rpckbd_write; serio->open = rpckbd_open; serio->close = rpckbd_close; serio->dev.parent = &dev->dev; + serio->port_data = rpckbd; strlcpy(serio->name, "RiscPC PS/2 kbd port", sizeof(serio->name)); strlcpy(serio->phys, "rpckbd/serio0", sizeof(serio->phys)); @@ -131,7 +157,11 @@ static int __devinit rpckbd_probe(struct platform_device *dev) static int __devexit rpckbd_remove(struct platform_device *dev) { struct serio *serio = platform_get_drvdata(dev); + struct rpckbd_data *rpckbd = serio->port_data; + serio_unregister_port(serio); + kfree(rpckbd); + return 0; } diff --git a/drivers/input/serio/sa1111ps2.c b/drivers/input/serio/sa1111ps2.c index 44fc8b4bcd81..5ebabe3fc845 100644 --- a/drivers/input/serio/sa1111ps2.c +++ b/drivers/input/serio/sa1111ps2.c @@ -24,6 +24,26 @@ #include <asm/hardware/sa1111.h> +#define PS2CR 0x0000 +#define PS2STAT 0x0004 +#define PS2DATA 0x0008 +#define PS2CLKDIV 0x000c +#define PS2PRECNT 0x0010 + +#define PS2CR_ENA 0x08 +#define PS2CR_FKD 0x02 +#define PS2CR_FKC 0x01 + +#define PS2STAT_STP 0x0100 +#define PS2STAT_TXE 0x0080 +#define PS2STAT_TXB 0x0040 +#define PS2STAT_RXF 0x0020 +#define PS2STAT_RXB 0x0010 +#define PS2STAT_ENA 0x0008 +#define PS2STAT_RXP 0x0004 +#define PS2STAT_KBD 0x0002 +#define PS2STAT_KBC 0x0001 + struct ps2if { struct serio *io; struct sa1111_dev *dev; @@ -45,22 +65,22 @@ static irqreturn_t ps2_rxint(int irq, void *dev_id) struct ps2if *ps2if = dev_id; unsigned int scancode, flag, status; - status = sa1111_readl(ps2if->base + SA1111_PS2STAT); + status = sa1111_readl(ps2if->base + PS2STAT); while (status & PS2STAT_RXF) { if (status & PS2STAT_STP) - sa1111_writel(PS2STAT_STP, ps2if->base + SA1111_PS2STAT); + sa1111_writel(PS2STAT_STP, ps2if->base + PS2STAT); flag = (status & PS2STAT_STP ? SERIO_FRAME : 0) | (status & PS2STAT_RXP ? 0 : SERIO_PARITY); - scancode = sa1111_readl(ps2if->base + SA1111_PS2DATA) & 0xff; + scancode = sa1111_readl(ps2if->base + PS2DATA) & 0xff; if (hweight8(scancode) & 1) flag ^= SERIO_PARITY; serio_interrupt(ps2if->io, scancode, flag); - status = sa1111_readl(ps2if->base + SA1111_PS2STAT); + status = sa1111_readl(ps2if->base + PS2STAT); } return IRQ_HANDLED; @@ -75,12 +95,12 @@ static irqreturn_t ps2_txint(int irq, void *dev_id) unsigned int status; spin_lock(&ps2if->lock); - status = sa1111_readl(ps2if->base + SA1111_PS2STAT); + status = sa1111_readl(ps2if->base + PS2STAT); if (ps2if->head == ps2if->tail) { disable_irq_nosync(irq); /* done */ } else if (status & PS2STAT_TXE) { - sa1111_writel(ps2if->buf[ps2if->tail], ps2if->base + SA1111_PS2DATA); + sa1111_writel(ps2if->buf[ps2if->tail], ps2if->base + PS2DATA); ps2if->tail = (ps2if->tail + 1) & (sizeof(ps2if->buf) - 1); } spin_unlock(&ps2if->lock); @@ -103,8 +123,8 @@ static int ps2_write(struct serio *io, unsigned char val) /* * If the TX register is empty, we can go straight out. */ - if (sa1111_readl(ps2if->base + SA1111_PS2STAT) & PS2STAT_TXE) { - sa1111_writel(val, ps2if->base + SA1111_PS2DATA); + if (sa1111_readl(ps2if->base + PS2STAT) & PS2STAT_TXE) { + sa1111_writel(val, ps2if->base + PS2DATA); } else { if (ps2if->head == ps2if->tail) enable_irq(ps2if->dev->irq[1]); @@ -124,13 +144,16 @@ static int ps2_open(struct serio *io) struct ps2if *ps2if = io->port_data; int ret; - sa1111_enable_device(ps2if->dev); + ret = sa1111_enable_device(ps2if->dev); + if (ret) + return ret; ret = request_irq(ps2if->dev->irq[0], ps2_rxint, 0, SA1111_DRIVER_NAME(ps2if->dev), ps2if); if (ret) { printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n", ps2if->dev->irq[0], ret); + sa1111_disable_device(ps2if->dev); return ret; } @@ -140,6 +163,7 @@ static int ps2_open(struct serio *io) printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n", ps2if->dev->irq[1], ret); free_irq(ps2if->dev->irq[0], ps2if); + sa1111_disable_device(ps2if->dev); return ret; } @@ -147,7 +171,7 @@ static int ps2_open(struct serio *io) enable_irq_wake(ps2if->dev->irq[0]); - sa1111_writel(PS2CR_ENA, ps2if->base + SA1111_PS2CR); + sa1111_writel(PS2CR_ENA, ps2if->base + PS2CR); return 0; } @@ -155,7 +179,7 @@ static void ps2_close(struct serio *io) { struct ps2if *ps2if = io->port_data; - sa1111_writel(0, ps2if->base + SA1111_PS2CR); + sa1111_writel(0, ps2if->base + PS2CR); disable_irq_wake(ps2if->dev->irq[0]); @@ -175,7 +199,7 @@ static void __devinit ps2_clear_input(struct ps2if *ps2if) int maxread = 100; while (maxread--) { - if ((sa1111_readl(ps2if->base + SA1111_PS2DATA) & 0xff) == 0xff) + if ((sa1111_readl(ps2if->base + PS2DATA) & 0xff) == 0xff) break; } } @@ -185,11 +209,11 @@ static unsigned int __devinit ps2_test_one(struct ps2if *ps2if, { unsigned int val; - sa1111_writel(PS2CR_ENA | mask, ps2if->base + SA1111_PS2CR); + sa1111_writel(PS2CR_ENA | mask, ps2if->base + PS2CR); udelay(2); - val = sa1111_readl(ps2if->base + SA1111_PS2STAT); + val = sa1111_readl(ps2if->base + PS2STAT); return val & (PS2STAT_KBC | PS2STAT_KBD); } @@ -220,7 +244,7 @@ static int __devinit ps2_test(struct ps2if *ps2if) ret = -ENODEV; } - sa1111_writel(0, ps2if->base + SA1111_PS2CR); + sa1111_writel(0, ps2if->base + PS2CR); return ret; } @@ -274,8 +298,8 @@ static int __devinit ps2_probe(struct sa1111_dev *dev) sa1111_enable_device(ps2if->dev); /* Incoming clock is 8MHz */ - sa1111_writel(0, ps2if->base + SA1111_PS2CLKDIV); - sa1111_writel(127, ps2if->base + SA1111_PS2PRECNT); + sa1111_writel(0, ps2if->base + PS2CLKDIV); + sa1111_writel(127, ps2if->base + PS2PRECNT); /* * Flush any pending input. @@ -330,6 +354,7 @@ static int __devexit ps2_remove(struct sa1111_dev *dev) static struct sa1111_driver ps2_driver = { .drv = { .name = "sa1111-ps2", + .owner = THIS_MODULE, }, .devid = SA1111_DEVID_PS2, .probe = ps2_probe, diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index 2a97b7e76db1..ca28066dc81e 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -176,7 +176,7 @@ static int wacom_parse_logical_collection(unsigned char *report, /* Logical collection is only used by 3rd gen Bamboo Touch */ features->pktlen = WACOM_PKGLEN_BBTOUCH3; - features->device_type = BTN_TOOL_DOUBLETAP; + features->device_type = BTN_TOOL_FINGER; /* * Stylus and Touch have same active area @@ -184,9 +184,9 @@ static int wacom_parse_logical_collection(unsigned char *report, * data before its overwritten. */ features->x_phy = - (features->x_max * features->x_resolution) / 100; + (features->x_max * 100) / features->x_resolution; features->y_phy = - (features->y_max * features->y_resolution) / 100; + (features->y_max * 100) / features->y_resolution; features->x_max = features->y_max = get_unaligned_le16(&report[10]); @@ -286,12 +286,10 @@ static int wacom_parse_hid(struct usb_interface *intf, if (features->type == TABLETPC2FG) { /* need to reset back */ features->pktlen = WACOM_PKGLEN_TPC2FG; - features->device_type = BTN_TOOL_DOUBLETAP; } if (features->type == BAMBOO_PT) { /* need to reset back */ features->pktlen = WACOM_PKGLEN_BBTOUCH; - features->device_type = BTN_TOOL_DOUBLETAP; features->x_phy = get_unaligned_le16(&report[i + 5]); features->x_max = @@ -325,7 +323,6 @@ static int wacom_parse_hid(struct usb_interface *intf, if (features->type == TABLETPC2FG) { /* need to reset back */ features->pktlen = WACOM_PKGLEN_TPC2FG; - features->device_type = BTN_TOOL_DOUBLETAP; features->y_max = get_unaligned_le16(&report[i + 3]); features->y_phy = @@ -334,7 +331,6 @@ static int wacom_parse_hid(struct usb_interface *intf, } else if (features->type == BAMBOO_PT) { /* need to reset back */ features->pktlen = WACOM_PKGLEN_BBTOUCH; - features->device_type = BTN_TOOL_DOUBLETAP; features->y_phy = get_unaligned_le16(&report[i + 3]); features->y_max = diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index cd3ed29e0801..89a96427faa0 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -832,12 +832,24 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len) dbg("wacom_tpc_irq: received report #%d", data[0]); - if (len == WACOM_PKGLEN_TPC1FG || data[0] == WACOM_REPORT_TPC1FG) - return wacom_tpc_single_touch(wacom, len); - else if (data[0] == WACOM_REPORT_TPC2FG) - return wacom_tpc_mt_touch(wacom); - else if (data[0] == WACOM_REPORT_PENABLED) - return wacom_tpc_pen(wacom); + switch (len) { + case WACOM_PKGLEN_TPC1FG: + return wacom_tpc_single_touch(wacom, len); + + case WACOM_PKGLEN_TPC2FG: + return wacom_tpc_mt_touch(wacom); + + default: + switch (data[0]) { + case WACOM_REPORT_TPC1FG: + case WACOM_REPORT_TPCHID: + case WACOM_REPORT_TPCST: + return wacom_tpc_single_touch(wacom, len); + + case WACOM_REPORT_PENABLED: + return wacom_tpc_pen(wacom); + } + } return 0; } @@ -1317,7 +1329,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, break; case TABLETPC2FG: - if (features->device_type == BTN_TOOL_DOUBLETAP) { + if (features->device_type == BTN_TOOL_FINGER) { input_mt_init_slots(input_dev, 2); input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, @@ -1366,7 +1378,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, __set_bit(INPUT_PROP_POINTER, input_dev->propbit); - if (features->device_type == BTN_TOOL_DOUBLETAP) { + if (features->device_type == BTN_TOOL_FINGER) { __set_bit(BTN_LEFT, input_dev->keybit); __set_bit(BTN_FORWARD, input_dev->keybit); __set_bit(BTN_BACK, input_dev->keybit); diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index 050acaefee7d..4f0ba21b0196 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -39,6 +39,8 @@ #define WACOM_REPORT_INTUOSPAD 12 #define WACOM_REPORT_TPC1FG 6 #define WACOM_REPORT_TPC2FG 13 +#define WACOM_REPORT_TPCHID 15 +#define WACOM_REPORT_TPCST 16 /* device quirks */ #define WACOM_QUIRK_MULTI_INPUT 0x0001 diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 4af2a18eb3ba..2a2141915aa0 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -139,7 +139,6 @@ config TOUCHSCREEN_CY8CTMG110 tristate "cy8ctmg110 touchscreen" depends on I2C depends on GPIOLIB - help Say Y here if you have a cy8ctmg110 capacitive touchscreen on an AAVA device. @@ -149,6 +148,37 @@ config TOUCHSCREEN_CY8CTMG110 To compile this driver as a module, choose M here: the module will be called cy8ctmg110_ts. +config TOUCHSCREEN_CYTTSP_CORE + tristate "Cypress TTSP touchscreen" + help + Say Y here if you have a touchscreen using controller from + the Cypress TrueTouch(tm) Standard Product family connected + to your system. You will also need to select appropriate + bus connection below. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called cyttsp_core. + +config TOUCHSCREEN_CYTTSP_I2C + tristate "support I2C bus connection" + depends on TOUCHSCREEN_CYTTSP_CORE && I2C + help + Say Y here if the touchscreen is connected via I2C bus. + + To compile this driver as a module, choose M here: the + module will be called cyttsp_i2c. + +config TOUCHSCREEN_CYTTSP_SPI + tristate "support SPI bus connection" + depends on TOUCHSCREEN_CYTTSP_CORE && SPI_MASTER + help + Say Y here if the touchscreen is connected via SPI bus. + + To compile this driver as a module, choose M here: the + module will be called cyttsp_spi. + config TOUCHSCREEN_DA9034 tristate "Touchscreen support for Dialog Semiconductor DA9034" depends on PMIC_DA903X @@ -213,9 +243,24 @@ config TOUCHSCREEN_FUJITSU To compile this driver as a module, choose M here: the module will be called fujitsu-ts. +config TOUCHSCREEN_ILI210X + tristate "Ilitek ILI210X based touchscreen" + depends on I2C + help + Say Y here if you have a ILI210X based touchscreen + controller. This driver supports models ILI2102, + ILI2102s, ILI2103, ILI2103s and ILI2105. + Such kind of chipsets can be found in Amazon Kindle Fire + touchscreens. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ili210x. + config TOUCHSCREEN_S3C2410 tristate "Samsung S3C2410/generic touchscreen input driver" - depends on ARCH_S3C2410 || SAMSUNG_DEV_TS + depends on ARCH_S3C24XX || SAMSUNG_DEV_TS select S3C_ADC help Say Y here if you have the s3c2410 touchscreen. @@ -430,6 +475,18 @@ config TOUCHSCREEN_TOUCHWIN To compile this driver as a module, choose M here: the module will be called touchwin. +config TOUCHSCREEN_TI_TSCADC + tristate "TI Touchscreen Interface" + depends on ARCH_OMAP2PLUS + help + Say Y here if you have 4/5/8 wire touchscreen controller + to be connected to the ADC controller on your TI AM335x SoC. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ti_tscadc. + config TOUCHSCREEN_ATMEL_TSADCC tristate "Atmel Touchscreen Interface" depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 @@ -577,6 +634,7 @@ config TOUCHSCREEN_USB_COMPOSITE - JASTEC USB Touch Controller/DigiTech DTR-02U - Zytronic controllers - Elo TouchSystems 2700 IntelliTouch + - EasyTouch USB Touch Controller from Data Modul Have a look at <http://linux.chapter7.ch/touchkit/> for a usage description and the required user-space stuff. @@ -681,6 +739,14 @@ config TOUCHSCREEN_USB_NEXIO bool "NEXIO/iNexio device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE +config TOUCHSCREEN_USB_EASYTOUCH + default y + bool "EasyTouch USB Touch controller device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + help + Say Y here if you have a EasyTouch USB Touch controller device support. + If unsure, say N. + config TOUCHSCREEN_TOUCHIT213 tristate "Sahara TouchIT-213 touchscreen" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 496091e88460..3d5cf8cbf89c 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -16,8 +16,11 @@ obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o -obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o +obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o @@ -26,6 +29,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o +obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o @@ -45,6 +49,7 @@ obj-$(CONFIG_TOUCHSCREEN_PIXCIR) += pixcir_i2c_ts.o obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o +obj-$(CONFIG_TOUCHSCREEN_TI_TSCADC) += ti_tscadc.o obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index 49a36df0b752..2c7692108e6c 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -860,17 +860,7 @@ static struct spi_driver ad7877_driver = { .remove = __devexit_p(ad7877_remove), }; -static int __init ad7877_init(void) -{ - return spi_register_driver(&ad7877_driver); -} -module_init(ad7877_init); - -static void __exit ad7877_exit(void) -{ - spi_unregister_driver(&ad7877_driver); -} -module_exit(ad7877_exit); +module_spi_driver(ad7877_driver); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("AD7877 touchscreen Driver"); diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c index 0dac6712f42b..3054354d0dd3 100644 --- a/drivers/input/touchscreen/ad7879-i2c.c +++ b/drivers/input/touchscreen/ad7879-i2c.c @@ -102,17 +102,7 @@ static struct i2c_driver ad7879_i2c_driver = { .id_table = ad7879_id, }; -static int __init ad7879_i2c_init(void) -{ - return i2c_add_driver(&ad7879_i2c_driver); -} -module_init(ad7879_i2c_init); - -static void __exit ad7879_i2c_exit(void) -{ - i2c_del_driver(&ad7879_i2c_driver); -} -module_exit(ad7879_i2c_exit); +module_i2c_driver(ad7879_i2c_driver); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("AD7879(-1) touchscreen I2C bus driver"); diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c index 9b2e1c2b1971..db49abf056ba 100644 --- a/drivers/input/touchscreen/ad7879-spi.c +++ b/drivers/input/touchscreen/ad7879-spi.c @@ -157,17 +157,7 @@ static struct spi_driver ad7879_spi_driver = { .remove = __devexit_p(ad7879_spi_remove), }; -static int __init ad7879_spi_init(void) -{ - return spi_register_driver(&ad7879_spi_driver); -} -module_init(ad7879_spi_init); - -static void __exit ad7879_spi_exit(void) -{ - spi_unregister_driver(&ad7879_spi_driver); -} -module_exit(ad7879_spi_exit); +module_spi_driver(ad7879_spi_driver); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("AD7879(-1) touchscreen SPI bus driver"); diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 23fd90185659..f02028ec3db6 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -1433,17 +1433,7 @@ static struct spi_driver ads7846_driver = { .remove = __devexit_p(ads7846_remove), }; -static int __init ads7846_init(void) -{ - return spi_register_driver(&ads7846_driver); -} -module_init(ads7846_init); - -static void __exit ads7846_exit(void) -{ - spi_unregister_driver(&ads7846_driver); -} -module_exit(ads7846_exit); +module_spi_driver(ads7846_driver); MODULE_DESCRIPTION("ADS7846 TouchScreen Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/atmel-wm97xx.c b/drivers/input/touchscreen/atmel-wm97xx.c index 8034cbb20f74..c5c2dbb93869 100644 --- a/drivers/input/touchscreen/atmel-wm97xx.c +++ b/drivers/input/touchscreen/atmel-wm97xx.c @@ -392,9 +392,10 @@ static int __exit atmel_wm97xx_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int atmel_wm97xx_suspend(struct platform_device *pdev, pm_message_t msg) +#ifdef CONFIG_PM_SLEEP +static int atmel_wm97xx_suspend(struct *dev) { + struct platform_device *pdev = to_platform_device(dev); struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev); ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT); @@ -404,8 +405,9 @@ static int atmel_wm97xx_suspend(struct platform_device *pdev, pm_message_t msg) return 0; } -static int atmel_wm97xx_resume(struct platform_device *pdev) +static int atmel_wm97xx_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev); struct wm97xx *wm = atmel_wm97xx->wm; @@ -416,18 +418,18 @@ static int atmel_wm97xx_resume(struct platform_device *pdev) return 0; } -#else -#define atmel_wm97xx_suspend NULL -#define atmel_wm97xx_resume NULL #endif +static SIMPLE_DEV_PM_OPS(atmel_wm97xx_pm_ops, + atmel_wm97xx_suspend, atmel_wm97xx_resume); + static struct platform_driver atmel_wm97xx_driver = { .remove = __exit_p(atmel_wm97xx_remove), .driver = { - .name = "wm97xx-touch", + .name = "wm97xx-touch", + .owner = THIS_MODULE, + .pm = &atmel_wm97xx_pm_ops, }, - .suspend = atmel_wm97xx_suspend, - .resume = atmel_wm97xx_resume, }; static int __init atmel_wm97xx_init(void) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index a596c2775d1a..19d4ea65ea01 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1267,18 +1267,7 @@ static struct i2c_driver mxt_driver = { .id_table = mxt_id, }; -static int __init mxt_init(void) -{ - return i2c_add_driver(&mxt_driver); -} - -static void __exit mxt_exit(void) -{ - i2c_del_driver(&mxt_driver); -} - -module_init(mxt_init); -module_exit(mxt_exit); +module_i2c_driver(mxt_driver); /* Module information */ MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c index 94fb9fbb08a9..c7047b6bb020 100644 --- a/drivers/input/touchscreen/auo-pixcir-ts.c +++ b/drivers/input/touchscreen/auo-pixcir-ts.c @@ -635,17 +635,7 @@ static struct i2c_driver auo_pixcir_driver = { .id_table = auo_pixcir_idtable, }; -static int __init auo_pixcir_init(void) -{ - return i2c_add_driver(&auo_pixcir_driver); -} -module_init(auo_pixcir_init); - -static void __exit auo_pixcir_exit(void) -{ - i2c_del_driver(&auo_pixcir_driver); -} -module_exit(auo_pixcir_exit); +module_i2c_driver(auo_pixcir_driver); MODULE_DESCRIPTION("AUO-PIXCIR touchscreen driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c index 902c7214e887..f2d03c06c2da 100644 --- a/drivers/input/touchscreen/bu21013_ts.c +++ b/drivers/input/touchscreen/bu21013_ts.c @@ -652,30 +652,7 @@ static struct i2c_driver bu21013_driver = { .id_table = bu21013_id, }; -/** - * bu21013_init() - initializes the bu21013 touchscreen driver - * - * This function used to initializes the bu21013 - * touchscreen driver and returns integer. - */ -static int __init bu21013_init(void) -{ - return i2c_add_driver(&bu21013_driver); -} - -/** - * bu21013_exit() - de-initializes the bu21013 touchscreen driver - * - * This function uses to de-initializes the bu21013 - * touchscreen driver and returns none. - */ -static void __exit bu21013_exit(void) -{ - i2c_del_driver(&bu21013_driver); -} - -module_init(bu21013_init); -module_exit(bu21013_exit); +module_i2c_driver(bu21013_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Naveen Kumar G <naveen.gaddipati@stericsson.com>"); diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c index d8815c5d54ad..237753ad1031 100644 --- a/drivers/input/touchscreen/cy8ctmg110_ts.c +++ b/drivers/input/touchscreen/cy8ctmg110_ts.c @@ -350,18 +350,7 @@ static struct i2c_driver cy8ctmg110_driver = { .remove = __devexit_p(cy8ctmg110_remove), }; -static int __init cy8ctmg110_init(void) -{ - return i2c_add_driver(&cy8ctmg110_driver); -} - -static void __exit cy8ctmg110_exit(void) -{ - i2c_del_driver(&cy8ctmg110_driver); -} - -module_init(cy8ctmg110_init); -module_exit(cy8ctmg110_exit); +module_i2c_driver(cy8ctmg110_driver); MODULE_AUTHOR("Samuli Konttila <samuli.konttila@aavamobile.com>"); MODULE_DESCRIPTION("cy8ctmg110 TouchScreen Driver"); diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c new file mode 100644 index 000000000000..f030d9ec795d --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -0,0 +1,625 @@ +/* + * Core Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com> + * + */ + +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/slab.h> + +#include "cyttsp_core.h" + +/* Bootloader number of command keys */ +#define CY_NUM_BL_KEYS 8 + +/* helpers */ +#define GET_NUM_TOUCHES(x) ((x) & 0x0F) +#define IS_LARGE_AREA(x) (((x) & 0x10) >> 4) +#define IS_BAD_PKT(x) ((x) & 0x20) +#define IS_VALID_APP(x) ((x) & 0x01) +#define IS_OPERATIONAL_ERR(x) ((x) & 0x3F) +#define GET_HSTMODE(reg) (((reg) & 0x70) >> 4) +#define GET_BOOTLOADERMODE(reg) (((reg) & 0x10) >> 4) + +#define CY_REG_BASE 0x00 +#define CY_REG_ACT_DIST 0x1E +#define CY_REG_ACT_INTRVL 0x1D +#define CY_REG_TCH_TMOUT (CY_REG_ACT_INTRVL + 1) +#define CY_REG_LP_INTRVL (CY_REG_TCH_TMOUT + 1) +#define CY_MAXZ 255 +#define CY_DELAY_DFLT 20 /* ms */ +#define CY_DELAY_MAX 500 +#define CY_ACT_DIST_DFLT 0xF8 +#define CY_HNDSHK_BIT 0x80 +/* device mode bits */ +#define CY_OPERATE_MODE 0x00 +#define CY_SYSINFO_MODE 0x10 +/* power mode select bits */ +#define CY_SOFT_RESET_MODE 0x01 /* return to Bootloader mode */ +#define CY_DEEP_SLEEP_MODE 0x02 +#define CY_LOW_POWER_MODE 0x04 + +/* Slots management */ +#define CY_MAX_FINGER 4 +#define CY_MAX_ID 16 + +static const u8 bl_command[] = { + 0x00, /* file offset */ + 0xFF, /* command */ + 0xA5, /* exit bootloader command */ + 0, 1, 2, 3, 4, 5, 6, 7 /* default keys */ +}; + +static int ttsp_read_block_data(struct cyttsp *ts, u8 command, + u8 length, void *buf) +{ + int error; + int tries; + + for (tries = 0; tries < CY_NUM_RETRY; tries++) { + error = ts->bus_ops->read(ts, command, length, buf); + if (!error) + return 0; + + msleep(CY_DELAY_DFLT); + } + + return -EIO; +} + +static int ttsp_write_block_data(struct cyttsp *ts, u8 command, + u8 length, void *buf) +{ + int error; + int tries; + + for (tries = 0; tries < CY_NUM_RETRY; tries++) { + error = ts->bus_ops->write(ts, command, length, buf); + if (!error) + return 0; + + msleep(CY_DELAY_DFLT); + } + + return -EIO; +} + +static int ttsp_send_command(struct cyttsp *ts, u8 cmd) +{ + return ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd); +} + +static int cyttsp_load_bl_regs(struct cyttsp *ts) +{ + memset(&ts->bl_data, 0, sizeof(ts->bl_data)); + ts->bl_data.bl_status = 0x10; + + return ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(ts->bl_data), &ts->bl_data); +} + +static int cyttsp_exit_bl_mode(struct cyttsp *ts) +{ + int error; + u8 bl_cmd[sizeof(bl_command)]; + + memcpy(bl_cmd, bl_command, sizeof(bl_command)); + if (ts->pdata->bl_keys) + memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS], + ts->pdata->bl_keys, sizeof(bl_command)); + + error = ttsp_write_block_data(ts, CY_REG_BASE, + sizeof(bl_cmd), bl_cmd); + if (error) + return error; + + /* wait for TTSP Device to complete the operation */ + msleep(CY_DELAY_DFLT); + + error = cyttsp_load_bl_regs(ts); + if (error) + return error; + + if (GET_BOOTLOADERMODE(ts->bl_data.bl_status)) + return -EIO; + + return 0; +} + +static int cyttsp_set_operational_mode(struct cyttsp *ts) +{ + int error; + + error = ttsp_send_command(ts, CY_OPERATE_MODE); + if (error) + return error; + + /* wait for TTSP Device to complete switch to Operational mode */ + error = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(ts->xy_data), &ts->xy_data); + if (error) + return error; + + return ts->xy_data.act_dist == CY_ACT_DIST_DFLT ? -EIO : 0; +} + +static int cyttsp_set_sysinfo_mode(struct cyttsp *ts) +{ + int error; + + memset(&ts->sysinfo_data, 0, sizeof(ts->sysinfo_data)); + + /* switch to sysinfo mode */ + error = ttsp_send_command(ts, CY_SYSINFO_MODE); + if (error) + return error; + + /* read sysinfo registers */ + msleep(CY_DELAY_DFLT); + error = ttsp_read_block_data(ts, CY_REG_BASE, sizeof(ts->sysinfo_data), + &ts->sysinfo_data); + if (error) + return error; + + if (!ts->sysinfo_data.tts_verh && !ts->sysinfo_data.tts_verl) + return -EIO; + + return 0; +} + +static int cyttsp_set_sysinfo_regs(struct cyttsp *ts) +{ + int retval = 0; + + if (ts->pdata->act_intrvl != CY_ACT_INTRVL_DFLT || + ts->pdata->tch_tmout != CY_TCH_TMOUT_DFLT || + ts->pdata->lp_intrvl != CY_LP_INTRVL_DFLT) { + + u8 intrvl_ray[] = { + ts->pdata->act_intrvl, + ts->pdata->tch_tmout, + ts->pdata->lp_intrvl + }; + + /* set intrvl registers */ + retval = ttsp_write_block_data(ts, CY_REG_ACT_INTRVL, + sizeof(intrvl_ray), intrvl_ray); + msleep(CY_DELAY_DFLT); + } + + return retval; +} + +static int cyttsp_soft_reset(struct cyttsp *ts) +{ + unsigned long timeout; + int retval; + + /* wait for interrupt to set ready completion */ + INIT_COMPLETION(ts->bl_ready); + ts->state = CY_BL_STATE; + + enable_irq(ts->irq); + + retval = ttsp_send_command(ts, CY_SOFT_RESET_MODE); + if (retval) + goto out; + + timeout = wait_for_completion_timeout(&ts->bl_ready, + msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX)); + retval = timeout ? 0 : -EIO; + +out: + ts->state = CY_IDLE_STATE; + disable_irq(ts->irq); + return retval; +} + +static int cyttsp_act_dist_setup(struct cyttsp *ts) +{ + u8 act_dist_setup = ts->pdata->act_dist; + + /* Init gesture; active distance setup */ + return ttsp_write_block_data(ts, CY_REG_ACT_DIST, + sizeof(act_dist_setup), &act_dist_setup); +} + +static void cyttsp_extract_track_ids(struct cyttsp_xydata *xy_data, int *ids) +{ + ids[0] = xy_data->touch12_id >> 4; + ids[1] = xy_data->touch12_id & 0xF; + ids[2] = xy_data->touch34_id >> 4; + ids[3] = xy_data->touch34_id & 0xF; +} + +static const struct cyttsp_tch *cyttsp_get_tch(struct cyttsp_xydata *xy_data, + int idx) +{ + switch (idx) { + case 0: + return &xy_data->tch1; + case 1: + return &xy_data->tch2; + case 2: + return &xy_data->tch3; + case 3: + return &xy_data->tch4; + default: + return NULL; + } +} + +static void cyttsp_report_tchdata(struct cyttsp *ts) +{ + struct cyttsp_xydata *xy_data = &ts->xy_data; + struct input_dev *input = ts->input; + int num_tch = GET_NUM_TOUCHES(xy_data->tt_stat); + const struct cyttsp_tch *tch; + int ids[CY_MAX_ID]; + int i; + DECLARE_BITMAP(used, CY_MAX_ID); + + if (IS_LARGE_AREA(xy_data->tt_stat) == 1) { + /* terminate all active tracks */ + num_tch = 0; + dev_dbg(ts->dev, "%s: Large area detected\n", __func__); + } else if (num_tch > CY_MAX_FINGER) { + /* terminate all active tracks */ + num_tch = 0; + dev_dbg(ts->dev, "%s: Num touch error detected\n", __func__); + } else if (IS_BAD_PKT(xy_data->tt_mode)) { + /* terminate all active tracks */ + num_tch = 0; + dev_dbg(ts->dev, "%s: Invalid buffer detected\n", __func__); + } + + cyttsp_extract_track_ids(xy_data, ids); + + bitmap_zero(used, CY_MAX_ID); + + for (i = 0; i < num_tch; i++) { + tch = cyttsp_get_tch(xy_data, i); + + input_mt_slot(input, ids[i]); + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); + input_report_abs(input, ABS_MT_POSITION_X, be16_to_cpu(tch->x)); + input_report_abs(input, ABS_MT_POSITION_Y, be16_to_cpu(tch->y)); + input_report_abs(input, ABS_MT_TOUCH_MAJOR, tch->z); + + __set_bit(ids[i], used); + } + + for (i = 0; i < CY_MAX_ID; i++) { + if (test_bit(i, used)) + continue; + + input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, false); + } + + input_sync(input); +} + +static irqreturn_t cyttsp_irq(int irq, void *handle) +{ + struct cyttsp *ts = handle; + int error; + + if (unlikely(ts->state == CY_BL_STATE)) { + complete(&ts->bl_ready); + goto out; + } + + /* Get touch data from CYTTSP device */ + error = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(struct cyttsp_xydata), &ts->xy_data); + if (error) + goto out; + + /* provide flow control handshake */ + if (ts->pdata->use_hndshk) { + error = ttsp_send_command(ts, + ts->xy_data.hst_mode ^ CY_HNDSHK_BIT); + if (error) + goto out; + } + + if (unlikely(ts->state == CY_IDLE_STATE)) + goto out; + + if (GET_BOOTLOADERMODE(ts->xy_data.tt_mode)) { + /* + * TTSP device has reset back to bootloader mode. + * Restore to operational mode. + */ + error = cyttsp_exit_bl_mode(ts); + if (error) { + dev_err(ts->dev, + "Could not return to operational mode, err: %d\n", + error); + ts->state = CY_IDLE_STATE; + } + } else { + cyttsp_report_tchdata(ts); + } + +out: + return IRQ_HANDLED; +} + +static int cyttsp_power_on(struct cyttsp *ts) +{ + int error; + + error = cyttsp_soft_reset(ts); + if (error) + return error; + + error = cyttsp_load_bl_regs(ts); + if (error) + return error; + + if (GET_BOOTLOADERMODE(ts->bl_data.bl_status) && + IS_VALID_APP(ts->bl_data.bl_status)) { + error = cyttsp_exit_bl_mode(ts); + if (error) + return error; + } + + if (GET_HSTMODE(ts->bl_data.bl_file) != CY_OPERATE_MODE || + IS_OPERATIONAL_ERR(ts->bl_data.bl_status)) { + return -ENODEV; + } + + error = cyttsp_set_sysinfo_mode(ts); + if (error) + return error; + + error = cyttsp_set_sysinfo_regs(ts); + if (error) + return error; + + error = cyttsp_set_operational_mode(ts); + if (error) + return error; + + /* init active distance */ + error = cyttsp_act_dist_setup(ts); + if (error) + return error; + + ts->state = CY_ACTIVE_STATE; + + return 0; +} + +static int cyttsp_enable(struct cyttsp *ts) +{ + int error; + + /* + * The device firmware can wake on an I2C or SPI memory slave + * address match. So just reading a register is sufficient to + * wake up the device. The first read attempt will fail but it + * will wake it up making the second read attempt successful. + */ + error = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(ts->xy_data), &ts->xy_data); + if (error) + return error; + + if (GET_HSTMODE(ts->xy_data.hst_mode)) + return -EIO; + + enable_irq(ts->irq); + + return 0; +} + +static int cyttsp_disable(struct cyttsp *ts) +{ + int error; + + error = ttsp_send_command(ts, CY_LOW_POWER_MODE); + if (error) + return error; + + disable_irq(ts->irq); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cyttsp_suspend(struct device *dev) +{ + struct cyttsp *ts = dev_get_drvdata(dev); + int retval = 0; + + mutex_lock(&ts->input->mutex); + + if (ts->input->users) { + retval = cyttsp_disable(ts); + if (retval == 0) + ts->suspended = true; + } + + mutex_unlock(&ts->input->mutex); + + return retval; +} + +static int cyttsp_resume(struct device *dev) +{ + struct cyttsp *ts = dev_get_drvdata(dev); + + mutex_lock(&ts->input->mutex); + + if (ts->input->users) + cyttsp_enable(ts); + + ts->suspended = false; + + mutex_unlock(&ts->input->mutex); + + return 0; +} + +#endif + +SIMPLE_DEV_PM_OPS(cyttsp_pm_ops, cyttsp_suspend, cyttsp_resume); +EXPORT_SYMBOL_GPL(cyttsp_pm_ops); + +static int cyttsp_open(struct input_dev *dev) +{ + struct cyttsp *ts = input_get_drvdata(dev); + int retval = 0; + + if (!ts->suspended) + retval = cyttsp_enable(ts); + + return retval; +} + +static void cyttsp_close(struct input_dev *dev) +{ + struct cyttsp *ts = input_get_drvdata(dev); + + if (!ts->suspended) + cyttsp_disable(ts); +} + +struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, + struct device *dev, int irq, size_t xfer_buf_size) +{ + const struct cyttsp_platform_data *pdata = dev->platform_data; + struct cyttsp *ts; + struct input_dev *input_dev; + int error; + + if (!pdata || !pdata->name || irq <= 0) { + error = -EINVAL; + goto err_out; + } + + ts = kzalloc(sizeof(*ts) + xfer_buf_size, GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + ts->dev = dev; + ts->input = input_dev; + ts->pdata = dev->platform_data; + ts->bus_ops = bus_ops; + ts->irq = irq; + + init_completion(&ts->bl_ready); + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); + + if (pdata->init) { + error = pdata->init(); + if (error) { + dev_err(ts->dev, "platform init failed, err: %d\n", + error); + goto err_free_mem; + } + } + + input_dev->name = pdata->name; + input_dev->phys = ts->phys; + input_dev->id.bustype = bus_ops->bustype; + input_dev->dev.parent = ts->dev; + + input_dev->open = cyttsp_open; + input_dev->close = cyttsp_close; + + input_set_drvdata(input_dev, ts); + + __set_bit(EV_ABS, input_dev->evbit); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, pdata->maxx, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, pdata->maxy, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, CY_MAXZ, 0, 0); + + input_mt_init_slots(input_dev, CY_MAX_ID); + + error = request_threaded_irq(ts->irq, NULL, cyttsp_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + pdata->name, ts); + if (error) { + dev_err(ts->dev, "failed to request IRQ %d, err: %d\n", + ts->irq, error); + goto err_platform_exit; + } + + disable_irq(ts->irq); + + error = cyttsp_power_on(ts); + if (error) + goto err_free_irq; + + error = input_register_device(input_dev); + if (error) { + dev_err(ts->dev, "failed to register input device: %d\n", + error); + goto err_free_irq; + } + + return ts; + +err_free_irq: + free_irq(ts->irq, ts); +err_platform_exit: + if (pdata->exit) + pdata->exit(); +err_free_mem: + input_free_device(input_dev); + kfree(ts); +err_out: + return ERR_PTR(error); +} +EXPORT_SYMBOL_GPL(cyttsp_probe); + +void cyttsp_remove(struct cyttsp *ts) +{ + free_irq(ts->irq, ts); + input_unregister_device(ts->input); + if (ts->pdata->exit) + ts->pdata->exit(); + kfree(ts); +} +EXPORT_SYMBOL_GPL(cyttsp_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core"); +MODULE_AUTHOR("Cypress"); diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h new file mode 100644 index 000000000000..1aa3c6967e70 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_core.h @@ -0,0 +1,149 @@ +/* + * Header file for: + * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com> + * + */ + + +#ifndef __CYTTSP_CORE_H__ +#define __CYTTSP_CORE_H__ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/device.h> +#include <linux/input/cyttsp.h> + +#define CY_NUM_RETRY 16 /* max number of retries for read ops */ + +struct cyttsp_tch { + __be16 x, y; + u8 z; +} __packed; + +/* TrueTouch Standard Product Gen3 interface definition */ +struct cyttsp_xydata { + u8 hst_mode; + u8 tt_mode; + u8 tt_stat; + struct cyttsp_tch tch1; + u8 touch12_id; + struct cyttsp_tch tch2; + u8 gest_cnt; + u8 gest_id; + struct cyttsp_tch tch3; + u8 touch34_id; + struct cyttsp_tch tch4; + u8 tt_undef[3]; + u8 act_dist; + u8 tt_reserved; +} __packed; + + +/* TTSP System Information interface definition */ +struct cyttsp_sysinfo_data { + u8 hst_mode; + u8 mfg_cmd; + u8 mfg_stat; + u8 cid[3]; + u8 tt_undef1; + u8 uid[8]; + u8 bl_verh; + u8 bl_verl; + u8 tts_verh; + u8 tts_verl; + u8 app_idh; + u8 app_idl; + u8 app_verh; + u8 app_verl; + u8 tt_undef[5]; + u8 scn_typ; + u8 act_intrvl; + u8 tch_tmout; + u8 lp_intrvl; +}; + +/* TTSP Bootloader Register Map interface definition */ +#define CY_BL_CHKSUM_OK 0x01 +struct cyttsp_bootloader_data { + u8 bl_file; + u8 bl_status; + u8 bl_error; + u8 blver_hi; + u8 blver_lo; + u8 bld_blver_hi; + u8 bld_blver_lo; + u8 ttspver_hi; + u8 ttspver_lo; + u8 appid_hi; + u8 appid_lo; + u8 appver_hi; + u8 appver_lo; + u8 cid_0; + u8 cid_1; + u8 cid_2; +}; + +struct cyttsp; + +struct cyttsp_bus_ops { + u16 bustype; + int (*write)(struct cyttsp *ts, + u8 addr, u8 length, const void *values); + int (*read)(struct cyttsp *ts, u8 addr, u8 length, void *values); +}; + +enum cyttsp_state { + CY_IDLE_STATE, + CY_ACTIVE_STATE, + CY_BL_STATE, +}; + +struct cyttsp { + struct device *dev; + int irq; + struct input_dev *input; + char phys[32]; + const struct cyttsp_platform_data *pdata; + const struct cyttsp_bus_ops *bus_ops; + struct cyttsp_bootloader_data bl_data; + struct cyttsp_sysinfo_data sysinfo_data; + struct cyttsp_xydata xy_data; + struct completion bl_ready; + enum cyttsp_state state; + bool suspended; + + u8 xfer_buf[] ____cacheline_aligned; +}; + +struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, + struct device *dev, int irq, size_t xfer_buf_size); +void cyttsp_remove(struct cyttsp *ts); + +extern const struct dev_pm_ops cyttsp_pm_ops; + +#endif /* __CYTTSP_CORE_H__ */ diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c new file mode 100644 index 000000000000..2af1d0c52bcd --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_i2c.c @@ -0,0 +1,136 @@ +/* + * Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com> + * + */ + +#include "cyttsp_core.h" + +#include <linux/i2c.h> +#include <linux/input.h> + +#define CY_I2C_DATA_SIZE 128 + +static int cyttsp_i2c_read_block_data(struct cyttsp *ts, + u8 addr, u8 length, void *values) +{ + struct i2c_client *client = to_i2c_client(ts->dev); + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &addr, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = values, + }, + }; + int retval; + + retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (retval < 0) + return retval; + + return retval != ARRAY_SIZE(msgs) ? -EIO : 0; +} + +static int cyttsp_i2c_write_block_data(struct cyttsp *ts, + u8 addr, u8 length, const void *values) +{ + struct i2c_client *client = to_i2c_client(ts->dev); + int retval; + + ts->xfer_buf[0] = addr; + memcpy(&ts->xfer_buf[1], values, length); + + retval = i2c_master_send(client, ts->xfer_buf, length + 1); + + return retval < 0 ? retval : 0; +} + +static const struct cyttsp_bus_ops cyttsp_i2c_bus_ops = { + .bustype = BUS_I2C, + .write = cyttsp_i2c_write_block_data, + .read = cyttsp_i2c_read_block_data, +}; + +static int __devinit cyttsp_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cyttsp *ts; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "I2C functionality not Supported\n"); + return -EIO; + } + + ts = cyttsp_probe(&cyttsp_i2c_bus_ops, &client->dev, client->irq, + CY_I2C_DATA_SIZE); + + if (IS_ERR(ts)) + return PTR_ERR(ts); + + i2c_set_clientdata(client, ts); + + return 0; +} + +static int __devexit cyttsp_i2c_remove(struct i2c_client *client) +{ + struct cyttsp *ts = i2c_get_clientdata(client); + + cyttsp_remove(ts); + + return 0; +} + +static const struct i2c_device_id cyttsp_i2c_id[] = { + { CY_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cyttsp_i2c_id); + +static struct i2c_driver cyttsp_i2c_driver = { + .driver = { + .name = CY_I2C_NAME, + .owner = THIS_MODULE, + .pm = &cyttsp_pm_ops, + }, + .probe = cyttsp_i2c_probe, + .remove = __devexit_p(cyttsp_i2c_remove), + .id_table = cyttsp_i2c_id, +}; + +module_i2c_driver(cyttsp_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver"); +MODULE_AUTHOR("Cypress"); +MODULE_ALIAS("i2c:cyttsp"); diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c new file mode 100644 index 000000000000..9f263410407b --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_spi.c @@ -0,0 +1,200 @@ +/* + * Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) SPI touchscreen driver. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com> + * + */ + +#include "cyttsp_core.h" + +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/spi/spi.h> + +#define CY_SPI_WR_OP 0x00 /* r/~w */ +#define CY_SPI_RD_OP 0x01 +#define CY_SPI_CMD_BYTES 4 +#define CY_SPI_SYNC_BYTE 2 +#define CY_SPI_SYNC_ACK1 0x62 /* from protocol v.2 */ +#define CY_SPI_SYNC_ACK2 0x9D /* from protocol v.2 */ +#define CY_SPI_DATA_SIZE 128 +#define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE) +#define CY_SPI_BITS_PER_WORD 8 + +static int cyttsp_spi_xfer(struct cyttsp *ts, + u8 op, u8 reg, u8 *buf, int length) +{ + struct spi_device *spi = to_spi_device(ts->dev); + struct spi_message msg; + struct spi_transfer xfer[2]; + u8 *wr_buf = &ts->xfer_buf[0]; + u8 *rd_buf = &ts->xfer_buf[CY_SPI_DATA_BUF_SIZE]; + int retval; + int i; + + if (length > CY_SPI_DATA_SIZE) { + dev_err(ts->dev, "%s: length %d is too big.\n", + __func__, length); + return -EINVAL; + } + + memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE); + memset(rd_buf, 0, CY_SPI_DATA_BUF_SIZE); + + wr_buf[0] = 0x00; /* header byte 0 */ + wr_buf[1] = 0xFF; /* header byte 1 */ + wr_buf[2] = reg; /* reg index */ + wr_buf[3] = op; /* r/~w */ + if (op == CY_SPI_WR_OP) + memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length); + + memset(xfer, 0, sizeof(xfer)); + spi_message_init(&msg); + + /* + We set both TX and RX buffers because Cypress TTSP + requires full duplex operation. + */ + xfer[0].tx_buf = wr_buf; + xfer[0].rx_buf = rd_buf; + switch (op) { + case CY_SPI_WR_OP: + xfer[0].len = length + CY_SPI_CMD_BYTES; + spi_message_add_tail(&xfer[0], &msg); + break; + + case CY_SPI_RD_OP: + xfer[0].len = CY_SPI_CMD_BYTES; + spi_message_add_tail(&xfer[0], &msg); + + xfer[1].rx_buf = buf; + xfer[1].len = length; + spi_message_add_tail(&xfer[1], &msg); + break; + + default: + dev_err(ts->dev, "%s: bad operation code=%d\n", __func__, op); + return -EINVAL; + } + + retval = spi_sync(spi, &msg); + if (retval < 0) { + dev_dbg(ts->dev, "%s: spi_sync() error %d, len=%d, op=%d\n", + __func__, retval, xfer[1].len, op); + + /* + * do not return here since was a bad ACK sequence + * let the following ACK check handle any errors and + * allow silent retries + */ + } + + if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK1 || + rd_buf[CY_SPI_SYNC_BYTE + 1] != CY_SPI_SYNC_ACK2) { + + dev_dbg(ts->dev, "%s: operation %d failed\n", __func__, op); + + for (i = 0; i < CY_SPI_CMD_BYTES; i++) + dev_dbg(ts->dev, "%s: test rd_buf[%d]:0x%02x\n", + __func__, i, rd_buf[i]); + for (i = 0; i < length; i++) + dev_dbg(ts->dev, "%s: test buf[%d]:0x%02x\n", + __func__, i, buf[i]); + + return -EIO; + } + + return 0; +} + +static int cyttsp_spi_read_block_data(struct cyttsp *ts, + u8 addr, u8 length, void *data) +{ + return cyttsp_spi_xfer(ts, CY_SPI_RD_OP, addr, data, length); +} + +static int cyttsp_spi_write_block_data(struct cyttsp *ts, + u8 addr, u8 length, const void *data) +{ + return cyttsp_spi_xfer(ts, CY_SPI_WR_OP, addr, (void *)data, length); +} + +static const struct cyttsp_bus_ops cyttsp_spi_bus_ops = { + .bustype = BUS_SPI, + .write = cyttsp_spi_write_block_data, + .read = cyttsp_spi_read_block_data, +}; + +static int __devinit cyttsp_spi_probe(struct spi_device *spi) +{ + struct cyttsp *ts; + int error; + + /* Set up SPI*/ + spi->bits_per_word = CY_SPI_BITS_PER_WORD; + spi->mode = SPI_MODE_0; + error = spi_setup(spi); + if (error < 0) { + dev_err(&spi->dev, "%s: SPI setup error %d\n", + __func__, error); + return error; + } + + ts = cyttsp_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq, + CY_SPI_DATA_BUF_SIZE * 2); + if (IS_ERR(ts)) + return PTR_ERR(ts); + + spi_set_drvdata(spi, ts); + + return 0; +} + +static int __devexit cyttsp_spi_remove(struct spi_device *spi) +{ + struct cyttsp *ts = spi_get_drvdata(spi); + + cyttsp_remove(ts); + + return 0; +} + +static struct spi_driver cyttsp_spi_driver = { + .driver = { + .name = CY_SPI_NAME, + .owner = THIS_MODULE, + .pm = &cyttsp_pm_ops, + }, + .probe = cyttsp_spi_probe, + .remove = __devexit_p(cyttsp_spi_remove), +}; + +module_spi_driver(cyttsp_spi_driver); + +MODULE_ALIAS("spi:cyttsp"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver"); +MODULE_AUTHOR("Cypress"); +MODULE_ALIAS("spi:cyttsp"); diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c index 1df19bb8534a..503c7096ed36 100644 --- a/drivers/input/touchscreen/eeti_ts.c +++ b/drivers/input/touchscreen/eeti_ts.c @@ -320,20 +320,8 @@ static struct i2c_driver eeti_ts_driver = { .id_table = eeti_ts_id, }; -static int __init eeti_ts_init(void) -{ - return i2c_add_driver(&eeti_ts_driver); -} - -static void __exit eeti_ts_exit(void) -{ - i2c_del_driver(&eeti_ts_driver); -} +module_i2c_driver(eeti_ts_driver); MODULE_DESCRIPTION("EETI Touchscreen driver"); MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); MODULE_LICENSE("GPL"); - -module_init(eeti_ts_init); -module_exit(eeti_ts_exit); - diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c index eadcc2e83c77..70524dd34f42 100644 --- a/drivers/input/touchscreen/egalax_ts.c +++ b/drivers/input/touchscreen/egalax_ts.c @@ -285,18 +285,7 @@ static struct i2c_driver egalax_ts_driver = { .remove = __devexit_p(egalax_ts_remove), }; -static int __init egalax_ts_init(void) -{ - return i2c_add_driver(&egalax_ts_driver); -} - -static void __exit egalax_ts_exit(void) -{ - i2c_del_driver(&egalax_ts_driver); -} - -module_init(egalax_ts_init); -module_exit(egalax_ts_exit); +module_i2c_driver(egalax_ts_driver); MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_DESCRIPTION("Touchscreen driver for EETI eGalax touch controller"); diff --git a/drivers/input/touchscreen/hp680_ts_input.c b/drivers/input/touchscreen/hp680_ts_input.c index 639a6044183d..85cf9bee8018 100644 --- a/drivers/input/touchscreen/hp680_ts_input.c +++ b/drivers/input/touchscreen/hp680_ts_input.c @@ -93,7 +93,7 @@ static int __init hp680_ts_init(void) hp680_ts_dev->phys = "hp680_ts/input0"; if (request_irq(HP680_TS_IRQ, hp680_ts_interrupt, - 0, MODNAME, 0) < 0) { + 0, MODNAME, NULL) < 0) { printk(KERN_ERR "hp680_touchscreen.c: Can't allocate irq %d\n", HP680_TS_IRQ); err = -EBUSY; diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c new file mode 100644 index 000000000000..c0044175a921 --- /dev/null +++ b/drivers/input/touchscreen/ili210x.c @@ -0,0 +1,360 @@ +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/delay.h> +#include <linux/workqueue.h> +#include <linux/input/ili210x.h> + +#define MAX_TOUCHES 2 +#define DEFAULT_POLL_PERIOD 20 + +/* Touchscreen commands */ +#define REG_TOUCHDATA 0x10 +#define REG_PANEL_INFO 0x20 +#define REG_FIRMWARE_VERSION 0x40 +#define REG_CALIBRATE 0xcc + +struct finger { + u8 x_low; + u8 x_high; + u8 y_low; + u8 y_high; +} __packed; + +struct touchdata { + u8 status; + struct finger finger[MAX_TOUCHES]; +} __packed; + +struct panel_info { + struct finger finger_max; + u8 xchannel_num; + u8 ychannel_num; +} __packed; + +struct firmware_version { + u8 id; + u8 major; + u8 minor; +} __packed; + +struct ili210x { + struct i2c_client *client; + struct input_dev *input; + bool (*get_pendown_state)(void); + unsigned int poll_period; + struct delayed_work dwork; +}; + +static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, + size_t len) +{ + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = ®, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + } + }; + + if (i2c_transfer(client->adapter, msg, 2) != 2) { + dev_err(&client->dev, "i2c transfer failed\n"); + return -EIO; + } + + return 0; +} + +static void ili210x_report_events(struct input_dev *input, + const struct touchdata *touchdata) +{ + int i; + bool touch; + unsigned int x, y; + const struct finger *finger; + + for (i = 0; i < MAX_TOUCHES; i++) { + input_mt_slot(input, i); + + finger = &touchdata->finger[i]; + + touch = touchdata->status & (1 << i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); + if (touch) { + x = finger->x_low | (finger->x_high << 8); + y = finger->y_low | (finger->y_high << 8); + + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + } + } + + input_mt_report_pointer_emulation(input, false); + input_sync(input); +} + +static bool get_pendown_state(const struct ili210x *priv) +{ + bool state = false; + + if (priv->get_pendown_state) + state = priv->get_pendown_state(); + + return state; +} + +static void ili210x_work(struct work_struct *work) +{ + struct ili210x *priv = container_of(work, struct ili210x, + dwork.work); + struct i2c_client *client = priv->client; + struct touchdata touchdata; + int error; + + error = ili210x_read_reg(client, REG_TOUCHDATA, + &touchdata, sizeof(touchdata)); + if (error) { + dev_err(&client->dev, + "Unable to get touchdata, err = %d\n", error); + return; + } + + ili210x_report_events(priv->input, &touchdata); + + if ((touchdata.status & 0xf3) || get_pendown_state(priv)) + schedule_delayed_work(&priv->dwork, + msecs_to_jiffies(priv->poll_period)); +} + +static irqreturn_t ili210x_irq(int irq, void *irq_data) +{ + struct ili210x *priv = irq_data; + + schedule_delayed_work(&priv->dwork, 0); + + return IRQ_HANDLED; +} + +static ssize_t ili210x_calibrate(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ili210x *priv = i2c_get_clientdata(client); + unsigned long calibrate; + int rc; + u8 cmd = REG_CALIBRATE; + + if (kstrtoul(buf, 10, &calibrate)) + return -EINVAL; + + if (calibrate > 1) + return -EINVAL; + + if (calibrate) { + rc = i2c_master_send(priv->client, &cmd, sizeof(cmd)); + if (rc != sizeof(cmd)) + return -EIO; + } + + return count; +} +static DEVICE_ATTR(calibrate, 0644, NULL, ili210x_calibrate); + +static struct attribute *ili210x_attributes[] = { + &dev_attr_calibrate.attr, + NULL, +}; + +static const struct attribute_group ili210x_attr_group = { + .attrs = ili210x_attributes, +}; + +static int __devinit ili210x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + const struct ili210x_platform_data *pdata = dev->platform_data; + struct ili210x *priv; + struct input_dev *input; + struct panel_info panel; + struct firmware_version firmware; + int xmax, ymax; + int error; + + dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); + + if (!pdata) { + dev_err(dev, "No platform data!\n"); + return -EINVAL; + } + + if (client->irq <= 0) { + dev_err(dev, "No IRQ!\n"); + return -EINVAL; + } + + /* Get firmware version */ + error = ili210x_read_reg(client, REG_FIRMWARE_VERSION, + &firmware, sizeof(firmware)); + if (error) { + dev_err(dev, "Failed to get firmware version, err: %d\n", + error); + return error; + } + + /* get panel info */ + error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel)); + if (error) { + dev_err(dev, "Failed to get panel informations, err: %d\n", + error); + return error; + } + + xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8); + ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8); + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + input = input_allocate_device(); + if (!priv || !input) { + error = -ENOMEM; + goto err_free_mem; + } + + priv->client = client; + priv->input = input; + priv->get_pendown_state = pdata->get_pendown_state; + priv->poll_period = pdata->poll_period ? : DEFAULT_POLL_PERIOD; + INIT_DELAYED_WORK(&priv->dwork, ili210x_work); + + /* Setup input device */ + input->name = "ILI210x Touchscreen"; + input->id.bustype = BUS_I2C; + input->dev.parent = dev; + + __set_bit(EV_SYN, input->evbit); + __set_bit(EV_KEY, input->evbit); + __set_bit(EV_ABS, input->evbit); + __set_bit(BTN_TOUCH, input->keybit); + + /* Single touch */ + input_set_abs_params(input, ABS_X, 0, xmax, 0, 0); + input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0); + + /* Multi touch */ + input_mt_init_slots(input, MAX_TOUCHES); + input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); + + input_set_drvdata(input, priv); + i2c_set_clientdata(client, priv); + + error = request_irq(client->irq, ili210x_irq, pdata->irq_flags, + client->name, priv); + if (error) { + dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", + error); + goto err_free_mem; + } + + error = sysfs_create_group(&dev->kobj, &ili210x_attr_group); + if (error) { + dev_err(dev, "Unable to create sysfs attributes, err: %d\n", + error); + goto err_free_irq; + } + + error = input_register_device(priv->input); + if (error) { + dev_err(dev, "Cannot regiser input device, err: %d\n", error); + goto err_remove_sysfs; + } + + device_init_wakeup(&client->dev, 1); + + dev_dbg(dev, + "ILI210x initialized (IRQ: %d), firmware version %d.%d.%d", + client->irq, firmware.id, firmware.major, firmware.minor); + + return 0; + +err_remove_sysfs: + sysfs_remove_group(&dev->kobj, &ili210x_attr_group); +err_free_irq: + free_irq(client->irq, priv); +err_free_mem: + input_free_device(input); + kfree(priv); + return error; +} + +static int __devexit ili210x_i2c_remove(struct i2c_client *client) +{ + struct ili210x *priv = i2c_get_clientdata(client); + + sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group); + free_irq(priv->client->irq, priv); + cancel_delayed_work_sync(&priv->dwork); + input_unregister_device(priv->input); + kfree(priv); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int ili210x_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int ili210x_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm, + ili210x_i2c_suspend, ili210x_i2c_resume); + +static const struct i2c_device_id ili210x_i2c_id[] = { + { "ili210x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); + +static struct i2c_driver ili210x_ts_driver = { + .driver = { + .name = "ili210x_i2c", + .owner = THIS_MODULE, + .pm = &ili210x_i2c_pm, + }, + .id_table = ili210x_i2c_id, + .probe = ili210x_i2c_probe, + .remove = __devexit_p(ili210x_i2c_remove), +}; + +module_i2c_driver(ili210x_ts_driver); + +MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>"); +MODULE_DESCRIPTION("ILI210X I2C Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c index c3848ad2325b..d9be6eac99b1 100644 --- a/drivers/input/touchscreen/jornada720_ts.c +++ b/drivers/input/touchscreen/jornada720_ts.c @@ -22,6 +22,7 @@ #include <mach/hardware.h> #include <mach/jornada720.h> +#include <mach/irqs.h> MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>"); MODULE_DESCRIPTION("HP Jornada 710/720/728 touchscreen driver"); diff --git a/drivers/input/touchscreen/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c index 4627fe55b401..4eab50b856d7 100644 --- a/drivers/input/touchscreen/max11801_ts.c +++ b/drivers/input/touchscreen/max11801_ts.c @@ -255,18 +255,7 @@ static struct i2c_driver max11801_ts_driver = { .remove = __devexit_p(max11801_ts_remove), }; -static int __init max11801_ts_init(void) -{ - return i2c_add_driver(&max11801_ts_driver); -} - -static void __exit max11801_ts_exit(void) -{ - i2c_del_driver(&max11801_ts_driver); -} - -module_init(max11801_ts_init); -module_exit(max11801_ts_exit); +module_i2c_driver(max11801_ts_driver); MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@freescale.com>"); MODULE_DESCRIPTION("Touchscreen driver for MAXI MAX11801 controller"); diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c index 2d84c80ceb66..b528511861ce 100644 --- a/drivers/input/touchscreen/mcs5000_ts.c +++ b/drivers/input/touchscreen/mcs5000_ts.c @@ -302,18 +302,7 @@ static struct i2c_driver mcs5000_ts_driver = { .id_table = mcs5000_ts_id, }; -static int __init mcs5000_ts_init(void) -{ - return i2c_add_driver(&mcs5000_ts_driver); -} - -static void __exit mcs5000_ts_exit(void) -{ - i2c_del_driver(&mcs5000_ts_driver); -} - -module_init(mcs5000_ts_init); -module_exit(mcs5000_ts_exit); +module_i2c_driver(mcs5000_ts_driver); /* Module information */ MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); diff --git a/drivers/input/touchscreen/migor_ts.c b/drivers/input/touchscreen/migor_ts.c index 5226194aa78e..c038db93e2c3 100644 --- a/drivers/input/touchscreen/migor_ts.c +++ b/drivers/input/touchscreen/migor_ts.c @@ -242,19 +242,8 @@ static struct i2c_driver migor_ts_driver = { .id_table = migor_ts_id, }; -static int __init migor_ts_init(void) -{ - return i2c_add_driver(&migor_ts_driver); -} - -static void __exit migor_ts_exit(void) -{ - i2c_del_driver(&migor_ts_driver); -} +module_i2c_driver(migor_ts_driver); MODULE_DESCRIPTION("MigoR Touchscreen driver"); MODULE_AUTHOR("Magnus Damm <damm@opensource.se>"); MODULE_LICENSE("GPL"); - -module_init(migor_ts_init); -module_exit(migor_ts_exit); diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index d5ac09a1ee56..72f6ba3a4709 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -222,17 +222,7 @@ static struct i2c_driver pixcir_i2c_ts_driver = { .id_table = pixcir_i2c_ts_id, }; -static int __init pixcir_i2c_ts_init(void) -{ - return i2c_add_driver(&pixcir_i2c_ts_driver); -} -module_init(pixcir_i2c_ts_init); - -static void __exit pixcir_i2c_ts_exit(void) -{ - i2c_del_driver(&pixcir_i2c_ts_driver); -} -module_exit(pixcir_i2c_ts_exit); +module_i2c_driver(pixcir_i2c_ts_driver); MODULE_AUTHOR("Jianchun Bian <jcbian@pixcir.com.cn>, Dequan Meng <dqmeng@pixcir.com.cn>"); MODULE_DESCRIPTION("Pixcir I2C Touchscreen Driver"); diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 8825fe37d433..cbbf71b22696 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -268,17 +268,7 @@ static struct i2c_driver st1232_ts_driver = { }, }; -static int __init st1232_ts_init(void) -{ - return i2c_add_driver(&st1232_ts_driver); -} -module_init(st1232_ts_init); - -static void __exit st1232_ts_exit(void) -{ - i2c_del_driver(&st1232_ts_driver); -} -module_exit(st1232_ts_exit); +module_i2c_driver(st1232_ts_driver); MODULE_AUTHOR("Tony SIM <chinyeow.sim.xt@renesas.com>"); MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver"); diff --git a/drivers/input/touchscreen/ti_tscadc.c b/drivers/input/touchscreen/ti_tscadc.c new file mode 100644 index 000000000000..d229c741d544 --- /dev/null +++ b/drivers/input/touchscreen/ti_tscadc.c @@ -0,0 +1,486 @@ +/* + * TI Touch Screen driver + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/input/ti_tscadc.h> +#include <linux/delay.h> + +#define REG_IRQEOI 0x020 +#define REG_RAWIRQSTATUS 0x024 +#define REG_IRQSTATUS 0x028 +#define REG_IRQENABLE 0x02C +#define REG_IRQWAKEUP 0x034 +#define REG_CTRL 0x040 +#define REG_ADCFSM 0x044 +#define REG_CLKDIV 0x04C +#define REG_SE 0x054 +#define REG_IDLECONFIG 0x058 +#define REG_CHARGECONFIG 0x05C +#define REG_CHARGEDELAY 0x060 +#define REG_STEPCONFIG(n) (0x64 + ((n - 1) * 8)) +#define REG_STEPDELAY(n) (0x68 + ((n - 1) * 8)) +#define REG_STEPCONFIG13 0x0C4 +#define REG_STEPDELAY13 0x0C8 +#define REG_STEPCONFIG14 0x0CC +#define REG_STEPDELAY14 0x0D0 +#define REG_FIFO0CNT 0xE4 +#define REG_FIFO1THR 0xF4 +#define REG_FIFO0 0x100 +#define REG_FIFO1 0x200 + +/* Register Bitfields */ +#define IRQWKUP_ENB BIT(0) +#define STPENB_STEPENB 0x7FFF +#define IRQENB_FIFO1THRES BIT(5) +#define IRQENB_PENUP BIT(9) +#define STEPCONFIG_MODE_HWSYNC 0x2 +#define STEPCONFIG_SAMPLES_AVG (1 << 4) +#define STEPCONFIG_XPP (1 << 5) +#define STEPCONFIG_XNN (1 << 6) +#define STEPCONFIG_YPP (1 << 7) +#define STEPCONFIG_YNN (1 << 8) +#define STEPCONFIG_XNP (1 << 9) +#define STEPCONFIG_YPN (1 << 10) +#define STEPCONFIG_INM (1 << 18) +#define STEPCONFIG_INP (1 << 20) +#define STEPCONFIG_INP_5 (1 << 21) +#define STEPCONFIG_FIFO1 (1 << 26) +#define STEPCONFIG_OPENDLY 0xff +#define STEPCONFIG_Z1 (3 << 19) +#define STEPIDLE_INP (1 << 22) +#define STEPCHARGE_RFP (1 << 12) +#define STEPCHARGE_INM (1 << 15) +#define STEPCHARGE_INP (1 << 19) +#define STEPCHARGE_RFM (1 << 23) +#define STEPCHARGE_DELAY 0x1 +#define CNTRLREG_TSCSSENB (1 << 0) +#define CNTRLREG_STEPID (1 << 1) +#define CNTRLREG_STEPCONFIGWRT (1 << 2) +#define CNTRLREG_4WIRE (1 << 5) +#define CNTRLREG_5WIRE (1 << 6) +#define CNTRLREG_8WIRE (3 << 5) +#define CNTRLREG_TSCENB (1 << 7) +#define ADCFSM_STEPID 0x10 + +#define SEQ_SETTLE 275 +#define ADC_CLK 3000000 +#define MAX_12BIT ((1 << 12) - 1) +#define TSCADC_DELTA_X 15 +#define TSCADC_DELTA_Y 15 + +struct tscadc { + struct input_dev *input; + struct clk *tsc_ick; + void __iomem *tsc_base; + unsigned int irq; + unsigned int wires; + unsigned int x_plate_resistance; + bool pen_down; +}; + +static unsigned int tscadc_readl(struct tscadc *ts, unsigned int reg) +{ + return readl(ts->tsc_base + reg); +} + +static void tscadc_writel(struct tscadc *tsc, unsigned int reg, + unsigned int val) +{ + writel(val, tsc->tsc_base + reg); +} + +static void tscadc_step_config(struct tscadc *ts_dev) +{ + unsigned int config; + int i; + + /* Configure the Step registers */ + + config = STEPCONFIG_MODE_HWSYNC | + STEPCONFIG_SAMPLES_AVG | STEPCONFIG_XPP; + switch (ts_dev->wires) { + case 4: + config |= STEPCONFIG_INP | STEPCONFIG_XNN; + break; + case 5: + config |= STEPCONFIG_YNN | + STEPCONFIG_INP_5 | STEPCONFIG_XNN | + STEPCONFIG_YPP; + break; + case 8: + config |= STEPCONFIG_INP | STEPCONFIG_XNN; + break; + } + + for (i = 1; i < 7; i++) { + tscadc_writel(ts_dev, REG_STEPCONFIG(i), config); + tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); + } + + config = 0; + config = STEPCONFIG_MODE_HWSYNC | + STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YNN | + STEPCONFIG_INM | STEPCONFIG_FIFO1; + switch (ts_dev->wires) { + case 4: + config |= STEPCONFIG_YPP; + break; + case 5: + config |= STEPCONFIG_XPP | STEPCONFIG_INP_5 | + STEPCONFIG_XNP | STEPCONFIG_YPN; + break; + case 8: + config |= STEPCONFIG_YPP; + break; + } + + for (i = 7; i < 13; i++) { + tscadc_writel(ts_dev, REG_STEPCONFIG(i), config); + tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); + } + + config = 0; + /* Charge step configuration */ + config = STEPCONFIG_XPP | STEPCONFIG_YNN | + STEPCHARGE_RFP | STEPCHARGE_RFM | + STEPCHARGE_INM | STEPCHARGE_INP; + + tscadc_writel(ts_dev, REG_CHARGECONFIG, config); + tscadc_writel(ts_dev, REG_CHARGEDELAY, STEPCHARGE_DELAY); + + config = 0; + /* Configure to calculate pressure */ + config = STEPCONFIG_MODE_HWSYNC | + STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YPP | + STEPCONFIG_XNN | STEPCONFIG_INM; + tscadc_writel(ts_dev, REG_STEPCONFIG13, config); + tscadc_writel(ts_dev, REG_STEPDELAY13, STEPCONFIG_OPENDLY); + + config |= STEPCONFIG_Z1 | STEPCONFIG_FIFO1; + tscadc_writel(ts_dev, REG_STEPCONFIG14, config); + tscadc_writel(ts_dev, REG_STEPDELAY14, STEPCONFIG_OPENDLY); + + tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB); +} + +static void tscadc_idle_config(struct tscadc *ts_config) +{ + unsigned int idleconfig; + + idleconfig = STEPCONFIG_YNN | + STEPCONFIG_INM | + STEPCONFIG_YPN | STEPIDLE_INP; + tscadc_writel(ts_config, REG_IDLECONFIG, idleconfig); +} + +static void tscadc_read_coordinates(struct tscadc *ts_dev, + unsigned int *x, unsigned int *y) +{ + unsigned int fifocount = tscadc_readl(ts_dev, REG_FIFO0CNT); + unsigned int prev_val_x = ~0, prev_val_y = ~0; + unsigned int prev_diff_x = ~0, prev_diff_y = ~0; + unsigned int read, diff; + unsigned int i; + + /* + * Delta filter is used to remove large variations in sampled + * values from ADC. The filter tries to predict where the next + * coordinate could be. This is done by taking a previous + * coordinate and subtracting it form current one. Further the + * algorithm compares the difference with that of a present value, + * if true the value is reported to the sub system. + */ + for (i = 0; i < fifocount - 1; i++) { + read = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff; + diff = abs(read - prev_val_x); + if (diff < prev_diff_x) { + prev_diff_x = diff; + *x = read; + } + prev_val_x = read; + + read = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff; + diff = abs(read - prev_val_y); + if (diff < prev_diff_y) { + prev_diff_y = diff; + *y = read; + } + prev_val_y = read; + } +} + +static irqreturn_t tscadc_irq(int irq, void *dev) +{ + struct tscadc *ts_dev = dev; + struct input_dev *input_dev = ts_dev->input; + unsigned int status, irqclr = 0; + unsigned int x = 0, y = 0; + unsigned int z1, z2, z; + unsigned int fsm; + + status = tscadc_readl(ts_dev, REG_IRQSTATUS); + if (status & IRQENB_FIFO1THRES) { + tscadc_read_coordinates(ts_dev, &x, &y); + + z1 = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff; + z2 = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff; + + if (ts_dev->pen_down && z1 != 0 && z2 != 0) { + /* + * Calculate pressure using formula + * Resistance(touch) = x plate resistance * + * x postion/4096 * ((z2 / z1) - 1) + */ + z = z2 - z1; + z *= x; + z *= ts_dev->x_plate_resistance; + z /= z1; + z = (z + 2047) >> 12; + + if (z <= MAX_12BIT) { + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, y); + input_report_abs(input_dev, ABS_PRESSURE, z); + input_report_key(input_dev, BTN_TOUCH, 1); + input_sync(input_dev); + } + } + irqclr |= IRQENB_FIFO1THRES; + } + + /* + * Time for sequencer to settle, to read + * correct state of the sequencer. + */ + udelay(SEQ_SETTLE); + + status = tscadc_readl(ts_dev, REG_RAWIRQSTATUS); + if (status & IRQENB_PENUP) { + /* Pen up event */ + fsm = tscadc_readl(ts_dev, REG_ADCFSM); + if (fsm == ADCFSM_STEPID) { + ts_dev->pen_down = false; + input_report_key(input_dev, BTN_TOUCH, 0); + input_report_abs(input_dev, ABS_PRESSURE, 0); + input_sync(input_dev); + } else { + ts_dev->pen_down = true; + } + irqclr |= IRQENB_PENUP; + } + + tscadc_writel(ts_dev, REG_IRQSTATUS, irqclr); + /* check pending interrupts */ + tscadc_writel(ts_dev, REG_IRQEOI, 0x0); + + tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB); + return IRQ_HANDLED; +} + +/* + * The functions for inserting/removing driver as a module. + */ + +static int __devinit tscadc_probe(struct platform_device *pdev) +{ + const struct tsc_data *pdata = pdev->dev.platform_data; + struct resource *res; + struct tscadc *ts_dev; + struct input_dev *input_dev; + struct clk *clk; + int err; + int clk_value, ctrl, irq; + + if (!pdata) { + dev_err(&pdev->dev, "missing platform data.\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no memory resource defined.\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq ID is specified.\n"); + return -EINVAL; + } + + /* Allocate memory for device */ + ts_dev = kzalloc(sizeof(struct tscadc), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts_dev || !input_dev) { + dev_err(&pdev->dev, "failed to allocate memory.\n"); + err = -ENOMEM; + goto err_free_mem; + } + + ts_dev->input = input_dev; + ts_dev->irq = irq; + ts_dev->wires = pdata->wires; + ts_dev->x_plate_resistance = pdata->x_plate_resistance; + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + dev_err(&pdev->dev, "failed to reserve registers.\n"); + err = -EBUSY; + goto err_free_mem; + } + + ts_dev->tsc_base = ioremap(res->start, resource_size(res)); + if (!ts_dev->tsc_base) { + dev_err(&pdev->dev, "failed to map registers.\n"); + err = -ENOMEM; + goto err_release_mem_region; + } + + err = request_irq(ts_dev->irq, tscadc_irq, + 0, pdev->dev.driver->name, ts_dev); + if (err) { + dev_err(&pdev->dev, "failed to allocate irq.\n"); + goto err_unmap_regs; + } + + ts_dev->tsc_ick = clk_get(&pdev->dev, "adc_tsc_ick"); + if (IS_ERR(ts_dev->tsc_ick)) { + dev_err(&pdev->dev, "failed to get TSC ick\n"); + goto err_free_irq; + } + clk_enable(ts_dev->tsc_ick); + + clk = clk_get(&pdev->dev, "adc_tsc_fck"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get TSC fck\n"); + err = PTR_ERR(clk); + goto err_disable_clk; + } + + clk_value = clk_get_rate(clk) / ADC_CLK; + clk_put(clk); + + if (clk_value < 7) { + dev_err(&pdev->dev, "clock input less than min clock requirement\n"); + goto err_disable_clk; + } + /* CLKDIV needs to be configured to the value minus 1 */ + tscadc_writel(ts_dev, REG_CLKDIV, clk_value - 1); + + /* Enable wake-up of the SoC using touchscreen */ + tscadc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB); + + ctrl = CNTRLREG_STEPCONFIGWRT | + CNTRLREG_TSCENB | + CNTRLREG_STEPID; + switch (ts_dev->wires) { + case 4: + ctrl |= CNTRLREG_4WIRE; + break; + case 5: + ctrl |= CNTRLREG_5WIRE; + break; + case 8: + ctrl |= CNTRLREG_8WIRE; + break; + } + tscadc_writel(ts_dev, REG_CTRL, ctrl); + + tscadc_idle_config(ts_dev); + tscadc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO1THRES); + tscadc_step_config(ts_dev); + tscadc_writel(ts_dev, REG_FIFO1THR, 6); + + ctrl |= CNTRLREG_TSCSSENB; + tscadc_writel(ts_dev, REG_CTRL, ctrl); + + input_dev->name = "ti-tsc-adc"; + input_dev->dev.parent = &pdev->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); + + /* register to the input system */ + err = input_register_device(input_dev); + if (err) + goto err_disable_clk; + + platform_set_drvdata(pdev, ts_dev); + return 0; + +err_disable_clk: + clk_disable(ts_dev->tsc_ick); + clk_put(ts_dev->tsc_ick); +err_free_irq: + free_irq(ts_dev->irq, ts_dev); +err_unmap_regs: + iounmap(ts_dev->tsc_base); +err_release_mem_region: + release_mem_region(res->start, resource_size(res)); +err_free_mem: + input_free_device(input_dev); + kfree(ts_dev); + return err; +} + +static int __devexit tscadc_remove(struct platform_device *pdev) +{ + struct tscadc *ts_dev = platform_get_drvdata(pdev); + struct resource *res; + + free_irq(ts_dev->irq, ts_dev); + + input_unregister_device(ts_dev->input); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iounmap(ts_dev->tsc_base); + release_mem_region(res->start, resource_size(res)); + + clk_disable(ts_dev->tsc_ick); + clk_put(ts_dev->tsc_ick); + + kfree(ts_dev); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver ti_tsc_driver = { + .probe = tscadc_probe, + .remove = __devexit_p(tscadc_remove), + .driver = { + .name = "tsc", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(ti_tsc_driver); + +MODULE_DESCRIPTION("TI touchscreen controller driver"); +MODULE_AUTHOR("Rachna Patil <rachna@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c index 067d95662997..b6adeaee9cc5 100644 --- a/drivers/input/touchscreen/tsc2005.c +++ b/drivers/input/touchscreen/tsc2005.c @@ -747,17 +747,7 @@ static struct spi_driver tsc2005_driver = { .remove = __devexit_p(tsc2005_remove), }; -static int __init tsc2005_init(void) -{ - return spi_register_driver(&tsc2005_driver); -} -module_init(tsc2005_init); - -static void __exit tsc2005_exit(void) -{ - spi_unregister_driver(&tsc2005_driver); -} -module_exit(tsc2005_exit); +module_spi_driver(tsc2005_driver); MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>"); MODULE_DESCRIPTION("TSC2005 Touchscreen Driver"); diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c index 1f674cb6c55b..1473d2382afd 100644 --- a/drivers/input/touchscreen/tsc2007.c +++ b/drivers/input/touchscreen/tsc2007.c @@ -399,18 +399,7 @@ static struct i2c_driver tsc2007_driver = { .remove = __devexit_p(tsc2007_remove), }; -static int __init tsc2007_init(void) -{ - return i2c_add_driver(&tsc2007_driver); -} - -static void __exit tsc2007_exit(void) -{ - i2c_del_driver(&tsc2007_driver); -} - -module_init(tsc2007_init); -module_exit(tsc2007_exit); +module_i2c_driver(tsc2007_driver); MODULE_AUTHOR("Kwangwoo Lee <kwlee@mtekvision.com>"); MODULE_DESCRIPTION("TSC2007 TouchScreen Driver"); diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index 3a5ebf452e81..22cd96f58c99 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -17,6 +17,7 @@ * - Zytronic capacitive touchscreen * - NEXIO/iNexio * - Elo TouchSystems 2700 IntelliTouch + * - EasyTouch USB Dual/Multi touch controller from Data Modul * * Copyright (C) 2004-2007 by Daniel Ritz <daniel.ritz@gmx.ch> * Copyright (C) by Todd E. Johnson (mtouchusb.c) @@ -140,6 +141,7 @@ enum { DEVTYPE_TC45USB, DEVTYPE_NEXIO, DEVTYPE_ELO, + DEVTYPE_ETOUCH, }; #define USB_DEVICE_HID_CLASS(vend, prod) \ @@ -245,6 +247,10 @@ static const struct usb_device_id usbtouch_devices[] = { {USB_DEVICE(0x04e7, 0x0020), .driver_info = DEVTYPE_ELO}, #endif +#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH + {USB_DEVICE(0x7374, 0x0001), .driver_info = DEVTYPE_ETOUCH}, +#endif + {} }; @@ -326,6 +332,51 @@ static int egalax_get_pkt_len(unsigned char *buf, int len) } #endif +/***************************************************************************** + * EasyTouch part + */ + +#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH + +#ifndef MULTI_PACKET +#define MULTI_PACKET +#endif + +#define ETOUCH_PKT_TYPE_MASK 0xFE +#define ETOUCH_PKT_TYPE_REPT 0x80 +#define ETOUCH_PKT_TYPE_REPT2 0xB0 +#define ETOUCH_PKT_TYPE_DIAG 0x0A + +static int etouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + if ((pkt[0] & ETOUCH_PKT_TYPE_MASK) != ETOUCH_PKT_TYPE_REPT && + (pkt[0] & ETOUCH_PKT_TYPE_MASK) != ETOUCH_PKT_TYPE_REPT2) + return 0; + + dev->x = ((pkt[1] & 0x1F) << 7) | (pkt[2] & 0x7F); + dev->y = ((pkt[3] & 0x1F) << 7) | (pkt[4] & 0x7F); + dev->touch = pkt[0] & 0x01; + + return 1; +} + +static int etouch_get_pkt_len(unsigned char *buf, int len) +{ + switch (buf[0] & ETOUCH_PKT_TYPE_MASK) { + case ETOUCH_PKT_TYPE_REPT: + case ETOUCH_PKT_TYPE_REPT2: + return 5; + + case ETOUCH_PKT_TYPE_DIAG: + if (len < 2) + return -1; + + return buf[1] + 2; + } + + return 0; +} +#endif /***************************************************************************** * PanJit Part @@ -1175,6 +1226,18 @@ static struct usbtouch_device_info usbtouch_dev_info[] = { .exit = nexio_exit, }, #endif +#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH + [DEVTYPE_ETOUCH] = { + .min_xc = 0x0, + .max_xc = 0x07ff, + .min_yc = 0x0, + .max_yc = 0x07ff, + .rept_size = 16, + .process_pkt = usbtouch_process_multi, + .get_pkt_len = etouch_get_pkt_len, + .read_data = etouch_read_data, + }, +#endif }; diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index 6bea6962f8ee..3bd9fff5c589 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -142,4 +142,24 @@ config OMAP_IOMMU_DEBUG Say N unless you know you need this. +config TEGRA_IOMMU_GART + bool "Tegra GART IOMMU Support" + depends on ARCH_TEGRA_2x_SOC + select IOMMU_API + help + Enables support for remapping discontiguous physical memory + shared with the operating system into contiguous I/O virtual + space through the GART (Graphics Address Relocation Table) + hardware included on Tegra SoCs. + +config TEGRA_IOMMU_SMMU + bool "Tegra SMMU IOMMU Support" + depends on ARCH_TEGRA_3x_SOC + select IOMMU_API + help + Enables support for remapping discontiguous physical memory + shared with the operating system into contiguous I/O virtual + space through the SMMU (System Memory Management Unit) + hardware included on Tegra SoCs. + endif # IOMMU_SUPPORT diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index 0e36b4934aff..7ad7a3bc1242 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -8,3 +8,5 @@ obj-$(CONFIG_IRQ_REMAP) += intr_remapping.o obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o obj-$(CONFIG_OMAP_IOVMM) += omap-iovmm.o obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o +obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o +obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index a35e98ad9725..c56790375e0f 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -196,6 +196,8 @@ static u32 rlookup_table_size; /* size if the rlookup table */ */ extern void iommu_flush_all_caches(struct amd_iommu *iommu); +static int amd_iommu_enable_interrupts(void); + static inline void update_last_devid(u16 devid) { if (devid > amd_iommu_last_bdf) @@ -358,8 +360,6 @@ static void iommu_disable(struct amd_iommu *iommu) */ static u8 * __init iommu_map_mmio_space(u64 address) { - u8 *ret; - if (!request_mem_region(address, MMIO_REGION_LENGTH, "amd_iommu")) { pr_err("AMD-Vi: Can not reserve memory region %llx for mmio\n", address); @@ -367,13 +367,7 @@ static u8 * __init iommu_map_mmio_space(u64 address) return NULL; } - ret = ioremap_nocache(address, MMIO_REGION_LENGTH); - if (ret != NULL) - return ret; - - release_mem_region(address, MMIO_REGION_LENGTH); - - return NULL; + return ioremap_nocache(address, MMIO_REGION_LENGTH); } static void __init iommu_unmap_mmio_space(struct amd_iommu *iommu) @@ -1131,8 +1125,9 @@ static int iommu_setup_msi(struct amd_iommu *iommu) { int r; - if (pci_enable_msi(iommu->dev)) - return 1; + r = pci_enable_msi(iommu->dev); + if (r) + return r; r = request_threaded_irq(iommu->dev->irq, amd_iommu_int_handler, @@ -1142,27 +1137,36 @@ static int iommu_setup_msi(struct amd_iommu *iommu) if (r) { pci_disable_msi(iommu->dev); - return 1; + return r; } iommu->int_enabled = true; - iommu_feature_enable(iommu, CONTROL_EVT_INT_EN); - - if (iommu->ppr_log != NULL) - iommu_feature_enable(iommu, CONTROL_PPFINT_EN); return 0; } static int iommu_init_msi(struct amd_iommu *iommu) { + int ret; + if (iommu->int_enabled) - return 0; + goto enable_faults; if (pci_find_capability(iommu->dev, PCI_CAP_ID_MSI)) - return iommu_setup_msi(iommu); + ret = iommu_setup_msi(iommu); + else + ret = -ENODEV; - return 1; + if (ret) + return ret; + +enable_faults: + iommu_feature_enable(iommu, CONTROL_EVT_INT_EN); + + if (iommu->ppr_log != NULL) + iommu_feature_enable(iommu, CONTROL_PPFINT_EN); + + return 0; } /**************************************************************************** @@ -1381,7 +1385,6 @@ static void enable_iommus(void) iommu_enable_ppr_log(iommu); iommu_enable_gt(iommu); iommu_set_exclusion_range(iommu); - iommu_init_msi(iommu); iommu_enable(iommu); iommu_flush_all_caches(iommu); } @@ -1409,6 +1412,8 @@ static void amd_iommu_resume(void) /* re-load the hardware */ enable_iommus(); + + amd_iommu_enable_interrupts(); } static int amd_iommu_suspend(void) @@ -1424,10 +1429,40 @@ static struct syscore_ops amd_iommu_syscore_ops = { .resume = amd_iommu_resume, }; +static void __init free_on_init_error(void) +{ + amd_iommu_uninit_devices(); + + free_pages((unsigned long)amd_iommu_pd_alloc_bitmap, + get_order(MAX_DOMAIN_ID/8)); + + free_pages((unsigned long)amd_iommu_rlookup_table, + get_order(rlookup_table_size)); + + free_pages((unsigned long)amd_iommu_alias_table, + get_order(alias_table_size)); + + free_pages((unsigned long)amd_iommu_dev_table, + get_order(dev_table_size)); + + free_iommu_all(); + + free_unity_maps(); + +#ifdef CONFIG_GART_IOMMU + /* + * We failed to initialize the AMD IOMMU - try fallback to GART + * if possible. + */ + gart_iommu_init(); + +#endif +} + /* - * This is the core init function for AMD IOMMU hardware in the system. - * This function is called from the generic x86 DMA layer initialization - * code. + * This is the hardware init function for AMD IOMMU in the system. + * This function is called either from amd_iommu_init or from the interrupt + * remapping setup code. * * This function basically parses the ACPI table for AMD IOMMU (IVRS) * three times: @@ -1446,16 +1481,21 @@ static struct syscore_ops amd_iommu_syscore_ops = { * remapping requirements parsed out of the ACPI table in * this last pass. * - * After that the hardware is initialized and ready to go. In the last - * step we do some Linux specific things like registering the driver in - * the dma_ops interface and initializing the suspend/resume support - * functions. Finally it prints some information about AMD IOMMUs and - * the driver state and enables the hardware. + * After everything is set up the IOMMUs are enabled and the necessary + * hotplug and suspend notifiers are registered. */ -static int __init amd_iommu_init(void) +int __init amd_iommu_init_hardware(void) { int i, ret = 0; + if (!amd_iommu_detected) + return -ENODEV; + + if (amd_iommu_dev_table != NULL) { + /* Hardware already initialized */ + return 0; + } + /* * First parse ACPI tables to find the largest Bus/Dev/Func * we need to handle. Upon this information the shared data @@ -1472,9 +1512,8 @@ static int __init amd_iommu_init(void) alias_table_size = tbl_size(ALIAS_TABLE_ENTRY_SIZE); rlookup_table_size = tbl_size(RLOOKUP_TABLE_ENTRY_SIZE); - ret = -ENOMEM; - /* Device table - directly used by all IOMMUs */ + ret = -ENOMEM; amd_iommu_dev_table = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, get_order(dev_table_size)); if (amd_iommu_dev_table == NULL) @@ -1546,20 +1585,65 @@ static int __init amd_iommu_init(void) enable_iommus(); + amd_iommu_init_notifier(); + + register_syscore_ops(&amd_iommu_syscore_ops); + +out: + return ret; + +free: + free_on_init_error(); + + return ret; +} + +static int amd_iommu_enable_interrupts(void) +{ + struct amd_iommu *iommu; + int ret = 0; + + for_each_iommu(iommu) { + ret = iommu_init_msi(iommu); + if (ret) + goto out; + } + +out: + return ret; +} + +/* + * This is the core init function for AMD IOMMU hardware in the system. + * This function is called from the generic x86 DMA layer initialization + * code. + * + * The function calls amd_iommu_init_hardware() to setup and enable the + * IOMMU hardware if this has not happened yet. After that the driver + * registers for the DMA-API and for the IOMMU-API as necessary. + */ +static int __init amd_iommu_init(void) +{ + int ret = 0; + + ret = amd_iommu_init_hardware(); + if (ret) + goto out; + + ret = amd_iommu_enable_interrupts(); + if (ret) + goto free; + if (iommu_pass_through) ret = amd_iommu_init_passthrough(); else ret = amd_iommu_init_dma_ops(); if (ret) - goto free_disable; + goto free; amd_iommu_init_api(); - amd_iommu_init_notifier(); - - register_syscore_ops(&amd_iommu_syscore_ops); - if (iommu_pass_through) goto out; @@ -1569,39 +1653,14 @@ static int __init amd_iommu_init(void) printk(KERN_INFO "AMD-Vi: Lazy IO/TLB flushing enabled\n"); x86_platform.iommu_shutdown = disable_iommus; + out: return ret; -free_disable: - disable_iommus(); - free: - amd_iommu_uninit_devices(); - - free_pages((unsigned long)amd_iommu_pd_alloc_bitmap, - get_order(MAX_DOMAIN_ID/8)); - - free_pages((unsigned long)amd_iommu_rlookup_table, - get_order(rlookup_table_size)); - - free_pages((unsigned long)amd_iommu_alias_table, - get_order(alias_table_size)); - - free_pages((unsigned long)amd_iommu_dev_table, - get_order(dev_table_size)); - - free_iommu_all(); - - free_unity_maps(); - -#ifdef CONFIG_GART_IOMMU - /* - * We failed to initialize the AMD IOMMU - try fallback to GART - * if possible. - */ - gart_iommu_init(); + disable_iommus(); -#endif + free_on_init_error(); goto out; } diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c index 8add9f125d3e..036fe9bf157e 100644 --- a/drivers/iommu/amd_iommu_v2.c +++ b/drivers/iommu/amd_iommu_v2.c @@ -921,7 +921,16 @@ static int __init amd_iommu_v2_init(void) size_t state_table_size; int ret; - pr_info("AMD IOMMUv2 driver by Joerg Roedel <joerg.roedel@amd.com>"); + pr_info("AMD IOMMUv2 driver by Joerg Roedel <joerg.roedel@amd.com>\n"); + + if (!amd_iommu_v2_supported()) { + pr_info("AMD IOMMUv2 functionality not available on this sytem\n"); + /* + * Load anyway to provide the symbols to other modules + * which may use AMD IOMMUv2 optionally. + */ + return 0; + } spin_lock_init(&state_lock); @@ -961,6 +970,9 @@ static void __exit amd_iommu_v2_exit(void) size_t state_table_size; int i; + if (!amd_iommu_v2_supported()) + return; + profile_event_unregister(PROFILE_TASK_EXIT, &profile_nb); amd_iommu_unregister_ppr_notifier(&ppr_nb); diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c new file mode 100644 index 000000000000..779306ee7b16 --- /dev/null +++ b/drivers/iommu/tegra-gart.c @@ -0,0 +1,451 @@ +/* + * IOMMU API for GART in Tegra20 + * + * Copyright (c) 2010-2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define pr_fmt(fmt) "%s(): " fmt, __func__ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/mm.h> +#include <linux/list.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/iommu.h> + +#include <asm/cacheflush.h> + +/* bitmap of the page sizes currently supported */ +#define GART_IOMMU_PGSIZES (SZ_4K) + +#define GART_CONFIG 0x24 +#define GART_ENTRY_ADDR 0x28 +#define GART_ENTRY_DATA 0x2c +#define GART_ENTRY_PHYS_ADDR_VALID (1 << 31) + +#define GART_PAGE_SHIFT 12 +#define GART_PAGE_SIZE (1 << GART_PAGE_SHIFT) +#define GART_PAGE_MASK \ + (~(GART_PAGE_SIZE - 1) & ~GART_ENTRY_PHYS_ADDR_VALID) + +struct gart_client { + struct device *dev; + struct list_head list; +}; + +struct gart_device { + void __iomem *regs; + u32 *savedata; + u32 page_count; /* total remappable size */ + dma_addr_t iovmm_base; /* offset to vmm_area */ + spinlock_t pte_lock; /* for pagetable */ + struct list_head client; + spinlock_t client_lock; /* for client list */ + struct device *dev; +}; + +static struct gart_device *gart_handle; /* unique for a system */ + +#define GART_PTE(_pfn) \ + (GART_ENTRY_PHYS_ADDR_VALID | ((_pfn) << PAGE_SHIFT)) + +/* + * Any interaction between any block on PPSB and a block on APB or AHB + * must have these read-back to ensure the APB/AHB bus transaction is + * complete before initiating activity on the PPSB block. + */ +#define FLUSH_GART_REGS(gart) ((void)readl((gart)->regs + GART_CONFIG)) + +#define for_each_gart_pte(gart, iova) \ + for (iova = gart->iovmm_base; \ + iova < gart->iovmm_base + GART_PAGE_SIZE * gart->page_count; \ + iova += GART_PAGE_SIZE) + +static inline void gart_set_pte(struct gart_device *gart, + unsigned long offs, u32 pte) +{ + writel(offs, gart->regs + GART_ENTRY_ADDR); + writel(pte, gart->regs + GART_ENTRY_DATA); + + dev_dbg(gart->dev, "%s %08lx:%08x\n", + pte ? "map" : "unmap", offs, pte & GART_PAGE_MASK); +} + +static inline unsigned long gart_read_pte(struct gart_device *gart, + unsigned long offs) +{ + unsigned long pte; + + writel(offs, gart->regs + GART_ENTRY_ADDR); + pte = readl(gart->regs + GART_ENTRY_DATA); + + return pte; +} + +static void do_gart_setup(struct gart_device *gart, const u32 *data) +{ + unsigned long iova; + + for_each_gart_pte(gart, iova) + gart_set_pte(gart, iova, data ? *(data++) : 0); + + writel(1, gart->regs + GART_CONFIG); + FLUSH_GART_REGS(gart); +} + +#ifdef DEBUG +static void gart_dump_table(struct gart_device *gart) +{ + unsigned long iova; + unsigned long flags; + + spin_lock_irqsave(&gart->pte_lock, flags); + for_each_gart_pte(gart, iova) { + unsigned long pte; + + pte = gart_read_pte(gart, iova); + + dev_dbg(gart->dev, "%s %08lx:%08lx\n", + (GART_ENTRY_PHYS_ADDR_VALID & pte) ? "v" : " ", + iova, pte & GART_PAGE_MASK); + } + spin_unlock_irqrestore(&gart->pte_lock, flags); +} +#else +static inline void gart_dump_table(struct gart_device *gart) +{ +} +#endif + +static inline bool gart_iova_range_valid(struct gart_device *gart, + unsigned long iova, size_t bytes) +{ + unsigned long iova_start, iova_end, gart_start, gart_end; + + iova_start = iova; + iova_end = iova_start + bytes - 1; + gart_start = gart->iovmm_base; + gart_end = gart_start + gart->page_count * GART_PAGE_SIZE - 1; + + if (iova_start < gart_start) + return false; + if (iova_end > gart_end) + return false; + return true; +} + +static int gart_iommu_attach_dev(struct iommu_domain *domain, + struct device *dev) +{ + struct gart_device *gart; + struct gart_client *client, *c; + int err = 0; + + gart = dev_get_drvdata(dev->parent); + if (!gart) + return -EINVAL; + domain->priv = gart; + + client = devm_kzalloc(gart->dev, sizeof(*c), GFP_KERNEL); + if (!client) + return -ENOMEM; + client->dev = dev; + + spin_lock(&gart->client_lock); + list_for_each_entry(c, &gart->client, list) { + if (c->dev == dev) { + dev_err(gart->dev, + "%s is already attached\n", dev_name(dev)); + err = -EINVAL; + goto fail; + } + } + list_add(&client->list, &gart->client); + spin_unlock(&gart->client_lock); + dev_dbg(gart->dev, "Attached %s\n", dev_name(dev)); + return 0; + +fail: + devm_kfree(gart->dev, client); + spin_unlock(&gart->client_lock); + return err; +} + +static void gart_iommu_detach_dev(struct iommu_domain *domain, + struct device *dev) +{ + struct gart_device *gart = domain->priv; + struct gart_client *c; + + spin_lock(&gart->client_lock); + + list_for_each_entry(c, &gart->client, list) { + if (c->dev == dev) { + list_del(&c->list); + devm_kfree(gart->dev, c); + dev_dbg(gart->dev, "Detached %s\n", dev_name(dev)); + goto out; + } + } + dev_err(gart->dev, "Couldn't find\n"); +out: + spin_unlock(&gart->client_lock); +} + +static int gart_iommu_domain_init(struct iommu_domain *domain) +{ + return 0; +} + +static void gart_iommu_domain_destroy(struct iommu_domain *domain) +{ + struct gart_device *gart = domain->priv; + + if (!gart) + return; + + spin_lock(&gart->client_lock); + if (!list_empty(&gart->client)) { + struct gart_client *c; + + list_for_each_entry(c, &gart->client, list) + gart_iommu_detach_dev(domain, c->dev); + } + spin_unlock(&gart->client_lock); + domain->priv = NULL; +} + +static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova, + phys_addr_t pa, size_t bytes, int prot) +{ + struct gart_device *gart = domain->priv; + unsigned long flags; + unsigned long pfn; + + if (!gart_iova_range_valid(gart, iova, bytes)) + return -EINVAL; + + spin_lock_irqsave(&gart->pte_lock, flags); + pfn = __phys_to_pfn(pa); + if (!pfn_valid(pfn)) { + dev_err(gart->dev, "Invalid page: %08x\n", pa); + spin_unlock_irqrestore(&gart->pte_lock, flags); + return -EINVAL; + } + gart_set_pte(gart, iova, GART_PTE(pfn)); + FLUSH_GART_REGS(gart); + spin_unlock_irqrestore(&gart->pte_lock, flags); + return 0; +} + +static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova, + size_t bytes) +{ + struct gart_device *gart = domain->priv; + unsigned long flags; + + if (!gart_iova_range_valid(gart, iova, bytes)) + return 0; + + spin_lock_irqsave(&gart->pte_lock, flags); + gart_set_pte(gart, iova, 0); + FLUSH_GART_REGS(gart); + spin_unlock_irqrestore(&gart->pte_lock, flags); + return 0; +} + +static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain, + unsigned long iova) +{ + struct gart_device *gart = domain->priv; + unsigned long pte; + phys_addr_t pa; + unsigned long flags; + + if (!gart_iova_range_valid(gart, iova, 0)) + return -EINVAL; + + spin_lock_irqsave(&gart->pte_lock, flags); + pte = gart_read_pte(gart, iova); + spin_unlock_irqrestore(&gart->pte_lock, flags); + + pa = (pte & GART_PAGE_MASK); + if (!pfn_valid(__phys_to_pfn(pa))) { + dev_err(gart->dev, "No entry for %08lx:%08x\n", iova, pa); + gart_dump_table(gart); + return -EINVAL; + } + return pa; +} + +static int gart_iommu_domain_has_cap(struct iommu_domain *domain, + unsigned long cap) +{ + return 0; +} + +static struct iommu_ops gart_iommu_ops = { + .domain_init = gart_iommu_domain_init, + .domain_destroy = gart_iommu_domain_destroy, + .attach_dev = gart_iommu_attach_dev, + .detach_dev = gart_iommu_detach_dev, + .map = gart_iommu_map, + .unmap = gart_iommu_unmap, + .iova_to_phys = gart_iommu_iova_to_phys, + .domain_has_cap = gart_iommu_domain_has_cap, + .pgsize_bitmap = GART_IOMMU_PGSIZES, +}; + +static int tegra_gart_suspend(struct device *dev) +{ + struct gart_device *gart = dev_get_drvdata(dev); + unsigned long iova; + u32 *data = gart->savedata; + unsigned long flags; + + spin_lock_irqsave(&gart->pte_lock, flags); + for_each_gart_pte(gart, iova) + *(data++) = gart_read_pte(gart, iova); + spin_unlock_irqrestore(&gart->pte_lock, flags); + return 0; +} + +static int tegra_gart_resume(struct device *dev) +{ + struct gart_device *gart = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&gart->pte_lock, flags); + do_gart_setup(gart, gart->savedata); + spin_unlock_irqrestore(&gart->pte_lock, flags); + return 0; +} + +static int tegra_gart_probe(struct platform_device *pdev) +{ + struct gart_device *gart; + struct resource *res, *res_remap; + void __iomem *gart_regs; + int err; + struct device *dev = &pdev->dev; + + if (gart_handle) + return -EIO; + + BUILD_BUG_ON(PAGE_SHIFT != GART_PAGE_SHIFT); + + /* the GART memory aperture is required */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res_remap = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res || !res_remap) { + dev_err(dev, "GART memory aperture expected\n"); + return -ENXIO; + } + + gart = devm_kzalloc(dev, sizeof(*gart), GFP_KERNEL); + if (!gart) { + dev_err(dev, "failed to allocate gart_device\n"); + return -ENOMEM; + } + + gart_regs = devm_ioremap(dev, res->start, resource_size(res)); + if (!gart_regs) { + dev_err(dev, "failed to remap GART registers\n"); + err = -ENXIO; + goto fail; + } + + gart->dev = &pdev->dev; + spin_lock_init(&gart->pte_lock); + spin_lock_init(&gart->client_lock); + INIT_LIST_HEAD(&gart->client); + gart->regs = gart_regs; + gart->iovmm_base = (dma_addr_t)res_remap->start; + gart->page_count = (resource_size(res_remap) >> GART_PAGE_SHIFT); + + gart->savedata = vmalloc(sizeof(u32) * gart->page_count); + if (!gart->savedata) { + dev_err(dev, "failed to allocate context save area\n"); + err = -ENOMEM; + goto fail; + } + + platform_set_drvdata(pdev, gart); + do_gart_setup(gart, NULL); + + gart_handle = gart; + return 0; + +fail: + if (gart_regs) + devm_iounmap(dev, gart_regs); + if (gart && gart->savedata) + vfree(gart->savedata); + devm_kfree(dev, gart); + return err; +} + +static int tegra_gart_remove(struct platform_device *pdev) +{ + struct gart_device *gart = platform_get_drvdata(pdev); + struct device *dev = gart->dev; + + writel(0, gart->regs + GART_CONFIG); + if (gart->savedata) + vfree(gart->savedata); + if (gart->regs) + devm_iounmap(dev, gart->regs); + devm_kfree(dev, gart); + gart_handle = NULL; + return 0; +} + +const struct dev_pm_ops tegra_gart_pm_ops = { + .suspend = tegra_gart_suspend, + .resume = tegra_gart_resume, +}; + +static struct platform_driver tegra_gart_driver = { + .probe = tegra_gart_probe, + .remove = tegra_gart_remove, + .driver = { + .owner = THIS_MODULE, + .name = "tegra-gart", + .pm = &tegra_gart_pm_ops, + }, +}; + +static int __devinit tegra_gart_init(void) +{ + bus_set_iommu(&platform_bus_type, &gart_iommu_ops); + return platform_driver_register(&tegra_gart_driver); +} + +static void __exit tegra_gart_exit(void) +{ + platform_driver_unregister(&tegra_gart_driver); +} + +subsys_initcall(tegra_gart_init); +module_exit(tegra_gart_exit); + +MODULE_DESCRIPTION("IOMMU API for GART in Tegra20"); +MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c new file mode 100644 index 000000000000..eb93c821f592 --- /dev/null +++ b/drivers/iommu/tegra-smmu.c @@ -0,0 +1,1034 @@ +/* + * IOMMU API for SMMU in Tegra30 + * + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define pr_fmt(fmt) "%s(): " fmt, __func__ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/mm.h> +#include <linux/pagemap.h> +#include <linux/device.h> +#include <linux/sched.h> +#include <linux/iommu.h> +#include <linux/io.h> + +#include <asm/page.h> +#include <asm/cacheflush.h> + +#include <mach/iomap.h> +#include <mach/smmu.h> + +/* bitmap of the page sizes currently supported */ +#define SMMU_IOMMU_PGSIZES (SZ_4K) + +#define SMMU_CONFIG 0x10 +#define SMMU_CONFIG_DISABLE 0 +#define SMMU_CONFIG_ENABLE 1 + +#define SMMU_TLB_CONFIG 0x14 +#define SMMU_TLB_CONFIG_STATS__MASK (1 << 31) +#define SMMU_TLB_CONFIG_STATS__ENABLE (1 << 31) +#define SMMU_TLB_CONFIG_HIT_UNDER_MISS__ENABLE (1 << 29) +#define SMMU_TLB_CONFIG_ACTIVE_LINES__VALUE 0x10 +#define SMMU_TLB_CONFIG_RESET_VAL 0x20000010 + +#define SMMU_PTC_CONFIG 0x18 +#define SMMU_PTC_CONFIG_STATS__MASK (1 << 31) +#define SMMU_PTC_CONFIG_STATS__ENABLE (1 << 31) +#define SMMU_PTC_CONFIG_CACHE__ENABLE (1 << 29) +#define SMMU_PTC_CONFIG_INDEX_MAP__PATTERN 0x3f +#define SMMU_PTC_CONFIG_RESET_VAL 0x2000003f + +#define SMMU_PTB_ASID 0x1c +#define SMMU_PTB_ASID_CURRENT_SHIFT 0 + +#define SMMU_PTB_DATA 0x20 +#define SMMU_PTB_DATA_RESET_VAL 0 +#define SMMU_PTB_DATA_ASID_NONSECURE_SHIFT 29 +#define SMMU_PTB_DATA_ASID_WRITABLE_SHIFT 30 +#define SMMU_PTB_DATA_ASID_READABLE_SHIFT 31 + +#define SMMU_TLB_FLUSH 0x30 +#define SMMU_TLB_FLUSH_VA_MATCH_ALL 0 +#define SMMU_TLB_FLUSH_VA_MATCH_SECTION 2 +#define SMMU_TLB_FLUSH_VA_MATCH_GROUP 3 +#define SMMU_TLB_FLUSH_ASID_SHIFT 29 +#define SMMU_TLB_FLUSH_ASID_MATCH_DISABLE 0 +#define SMMU_TLB_FLUSH_ASID_MATCH_ENABLE 1 +#define SMMU_TLB_FLUSH_ASID_MATCH_SHIFT 31 + +#define SMMU_PTC_FLUSH 0x34 +#define SMMU_PTC_FLUSH_TYPE_ALL 0 +#define SMMU_PTC_FLUSH_TYPE_ADR 1 +#define SMMU_PTC_FLUSH_ADR_SHIFT 4 + +#define SMMU_ASID_SECURITY 0x38 + +#define SMMU_STATS_TLB_HIT_COUNT 0x1f0 +#define SMMU_STATS_TLB_MISS_COUNT 0x1f4 +#define SMMU_STATS_PTC_HIT_COUNT 0x1f8 +#define SMMU_STATS_PTC_MISS_COUNT 0x1fc + +#define SMMU_TRANSLATION_ENABLE_0 0x228 +#define SMMU_TRANSLATION_ENABLE_1 0x22c +#define SMMU_TRANSLATION_ENABLE_2 0x230 + +#define SMMU_AFI_ASID 0x238 /* PCIE */ +#define SMMU_AVPC_ASID 0x23c /* AVP */ +#define SMMU_DC_ASID 0x240 /* Display controller */ +#define SMMU_DCB_ASID 0x244 /* Display controller B */ +#define SMMU_EPP_ASID 0x248 /* Encoder pre-processor */ +#define SMMU_G2_ASID 0x24c /* 2D engine */ +#define SMMU_HC_ASID 0x250 /* Host1x */ +#define SMMU_HDA_ASID 0x254 /* High-def audio */ +#define SMMU_ISP_ASID 0x258 /* Image signal processor */ +#define SMMU_MPE_ASID 0x264 /* MPEG encoder */ +#define SMMU_NV_ASID 0x268 /* (3D) */ +#define SMMU_NV2_ASID 0x26c /* (3D) */ +#define SMMU_PPCS_ASID 0x270 /* AHB */ +#define SMMU_SATA_ASID 0x278 /* SATA */ +#define SMMU_VDE_ASID 0x27c /* Video decoder */ +#define SMMU_VI_ASID 0x280 /* Video input */ + +#define SMMU_PDE_NEXT_SHIFT 28 + +/* AHB Arbiter Registers */ +#define AHB_XBAR_CTRL 0xe0 +#define AHB_XBAR_CTRL_SMMU_INIT_DONE_DONE 1 +#define AHB_XBAR_CTRL_SMMU_INIT_DONE_SHIFT 17 + +#define SMMU_NUM_ASIDS 4 +#define SMMU_TLB_FLUSH_VA_SECTION__MASK 0xffc00000 +#define SMMU_TLB_FLUSH_VA_SECTION__SHIFT 12 /* right shift */ +#define SMMU_TLB_FLUSH_VA_GROUP__MASK 0xffffc000 +#define SMMU_TLB_FLUSH_VA_GROUP__SHIFT 12 /* right shift */ +#define SMMU_TLB_FLUSH_VA(iova, which) \ + ((((iova) & SMMU_TLB_FLUSH_VA_##which##__MASK) >> \ + SMMU_TLB_FLUSH_VA_##which##__SHIFT) | \ + SMMU_TLB_FLUSH_VA_MATCH_##which) +#define SMMU_PTB_ASID_CUR(n) \ + ((n) << SMMU_PTB_ASID_CURRENT_SHIFT) +#define SMMU_TLB_FLUSH_ASID_MATCH_disable \ + (SMMU_TLB_FLUSH_ASID_MATCH_DISABLE << \ + SMMU_TLB_FLUSH_ASID_MATCH_SHIFT) +#define SMMU_TLB_FLUSH_ASID_MATCH__ENABLE \ + (SMMU_TLB_FLUSH_ASID_MATCH_ENABLE << \ + SMMU_TLB_FLUSH_ASID_MATCH_SHIFT) + +#define SMMU_PAGE_SHIFT 12 +#define SMMU_PAGE_SIZE (1 << SMMU_PAGE_SHIFT) + +#define SMMU_PDIR_COUNT 1024 +#define SMMU_PDIR_SIZE (sizeof(unsigned long) * SMMU_PDIR_COUNT) +#define SMMU_PTBL_COUNT 1024 +#define SMMU_PTBL_SIZE (sizeof(unsigned long) * SMMU_PTBL_COUNT) +#define SMMU_PDIR_SHIFT 12 +#define SMMU_PDE_SHIFT 12 +#define SMMU_PTE_SHIFT 12 +#define SMMU_PFN_MASK 0x000fffff + +#define SMMU_ADDR_TO_PFN(addr) ((addr) >> 12) +#define SMMU_ADDR_TO_PDN(addr) ((addr) >> 22) +#define SMMU_PDN_TO_ADDR(addr) ((pdn) << 22) + +#define _READABLE (1 << SMMU_PTB_DATA_ASID_READABLE_SHIFT) +#define _WRITABLE (1 << SMMU_PTB_DATA_ASID_WRITABLE_SHIFT) +#define _NONSECURE (1 << SMMU_PTB_DATA_ASID_NONSECURE_SHIFT) +#define _PDE_NEXT (1 << SMMU_PDE_NEXT_SHIFT) +#define _MASK_ATTR (_READABLE | _WRITABLE | _NONSECURE) + +#define _PDIR_ATTR (_READABLE | _WRITABLE | _NONSECURE) + +#define _PDE_ATTR (_READABLE | _WRITABLE | _NONSECURE) +#define _PDE_ATTR_N (_PDE_ATTR | _PDE_NEXT) +#define _PDE_VACANT(pdn) (((pdn) << 10) | _PDE_ATTR) + +#define _PTE_ATTR (_READABLE | _WRITABLE | _NONSECURE) +#define _PTE_VACANT(addr) (((addr) >> SMMU_PAGE_SHIFT) | _PTE_ATTR) + +#define SMMU_MK_PDIR(page, attr) \ + ((page_to_phys(page) >> SMMU_PDIR_SHIFT) | (attr)) +#define SMMU_MK_PDE(page, attr) \ + (unsigned long)((page_to_phys(page) >> SMMU_PDE_SHIFT) | (attr)) +#define SMMU_EX_PTBL_PAGE(pde) \ + pfn_to_page((unsigned long)(pde) & SMMU_PFN_MASK) +#define SMMU_PFN_TO_PTE(pfn, attr) (unsigned long)((pfn) | (attr)) + +#define SMMU_ASID_ENABLE(asid) ((asid) | (1 << 31)) +#define SMMU_ASID_DISABLE 0 +#define SMMU_ASID_ASID(n) ((n) & ~SMMU_ASID_ENABLE(0)) + +#define smmu_client_enable_hwgrp(c, m) smmu_client_set_hwgrp(c, m, 1) +#define smmu_client_disable_hwgrp(c) smmu_client_set_hwgrp(c, 0, 0) +#define __smmu_client_enable_hwgrp(c, m) __smmu_client_set_hwgrp(c, m, 1) +#define __smmu_client_disable_hwgrp(c) __smmu_client_set_hwgrp(c, 0, 0) + +#define HWGRP_INIT(client) [HWGRP_##client] = SMMU_##client##_ASID + +static const u32 smmu_hwgrp_asid_reg[] = { + HWGRP_INIT(AFI), + HWGRP_INIT(AVPC), + HWGRP_INIT(DC), + HWGRP_INIT(DCB), + HWGRP_INIT(EPP), + HWGRP_INIT(G2), + HWGRP_INIT(HC), + HWGRP_INIT(HDA), + HWGRP_INIT(ISP), + HWGRP_INIT(MPE), + HWGRP_INIT(NV), + HWGRP_INIT(NV2), + HWGRP_INIT(PPCS), + HWGRP_INIT(SATA), + HWGRP_INIT(VDE), + HWGRP_INIT(VI), +}; +#define HWGRP_ASID_REG(x) (smmu_hwgrp_asid_reg[x]) + +/* + * Per client for address space + */ +struct smmu_client { + struct device *dev; + struct list_head list; + struct smmu_as *as; + u32 hwgrp; +}; + +/* + * Per address space + */ +struct smmu_as { + struct smmu_device *smmu; /* back pointer to container */ + unsigned int asid; + spinlock_t lock; /* for pagetable */ + struct page *pdir_page; + unsigned long pdir_attr; + unsigned long pde_attr; + unsigned long pte_attr; + unsigned int *pte_count; + + struct list_head client; + spinlock_t client_lock; /* for client list */ +}; + +/* + * Per SMMU device - IOMMU device + */ +struct smmu_device { + void __iomem *regs, *regs_ahbarb; + unsigned long iovmm_base; /* remappable base address */ + unsigned long page_count; /* total remappable size */ + spinlock_t lock; + char *name; + struct device *dev; + int num_as; + struct smmu_as *as; /* Run-time allocated array */ + struct page *avp_vector_page; /* dummy page shared by all AS's */ + + /* + * Register image savers for suspend/resume + */ + unsigned long translation_enable_0; + unsigned long translation_enable_1; + unsigned long translation_enable_2; + unsigned long asid_security; +}; + +static struct smmu_device *smmu_handle; /* unique for a system */ + +/* + * SMMU/AHB register accessors + */ +static inline u32 smmu_read(struct smmu_device *smmu, size_t offs) +{ + return readl(smmu->regs + offs); +} +static inline void smmu_write(struct smmu_device *smmu, u32 val, size_t offs) +{ + writel(val, smmu->regs + offs); +} + +static inline u32 ahb_read(struct smmu_device *smmu, size_t offs) +{ + return readl(smmu->regs_ahbarb + offs); +} +static inline void ahb_write(struct smmu_device *smmu, u32 val, size_t offs) +{ + writel(val, smmu->regs_ahbarb + offs); +} + +#define VA_PAGE_TO_PA(va, page) \ + (page_to_phys(page) + ((unsigned long)(va) & ~PAGE_MASK)) + +#define FLUSH_CPU_DCACHE(va, page, size) \ + do { \ + unsigned long _pa_ = VA_PAGE_TO_PA(va, page); \ + __cpuc_flush_dcache_area((void *)(va), (size_t)(size)); \ + outer_flush_range(_pa_, _pa_+(size_t)(size)); \ + } while (0) + +/* + * Any interaction between any block on PPSB and a block on APB or AHB + * must have these read-back barriers to ensure the APB/AHB bus + * transaction is complete before initiating activity on the PPSB + * block. + */ +#define FLUSH_SMMU_REGS(smmu) smmu_read(smmu, SMMU_CONFIG) + +#define smmu_client_hwgrp(c) (u32)((c)->dev->platform_data) + +static int __smmu_client_set_hwgrp(struct smmu_client *c, + unsigned long map, int on) +{ + int i; + struct smmu_as *as = c->as; + u32 val, offs, mask = SMMU_ASID_ENABLE(as->asid); + struct smmu_device *smmu = as->smmu; + + WARN_ON(!on && map); + if (on && !map) + return -EINVAL; + if (!on) + map = smmu_client_hwgrp(c); + + for_each_set_bit(i, &map, HWGRP_COUNT) { + offs = HWGRP_ASID_REG(i); + val = smmu_read(smmu, offs); + if (on) { + if (WARN_ON(val & mask)) + goto err_hw_busy; + val |= mask; + } else { + WARN_ON((val & mask) == mask); + val &= ~mask; + } + smmu_write(smmu, val, offs); + } + FLUSH_SMMU_REGS(smmu); + c->hwgrp = map; + return 0; + +err_hw_busy: + for_each_set_bit(i, &map, HWGRP_COUNT) { + offs = HWGRP_ASID_REG(i); + val = smmu_read(smmu, offs); + val &= ~mask; + smmu_write(smmu, val, offs); + } + return -EBUSY; +} + +static int smmu_client_set_hwgrp(struct smmu_client *c, u32 map, int on) +{ + u32 val; + unsigned long flags; + struct smmu_as *as = c->as; + struct smmu_device *smmu = as->smmu; + + spin_lock_irqsave(&smmu->lock, flags); + val = __smmu_client_set_hwgrp(c, map, on); + spin_unlock_irqrestore(&smmu->lock, flags); + return val; +} + +/* + * Flush all TLB entries and all PTC entries + * Caller must lock smmu + */ +static void smmu_flush_regs(struct smmu_device *smmu, int enable) +{ + u32 val; + + smmu_write(smmu, SMMU_PTC_FLUSH_TYPE_ALL, SMMU_PTC_FLUSH); + FLUSH_SMMU_REGS(smmu); + val = SMMU_TLB_FLUSH_VA_MATCH_ALL | + SMMU_TLB_FLUSH_ASID_MATCH_disable; + smmu_write(smmu, val, SMMU_TLB_FLUSH); + + if (enable) + smmu_write(smmu, SMMU_CONFIG_ENABLE, SMMU_CONFIG); + FLUSH_SMMU_REGS(smmu); +} + +static void smmu_setup_regs(struct smmu_device *smmu) +{ + int i; + u32 val; + + for (i = 0; i < smmu->num_as; i++) { + struct smmu_as *as = &smmu->as[i]; + struct smmu_client *c; + + smmu_write(smmu, SMMU_PTB_ASID_CUR(as->asid), SMMU_PTB_ASID); + val = as->pdir_page ? + SMMU_MK_PDIR(as->pdir_page, as->pdir_attr) : + SMMU_PTB_DATA_RESET_VAL; + smmu_write(smmu, val, SMMU_PTB_DATA); + + list_for_each_entry(c, &as->client, list) + __smmu_client_set_hwgrp(c, c->hwgrp, 1); + } + + smmu_write(smmu, smmu->translation_enable_0, SMMU_TRANSLATION_ENABLE_0); + smmu_write(smmu, smmu->translation_enable_1, SMMU_TRANSLATION_ENABLE_1); + smmu_write(smmu, smmu->translation_enable_2, SMMU_TRANSLATION_ENABLE_2); + smmu_write(smmu, smmu->asid_security, SMMU_ASID_SECURITY); + smmu_write(smmu, SMMU_TLB_CONFIG_RESET_VAL, SMMU_TLB_CONFIG); + smmu_write(smmu, SMMU_PTC_CONFIG_RESET_VAL, SMMU_PTC_CONFIG); + + smmu_flush_regs(smmu, 1); + + val = ahb_read(smmu, AHB_XBAR_CTRL); + val |= AHB_XBAR_CTRL_SMMU_INIT_DONE_DONE << + AHB_XBAR_CTRL_SMMU_INIT_DONE_SHIFT; + ahb_write(smmu, val, AHB_XBAR_CTRL); +} + +static void flush_ptc_and_tlb(struct smmu_device *smmu, + struct smmu_as *as, dma_addr_t iova, + unsigned long *pte, struct page *page, int is_pde) +{ + u32 val; + unsigned long tlb_flush_va = is_pde + ? SMMU_TLB_FLUSH_VA(iova, SECTION) + : SMMU_TLB_FLUSH_VA(iova, GROUP); + + val = SMMU_PTC_FLUSH_TYPE_ADR | VA_PAGE_TO_PA(pte, page); + smmu_write(smmu, val, SMMU_PTC_FLUSH); + FLUSH_SMMU_REGS(smmu); + val = tlb_flush_va | + SMMU_TLB_FLUSH_ASID_MATCH__ENABLE | + (as->asid << SMMU_TLB_FLUSH_ASID_SHIFT); + smmu_write(smmu, val, SMMU_TLB_FLUSH); + FLUSH_SMMU_REGS(smmu); +} + +static void free_ptbl(struct smmu_as *as, dma_addr_t iova) +{ + unsigned long pdn = SMMU_ADDR_TO_PDN(iova); + unsigned long *pdir = (unsigned long *)page_address(as->pdir_page); + + if (pdir[pdn] != _PDE_VACANT(pdn)) { + dev_dbg(as->smmu->dev, "pdn: %lx\n", pdn); + + ClearPageReserved(SMMU_EX_PTBL_PAGE(pdir[pdn])); + __free_page(SMMU_EX_PTBL_PAGE(pdir[pdn])); + pdir[pdn] = _PDE_VACANT(pdn); + FLUSH_CPU_DCACHE(&pdir[pdn], as->pdir_page, sizeof pdir[pdn]); + flush_ptc_and_tlb(as->smmu, as, iova, &pdir[pdn], + as->pdir_page, 1); + } +} + +static void free_pdir(struct smmu_as *as) +{ + unsigned addr; + int count; + struct device *dev = as->smmu->dev; + + if (!as->pdir_page) + return; + + addr = as->smmu->iovmm_base; + count = as->smmu->page_count; + while (count-- > 0) { + free_ptbl(as, addr); + addr += SMMU_PAGE_SIZE * SMMU_PTBL_COUNT; + } + ClearPageReserved(as->pdir_page); + __free_page(as->pdir_page); + as->pdir_page = NULL; + devm_kfree(dev, as->pte_count); + as->pte_count = NULL; +} + +/* + * Maps PTBL for given iova and returns the PTE address + * Caller must unmap the mapped PTBL returned in *ptbl_page_p + */ +static unsigned long *locate_pte(struct smmu_as *as, + dma_addr_t iova, bool allocate, + struct page **ptbl_page_p, + unsigned int **count) +{ + unsigned long ptn = SMMU_ADDR_TO_PFN(iova); + unsigned long pdn = SMMU_ADDR_TO_PDN(iova); + unsigned long *pdir = page_address(as->pdir_page); + unsigned long *ptbl; + + if (pdir[pdn] != _PDE_VACANT(pdn)) { + /* Mapped entry table already exists */ + *ptbl_page_p = SMMU_EX_PTBL_PAGE(pdir[pdn]); + ptbl = page_address(*ptbl_page_p); + } else if (!allocate) { + return NULL; + } else { + int pn; + unsigned long addr = SMMU_PDN_TO_ADDR(pdn); + + /* Vacant - allocate a new page table */ + dev_dbg(as->smmu->dev, "New PTBL pdn: %lx\n", pdn); + + *ptbl_page_p = alloc_page(GFP_ATOMIC); + if (!*ptbl_page_p) { + dev_err(as->smmu->dev, + "failed to allocate smmu_device page table\n"); + return NULL; + } + SetPageReserved(*ptbl_page_p); + ptbl = (unsigned long *)page_address(*ptbl_page_p); + for (pn = 0; pn < SMMU_PTBL_COUNT; + pn++, addr += SMMU_PAGE_SIZE) { + ptbl[pn] = _PTE_VACANT(addr); + } + FLUSH_CPU_DCACHE(ptbl, *ptbl_page_p, SMMU_PTBL_SIZE); + pdir[pdn] = SMMU_MK_PDE(*ptbl_page_p, + as->pde_attr | _PDE_NEXT); + FLUSH_CPU_DCACHE(&pdir[pdn], as->pdir_page, sizeof pdir[pdn]); + flush_ptc_and_tlb(as->smmu, as, iova, &pdir[pdn], + as->pdir_page, 1); + } + *count = &as->pte_count[pdn]; + + return &ptbl[ptn % SMMU_PTBL_COUNT]; +} + +#ifdef CONFIG_SMMU_SIG_DEBUG +static void put_signature(struct smmu_as *as, + dma_addr_t iova, unsigned long pfn) +{ + struct page *page; + unsigned long *vaddr; + + page = pfn_to_page(pfn); + vaddr = page_address(page); + if (!vaddr) + return; + + vaddr[0] = iova; + vaddr[1] = pfn << PAGE_SHIFT; + FLUSH_CPU_DCACHE(vaddr, page, sizeof(vaddr[0]) * 2); +} +#else +static inline void put_signature(struct smmu_as *as, + unsigned long addr, unsigned long pfn) +{ +} +#endif + +/* + * Caller must lock/unlock as + */ +static int alloc_pdir(struct smmu_as *as) +{ + unsigned long *pdir; + int pdn; + u32 val; + struct smmu_device *smmu = as->smmu; + + if (as->pdir_page) + return 0; + + as->pte_count = devm_kzalloc(smmu->dev, + sizeof(as->pte_count[0]) * SMMU_PDIR_COUNT, GFP_KERNEL); + if (!as->pte_count) { + dev_err(smmu->dev, + "failed to allocate smmu_device PTE cunters\n"); + return -ENOMEM; + } + as->pdir_page = alloc_page(GFP_KERNEL | __GFP_DMA); + if (!as->pdir_page) { + dev_err(smmu->dev, + "failed to allocate smmu_device page directory\n"); + devm_kfree(smmu->dev, as->pte_count); + as->pte_count = NULL; + return -ENOMEM; + } + SetPageReserved(as->pdir_page); + pdir = page_address(as->pdir_page); + + for (pdn = 0; pdn < SMMU_PDIR_COUNT; pdn++) + pdir[pdn] = _PDE_VACANT(pdn); + FLUSH_CPU_DCACHE(pdir, as->pdir_page, SMMU_PDIR_SIZE); + val = SMMU_PTC_FLUSH_TYPE_ADR | VA_PAGE_TO_PA(pdir, as->pdir_page); + smmu_write(smmu, val, SMMU_PTC_FLUSH); + FLUSH_SMMU_REGS(as->smmu); + val = SMMU_TLB_FLUSH_VA_MATCH_ALL | + SMMU_TLB_FLUSH_ASID_MATCH__ENABLE | + (as->asid << SMMU_TLB_FLUSH_ASID_SHIFT); + smmu_write(smmu, val, SMMU_TLB_FLUSH); + FLUSH_SMMU_REGS(as->smmu); + + return 0; +} + +static void __smmu_iommu_unmap(struct smmu_as *as, dma_addr_t iova) +{ + unsigned long *pte; + struct page *page; + unsigned int *count; + + pte = locate_pte(as, iova, false, &page, &count); + if (WARN_ON(!pte)) + return; + + if (WARN_ON(*pte == _PTE_VACANT(iova))) + return; + + *pte = _PTE_VACANT(iova); + FLUSH_CPU_DCACHE(pte, page, sizeof(*pte)); + flush_ptc_and_tlb(as->smmu, as, iova, pte, page, 0); + if (!--(*count)) { + free_ptbl(as, iova); + smmu_flush_regs(as->smmu, 0); + } +} + +static void __smmu_iommu_map_pfn(struct smmu_as *as, dma_addr_t iova, + unsigned long pfn) +{ + struct smmu_device *smmu = as->smmu; + unsigned long *pte; + unsigned int *count; + struct page *page; + + pte = locate_pte(as, iova, true, &page, &count); + if (WARN_ON(!pte)) + return; + + if (*pte == _PTE_VACANT(iova)) + (*count)++; + *pte = SMMU_PFN_TO_PTE(pfn, as->pte_attr); + if (unlikely((*pte == _PTE_VACANT(iova)))) + (*count)--; + FLUSH_CPU_DCACHE(pte, page, sizeof(*pte)); + flush_ptc_and_tlb(smmu, as, iova, pte, page, 0); + put_signature(as, iova, pfn); +} + +static int smmu_iommu_map(struct iommu_domain *domain, unsigned long iova, + phys_addr_t pa, size_t bytes, int prot) +{ + struct smmu_as *as = domain->priv; + unsigned long pfn = __phys_to_pfn(pa); + unsigned long flags; + + dev_dbg(as->smmu->dev, "[%d] %08lx:%08x\n", as->asid, iova, pa); + + if (!pfn_valid(pfn)) + return -ENOMEM; + + spin_lock_irqsave(&as->lock, flags); + __smmu_iommu_map_pfn(as, iova, pfn); + spin_unlock_irqrestore(&as->lock, flags); + return 0; +} + +static size_t smmu_iommu_unmap(struct iommu_domain *domain, unsigned long iova, + size_t bytes) +{ + struct smmu_as *as = domain->priv; + unsigned long flags; + + dev_dbg(as->smmu->dev, "[%d] %08lx\n", as->asid, iova); + + spin_lock_irqsave(&as->lock, flags); + __smmu_iommu_unmap(as, iova); + spin_unlock_irqrestore(&as->lock, flags); + return SMMU_PAGE_SIZE; +} + +static phys_addr_t smmu_iommu_iova_to_phys(struct iommu_domain *domain, + unsigned long iova) +{ + struct smmu_as *as = domain->priv; + unsigned long *pte; + unsigned int *count; + struct page *page; + unsigned long pfn; + unsigned long flags; + + spin_lock_irqsave(&as->lock, flags); + + pte = locate_pte(as, iova, true, &page, &count); + pfn = *pte & SMMU_PFN_MASK; + WARN_ON(!pfn_valid(pfn)); + dev_dbg(as->smmu->dev, + "iova:%08lx pfn:%08lx asid:%d\n", iova, pfn, as->asid); + + spin_unlock_irqrestore(&as->lock, flags); + return PFN_PHYS(pfn); +} + +static int smmu_iommu_domain_has_cap(struct iommu_domain *domain, + unsigned long cap) +{ + return 0; +} + +static int smmu_iommu_attach_dev(struct iommu_domain *domain, + struct device *dev) +{ + struct smmu_as *as = domain->priv; + struct smmu_device *smmu = as->smmu; + struct smmu_client *client, *c; + u32 map; + int err; + + client = devm_kzalloc(smmu->dev, sizeof(*c), GFP_KERNEL); + if (!client) + return -ENOMEM; + client->dev = dev; + client->as = as; + map = (unsigned long)dev->platform_data; + if (!map) + return -EINVAL; + + err = smmu_client_enable_hwgrp(client, map); + if (err) + goto err_hwgrp; + + spin_lock(&as->client_lock); + list_for_each_entry(c, &as->client, list) { + if (c->dev == dev) { + dev_err(smmu->dev, + "%s is already attached\n", dev_name(c->dev)); + err = -EINVAL; + goto err_client; + } + } + list_add(&client->list, &as->client); + spin_unlock(&as->client_lock); + + /* + * Reserve "page zero" for AVP vectors using a common dummy + * page. + */ + if (map & HWG_AVPC) { + struct page *page; + + page = as->smmu->avp_vector_page; + __smmu_iommu_map_pfn(as, 0, page_to_pfn(page)); + + pr_info("Reserve \"page zero\" for AVP vectors using a common dummy\n"); + } + + dev_dbg(smmu->dev, "%s is attached\n", dev_name(c->dev)); + return 0; + +err_client: + smmu_client_disable_hwgrp(client); + spin_unlock(&as->client_lock); +err_hwgrp: + devm_kfree(smmu->dev, client); + return err; +} + +static void smmu_iommu_detach_dev(struct iommu_domain *domain, + struct device *dev) +{ + struct smmu_as *as = domain->priv; + struct smmu_device *smmu = as->smmu; + struct smmu_client *c; + + spin_lock(&as->client_lock); + + list_for_each_entry(c, &as->client, list) { + if (c->dev == dev) { + smmu_client_disable_hwgrp(c); + list_del(&c->list); + devm_kfree(smmu->dev, c); + c->as = NULL; + dev_dbg(smmu->dev, + "%s is detached\n", dev_name(c->dev)); + goto out; + } + } + dev_err(smmu->dev, "Couldn't find %s\n", dev_name(c->dev)); +out: + spin_unlock(&as->client_lock); +} + +static int smmu_iommu_domain_init(struct iommu_domain *domain) +{ + int i; + unsigned long flags; + struct smmu_as *as; + struct smmu_device *smmu = smmu_handle; + + /* Look for a free AS with lock held */ + for (i = 0; i < smmu->num_as; i++) { + struct smmu_as *tmp = &smmu->as[i]; + + spin_lock_irqsave(&tmp->lock, flags); + if (!tmp->pdir_page) { + as = tmp; + goto found; + } + spin_unlock_irqrestore(&tmp->lock, flags); + } + dev_err(smmu->dev, "no free AS\n"); + return -ENODEV; + +found: + if (alloc_pdir(as) < 0) + goto err_alloc_pdir; + + spin_lock(&smmu->lock); + + /* Update PDIR register */ + smmu_write(smmu, SMMU_PTB_ASID_CUR(as->asid), SMMU_PTB_ASID); + smmu_write(smmu, + SMMU_MK_PDIR(as->pdir_page, as->pdir_attr), SMMU_PTB_DATA); + FLUSH_SMMU_REGS(smmu); + + spin_unlock(&smmu->lock); + + spin_unlock_irqrestore(&as->lock, flags); + domain->priv = as; + + dev_dbg(smmu->dev, "smmu_as@%p\n", as); + return 0; + +err_alloc_pdir: + spin_unlock_irqrestore(&as->lock, flags); + return -ENODEV; +} + +static void smmu_iommu_domain_destroy(struct iommu_domain *domain) +{ + struct smmu_as *as = domain->priv; + struct smmu_device *smmu = as->smmu; + unsigned long flags; + + spin_lock_irqsave(&as->lock, flags); + + if (as->pdir_page) { + spin_lock(&smmu->lock); + smmu_write(smmu, SMMU_PTB_ASID_CUR(as->asid), SMMU_PTB_ASID); + smmu_write(smmu, SMMU_PTB_DATA_RESET_VAL, SMMU_PTB_DATA); + FLUSH_SMMU_REGS(smmu); + spin_unlock(&smmu->lock); + + free_pdir(as); + } + + if (!list_empty(&as->client)) { + struct smmu_client *c; + + list_for_each_entry(c, &as->client, list) + smmu_iommu_detach_dev(domain, c->dev); + } + + spin_unlock_irqrestore(&as->lock, flags); + + domain->priv = NULL; + dev_dbg(smmu->dev, "smmu_as@%p\n", as); +} + +static struct iommu_ops smmu_iommu_ops = { + .domain_init = smmu_iommu_domain_init, + .domain_destroy = smmu_iommu_domain_destroy, + .attach_dev = smmu_iommu_attach_dev, + .detach_dev = smmu_iommu_detach_dev, + .map = smmu_iommu_map, + .unmap = smmu_iommu_unmap, + .iova_to_phys = smmu_iommu_iova_to_phys, + .domain_has_cap = smmu_iommu_domain_has_cap, + .pgsize_bitmap = SMMU_IOMMU_PGSIZES, +}; + +static int tegra_smmu_suspend(struct device *dev) +{ + struct smmu_device *smmu = dev_get_drvdata(dev); + + smmu->translation_enable_0 = smmu_read(smmu, SMMU_TRANSLATION_ENABLE_0); + smmu->translation_enable_1 = smmu_read(smmu, SMMU_TRANSLATION_ENABLE_1); + smmu->translation_enable_2 = smmu_read(smmu, SMMU_TRANSLATION_ENABLE_2); + smmu->asid_security = smmu_read(smmu, SMMU_ASID_SECURITY); + return 0; +} + +static int tegra_smmu_resume(struct device *dev) +{ + struct smmu_device *smmu = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&smmu->lock, flags); + smmu_setup_regs(smmu); + spin_unlock_irqrestore(&smmu->lock, flags); + return 0; +} + +static int tegra_smmu_probe(struct platform_device *pdev) +{ + struct smmu_device *smmu; + struct resource *regs, *regs2, *window; + struct device *dev = &pdev->dev; + int i, err = 0; + + if (smmu_handle) + return -EIO; + + BUILD_BUG_ON(PAGE_SHIFT != SMMU_PAGE_SHIFT); + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + window = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!regs || !regs2 || !window) { + dev_err(dev, "No SMMU resources\n"); + return -ENODEV; + } + + smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); + if (!smmu) { + dev_err(dev, "failed to allocate smmu_device\n"); + return -ENOMEM; + } + + smmu->dev = dev; + smmu->num_as = SMMU_NUM_ASIDS; + smmu->iovmm_base = (unsigned long)window->start; + smmu->page_count = resource_size(window) >> SMMU_PAGE_SHIFT; + smmu->regs = devm_ioremap(dev, regs->start, resource_size(regs)); + smmu->regs_ahbarb = devm_ioremap(dev, regs2->start, + resource_size(regs2)); + if (!smmu->regs || !smmu->regs_ahbarb) { + dev_err(dev, "failed to remap SMMU registers\n"); + err = -ENXIO; + goto fail; + } + + smmu->translation_enable_0 = ~0; + smmu->translation_enable_1 = ~0; + smmu->translation_enable_2 = ~0; + smmu->asid_security = 0; + + smmu->as = devm_kzalloc(dev, + sizeof(smmu->as[0]) * smmu->num_as, GFP_KERNEL); + if (!smmu->as) { + dev_err(dev, "failed to allocate smmu_as\n"); + err = -ENOMEM; + goto fail; + } + + for (i = 0; i < smmu->num_as; i++) { + struct smmu_as *as = &smmu->as[i]; + + as->smmu = smmu; + as->asid = i; + as->pdir_attr = _PDIR_ATTR; + as->pde_attr = _PDE_ATTR; + as->pte_attr = _PTE_ATTR; + + spin_lock_init(&as->lock); + INIT_LIST_HEAD(&as->client); + } + spin_lock_init(&smmu->lock); + smmu_setup_regs(smmu); + platform_set_drvdata(pdev, smmu); + + smmu->avp_vector_page = alloc_page(GFP_KERNEL); + if (!smmu->avp_vector_page) + goto fail; + + smmu_handle = smmu; + return 0; + +fail: + if (smmu->avp_vector_page) + __free_page(smmu->avp_vector_page); + if (smmu->regs) + devm_iounmap(dev, smmu->regs); + if (smmu->regs_ahbarb) + devm_iounmap(dev, smmu->regs_ahbarb); + if (smmu && smmu->as) { + for (i = 0; i < smmu->num_as; i++) { + if (smmu->as[i].pdir_page) { + ClearPageReserved(smmu->as[i].pdir_page); + __free_page(smmu->as[i].pdir_page); + } + } + devm_kfree(dev, smmu->as); + } + devm_kfree(dev, smmu); + return err; +} + +static int tegra_smmu_remove(struct platform_device *pdev) +{ + struct smmu_device *smmu = platform_get_drvdata(pdev); + struct device *dev = smmu->dev; + + smmu_write(smmu, SMMU_CONFIG_DISABLE, SMMU_CONFIG); + platform_set_drvdata(pdev, NULL); + if (smmu->as) { + int i; + + for (i = 0; i < smmu->num_as; i++) + free_pdir(&smmu->as[i]); + devm_kfree(dev, smmu->as); + } + if (smmu->avp_vector_page) + __free_page(smmu->avp_vector_page); + if (smmu->regs) + devm_iounmap(dev, smmu->regs); + if (smmu->regs_ahbarb) + devm_iounmap(dev, smmu->regs_ahbarb); + devm_kfree(dev, smmu); + smmu_handle = NULL; + return 0; +} + +const struct dev_pm_ops tegra_smmu_pm_ops = { + .suspend = tegra_smmu_suspend, + .resume = tegra_smmu_resume, +}; + +static struct platform_driver tegra_smmu_driver = { + .probe = tegra_smmu_probe, + .remove = tegra_smmu_remove, + .driver = { + .owner = THIS_MODULE, + .name = "tegra-smmu", + .pm = &tegra_smmu_pm_ops, + }, +}; + +static int __devinit tegra_smmu_init(void) +{ + bus_set_iommu(&platform_bus_type, &smmu_iommu_ops); + return platform_driver_register(&tegra_smmu_driver); +} + +static void __exit tegra_smmu_exit(void) +{ + platform_driver_unregister(&tegra_smmu_driver); +} + +subsys_initcall(tegra_smmu_init); +module_exit(tegra_smmu_exit); + +MODULE_DESCRIPTION("IOMMU API for SMMU in Tegra30"); +MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 8c7a75d53101..ff4b8cfda585 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -17,7 +17,7 @@ menuconfig NEW_LEDS if NEW_LEDS config LEDS_CLASS - bool "LED Class Support" + tristate "LED Class Support" help This option enables the led sysfs class in /sys/class/leds. You'll need this to do anything useful with LEDs. If unsure, say N. @@ -69,18 +69,11 @@ config LEDS_MIKROTIK_RB532 config LEDS_S3C24XX tristate "LED Support for Samsung S3C24XX GPIO LEDs" depends on LEDS_CLASS - depends on ARCH_S3C2410 + depends on ARCH_S3C24XX help This option enables support for LEDs connected to GPIO lines on Samsung S3C24XX series CPUs, such as the S3C2410 and S3C2440. -config LEDS_AMS_DELTA - tristate "LED Support for the Amstrad Delta (E3)" - depends on LEDS_CLASS - depends on MACH_AMS_DELTA - help - This option enables support for the LEDs on Amstrad Delta (E3). - config LEDS_NET48XX tristate "LED Support for Soekris net48xx series Error LED" depends on LEDS_CLASS @@ -234,6 +227,14 @@ config LEDS_PCA955X LED driver chips accessed via the I2C bus. Supported devices include PCA9550, PCA9551, PCA9552, and PCA9553. +config LEDS_PCA9633 + tristate "LED support for PCA9633 I2C chip" + depends on LEDS_CLASS + depends on I2C + help + This option enables support for LEDs connected to the PCA9633 + LED driver chip accessed via the I2C bus. + config LEDS_WM831X_STATUS tristate "LED support for status LEDs on WM831x PMICs" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 6bcf4f695515..890481cb09f6 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -12,7 +12,6 @@ obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o -obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o @@ -30,6 +29,7 @@ obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o obj-$(CONFIG_LEDS_OT200) += leds-ot200.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o +obj-$(CONFIG_LEDS_PCA9633) += leds-pca9633.o obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 0c8739c448b1..5bff8439dc68 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -110,50 +110,6 @@ static void led_timer_function(unsigned long data) mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay)); } -static void led_stop_software_blink(struct led_classdev *led_cdev) -{ - /* deactivate previous settings */ - del_timer_sync(&led_cdev->blink_timer); - led_cdev->blink_delay_on = 0; - led_cdev->blink_delay_off = 0; -} - -static void led_set_software_blink(struct led_classdev *led_cdev, - unsigned long delay_on, - unsigned long delay_off) -{ - int current_brightness; - - current_brightness = led_get_brightness(led_cdev); - if (current_brightness) - led_cdev->blink_brightness = current_brightness; - if (!led_cdev->blink_brightness) - led_cdev->blink_brightness = led_cdev->max_brightness; - - if (led_get_trigger_data(led_cdev) && - delay_on == led_cdev->blink_delay_on && - delay_off == led_cdev->blink_delay_off) - return; - - led_stop_software_blink(led_cdev); - - led_cdev->blink_delay_on = delay_on; - led_cdev->blink_delay_off = delay_off; - - /* never on - don't blink */ - if (!delay_on) - return; - - /* never off - just set to brightness */ - if (!delay_off) { - led_set_brightness(led_cdev, led_cdev->blink_brightness); - return; - } - - mod_timer(&led_cdev->blink_timer, jiffies + 1); -} - - /** * led_classdev_suspend - suspend an led_classdev. * @led_cdev: the led_classdev to suspend. @@ -262,32 +218,6 @@ void led_classdev_unregister(struct led_classdev *led_cdev) } EXPORT_SYMBOL_GPL(led_classdev_unregister); -void led_blink_set(struct led_classdev *led_cdev, - unsigned long *delay_on, - unsigned long *delay_off) -{ - del_timer_sync(&led_cdev->blink_timer); - - if (led_cdev->blink_set && - !led_cdev->blink_set(led_cdev, delay_on, delay_off)) - return; - - /* blink with 1 Hz as default if nothing specified */ - if (!*delay_on && !*delay_off) - *delay_on = *delay_off = 500; - - led_set_software_blink(led_cdev, *delay_on, *delay_off); -} -EXPORT_SYMBOL(led_blink_set); - -void led_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - led_stop_software_blink(led_cdev); - led_cdev->brightness_set(led_cdev, brightness); -} -EXPORT_SYMBOL(led_brightness_set); - static int __init leds_init(void) { leds_class = class_create(THIS_MODULE, "leds"); diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 016d19f5486f..d6860043f6f9 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -23,3 +23,73 @@ EXPORT_SYMBOL_GPL(leds_list_lock); LIST_HEAD(leds_list); EXPORT_SYMBOL_GPL(leds_list); + +static void led_stop_software_blink(struct led_classdev *led_cdev) +{ + /* deactivate previous settings */ + del_timer_sync(&led_cdev->blink_timer); + led_cdev->blink_delay_on = 0; + led_cdev->blink_delay_off = 0; +} + +static void led_set_software_blink(struct led_classdev *led_cdev, + unsigned long delay_on, + unsigned long delay_off) +{ + int current_brightness; + + current_brightness = led_get_brightness(led_cdev); + if (current_brightness) + led_cdev->blink_brightness = current_brightness; + if (!led_cdev->blink_brightness) + led_cdev->blink_brightness = led_cdev->max_brightness; + + if (led_get_trigger_data(led_cdev) && + delay_on == led_cdev->blink_delay_on && + delay_off == led_cdev->blink_delay_off) + return; + + led_stop_software_blink(led_cdev); + + led_cdev->blink_delay_on = delay_on; + led_cdev->blink_delay_off = delay_off; + + /* never on - don't blink */ + if (!delay_on) + return; + + /* never off - just set to brightness */ + if (!delay_off) { + led_set_brightness(led_cdev, led_cdev->blink_brightness); + return; + } + + mod_timer(&led_cdev->blink_timer, jiffies + 1); +} + + +void led_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + del_timer_sync(&led_cdev->blink_timer); + + if (led_cdev->blink_set && + !led_cdev->blink_set(led_cdev, delay_on, delay_off)) + return; + + /* blink with 1 Hz as default if nothing specified */ + if (!*delay_on && !*delay_off) + *delay_on = *delay_off = 500; + + led_set_software_blink(led_cdev, *delay_on, *delay_off); +} +EXPORT_SYMBOL(led_blink_set); + +void led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + led_stop_software_blink(led_cdev); + led_cdev->brightness_set(led_cdev, brightness); +} +EXPORT_SYMBOL(led_brightness_set); diff --git a/drivers/leds/leds-ams-delta.c b/drivers/leds/leds-ams-delta.c deleted file mode 100644 index 07428357c83f..000000000000 --- a/drivers/leds/leds-ams-delta.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - * LEDs driver for Amstrad Delta (E3) - * - * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/leds.h> -#include <plat/board-ams-delta.h> - -/* - * Our context - */ -struct ams_delta_led { - struct led_classdev cdev; - u8 bitmask; -}; - -static void ams_delta_led_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - struct ams_delta_led *led_dev = - container_of(led_cdev, struct ams_delta_led, cdev); - - if (value) - ams_delta_latch1_write(led_dev->bitmask, led_dev->bitmask); - else - ams_delta_latch1_write(led_dev->bitmask, 0); -} - -static struct ams_delta_led ams_delta_leds[] = { - { - .cdev = { - .name = "ams-delta::camera", - .brightness_set = ams_delta_led_set, - }, - .bitmask = AMS_DELTA_LATCH1_LED_CAMERA, - }, - { - .cdev = { - .name = "ams-delta::advert", - .brightness_set = ams_delta_led_set, - }, - .bitmask = AMS_DELTA_LATCH1_LED_ADVERT, - }, - { - .cdev = { - .name = "ams-delta::email", - .brightness_set = ams_delta_led_set, - }, - .bitmask = AMS_DELTA_LATCH1_LED_EMAIL, - }, - { - .cdev = { - .name = "ams-delta::handsfree", - .brightness_set = ams_delta_led_set, - }, - .bitmask = AMS_DELTA_LATCH1_LED_HANDSFREE, - }, - { - .cdev = { - .name = "ams-delta::voicemail", - .brightness_set = ams_delta_led_set, - }, - .bitmask = AMS_DELTA_LATCH1_LED_VOICEMAIL, - }, - { - .cdev = { - .name = "ams-delta::voice", - .brightness_set = ams_delta_led_set, - }, - .bitmask = AMS_DELTA_LATCH1_LED_VOICE, - }, -}; - -static int ams_delta_led_probe(struct platform_device *pdev) -{ - int i, ret; - - for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) { - ams_delta_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME; - ret = led_classdev_register(&pdev->dev, - &ams_delta_leds[i].cdev); - if (ret < 0) - goto fail; - } - - return 0; -fail: - while (--i >= 0) - led_classdev_unregister(&ams_delta_leds[i].cdev); - return ret; -} - -static int ams_delta_led_remove(struct platform_device *pdev) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) - led_classdev_unregister(&ams_delta_leds[i].cdev); - - return 0; -} - -static struct platform_driver ams_delta_led_driver = { - .probe = ams_delta_led_probe, - .remove = ams_delta_led_remove, - .driver = { - .name = "ams-delta-led", - .owner = THIS_MODULE, - }, -}; - -module_platform_driver(ams_delta_led_driver); - -MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>"); -MODULE_DESCRIPTION("Amstrad Delta LED driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:ams-delta-led"); diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 7df74cb97e70..f4c470a3bc8d 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -13,6 +13,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> +#include <linux/gpio.h> #include <linux/leds.h> #include <linux/of_platform.h> #include <linux/of_gpio.h> @@ -20,8 +21,6 @@ #include <linux/workqueue.h> #include <linux/module.h> -#include <asm/gpio.h> - struct gpio_led_data { struct led_classdev cdev; unsigned gpio; diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c index e59c166a0ce2..968fd5fef4fc 100644 --- a/drivers/leds/leds-lm3530.c +++ b/drivers/leds/leds-lm3530.c @@ -26,7 +26,6 @@ #define LM3530_GEN_CONFIG 0x10 #define LM3530_ALS_CONFIG 0x20 #define LM3530_BRT_RAMP_RATE 0x30 -#define LM3530_ALS_ZONE_REG 0x40 #define LM3530_ALS_IMP_SELECT 0x41 #define LM3530_BRT_CTRL_REG 0xA0 #define LM3530_ALS_ZB0_REG 0x60 @@ -38,7 +37,7 @@ #define LM3530_ALS_Z2T_REG 0x72 #define LM3530_ALS_Z3T_REG 0x73 #define LM3530_ALS_Z4T_REG 0x74 -#define LM3530_REG_MAX 15 +#define LM3530_REG_MAX 14 /* General Control Register */ #define LM3530_EN_I2C_SHIFT (0) @@ -80,6 +79,9 @@ #define LM3530_DEF_ZT_3 (0x33) #define LM3530_DEF_ZT_4 (0x19) +/* 7 bits are used for the brightness : LM3530_BRT_CTRL_REG */ +#define MAX_BRIGHTNESS (127) + struct lm3530_mode_map { const char *mode; enum lm3530_mode mode_val; @@ -115,7 +117,6 @@ static const u8 lm3530_reg[LM3530_REG_MAX] = { LM3530_GEN_CONFIG, LM3530_ALS_CONFIG, LM3530_BRT_RAMP_RATE, - LM3530_ALS_ZONE_REG, LM3530_ALS_IMP_SELECT, LM3530_BRT_CTRL_REG, LM3530_ALS_ZB0_REG, @@ -152,27 +153,35 @@ static int lm3530_init_registers(struct lm3530_data *drvdata) u8 reg_val[LM3530_REG_MAX]; u8 zones[LM3530_ALS_ZB_MAX]; u32 als_vmin, als_vmax, als_vstep; - struct lm3530_platform_data *pltfm = drvdata->pdata; + struct lm3530_platform_data *pdata = drvdata->pdata; struct i2c_client *client = drvdata->client; + struct lm3530_pwm_data *pwm = &pdata->pwm_data; - gen_config = (pltfm->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) | - ((pltfm->max_current & 7) << LM3530_MAX_CURR_SHIFT); + gen_config = (pdata->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) | + ((pdata->max_current & 7) << LM3530_MAX_CURR_SHIFT); - if (drvdata->mode == LM3530_BL_MODE_MANUAL || - drvdata->mode == LM3530_BL_MODE_ALS) - gen_config |= (LM3530_ENABLE_I2C); + switch (drvdata->mode) { + case LM3530_BL_MODE_MANUAL: + case LM3530_BL_MODE_ALS: + gen_config |= LM3530_ENABLE_I2C; + break; + case LM3530_BL_MODE_PWM: + gen_config |= LM3530_ENABLE_PWM | LM3530_ENABLE_PWM_SIMPLE | + (pdata->pwm_pol_hi << LM3530_PWM_POL_SHIFT); + break; + } if (drvdata->mode == LM3530_BL_MODE_ALS) { - if (pltfm->als_vmax == 0) { - pltfm->als_vmin = 0; - pltfm->als_vmax = LM3530_ALS_WINDOW_mV; + if (pdata->als_vmax == 0) { + pdata->als_vmin = 0; + pdata->als_vmax = LM3530_ALS_WINDOW_mV; } - als_vmin = pltfm->als_vmin; - als_vmax = pltfm->als_vmax; + als_vmin = pdata->als_vmin; + als_vmax = pdata->als_vmax; if ((als_vmax - als_vmin) > LM3530_ALS_WINDOW_mV) - pltfm->als_vmax = als_vmax = + pdata->als_vmax = als_vmax = als_vmin + LM3530_ALS_WINDOW_mV; /* n zone boundary makes n+1 zones */ @@ -184,44 +193,41 @@ static int lm3530_init_registers(struct lm3530_data *drvdata) / 1000; als_config = - (pltfm->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) | + (pdata->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) | (LM3530_ENABLE_ALS) | - (pltfm->als_input_mode << LM3530_ALS_SEL_SHIFT); + (pdata->als_input_mode << LM3530_ALS_SEL_SHIFT); als_imp_sel = - (pltfm->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) | - (pltfm->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT); + (pdata->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) | + (pdata->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT); } - if (drvdata->mode == LM3530_BL_MODE_PWM) - gen_config |= (LM3530_ENABLE_PWM) | - (pltfm->pwm_pol_hi << LM3530_PWM_POL_SHIFT) | - (LM3530_ENABLE_PWM_SIMPLE); - - brt_ramp = (pltfm->brt_ramp_fall << LM3530_BRT_RAMP_FALL_SHIFT) | - (pltfm->brt_ramp_rise << LM3530_BRT_RAMP_RISE_SHIFT); + brt_ramp = (pdata->brt_ramp_fall << LM3530_BRT_RAMP_FALL_SHIFT) | + (pdata->brt_ramp_rise << LM3530_BRT_RAMP_RISE_SHIFT); if (drvdata->brightness) brightness = drvdata->brightness; else - brightness = drvdata->brightness = pltfm->brt_val; + brightness = drvdata->brightness = pdata->brt_val; + + if (brightness > drvdata->led_dev.max_brightness) + brightness = drvdata->led_dev.max_brightness; reg_val[0] = gen_config; /* LM3530_GEN_CONFIG */ reg_val[1] = als_config; /* LM3530_ALS_CONFIG */ reg_val[2] = brt_ramp; /* LM3530_BRT_RAMP_RATE */ - reg_val[3] = 0x00; /* LM3530_ALS_ZONE_REG */ - reg_val[4] = als_imp_sel; /* LM3530_ALS_IMP_SELECT */ - reg_val[5] = brightness; /* LM3530_BRT_CTRL_REG */ - reg_val[6] = zones[0]; /* LM3530_ALS_ZB0_REG */ - reg_val[7] = zones[1]; /* LM3530_ALS_ZB1_REG */ - reg_val[8] = zones[2]; /* LM3530_ALS_ZB2_REG */ - reg_val[9] = zones[3]; /* LM3530_ALS_ZB3_REG */ - reg_val[10] = LM3530_DEF_ZT_0; /* LM3530_ALS_Z0T_REG */ - reg_val[11] = LM3530_DEF_ZT_1; /* LM3530_ALS_Z1T_REG */ - reg_val[12] = LM3530_DEF_ZT_2; /* LM3530_ALS_Z2T_REG */ - reg_val[13] = LM3530_DEF_ZT_3; /* LM3530_ALS_Z3T_REG */ - reg_val[14] = LM3530_DEF_ZT_4; /* LM3530_ALS_Z4T_REG */ + reg_val[3] = als_imp_sel; /* LM3530_ALS_IMP_SELECT */ + reg_val[4] = brightness; /* LM3530_BRT_CTRL_REG */ + reg_val[5] = zones[0]; /* LM3530_ALS_ZB0_REG */ + reg_val[6] = zones[1]; /* LM3530_ALS_ZB1_REG */ + reg_val[7] = zones[2]; /* LM3530_ALS_ZB2_REG */ + reg_val[8] = zones[3]; /* LM3530_ALS_ZB3_REG */ + reg_val[9] = LM3530_DEF_ZT_0; /* LM3530_ALS_Z0T_REG */ + reg_val[10] = LM3530_DEF_ZT_1; /* LM3530_ALS_Z1T_REG */ + reg_val[11] = LM3530_DEF_ZT_2; /* LM3530_ALS_Z2T_REG */ + reg_val[12] = LM3530_DEF_ZT_3; /* LM3530_ALS_Z3T_REG */ + reg_val[13] = LM3530_DEF_ZT_4; /* LM3530_ALS_Z4T_REG */ if (!drvdata->enable) { ret = regulator_enable(drvdata->regulator); @@ -234,6 +240,15 @@ static int lm3530_init_registers(struct lm3530_data *drvdata) } for (i = 0; i < LM3530_REG_MAX; i++) { + /* do not update brightness register when pwm mode */ + if (lm3530_reg[i] == LM3530_BRT_CTRL_REG && + drvdata->mode == LM3530_BL_MODE_PWM) { + if (pwm->pwm_set_intensity) + pwm->pwm_set_intensity(reg_val[i], + drvdata->led_dev.max_brightness); + continue; + } + ret = i2c_smbus_write_byte_data(client, lm3530_reg[i], reg_val[i]); if (ret) @@ -249,6 +264,9 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev, int err; struct lm3530_data *drvdata = container_of(led_cdev, struct lm3530_data, led_dev); + struct lm3530_platform_data *pdata = drvdata->pdata; + struct lm3530_pwm_data *pwm = &pdata->pwm_data; + u8 max_brightness = led_cdev->max_brightness; switch (drvdata->mode) { case LM3530_BL_MODE_MANUAL: @@ -264,12 +282,12 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev, /* set the brightness in brightness control register*/ err = i2c_smbus_write_byte_data(drvdata->client, - LM3530_BRT_CTRL_REG, brt_val / 2); + LM3530_BRT_CTRL_REG, brt_val); if (err) dev_err(&drvdata->client->dev, "Unable to set brightness: %d\n", err); else - drvdata->brightness = brt_val / 2; + drvdata->brightness = brt_val; if (brt_val == 0) { err = regulator_disable(drvdata->regulator); @@ -282,6 +300,8 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev, case LM3530_BL_MODE_ALS: break; case LM3530_BL_MODE_PWM: + if (pwm->pwm_set_intensity) + pwm->pwm_set_intensity(brt_val, max_brightness); break; default: break; @@ -291,11 +311,11 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev, static ssize_t lm3530_mode_get(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = container_of( - dev->parent, struct i2c_client, dev); - struct lm3530_data *drvdata = i2c_get_clientdata(client); + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lm3530_data *drvdata; int i, len = 0; + drvdata = container_of(led_cdev, struct lm3530_data, led_dev); for (i = 0; i < ARRAY_SIZE(mode_map); i++) if (drvdata->mode == mode_map[i].mode_val) len += sprintf(buf + len, "[%s] ", mode_map[i].mode); @@ -310,26 +330,26 @@ static ssize_t lm3530_mode_get(struct device *dev, static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - int err; - struct i2c_client *client = container_of( - dev->parent, struct i2c_client, dev); - struct lm3530_data *drvdata = i2c_get_clientdata(client); - int mode; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lm3530_data *drvdata; + struct lm3530_pwm_data *pwm; + u8 max_brightness; + int mode, err; + drvdata = container_of(led_cdev, struct lm3530_data, led_dev); + pwm = &drvdata->pdata->pwm_data; + max_brightness = led_cdev->max_brightness; mode = lm3530_get_mode_from_str(buf); if (mode < 0) { dev_err(dev, "Invalid mode\n"); return -EINVAL; } - if (mode == LM3530_BL_MODE_MANUAL) - drvdata->mode = LM3530_BL_MODE_MANUAL; - else if (mode == LM3530_BL_MODE_ALS) - drvdata->mode = LM3530_BL_MODE_ALS; - else if (mode == LM3530_BL_MODE_PWM) { - dev_err(dev, "PWM mode not supported\n"); - return -EINVAL; - } + drvdata->mode = mode; + + /* set pwm to low if unnecessary */ + if (mode != LM3530_BL_MODE_PWM && pwm->pwm_set_intensity) + pwm->pwm_set_intensity(0, max_brightness); err = lm3530_init_registers(drvdata); if (err) { @@ -380,6 +400,7 @@ static int __devinit lm3530_probe(struct i2c_client *client, drvdata->enable = false; drvdata->led_dev.name = LM3530_LED_DEV; drvdata->led_dev.brightness_set = lm3530_brightness_set; + drvdata->led_dev.max_brightness = MAX_BRIGHTNESS; i2c_set_clientdata(client, drvdata); diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index d62a7982a5e6..410a723b8691 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -81,18 +81,10 @@ #define LP5521_MASTER_ENABLE 0x40 /* Chip master enable */ #define LP5521_LOGARITHMIC_PWM 0x80 /* Logarithmic PWM adjustment */ #define LP5521_EXEC_RUN 0x2A - -/* Bits in CONFIG register */ -#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */ -#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */ -#define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */ -#define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */ -#define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */ -#define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */ -#define LP5521_R_TO_BATT 4 /* R out: 0 = CP, 1 = Vbat */ -#define LP5521_CLK_SRC_EXT 0 /* Ext-clk source (CLK_32K) */ -#define LP5521_CLK_INT 1 /* Internal clock */ -#define LP5521_CLK_AUTO 2 /* Automatic clock selection */ +#define LP5521_ENABLE_DEFAULT \ + (LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM) +#define LP5521_ENABLE_RUN_PROGRAM \ + (LP5521_ENABLE_DEFAULT | LP5521_EXEC_RUN) /* Status */ #define LP5521_EXT_CLK_USED 0x08 @@ -100,6 +92,9 @@ /* default R channel current register value */ #define LP5521_REG_R_CURR_DEFAULT 0xAF +/* Pattern Mode */ +#define PATTERN_OFF 0 + struct lp5521_engine { int id; u8 mode; @@ -241,15 +236,16 @@ static int lp5521_configure(struct i2c_client *client) { struct lp5521_chip *chip = i2c_get_clientdata(client); int ret; + u8 cfg; lp5521_init_engine(chip); /* Set all PWMs to direct control mode */ - ret = lp5521_write(client, LP5521_REG_OP_MODE, 0x3F); + ret = lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); - /* Enable auto-powersave, set charge pump to auto, red to battery */ - ret |= lp5521_write(client, LP5521_REG_CONFIG, - LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT); + cfg = chip->pdata->update_config ? + : (LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT); + ret |= lp5521_write(client, LP5521_REG_CONFIG, cfg); /* Initialize all channels PWM to zero -> leds off */ ret |= lp5521_write(client, LP5521_REG_R_PWM, 0); @@ -258,8 +254,7 @@ static int lp5521_configure(struct i2c_client *client) /* Set engines are set to run state when OP_MODE enables engines */ ret |= lp5521_write(client, LP5521_REG_ENABLE, - LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM | - LP5521_EXEC_RUN); + LP5521_ENABLE_RUN_PROGRAM); /* enable takes 500us. 1 - 2 ms leaves some margin */ usleep_range(1000, 2000); @@ -310,8 +305,7 @@ static int lp5521_detect(struct i2c_client *client) int ret; u8 buf; - ret = lp5521_write(client, LP5521_REG_ENABLE, - LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM); + ret = lp5521_write(client, LP5521_REG_ENABLE, LP5521_ENABLE_DEFAULT); if (ret) return ret; /* enable takes 500us. 1 - 2 ms leaves some margin */ @@ -319,7 +313,7 @@ static int lp5521_detect(struct i2c_client *client) ret = lp5521_read(client, LP5521_REG_ENABLE, &buf); if (ret) return ret; - if (buf != (LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM)) + if (buf != LP5521_ENABLE_DEFAULT) return -ENODEV; return 0; @@ -504,7 +498,7 @@ static ssize_t store_current(struct device *dev, ssize_t ret; unsigned long curr; - if (strict_strtoul(buf, 0, &curr)) + if (kstrtoul(buf, 0, &curr)) return -EINVAL; if (curr > led->max_current) @@ -536,6 +530,97 @@ static ssize_t lp5521_selftest(struct device *dev, return sprintf(buf, "%s\n", ret ? "FAIL" : "OK"); } +static void lp5521_clear_program_memory(struct i2c_client *cl) +{ + int i; + u8 rgb_mem[] = { + LP5521_REG_R_PROG_MEM, + LP5521_REG_G_PROG_MEM, + LP5521_REG_B_PROG_MEM, + }; + + for (i = 0; i < ARRAY_SIZE(rgb_mem); i++) { + lp5521_write(cl, rgb_mem[i], 0); + lp5521_write(cl, rgb_mem[i] + 1, 0); + } +} + +static void lp5521_write_program_memory(struct i2c_client *cl, + u8 base, u8 *rgb, int size) +{ + int i; + + if (!rgb || size <= 0) + return; + + for (i = 0; i < size; i++) + lp5521_write(cl, base + i, *(rgb + i)); + + lp5521_write(cl, base + i, 0); + lp5521_write(cl, base + i + 1, 0); +} + +static inline struct lp5521_led_pattern *lp5521_get_pattern + (struct lp5521_chip *chip, u8 offset) +{ + struct lp5521_led_pattern *ptn; + ptn = chip->pdata->patterns + (offset - 1); + return ptn; +} + +static void lp5521_run_led_pattern(int mode, struct lp5521_chip *chip) +{ + struct lp5521_led_pattern *ptn; + struct i2c_client *cl = chip->client; + int num_patterns = chip->pdata->num_patterns; + + if (mode > num_patterns || !(chip->pdata->patterns)) + return; + + if (mode == PATTERN_OFF) { + lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_DEFAULT); + usleep_range(1000, 2000); + lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); + } else { + ptn = lp5521_get_pattern(chip, mode); + if (!ptn) + return; + + lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_LOAD); + usleep_range(1000, 2000); + + lp5521_clear_program_memory(cl); + + lp5521_write_program_memory(cl, LP5521_REG_R_PROG_MEM, + ptn->r, ptn->size_r); + lp5521_write_program_memory(cl, LP5521_REG_G_PROG_MEM, + ptn->g, ptn->size_g); + lp5521_write_program_memory(cl, LP5521_REG_B_PROG_MEM, + ptn->b, ptn->size_b); + + lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_RUN); + usleep_range(1000, 2000); + lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM); + } +} + +static ssize_t store_led_pattern(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct lp5521_chip *chip = i2c_get_clientdata(to_i2c_client(dev)); + unsigned long val; + int ret; + + ret = strict_strtoul(buf, 16, &val); + if (ret) + return ret; + + lp5521_run_led_pattern(val, chip); + + return len; +} + /* led class device attributes */ static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current); static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); @@ -561,6 +646,7 @@ static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load); static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load); static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load); static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); +static DEVICE_ATTR(led_pattern, S_IWUSR, NULL, store_led_pattern); static struct attribute *lp5521_attributes[] = { &dev_attr_engine1_mode.attr, @@ -570,6 +656,7 @@ static struct attribute *lp5521_attributes[] = { &dev_attr_engine1_load.attr, &dev_attr_engine2_load.attr, &dev_attr_engine3_load.attr, + &dev_attr_led_pattern.attr, NULL }; @@ -620,10 +707,15 @@ static int __devinit lp5521_init_led(struct lp5521_led *led, return -EINVAL; } - snprintf(name, sizeof(name), "%s:channel%d", - pdata->label ?: client->name, chan); led->cdev.brightness_set = lp5521_set_brightness; - led->cdev.name = name; + if (pdata->led_config[chan].name) { + led->cdev.name = pdata->led_config[chan].name; + } else { + snprintf(name, sizeof(name), "%s:channel%d", + pdata->label ?: client->name, chan); + led->cdev.name = name; + } + res = led_classdev_register(dev, &led->cdev); if (res < 0) { dev_err(dev, "couldn't register led on channel %d\n", chan); @@ -692,9 +784,9 @@ static int __devinit lp5521_probe(struct i2c_client *client, * otherwise further access to the R G B channels in the * LP5521_REG_ENABLE register will not have any effect - strange! */ - lp5521_read(client, LP5521_REG_R_CURRENT, &buf); + ret = lp5521_read(client, LP5521_REG_R_CURRENT, &buf); if (buf != LP5521_REG_R_CURR_DEFAULT) { - dev_err(&client->dev, "error in reseting chip\n"); + dev_err(&client->dev, "error in resetting chip\n"); goto fail2; } usleep_range(10000, 20000); @@ -767,6 +859,7 @@ static int __devexit lp5521_remove(struct i2c_client *client) struct lp5521_chip *chip = i2c_get_clientdata(client); int i; + lp5521_run_led_pattern(PATTERN_OFF, chip); lp5521_unregister_sysfs(client); for (i = 0; i < chip->num_leds; i++) { diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 73e791ae7259..857a3e15f2dd 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -152,7 +152,7 @@ static inline struct lp5523_chip *led_to_lp5523(struct lp5523_led *led) static int lp5523_set_mode(struct lp5523_engine *engine, u8 mode); static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode); -static int lp5523_load_program(struct lp5523_engine *engine, u8 *pattern); +static int lp5523_load_program(struct lp5523_engine *engine, const u8 *pattern); static void lp5523_led_brightness_work(struct work_struct *work); @@ -196,7 +196,7 @@ static int lp5523_configure(struct i2c_client *client) u8 status; /* one pattern per engine setting led mux start and stop addresses */ - u8 pattern[][LP5523_PROGRAM_LENGTH] = { + static const u8 pattern[][LP5523_PROGRAM_LENGTH] = { { 0x9c, 0x30, 0x9c, 0xb0, 0x9d, 0x80, 0xd8, 0x00, 0}, { 0x9c, 0x40, 0x9c, 0xc0, 0x9d, 0x80, 0xd8, 0x00, 0}, { 0x9c, 0x50, 0x9c, 0xd0, 0x9d, 0x80, 0xd8, 0x00, 0}, @@ -301,7 +301,7 @@ static int lp5523_load_mux(struct lp5523_engine *engine, u16 mux) return ret; } -static int lp5523_load_program(struct lp5523_engine *engine, u8 *pattern) +static int lp5523_load_program(struct lp5523_engine *engine, const u8 *pattern) { struct lp5523_chip *chip = engine_to_lp5523(engine); struct i2c_client *client = chip->client; diff --git a/drivers/leds/leds-pca9633.c b/drivers/leds/leds-pca9633.c new file mode 100644 index 000000000000..d8926fd031aa --- /dev/null +++ b/drivers/leds/leds-pca9633.c @@ -0,0 +1,193 @@ +/* + * Copyright 2011 bct electronic GmbH + * + * Author: Peter Meerwald <p.meerwald@bct-electronic.com> + * + * Based on leds-pca955x.c + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * LED driver for the PCA9633 I2C LED driver (7-bit slave address 0x62) + * + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/leds.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/workqueue.h> +#include <linux/slab.h> + +/* LED select registers determine the source that drives LED outputs */ +#define PCA9633_LED_OFF 0x0 /* LED driver off */ +#define PCA9633_LED_ON 0x1 /* LED driver on */ +#define PCA9633_LED_PWM 0x2 /* Controlled through PWM */ +#define PCA9633_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */ + +#define PCA9633_MODE1 0x00 +#define PCA9633_MODE2 0x01 +#define PCA9633_PWM_BASE 0x02 +#define PCA9633_LEDOUT 0x08 + +static const struct i2c_device_id pca9633_id[] = { + { "pca9633", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pca9633_id); + +struct pca9633_led { + struct i2c_client *client; + struct work_struct work; + enum led_brightness brightness; + struct led_classdev led_cdev; + int led_num; /* 0 .. 3 potentially */ + char name[32]; +}; + +static void pca9633_led_work(struct work_struct *work) +{ + struct pca9633_led *pca9633 = container_of(work, + struct pca9633_led, work); + u8 ledout = i2c_smbus_read_byte_data(pca9633->client, PCA9633_LEDOUT); + int shift = 2 * pca9633->led_num; + u8 mask = 0x3 << shift; + + switch (pca9633->brightness) { + case LED_FULL: + i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT, + (ledout & ~mask) | (PCA9633_LED_ON << shift)); + break; + case LED_OFF: + i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT, + ledout & ~mask); + break; + default: + i2c_smbus_write_byte_data(pca9633->client, + PCA9633_PWM_BASE + pca9633->led_num, + pca9633->brightness); + i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT, + (ledout & ~mask) | (PCA9633_LED_PWM << shift)); + break; + } +} + +static void pca9633_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct pca9633_led *pca9633; + + pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev); + + pca9633->brightness = value; + + /* + * Must use workqueue for the actual I/O since I2C operations + * can sleep. + */ + schedule_work(&pca9633->work); +} + +static int __devinit pca9633_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pca9633_led *pca9633; + struct led_platform_data *pdata; + int i, err; + + pdata = client->dev.platform_data; + + if (pdata) { + if (pdata->num_leds <= 0 || pdata->num_leds > 4) { + dev_err(&client->dev, "board info must claim at most 4 LEDs"); + return -EINVAL; + } + } + + pca9633 = kcalloc(4, sizeof(*pca9633), GFP_KERNEL); + if (!pca9633) + return -ENOMEM; + + i2c_set_clientdata(client, pca9633); + + for (i = 0; i < 4; i++) { + pca9633[i].client = client; + pca9633[i].led_num = i; + + /* Platform data can specify LED names and default triggers */ + if (pdata && i < pdata->num_leds) { + if (pdata->leds[i].name) + snprintf(pca9633[i].name, + sizeof(pca9633[i].name), "pca9633:%s", + pdata->leds[i].name); + if (pdata->leds[i].default_trigger) + pca9633[i].led_cdev.default_trigger = + pdata->leds[i].default_trigger; + } else { + snprintf(pca9633[i].name, sizeof(pca9633[i].name), + "pca9633:%d", i); + } + + pca9633[i].led_cdev.name = pca9633[i].name; + pca9633[i].led_cdev.brightness_set = pca9633_led_set; + + INIT_WORK(&pca9633[i].work, pca9633_led_work); + + err = led_classdev_register(&client->dev, &pca9633[i].led_cdev); + if (err < 0) + goto exit; + } + + /* Disable LED all-call address and set normal mode */ + i2c_smbus_write_byte_data(client, PCA9633_MODE1, 0x00); + + /* Turn off LEDs */ + i2c_smbus_write_byte_data(client, PCA9633_LEDOUT, 0x00); + + return 0; + +exit: + while (i--) { + led_classdev_unregister(&pca9633[i].led_cdev); + cancel_work_sync(&pca9633[i].work); + } + + kfree(pca9633); + + return err; +} + +static int __devexit pca9633_remove(struct i2c_client *client) +{ + struct pca9633_led *pca9633 = i2c_get_clientdata(client); + int i; + + for (i = 0; i < 4; i++) { + led_classdev_unregister(&pca9633[i].led_cdev); + cancel_work_sync(&pca9633[i].work); + } + + kfree(pca9633); + + return 0; +} + +static struct i2c_driver pca9633_driver = { + .driver = { + .name = "leds-pca9633", + .owner = THIS_MODULE, + }, + .probe = pca9633_probe, + .remove = __devexit_p(pca9633_remove), + .id_table = pca9633_id, +}; + +module_i2c_driver(pca9633_driver); + +MODULE_AUTHOR("Peter Meerwald <p.meerwald@bct-electronic.com>"); +MODULE_DESCRIPTION("PCA9633 LED driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c index 133f89fb7071..6c1c14f31635 100644 --- a/drivers/leds/leds-tca6507.c +++ b/drivers/leds/leds-tca6507.c @@ -687,10 +687,9 @@ static int __devinit tca6507_probe(struct i2c_client *client, NUM_LEDS); return -ENODEV; } - err = -ENOMEM; tca = kzalloc(sizeof(*tca), GFP_KERNEL); if (!tca) - goto exit; + return -ENOMEM; tca->client = client; INIT_WORK(&tca->work, tca6507_work); @@ -724,11 +723,10 @@ static int __devinit tca6507_probe(struct i2c_client *client, return 0; exit: - while (i--) + while (i--) { if (tca->leds[i].led_cdev.name) led_classdev_unregister(&tca->leds[i].led_cdev); - cancel_work_sync(&tca->work); - i2c_set_clientdata(client, NULL); + } kfree(tca); return err; } @@ -745,7 +743,6 @@ static int __devexit tca6507_remove(struct i2c_client *client) } tca6507_remove_gpio(tca); cancel_work_sync(&tca->work); - i2c_set_clientdata(client, NULL); kfree(tca); return 0; diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile index 8295854ab94b..f80407eb8998 100644 --- a/drivers/media/common/tuners/Makefile +++ b/drivers/media/common/tuners/Makefile @@ -29,5 +29,5 @@ obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o -ccflags-y += -Idrivers/media/dvb/dvb-core -ccflags-y += -Idrivers/media/dvb/frontends +ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core +ccflags-y += -I$(srctree)/drivers/media/dvb/frontends diff --git a/drivers/media/common/tuners/max2165.c b/drivers/media/common/tuners/max2165.c index cb2c98fbad1b..ba84936aafd6 100644 --- a/drivers/media/common/tuners/max2165.c +++ b/drivers/media/common/tuners/max2165.c @@ -168,7 +168,7 @@ int fixpt_div32(u32 dividend, u32 divisor, u32 *quotient, u32 *fraction) int i; if (0 == divisor) - return -1; + return -EINVAL; q = dividend / divisor; remainder = dividend - q * divisor; @@ -194,10 +194,13 @@ static int max2165_set_rf(struct max2165_priv *priv, u32 freq) u8 tf_ntch; u32 t; u32 quotient, fraction; + int ret; /* Set PLL divider according to RF frequency */ - fixpt_div32(freq / 1000, priv->config->osc_clk * 1000, - "ient, &fraction); + ret = fixpt_div32(freq / 1000, priv->config->osc_clk * 1000, + "ient, &fraction); + if (ret != 0) + return ret; /* 20-bit fraction */ fraction >>= 12; diff --git a/drivers/media/common/tuners/mt2063.c b/drivers/media/common/tuners/mt2063.c index c89af3cd5eba..0ed9091ff48e 100644 --- a/drivers/media/common/tuners/mt2063.c +++ b/drivers/media/common/tuners/mt2063.c @@ -350,7 +350,7 @@ static int MT2063_Sleep(struct dvb_frontend *fe) /* * ToDo: Add code here to implement a OS blocking */ - msleep(10); + msleep(100); return 0; } @@ -2226,7 +2226,7 @@ static struct dvb_tuner_ops mt2063_ops = { .info = { .name = "MT2063 Silicon Tuner", .frequency_min = 45000000, - .frequency_max = 850000000, + .frequency_max = 865000000, .frequency_step = 0, }, diff --git a/drivers/media/common/tuners/mt2063.h b/drivers/media/common/tuners/mt2063.h index 62d0e8ec4e99..3f5cfd93713f 100644 --- a/drivers/media/common/tuners/mt2063.h +++ b/drivers/media/common/tuners/mt2063.h @@ -23,10 +23,6 @@ static inline struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe, return NULL; } -int mt2063_setTune(struct dvb_frontend *fe, u32 f_in, - u32 bw_in, - enum MTTune_atv_standard tv_type); - /* FIXME: Should use the standard DVB attachment interfaces */ unsigned int tuner_MT2063_SoftwareShutdown(struct dvb_frontend *fe); unsigned int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe); diff --git a/drivers/media/common/tuners/tuner-types.c b/drivers/media/common/tuners/tuner-types.c index e13683bab6b3..2da4440c16ee 100644 --- a/drivers/media/common/tuners/tuner-types.c +++ b/drivers/media/common/tuners/tuner-types.c @@ -1868,6 +1868,10 @@ struct tunertype tuners[] = { .params = tuner_tena_tnf_5337_params, .count = ARRAY_SIZE(tuner_tena_tnf_5337_params), }, + [TUNER_XC5000C] = { /* Xceive 5000C */ + .name = "Xceive 5000C tuner", + /* see xc5000.c for details */ + }, }; EXPORT_SYMBOL(tuners); diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index 296df05b8cda..7f98984e4fad 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -49,9 +49,6 @@ static LIST_HEAD(hybrid_tuner_instance_list); #define dprintk(level, fmt, arg...) if (debug >= level) \ printk(KERN_INFO "%s: " fmt, "xc5000", ## arg) -#define XC5000_DEFAULT_FIRMWARE "dvb-fe-xc5000-1.6.114.fw" -#define XC5000_DEFAULT_FIRMWARE_SIZE 12401 - struct xc5000_priv { struct tuner_i2c_props i2c_props; struct list_head hybrid_tuner_instance_list; @@ -62,6 +59,8 @@ struct xc5000_priv { u8 video_standard; u8 rf_mode; u8 radio_input; + + int chip_id; }; /* Misc Defines */ @@ -204,6 +203,33 @@ static struct XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = { {"FM Radio-INPUT1_MONO", 0x0278, 0x9002} }; + +struct xc5000_fw_cfg { + char *name; + u16 size; +}; + +static const struct xc5000_fw_cfg xc5000a_1_6_114 = { + .name = "dvb-fe-xc5000-1.6.114.fw", + .size = 12401, +}; + +static const struct xc5000_fw_cfg xc5000c_41_024_5_31875 = { + .name = "dvb-fe-xc5000c-41.024.5-31875.fw", + .size = 16503, +}; + +static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id) +{ + switch (chip_id) { + default: + case XC5000A: + return &xc5000a_1_6_114; + case XC5000C: + return &xc5000c_41_024_5_31875; + } +} + static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe); static int xc5000_is_firmware_loaded(struct dvb_frontend *fe); static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val); @@ -552,12 +578,14 @@ static int xc5000_fwupload(struct dvb_frontend *fe) struct xc5000_priv *priv = fe->tuner_priv; const struct firmware *fw; int ret; + const struct xc5000_fw_cfg *desired_fw = + xc5000_assign_firmware(priv->chip_id); /* request the firmware, this will block and timeout */ printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n", - XC5000_DEFAULT_FIRMWARE); + desired_fw->name); - ret = request_firmware(&fw, XC5000_DEFAULT_FIRMWARE, + ret = request_firmware(&fw, desired_fw->name, priv->i2c_props.adap->dev.parent); if (ret) { printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n"); @@ -569,7 +597,7 @@ static int xc5000_fwupload(struct dvb_frontend *fe) ret = XC_RESULT_SUCCESS; } - if (fw->size != XC5000_DEFAULT_FIRMWARE_SIZE) { + if (fw->size != desired_fw->size) { printk(KERN_ERR "xc5000: firmware incorrect size\n"); ret = XC_RESULT_RESET_FAILURE; } else { @@ -1139,6 +1167,13 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, if (priv->radio_input == 0) priv->radio_input = cfg->radio_input; + /* don't override chip id if it's already been set + unless explicitly specified */ + if ((priv->chip_id == 0) || (cfg->chip_id)) + /* use default chip id if none specified, set to 0 so + it can be overridden if this is a hybrid driver */ + priv->chip_id = (cfg->chip_id) ? cfg->chip_id : 0; + /* Check if firmware has been loaded. It is possible that another instance of the driver has loaded the firmware. */ diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h index e2957451b532..3396f8e02b40 100644 --- a/drivers/media/common/tuners/xc5000.h +++ b/drivers/media/common/tuners/xc5000.h @@ -27,10 +27,15 @@ struct dvb_frontend; struct i2c_adapter; +#define XC5000A 1 +#define XC5000C 2 + struct xc5000_config { u8 i2c_address; u32 if_khz; u8 radio_input; + + int chip_id; }; /* xc5000 callback command */ diff --git a/drivers/media/dvb/ddbridge/ddbridge-core.c b/drivers/media/dvb/ddbridge/ddbridge-core.c index ce4f85849e7b..d88c4aa7d24d 100644 --- a/drivers/media/dvb/ddbridge/ddbridge-core.c +++ b/drivers/media/dvb/ddbridge/ddbridge-core.c @@ -578,6 +578,7 @@ static int demod_attach_drxk(struct ddb_input *input) struct drxk_config config; memset(&config, 0, sizeof(config)); + config.microcode_name = "drxk_a3.mc"; config.adr = 0x29 + (input->nr & 1); fe = input->fe = dvb_attach(drxk_attach, &config, i2c); diff --git a/drivers/media/dvb/ddbridge/ddbridge.h b/drivers/media/dvb/ddbridge/ddbridge.h index 6d14893218f4..8b1b41d2a52d 100644 --- a/drivers/media/dvb/ddbridge/ddbridge.h +++ b/drivers/media/dvb/ddbridge/ddbridge.h @@ -32,8 +32,6 @@ #include <asm/dma.h> #include <linux/dvb/frontend.h> #include <linux/dvb/ca.h> -#include <linux/dvb/video.h> -#include <linux/dvb/audio.h> #include <linux/socket.h> #include "dmxdev.h" diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index fbbe545a74cb..4555baa383b2 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -655,6 +655,8 @@ restart: dprintk("%s: Retune requested, FESTATE_RETUNE\n", __func__); re_tune = true; fepriv->state = FESTATE_TUNED; + } else { + re_tune = false; } if (fe->ops.tune) diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 9f203c6767a6..63bf45679f98 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -361,6 +361,14 @@ config DVB_USB_EC168 help Say Y here to support the E3C EC168 DVB-T USB2.0 receiver. +config DVB_USB_AZ6007 + tristate "AzureWave 6007 and clones DVB-T/C USB2.0 support" + depends on DVB_USB + select DVB_DRXK if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2063 if !DVB_FE_CUSTOMISE + help + Say Y here to support theAfatech AF9005 based DVB-T/DVB-C receivers. + config DVB_USB_AZ6027 tristate "Azurewave DVB-S/S2 USB2.0 AZ6027 support" depends on DVB_USB @@ -378,6 +386,7 @@ config DVB_USB_LME2510 select DVB_IX2505V if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_M88RS2000 if !DVB_FE_CUSTOMISE help Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 . @@ -403,3 +412,13 @@ config DVB_USB_MXL111SF select VIDEO_TVEEPROM help Say Y here to support the MxL111SF USB2.0 DTV receiver. + +config DVB_USB_RTL28XXU + tristate "Realtek RTL28xxU DVB USB support" + depends on DVB_USB && EXPERIMENTAL + select DVB_RTL2830 + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE + help + Say Y here to support the Realtek RTL28xxU DVB USB receiver. diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index 26c8b9e57050..b76acb5387e6 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -54,7 +54,6 @@ obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o dvb-usb-opera-objs = opera1.o obj-$(CONFIG_DVB_USB_OPERA1) += dvb-usb-opera.o - dvb-usb-af9005-objs = af9005.o af9005-fe.o obj-$(CONFIG_DVB_USB_AF9005) += dvb-usb-af9005.o @@ -88,6 +87,9 @@ obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o dvb-usb-ec168-objs = ec168.o obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o +dvb-usb-az6007-objs = az6007.o +obj-$(CONFIG_DVB_USB_AZ6007) += dvb-usb-az6007.o + dvb-usb-az6027-objs = az6027.o obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.o @@ -105,8 +107,12 @@ obj-$(CONFIG_DVB_USB_MXL111SF) += dvb-usb-mxl111sf.o obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-demod.o obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o -ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ +dvb-usb-rtl28xxu-objs = rtl28xxu.o +obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o + +ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core +ccflags-y += -I$(srctree)/drivers/media/dvb/frontends/ # due to tuner-xc3028 -ccflags-y += -Idrivers/media/common/tuners -EXTRA_CFLAGS += -Idrivers/media/dvb/ttpci +ccflags-y += -I$(srctree)/drivers/media/common/tuners +ccflags-y += -I$(srctree)/drivers/media/dvb/ttpci diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 282a43d648df..7e70ea50ef26 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -1164,6 +1164,41 @@ static int af9015_af9013_sleep(struct dvb_frontend *fe) return ret; } +/* override tuner callbacks for resource locking */ +static int af9015_tuner_init(struct dvb_frontend *fe) +{ + int ret; + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct af9015_state *priv = adap->dev->priv; + + if (mutex_lock_interruptible(&adap->dev->usb_mutex)) + return -EAGAIN; + + ret = priv->tuner_init[adap->id](fe); + + mutex_unlock(&adap->dev->usb_mutex); + + return ret; +} + +/* override tuner callbacks for resource locking */ +static int af9015_tuner_sleep(struct dvb_frontend *fe) +{ + int ret; + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct af9015_state *priv = adap->dev->priv; + + if (mutex_lock_interruptible(&adap->dev->usb_mutex)) + return -EAGAIN; + + ret = priv->tuner_sleep[adap->id](fe); + + mutex_unlock(&adap->dev->usb_mutex); + + return ret; +} + + static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap) { int ret; @@ -1283,6 +1318,7 @@ static struct mxl5007t_config af9015_mxl5007t_config = { static int af9015_tuner_attach(struct dvb_usb_adapter *adap) { int ret; + struct af9015_state *state = adap->dev->priv; deb_info("%s:\n", __func__); switch (af9015_af9013_config[adap->id].tuner) { @@ -1340,6 +1376,19 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap) err("Unknown tuner id:%d", af9015_af9013_config[adap->id].tuner); } + + if (adap->fe_adap[0].fe->ops.tuner_ops.init) { + state->tuner_init[adap->id] = + adap->fe_adap[0].fe->ops.tuner_ops.init; + adap->fe_adap[0].fe->ops.tuner_ops.init = af9015_tuner_init; + } + + if (adap->fe_adap[0].fe->ops.tuner_ops.sleep) { + state->tuner_sleep[adap->id] = + adap->fe_adap[0].fe->ops.tuner_ops.sleep; + adap->fe_adap[0].fe->ops.tuner_ops.sleep = af9015_tuner_sleep; + } + return ret; } diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h index f619063fa72f..2f68419e899b 100644 --- a/drivers/media/dvb/dvb-usb/af9015.h +++ b/drivers/media/dvb/dvb-usb/af9015.h @@ -108,6 +108,8 @@ struct af9015_state { int (*read_status[2]) (struct dvb_frontend *fe, fe_status_t *status); int (*init[2]) (struct dvb_frontend *fe); int (*sleep[2]) (struct dvb_frontend *fe); + int (*tuner_init[2]) (struct dvb_frontend *fe); + int (*tuner_sleep[2]) (struct dvb_frontend *fe); }; struct af9015_config { diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c index cf0c318d6989..03c28655af1b 100644 --- a/drivers/media/dvb/dvb-usb/anysee.c +++ b/drivers/media/dvb/dvb-usb/anysee.c @@ -58,7 +58,7 @@ static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen, u8 *rbuf, u8 rlen) { struct anysee_state *state = d->priv; - int act_len, ret; + int act_len, ret, i; u8 buf[64]; memcpy(&buf[0], sbuf, slen); @@ -73,26 +73,52 @@ static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen, /* We need receive one message more after dvb_usb_generic_rw due to weird transaction flow, which is 1 x send + 2 x receive. */ ret = dvb_usb_generic_rw(d, buf, sizeof(buf), buf, sizeof(buf), 0); - if (!ret) { + if (ret) + goto error_unlock; + + /* TODO FIXME: dvb_usb_generic_rw() fails rarely with error code -32 + * (EPIPE, Broken pipe). Function supports currently msleep() as a + * parameter but I would not like to use it, since according to + * Documentation/timers/timers-howto.txt it should not be used such + * short, under < 20ms, sleeps. Repeating failed message would be + * better choice as not to add unwanted delays... + * Fixing that correctly is one of those or both; + * 1) use repeat if possible + * 2) add suitable delay + */ + + /* get answer, retry few times if error returned */ + for (i = 0; i < 3; i++) { /* receive 2nd answer */ ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint), buf, sizeof(buf), &act_len, 2000); - if (ret) - err("%s: recv bulk message failed: %d", __func__, ret); - else { + + if (ret) { + deb_info("%s: recv bulk message failed: %d", + __func__, ret); + } else { deb_xfer("<<< "); debug_dump(buf, rlen, deb_xfer); if (buf[63] != 0x4f) deb_info("%s: cmd failed\n", __func__); + + break; } } + if (ret) { + /* all retries failed, it is fatal */ + err("%s: recv bulk message failed: %d", __func__, ret); + goto error_unlock; + } + /* read request, copy returned data to return buf */ - if (!ret && rbuf && rlen) + if (rbuf && rlen) memcpy(rbuf, buf, rlen); +error_unlock: mutex_unlock(&anysee_usb_mutex); return ret; diff --git a/drivers/media/dvb/dvb-usb/az6007.c b/drivers/media/dvb/dvb-usb/az6007.c new file mode 100644 index 000000000000..4008b9c50fbd --- /dev/null +++ b/drivers/media/dvb/dvb-usb/az6007.c @@ -0,0 +1,957 @@ +/* + * Driver for AzureWave 6007 DVB-C/T USB2.0 and clones + * + * Copyright (c) Henry Wang <Henry.wang@AzureWave.com> + * + * This driver was made publicly available by Terratec, at: + * http://linux.terratec.de/files/TERRATEC_H7/20110323_TERRATEC_H7_Linux.tar.gz + * The original driver's license is GPL, as declared with MODULE_LICENSE() + * + * Copyright (c) 2010-2011 Mauro Carvalho Chehab <mchehab@redhat.com> + * Driver modified by in order to work with upstream drxk driver, and + * tons of bugs got fixed. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "drxk.h" +#include "mt2063.h" +#include "dvb_ca_en50221.h" + +#define DVB_USB_LOG_PREFIX "az6007" +#include "dvb-usb.h" + +/* debug */ +int dvb_usb_az6007_debug; +module_param_named(debug, dvb_usb_az6007_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." + DVB_USB_DEBUG_STATUS); + +#define deb_info(args...) dprintk(dvb_usb_az6007_debug, 0x01, args) +#define deb_xfer(args...) dprintk(dvb_usb_az6007_debug, 0x02, args) +#define deb_rc(args...) dprintk(dvb_usb_az6007_debug, 0x04, args) +#define deb_fe(args...) dprintk(dvb_usb_az6007_debug, 0x08, args) + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +/* Known requests (Cypress FX2 firmware + az6007 "private" ones*/ + +#define FX2_OED 0xb5 +#define AZ6007_READ_DATA 0xb7 +#define AZ6007_I2C_RD 0xb9 +#define AZ6007_POWER 0xbc +#define AZ6007_I2C_WR 0xbd +#define FX2_SCON1 0xc0 +#define AZ6007_TS_THROUGH 0xc7 +#define AZ6007_READ_IR 0xb4 + +struct az6007_device_state { + struct mutex mutex; + struct mutex ca_mutex; + struct dvb_ca_en50221 ca; + unsigned warm:1; + int (*gate_ctrl) (struct dvb_frontend *, int); + unsigned char data[4096]; +}; + +static struct drxk_config terratec_h7_drxk = { + .adr = 0x29, + .parallel_ts = true, + .dynamic_clk = true, + .single_master = true, + .enable_merr_cfg = true, + .no_i2c_bridge = false, + .chunk_size = 64, + .mpeg_out_clk_strength = 0x02, + .microcode_name = "dvb-usb-terratec-h7-drxk.fw", +}; + +static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct dvb_usb_adapter *adap = fe->sec_priv; + struct az6007_device_state *st; + int status = 0; + + deb_info("%s: %s\n", __func__, enable ? "enable" : "disable"); + + if (!adap) + return -EINVAL; + + st = adap->dev->priv; + + if (!st) + return -EINVAL; + + if (enable) + status = st->gate_ctrl(fe, 1); + else + status = st->gate_ctrl(fe, 0); + + return status; +} + +static struct mt2063_config az6007_mt2063_config = { + .tuner_address = 0x60, + .refclock = 36125000, +}; + +static int __az6007_read(struct usb_device *udev, u8 req, u16 value, + u16 index, u8 *b, int blen) +{ + int ret; + + ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + req, + USB_TYPE_VENDOR | USB_DIR_IN, + value, index, b, blen, 5000); + if (ret < 0) { + warn("usb read operation failed. (%d)", ret); + return -EIO; + } + + deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ", req, value, + index); + debug_dump(b, blen, deb_xfer); + + return ret; +} + +static int az6007_read(struct dvb_usb_device *d, u8 req, u16 value, + u16 index, u8 *b, int blen) +{ + struct az6007_device_state *st = d->priv; + int ret; + + if (mutex_lock_interruptible(&st->mutex) < 0) + return -EAGAIN; + + ret = __az6007_read(d->udev, req, value, index, b, blen); + + mutex_unlock(&st->mutex); + + return ret; +} + +static int __az6007_write(struct usb_device *udev, u8 req, u16 value, + u16 index, u8 *b, int blen) +{ + int ret; + + deb_xfer("out: req. %02x, val: %04x, ind: %04x, buffer: ", req, value, + index); + debug_dump(b, blen, deb_xfer); + + if (blen > 64) { + err("az6007: tried to write %d bytes, but I2C max size is 64 bytes\n", + blen); + return -EOPNOTSUPP; + } + + ret = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + req, + USB_TYPE_VENDOR | USB_DIR_OUT, + value, index, b, blen, 5000); + if (ret != blen) { + err("usb write operation failed. (%d)", ret); + return -EIO; + } + + return 0; +} + +static int az6007_write(struct dvb_usb_device *d, u8 req, u16 value, + u16 index, u8 *b, int blen) +{ + struct az6007_device_state *st = d->priv; + int ret; + + if (mutex_lock_interruptible(&st->mutex) < 0) + return -EAGAIN; + + ret = __az6007_write(d->udev, req, value, index, b, blen); + + mutex_unlock(&st->mutex); + + return ret; +} + +static int az6007_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + struct dvb_usb_device *d = adap->dev; + + deb_info("%s: %s", __func__, onoff ? "enable" : "disable"); + + return az6007_write(d, 0xbc, onoff, 0, NULL, 0); +} + +/* remote control stuff (does not work with my box) */ +static int az6007_rc_query(struct dvb_usb_device *d) +{ + struct az6007_device_state *st = d->priv; + unsigned code = 0; + + az6007_read(d, AZ6007_READ_IR, 0, 0, st->data, 10); + + if (st->data[1] == 0x44) + return 0; + + if ((st->data[1] ^ st->data[2]) == 0xff) + code = st->data[1]; + else + code = st->data[1] << 8 | st->data[2]; + + if ((st->data[3] ^ st->data[4]) == 0xff) + code = code << 8 | st->data[3]; + else + code = code << 16 | st->data[3] << 8 | st->data[4]; + + rc_keydown(d->rc_dev, code, st->data[5]); + + return 0; +} + +static int az6007_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, + int address) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = (struct az6007_device_state *)d->priv; + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 *b; + + if (slot != 0) + return -EINVAL; + + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; + + mutex_lock(&state->ca_mutex); + + req = 0xC1; + value = address; + index = 0; + blen = 1; + + ret = az6007_read(d, req, value, index, b, blen); + if (ret < 0) { + warn("usb in operation failed. (%d)", ret); + ret = -EINVAL; + } else { + ret = b[0]; + } + + mutex_unlock(&state->ca_mutex); + kfree(b); + return ret; +} + +static int az6007_ci_write_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, + int address, + u8 value) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = (struct az6007_device_state *)d->priv; + + int ret; + u8 req; + u16 value1; + u16 index; + int blen; + + deb_info("%s %d", __func__, slot); + if (slot != 0) + return -EINVAL; + + mutex_lock(&state->ca_mutex); + req = 0xC2; + value1 = address; + index = value; + blen = 0; + + ret = az6007_write(d, req, value1, index, NULL, blen); + if (ret != 0) + warn("usb out operation failed. (%d)", ret); + + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int az6007_ci_read_cam_control(struct dvb_ca_en50221 *ca, + int slot, + u8 address) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = (struct az6007_device_state *)d->priv; + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 *b; + + if (slot != 0) + return -EINVAL; + + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; + + mutex_lock(&state->ca_mutex); + + req = 0xC3; + value = address; + index = 0; + blen = 2; + + ret = az6007_read(d, req, value, index, b, blen); + if (ret < 0) { + warn("usb in operation failed. (%d)", ret); + ret = -EINVAL; + } else { + if (b[0] == 0) + warn("Read CI IO error"); + + ret = b[1]; + deb_info("read cam data = %x from 0x%x", b[1], value); + } + + mutex_unlock(&state->ca_mutex); + kfree(b); + return ret; +} + +static int az6007_ci_write_cam_control(struct dvb_ca_en50221 *ca, + int slot, + u8 address, + u8 value) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = (struct az6007_device_state *)d->priv; + + int ret; + u8 req; + u16 value1; + u16 index; + int blen; + + if (slot != 0) + return -EINVAL; + + mutex_lock(&state->ca_mutex); + req = 0xC4; + value1 = address; + index = value; + blen = 0; + + ret = az6007_write(d, req, value1, index, NULL, blen); + if (ret != 0) { + warn("usb out operation failed. (%d)", ret); + goto failed; + } + +failed: + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int CI_CamReady(struct dvb_ca_en50221 *ca, int slot) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 *b; + + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; + + req = 0xC8; + value = 0; + index = 0; + blen = 1; + + ret = az6007_read(d, req, value, index, b, blen); + if (ret < 0) { + warn("usb in operation failed. (%d)", ret); + ret = -EIO; + } else{ + ret = b[0]; + } + kfree(b); + return ret; +} + +static int az6007_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = (struct az6007_device_state *)d->priv; + + int ret, i; + u8 req; + u16 value; + u16 index; + int blen; + + mutex_lock(&state->ca_mutex); + + req = 0xC6; + value = 1; + index = 0; + blen = 0; + + ret = az6007_write(d, req, value, index, NULL, blen); + if (ret != 0) { + warn("usb out operation failed. (%d)", ret); + goto failed; + } + + msleep(500); + req = 0xC6; + value = 0; + index = 0; + blen = 0; + + ret = az6007_write(d, req, value, index, NULL, blen); + if (ret != 0) { + warn("usb out operation failed. (%d)", ret); + goto failed; + } + + for (i = 0; i < 15; i++) { + msleep(100); + + if (CI_CamReady(ca, slot)) { + deb_info("CAM Ready"); + break; + } + } + msleep(5000); + +failed: + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int az6007_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + return 0; +} + +static int az6007_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = (struct az6007_device_state *)d->priv; + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + + deb_info("%s", __func__); + mutex_lock(&state->ca_mutex); + req = 0xC7; + value = 1; + index = 0; + blen = 0; + + ret = az6007_write(d, req, value, index, NULL, blen); + if (ret != 0) { + warn("usb out operation failed. (%d)", ret); + goto failed; + } + +failed: + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int az6007_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = (struct az6007_device_state *)d->priv; + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 *b; + + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; + mutex_lock(&state->ca_mutex); + + req = 0xC5; + value = 0; + index = 0; + blen = 1; + + ret = az6007_read(d, req, value, index, b, blen); + if (ret < 0) { + warn("usb in operation failed. (%d)", ret); + ret = -EIO; + } else + ret = 0; + + if (!ret && b[0] == 1) { + ret = DVB_CA_EN50221_POLL_CAM_PRESENT | + DVB_CA_EN50221_POLL_CAM_READY; + } + + mutex_unlock(&state->ca_mutex); + kfree(b); + return ret; +} + + +static void az6007_ci_uninit(struct dvb_usb_device *d) +{ + struct az6007_device_state *state; + + deb_info("%s", __func__); + + if (NULL == d) + return; + + state = (struct az6007_device_state *)d->priv; + if (NULL == state) + return; + + if (NULL == state->ca.data) + return; + + dvb_ca_en50221_release(&state->ca); + + memset(&state->ca, 0, sizeof(state->ca)); +} + + +static int az6007_ci_init(struct dvb_usb_adapter *a) +{ + struct dvb_usb_device *d = a->dev; + struct az6007_device_state *state = (struct az6007_device_state *)d->priv; + int ret; + + deb_info("%s", __func__); + + mutex_init(&state->ca_mutex); + + state->ca.owner = THIS_MODULE; + state->ca.read_attribute_mem = az6007_ci_read_attribute_mem; + state->ca.write_attribute_mem = az6007_ci_write_attribute_mem; + state->ca.read_cam_control = az6007_ci_read_cam_control; + state->ca.write_cam_control = az6007_ci_write_cam_control; + state->ca.slot_reset = az6007_ci_slot_reset; + state->ca.slot_shutdown = az6007_ci_slot_shutdown; + state->ca.slot_ts_enable = az6007_ci_slot_ts_enable; + state->ca.poll_slot_status = az6007_ci_poll_slot_status; + state->ca.data = d; + + ret = dvb_ca_en50221_init(&a->dvb_adap, + &state->ca, + 0, /* flags */ + 1);/* n_slots */ + if (ret != 0) { + err("Cannot initialize CI: Error %d.", ret); + memset(&state->ca, 0, sizeof(state->ca)); + return ret; + } + + deb_info("CI initialized."); + + return 0; +} + +static int az6007_read_mac_addr(struct dvb_usb_device *d, u8 mac[6]) +{ + struct az6007_device_state *st = d->priv; + int ret; + + ret = az6007_read(d, AZ6007_READ_DATA, 6, 0, st->data, 6); + memcpy(mac, st->data, sizeof(mac)); + + if (ret > 0) + deb_info("%s: mac is %02x:%02x:%02x:%02x:%02x:%02x\n", + __func__, mac[0], mac[1], mac[2], + mac[3], mac[4], mac[5]); + + return ret; +} + +static int az6007_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct az6007_device_state *st = adap->dev->priv; + + deb_info("attaching demod drxk"); + + adap->fe_adap[0].fe = dvb_attach(drxk_attach, &terratec_h7_drxk, + &adap->dev->i2c_adap); + if (!adap->fe_adap[0].fe) + return -EINVAL; + + adap->fe_adap[0].fe->sec_priv = adap; + st->gate_ctrl = adap->fe_adap[0].fe->ops.i2c_gate_ctrl; + adap->fe_adap[0].fe->ops.i2c_gate_ctrl = drxk_gate_ctrl; + + az6007_ci_init(adap); + + return 0; +} + +static int az6007_tuner_attach(struct dvb_usb_adapter *adap) +{ + deb_info("attaching tuner mt2063"); + + /* Attach mt2063 to DVB-C frontend */ + if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl) + adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 1); + if (!dvb_attach(mt2063_attach, adap->fe_adap[0].fe, + &az6007_mt2063_config, + &adap->dev->i2c_adap)) + return -EINVAL; + + if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl) + adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 0); + + return 0; +} + +int az6007_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + struct az6007_device_state *st = d->priv; + int ret; + + deb_info("%s()\n", __func__); + + if (!st->warm) { + mutex_init(&st->mutex); + + ret = az6007_write(d, AZ6007_POWER, 0, 2, NULL, 0); + if (ret < 0) + return ret; + msleep(60); + ret = az6007_write(d, AZ6007_POWER, 1, 4, NULL, 0); + if (ret < 0) + return ret; + msleep(100); + ret = az6007_write(d, AZ6007_POWER, 1, 3, NULL, 0); + if (ret < 0) + return ret; + msleep(20); + ret = az6007_write(d, AZ6007_POWER, 1, 4, NULL, 0); + if (ret < 0) + return ret; + + msleep(400); + ret = az6007_write(d, FX2_SCON1, 0, 3, NULL, 0); + if (ret < 0) + return ret; + msleep(150); + ret = az6007_write(d, FX2_SCON1, 1, 3, NULL, 0); + if (ret < 0) + return ret; + msleep(430); + ret = az6007_write(d, AZ6007_POWER, 0, 0, NULL, 0); + if (ret < 0) + return ret; + + st->warm = true; + + return 0; + } + + if (!onoff) + return 0; + + az6007_write(d, AZ6007_POWER, 0, 0, NULL, 0); + az6007_write(d, AZ6007_TS_THROUGH, 0, 0, NULL, 0); + + return 0; +} + +/* I2C */ +static int az6007_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct az6007_device_state *st = d->priv; + int i, j, len; + int ret = 0; + u16 index; + u16 value; + int length; + u8 req, addr; + + if (mutex_lock_interruptible(&st->mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + addr = msgs[i].addr << 1; + if (((i + 1) < num) + && (msgs[i].len == 1) + && (!msgs[i].flags & I2C_M_RD) + && (msgs[i + 1].flags & I2C_M_RD) + && (msgs[i].addr == msgs[i + 1].addr)) { + /* + * A write + read xfer for the same address, where + * the first xfer has just 1 byte length. + * Need to join both into one operation + */ + if (dvb_usb_az6007_debug & 2) + printk(KERN_DEBUG + "az6007 I2C xfer write+read addr=0x%x len=%d/%d: ", + addr, msgs[i].len, msgs[i + 1].len); + req = AZ6007_I2C_RD; + index = msgs[i].buf[0]; + value = addr | (1 << 8); + length = 6 + msgs[i + 1].len; + len = msgs[i + 1].len; + ret = __az6007_read(d->udev, req, value, index, + st->data, length); + if (ret >= len) { + for (j = 0; j < len; j++) { + msgs[i + 1].buf[j] = st->data[j + 5]; + if (dvb_usb_az6007_debug & 2) + printk(KERN_CONT + "0x%02x ", + msgs[i + 1].buf[j]); + } + } else + ret = -EIO; + i++; + } else if (!(msgs[i].flags & I2C_M_RD)) { + /* write bytes */ + if (dvb_usb_az6007_debug & 2) + printk(KERN_DEBUG + "az6007 I2C xfer write addr=0x%x len=%d: ", + addr, msgs[i].len); + req = AZ6007_I2C_WR; + index = msgs[i].buf[0]; + value = addr | (1 << 8); + length = msgs[i].len - 1; + len = msgs[i].len - 1; + if (dvb_usb_az6007_debug & 2) + printk(KERN_CONT "(0x%02x) ", msgs[i].buf[0]); + for (j = 0; j < len; j++) { + st->data[j] = msgs[i].buf[j + 1]; + if (dvb_usb_az6007_debug & 2) + printk(KERN_CONT "0x%02x ", + st->data[j]); + } + ret = __az6007_write(d->udev, req, value, index, + st->data, length); + } else { + /* read bytes */ + if (dvb_usb_az6007_debug & 2) + printk(KERN_DEBUG + "az6007 I2C xfer read addr=0x%x len=%d: ", + addr, msgs[i].len); + req = AZ6007_I2C_RD; + index = msgs[i].buf[0]; + value = addr; + length = msgs[i].len + 6; + len = msgs[i].len; + ret = __az6007_read(d->udev, req, value, index, + st->data, length); + for (j = 0; j < len; j++) { + msgs[i].buf[j] = st->data[j + 5]; + if (dvb_usb_az6007_debug & 2) + printk(KERN_CONT + "0x%02x ", st->data[j + 5]); + } + } + if (dvb_usb_az6007_debug & 2) + printk(KERN_CONT "\n"); + if (ret < 0) + goto err; + } +err: + mutex_unlock(&st->mutex); + + if (ret < 0) { + info("%s ERROR: %i", __func__, ret); + return ret; + } + return num; +} + +static u32 az6007_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm az6007_i2c_algo = { + .master_xfer = az6007_i2c_xfer, + .functionality = az6007_i2c_func, +}; + +int az6007_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, int *cold) +{ + int ret; + u8 *mac; + + mac = kmalloc(6, GFP_ATOMIC); + if (!mac) + return -ENOMEM; + + /* Try to read the mac address */ + ret = __az6007_read(udev, AZ6007_READ_DATA, 6, 0, mac, 6); + if (ret == 6) + *cold = 0; + else + *cold = 1; + + kfree(mac); + + if (*cold) { + __az6007_write(udev, 0x09, 1, 0, NULL, 0); + __az6007_write(udev, 0x00, 0, 0, NULL, 0); + __az6007_write(udev, 0x00, 0, 0, NULL, 0); + } + + deb_info("Device is on %s state\n", *cold ? "warm" : "cold"); + return 0; +} + +static struct dvb_usb_device_properties az6007_properties; + +static void az6007_usb_disconnect(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + az6007_ci_uninit(d); + dvb_usb_device_exit(intf); +} + +static int az6007_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf, &az6007_properties, + THIS_MODULE, NULL, adapter_nr); +} + +static struct usb_device_id az6007_usb_table[] = { + {USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_6007)}, + {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7)}, + {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7_2)}, + {0}, +}; + +MODULE_DEVICE_TABLE(usb, az6007_usb_table); + +static struct dvb_usb_device_properties az6007_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-terratec-h7-az6007.fw", + .no_reconnect = 1, + .size_of_priv = sizeof(struct az6007_device_state), + .identify_state = az6007_identify_state, + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = az6007_streaming_ctrl, + .tuner_attach = az6007_tuner_attach, + .frontend_attach = az6007_frontend_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 10, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + } } + } }, + .power_ctrl = az6007_power_ctrl, + .read_mac_address = az6007_read_mac_addr, + + .rc.core = { + .rc_interval = 400, + .rc_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS, + .module_name = "az6007", + .rc_query = az6007_rc_query, + .allowed_protos = RC_TYPE_NEC, + }, + .i2c_algo = &az6007_i2c_algo, + + .num_device_descs = 2, + .devices = { + { .name = "AzureWave DTV StarBox DVB-T/C USB2.0 (az6007)", + .cold_ids = { &az6007_usb_table[0], NULL }, + .warm_ids = { NULL }, + }, + { .name = "TerraTec DTV StarBox DVB-T/C USB2.0 (az6007)", + .cold_ids = { &az6007_usb_table[1], &az6007_usb_table[2], NULL }, + .warm_ids = { NULL }, + }, + { NULL }, + } +}; + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver az6007_usb_driver = { + .name = "dvb_usb_az6007", + .probe = az6007_usb_probe, + .disconnect = az6007_usb_disconnect, + .id_table = az6007_usb_table, +}; + +/* module stuff */ +static int __init az6007_usb_module_init(void) +{ + int result; + deb_info("az6007 usb module init\n"); + + result = usb_register(&az6007_usb_driver); + if (result) { + err("usb_register failed. (%d)", result); + return result; + } + + return 0; +} + +static void __exit az6007_usb_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + deb_info("az6007 usb module exit\n"); + usb_deregister(&az6007_usb_driver); +} + +module_init(az6007_usb_module_init); +module_exit(az6007_usb_module_exit); + +MODULE_AUTHOR("Henry Wang <Henry.wang@AzureWave.com>"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); +MODULE_DESCRIPTION("Driver for AzureWave 6007 DVB-C/T USB2.0 and clones"); +MODULE_VERSION("1.1"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c index 070e82aa53f5..02290c60f72f 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -677,11 +677,9 @@ static void dib0700_rc_urb_completion(struct urb *purb) u8 toggle; deb_info("%s()\n", __func__); - if (d == NULL) - return; - if (d->rc_dev == NULL) { /* This will occur if disable_rc_polling=1 */ + kfree(purb->transfer_buffer); usb_free_urb(purb); return; } @@ -690,6 +688,7 @@ static void dib0700_rc_urb_completion(struct urb *purb) if (purb->status < 0) { deb_info("discontinuing polling\n"); + kfree(purb->transfer_buffer); usb_free_urb(purb); return; } @@ -784,8 +783,11 @@ int dib0700_rc_setup(struct dvb_usb_device *d) dib0700_rc_urb_completion, d); ret = usb_submit_urb(purb, GFP_ATOMIC); - if (ret) + if (ret) { err("rc submit urb failed\n"); + kfree(purb->transfer_buffer); + usb_free_urb(purb); + } return ret; } diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index d390ddaa5a53..397d8f232731 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -51,6 +51,7 @@ #define USB_VID_PINNACLE 0x2304 #define USB_VID_PCTV 0x2013 #define USB_VID_PIXELVIEW 0x1554 +#define USB_VID_REALTEK 0x0bda #define USB_VID_TECHNOTREND 0x0b48 #define USB_VID_TERRATEC 0x0ccd #define USB_VID_TELESTAR 0x10b9 @@ -80,6 +81,7 @@ #define USB_PID_ANSONIC_DVBT_USB 0x6000 #define USB_PID_ANYSEE 0x861f #define USB_PID_AZUREWAVE_AD_TU700 0x3237 +#define USB_PID_AZUREWAVE_6007 0x0ccd #define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001 #define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002 #define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800 @@ -125,6 +127,8 @@ #define USB_PID_E3C_EC168_3 0xfffb #define USB_PID_E3C_EC168_4 0x1001 #define USB_PID_E3C_EC168_5 0x1002 +#define USB_PID_FREECOM_DVBT 0x0160 +#define USB_PID_FREECOM_DVBT_2 0x0161 #define USB_PID_UNIWILL_STK7700P 0x6003 #define USB_PID_GENIUS_TVGO_DVB_T03 0x4012 #define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 @@ -226,6 +230,8 @@ #define USB_PID_TERRATEC_CINERGY_T_EXPRESS 0x0062 #define USB_PID_TERRATEC_CINERGY_T_XXS 0x0078 #define USB_PID_TERRATEC_CINERGY_T_XXS_2 0x00ab +#define USB_PID_TERRATEC_H7 0x10b4 +#define USB_PID_TERRATEC_H7_2 0x10a3 #define USB_PID_TERRATEC_T3 0x10a0 #define USB_PID_TERRATEC_T5 0x10a1 #define USB_PID_PINNACLE_EXPRESSCARD_320CX 0x022e @@ -249,6 +255,8 @@ #define USB_PID_PCTV_400E 0x020f #define USB_PID_PCTV_450E 0x0222 #define USB_PID_PCTV_452E 0x021f +#define USB_PID_REALTEK_RTL2831U 0x2831 +#define USB_PID_REALTEK_RTL2832U 0x2832 #define USB_PID_TECHNOTREND_CONNECT_S2_3600 0x3007 #define USB_PID_TECHNOTREND_CONNECT_S2_3650_CI 0x300a #define USB_PID_NEBULA_DIGITV 0x0201 diff --git a/drivers/media/dvb/dvb-usb/it913x.c b/drivers/media/dvb/dvb-usb/it913x.c index 9f01cd7a6e3f..3b7b102f20ae 100644 --- a/drivers/media/dvb/dvb-usb/it913x.c +++ b/drivers/media/dvb/dvb-usb/it913x.c @@ -64,6 +64,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); struct it913x_state { u8 id; struct ite_config it913x_config; + u8 pid_filter_onoff; }; struct ite_config it913x_config; @@ -259,15 +260,16 @@ static u32 it913x_query(struct usb_device *udev, u8 pro) static int it913x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) { + struct it913x_state *st = adap->dev->priv; struct usb_device *udev = adap->dev->udev; int ret; u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD; - if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0) - return -EAGAIN; + mutex_lock(&adap->dev->i2c_mutex); + deb_info(1, "PID_C (%02x)", onoff); - ret = it913x_wr_reg(udev, pro, PID_EN, onoff); + ret = it913x_wr_reg(udev, pro, PID_EN, st->pid_filter_onoff); mutex_unlock(&adap->dev->i2c_mutex); return ret; @@ -276,12 +278,13 @@ static int it913x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) static int it913x_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, int onoff) { + struct it913x_state *st = adap->dev->priv; struct usb_device *udev = adap->dev->udev; int ret; u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD; - if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0) - return -EAGAIN; + mutex_lock(&adap->dev->i2c_mutex); + deb_info(1, "PID_F (%02x)", onoff); ret = it913x_wr_reg(udev, pro, PID_LSB, (u8)(pid & 0xff)); @@ -292,6 +295,13 @@ static int it913x_pid_filter(struct dvb_usb_adapter *adap, ret |= it913x_wr_reg(udev, pro, PID_INX, (u8)(index & 0x1f)); + if (udev->speed == USB_SPEED_HIGH && pid == 0x2000) { + ret |= it913x_wr_reg(udev, pro, PID_EN, !onoff); + st->pid_filter_onoff = !onoff; + } else + st->pid_filter_onoff = + adap->fe_adap[adap->active_fe].pid_filtering; + mutex_unlock(&adap->dev->i2c_mutex); return 0; } @@ -316,8 +326,8 @@ static int it913x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int ret; u32 reg; u8 pro; - if (mutex_lock_interruptible(&d->i2c_mutex) < 0) - return -EAGAIN; + + mutex_lock(&d->i2c_mutex); debug_data_snipet(1, "Message out", msg[0].buf); deb_info(2, "num of messages %d address %02x", num, msg[0].addr); @@ -358,8 +368,7 @@ static int it913x_rc_query(struct dvb_usb_device *d) int ret; u32 key; /* Avoid conflict with frontends*/ - if (mutex_lock_interruptible(&d->i2c_mutex) < 0) - return -EAGAIN; + mutex_lock(&d->i2c_mutex); ret = it913x_io(d->udev, READ_LONG, PRO_LINK, CMD_IR_GET, 0, 0, &ibuf[0], sizeof(ibuf)); @@ -388,19 +397,12 @@ static int ite_firmware_select(struct usb_device *udev, { int sw; /* auto switch */ - if (le16_to_cpu(udev->descriptor.idProduct) == - USB_PID_ITETECH_IT9135) - sw = IT9135_V1_FW; - else if (le16_to_cpu(udev->descriptor.idProduct) == - USB_PID_ITETECH_IT9135_9005) + if (le16_to_cpu(udev->descriptor.idVendor) == USB_VID_KWORLD_2) + sw = IT9137_FW; + else if (it913x_config.chip_ver == 1) sw = IT9135_V1_FW; - else if (le16_to_cpu(udev->descriptor.idProduct) == - USB_PID_ITETECH_IT9135_9006) { + else sw = IT9135_V2_FW; - if (it913x_config.tuner_id_0 == 0) - it913x_config.tuner_id_0 = IT9135_60; - } else - sw = IT9137_FW; /* force switch */ if (dvb_usb_it913x_firmware != IT9135_AUTO) @@ -410,41 +412,103 @@ static int ite_firmware_select(struct usb_device *udev, case IT9135_V1_FW: it913x_config.firmware_ver = 1; it913x_config.adc_x2 = 1; + it913x_config.read_slevel = false; props->firmware = fw_it9135_v1; break; case IT9135_V2_FW: it913x_config.firmware_ver = 1; it913x_config.adc_x2 = 1; + it913x_config.read_slevel = false; props->firmware = fw_it9135_v2; + switch (it913x_config.tuner_id_0) { + case IT9135_61: + case IT9135_62: + break; + default: + info("Unknown tuner ID applying default 0x60"); + case IT9135_60: + it913x_config.tuner_id_0 = IT9135_60; + } break; case IT9137_FW: default: it913x_config.firmware_ver = 0; it913x_config.adc_x2 = 0; + it913x_config.read_slevel = true; props->firmware = fw_it9137; } return 0; } +static void it913x_select_remote(struct usb_device *udev, + struct dvb_usb_device_properties *props) +{ + switch (le16_to_cpu(udev->descriptor.idProduct)) { + case USB_PID_ITETECH_IT9135_9005: + props->rc.core.rc_codes = RC_MAP_IT913X_V2; + return; + default: + props->rc.core.rc_codes = RC_MAP_IT913X_V1; + } + return; +} + #define TS_MPEG_PKT_SIZE 188 #define EP_LOW 21 #define TS_BUFFER_SIZE_PID (EP_LOW*TS_MPEG_PKT_SIZE) #define EP_HIGH 348 #define TS_BUFFER_SIZE_MAX (EP_HIGH*TS_MPEG_PKT_SIZE) -static int it913x_identify_state(struct usb_device *udev, - struct dvb_usb_device_properties *props, - struct dvb_usb_device_description **desc, - int *cold) +static int it913x_select_config(struct usb_device *udev, + struct dvb_usb_device_properties *props) { - int ret = 0, firm_no; - u8 reg, remote; + int ret = 0, reg; + bool proprietary_ir = false; - firm_no = it913x_return_status(udev); + if (it913x_config.chip_ver == 0x02 + && it913x_config.chip_type == 0x9135) + reg = it913x_read_reg(udev, 0x461d); + else + reg = it913x_read_reg(udev, 0x461b); - /* checnk for dual mode */ - it913x_config.dual_mode = it913x_read_reg(udev, 0x49c5); + if (reg < 0) + return reg; + + if (reg == 0) { + it913x_config.dual_mode = 0; + it913x_config.tuner_id_0 = IT9135_38; + proprietary_ir = true; + } else { + /* TS mode */ + reg = it913x_read_reg(udev, 0x49c5); + if (reg < 0) + return reg; + it913x_config.dual_mode = reg; + + /* IR mode type */ + reg = it913x_read_reg(udev, 0x49ac); + if (reg < 0) + return reg; + if (reg == 5) { + info("Remote propriety (raw) mode"); + proprietary_ir = true; + } else if (reg == 1) { + info("Remote HID mode NOT SUPPORTED"); + proprietary_ir = false; + props->rc.core.rc_codes = NULL; + } else + props->rc.core.rc_codes = NULL; + + /* Tuner_id */ + reg = it913x_read_reg(udev, 0x49d0); + if (reg < 0) + return reg; + it913x_config.tuner_id_0 = reg; + } + + if (proprietary_ir) + it913x_select_remote(udev, props); if (udev->speed != USB_SPEED_HIGH) { props->adapter[0].fe[0].pid_filter_count = 5; @@ -459,17 +523,6 @@ static int it913x_identify_state(struct usb_device *udev, if(props->adapter[0].fe[0].pid_filter_count == 5) props->adapter[0].fe[0].pid_filter_count = 31; - /* TODO different remotes */ - remote = it913x_read_reg(udev, 0x49ac); /* Remote */ - if (remote == 0) - props->rc.core.rc_codes = NULL; - - /* TODO at the moment tuner_id is always assigned to 0x38 */ - it913x_config.tuner_id_0 = it913x_read_reg(udev, 0x49d0); - - info("Dual mode=%x Remote=%x Tuner Type=%x", it913x_config.dual_mode - , remote, it913x_config.tuner_id_0); - /* Select Stream Buffer Size and pid filter option*/ if (pid_filter) { props->adapter[0].fe[0].stream.u.bulk.buffersize = @@ -490,8 +543,29 @@ static int it913x_identify_state(struct usb_device *udev, } else props->num_adapters = 1; + info("Dual mode=%x Tuner Type=%x", it913x_config.dual_mode, + it913x_config.tuner_id_0); + ret = ite_firmware_select(udev, props); + return ret; +} + +static int it913x_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + int ret = 0, firm_no; + u8 reg; + + firm_no = it913x_return_status(udev); + + /* Read and select config */ + ret = it913x_select_config(udev, props); + if (ret < 0) + return ret; + if (firm_no > 0) { *cold = 0; return 0; @@ -538,18 +612,22 @@ static int it913x_identify_state(struct usb_device *udev, static int it913x_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) { + struct it913x_state *st = adap->dev->priv; int ret = 0; u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD; - if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0) - return -EAGAIN; deb_info(1, "STM (%02x)", onoff); - if (!onoff) + if (!onoff) { + mutex_lock(&adap->dev->i2c_mutex); + ret = it913x_wr_reg(adap->dev->udev, pro, PID_RST, 0x1); + mutex_unlock(&adap->dev->i2c_mutex); + st->pid_filter_onoff = + adap->fe_adap[adap->active_fe].pid_filtering; - mutex_unlock(&adap->dev->i2c_mutex); + } return ret; } @@ -789,7 +867,7 @@ static struct dvb_usb_device_properties it913x_properties = { .rc_query = it913x_rc_query, .rc_interval = IT913X_POLL, .allowed_protos = RC_TYPE_NEC, - .rc_codes = RC_MAP_MSI_DIGIVOX_III, + .rc_codes = RC_MAP_IT913X_V1, }, .i2c_algo = &it913x_i2c_algo, .num_device_descs = 5, @@ -823,5 +901,5 @@ module_usb_driver(it913x_driver); MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>"); MODULE_DESCRIPTION("it913x USB 2 Driver"); -MODULE_VERSION("1.22"); +MODULE_VERSION("1.27"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c index 291f6b110399..5dde06d066ff 100644 --- a/drivers/media/dvb/dvb-usb/lmedm04.c +++ b/drivers/media/dvb/dvb-usb/lmedm04.c @@ -77,6 +77,7 @@ #include "stv0299.h" #include "dvb-pll.h" #include "z0194a.h" +#include "m88rs2000.h" @@ -104,7 +105,7 @@ MODULE_PARM_DESC(firmware, "set default firmware 0=Sharp7395 1=LG"); static int pid_filter; module_param_named(pid, pid_filter, int, 0644); -MODULE_PARM_DESC(pid, "set default 0=on 1=off"); +MODULE_PARM_DESC(pid, "set default 0=default 1=off 2=on"); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); @@ -113,6 +114,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); #define TUNER_LG 0x1 #define TUNER_S7395 0x2 #define TUNER_S0194 0x3 +#define TUNER_RS2000 0x4 struct lme2510_state { u8 id; @@ -121,6 +123,8 @@ struct lme2510_state { u8 signal_level; u8 signal_sn; u8 time_key; + u8 last_key; + u8 key_timeout; u8 i2c_talk_onoff; u8 i2c_gate; u8 i2c_tuner_gate_w; @@ -128,6 +132,7 @@ struct lme2510_state { u8 i2c_tuner_addr; u8 stream_on; u8 pid_size; + u8 pid_off; void *buffer; struct urb *lme_urb; void *usb_buffer; @@ -178,14 +183,8 @@ static int lme2510_usb_talk(struct dvb_usb_device *d, /* the read/write capped at 64 */ memcpy(buff, wbuf, (wlen < 64) ? wlen : 64); - ret |= usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, 0x01)); - ret |= lme2510_bulk_write(d->udev, buff, wlen , 0x01); - msleep(10); - - ret |= usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x01)); - ret |= lme2510_bulk_read(d->udev, buff, (rlen < 64) ? rlen : 64 , 0x01); @@ -199,9 +198,14 @@ static int lme2510_usb_talk(struct dvb_usb_device *d, static int lme2510_stream_restart(struct dvb_usb_device *d) { - static u8 stream_on[] = LME_ST_ON_W; + struct lme2510_state *st = d->priv; + u8 all_pids[] = LME_ALL_PIDS; + u8 stream_on[] = LME_ST_ON_W; int ret; - u8 rbuff[10]; + u8 rbuff[1]; + if (st->pid_off) + ret = lme2510_usb_talk(d, all_pids, sizeof(all_pids), + rbuff, sizeof(rbuff)); /*Restart Stream Command*/ ret = lme2510_usb_talk(d, stream_on, sizeof(stream_on), rbuff, sizeof(rbuff)); @@ -308,6 +312,14 @@ static void lme2510_int_response(struct urb *lme_urb) ((ibuf[2] & 0x01) << 0x03); } break; + case TUNER_RS2000: + if (ibuf[2] > 0) + st->signal_lock = 0xff; + else + st->signal_lock = 0xf0; + st->signal_level = ibuf[4]; + st->signal_sn = ibuf[5]; + st->time_key = ibuf[7]; default: break; } @@ -359,19 +371,20 @@ static int lme2510_int_read(struct dvb_usb_adapter *adap) static int lme2510_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) { struct lme2510_state *st = adap->dev->priv; - static u8 clear_pid_reg[] = LME_CLEAR_PID; + static u8 clear_pid_reg[] = LME_ALL_PIDS; static u8 rbuf[1]; int ret; deb_info(1, "PID Clearing Filter"); - ret = mutex_lock_interruptible(&adap->dev->i2c_mutex); - if (ret < 0) - return -EAGAIN; + mutex_lock(&adap->dev->i2c_mutex); - if (!onoff) + if (!onoff) { ret |= lme2510_usb_talk(adap->dev, clear_pid_reg, sizeof(clear_pid_reg), rbuf, sizeof(rbuf)); + st->pid_off = true; + } else + st->pid_off = false; st->pid_size = 0; @@ -389,11 +402,9 @@ static int lme2510_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, pid, index, onoff); if (onoff) { - ret = mutex_lock_interruptible(&adap->dev->i2c_mutex); - if (ret < 0) - return -EAGAIN; - ret |= lme2510_enable_pid(adap->dev, index, pid); - mutex_unlock(&adap->dev->i2c_mutex); + mutex_lock(&adap->dev->i2c_mutex); + ret |= lme2510_enable_pid(adap->dev, index, pid); + mutex_unlock(&adap->dev->i2c_mutex); } @@ -425,9 +436,6 @@ static int lme2510_msg(struct dvb_usb_device *d, int ret = 0; struct lme2510_state *st = d->priv; - if (mutex_lock_interruptible(&d->i2c_mutex) < 0) - return -EAGAIN; - if (st->i2c_talk_onoff == 1) { ret = lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); @@ -456,8 +464,6 @@ static int lme2510_msg(struct dvb_usb_device *d, st->i2c_talk_onoff = 0; } } - if ((wbuf[3] != 0x6) & (wbuf[3] != 0x5)) - msleep(5); } break; case TUNER_S0194: @@ -472,10 +478,12 @@ static int lme2510_msg(struct dvb_usb_device *d, } } break; + case TUNER_RS2000: default: break; } } else { + /* TODO rewrite this section */ switch (st->tuner_config) { case TUNER_LG: switch (wbuf[3]) { @@ -559,6 +567,24 @@ static int lme2510_msg(struct dvb_usb_device *d, break; } break; + case TUNER_RS2000: + switch (wbuf[3]) { + case 0x8c: + rbuf[0] = 0x55; + rbuf[1] = 0xff; + if (st->last_key == st->time_key) { + st->key_timeout++; + if (st->key_timeout > 5) + rbuf[1] = 0; + } else + st->key_timeout = 0; + st->last_key = st->time_key; + break; + default: + lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); + st->i2c_talk_onoff = 1; + break; + } default: break; } @@ -568,8 +594,6 @@ static int lme2510_msg(struct dvb_usb_device *d, } - mutex_unlock(&d->i2c_mutex); - return ret; } @@ -584,6 +608,8 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], u16 len; u8 gate = st->i2c_gate; + mutex_lock(&d->i2c_mutex); + if (gate == 0) gate = 5; @@ -622,6 +648,7 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], if (lme2510_msg(d, obuf, len, ibuf, 64) < 0) { deb_info(1, "i2c transfer failed."); + mutex_unlock(&d->i2c_mutex); return -EAGAIN; } @@ -634,6 +661,8 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], } } } + + mutex_unlock(&d->i2c_mutex); return i; } @@ -653,7 +682,7 @@ static int lme2510_identify_state(struct usb_device *udev, struct dvb_usb_device_description **desc, int *cold) { - if (pid_filter > 0) + if (pid_filter != 2) props->adapter[0].fe[0].caps &= ~DVB_USB_ADAP_NEED_PID_FILTERING; *cold = 0; @@ -663,7 +692,7 @@ static int lme2510_identify_state(struct usb_device *udev, static int lme2510_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) { struct lme2510_state *st = adap->dev->priv; - static u8 clear_reg_3[] = LME_CLEAR_PID; + static u8 clear_reg_3[] = LME_ALL_PIDS; static u8 rbuf[1]; int ret = 0, rlen = sizeof(rbuf); @@ -675,8 +704,7 @@ static int lme2510_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) else { deb_info(1, "STM Steam Off"); /* mutex is here only to avoid collision with I2C */ - if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0) - return -EAGAIN; + mutex_lock(&adap->dev->i2c_mutex); ret = lme2510_usb_talk(adap->dev, clear_reg_3, sizeof(clear_reg_3), rbuf, rlen); @@ -781,16 +809,18 @@ static int lme_firmware_switch(struct usb_device *udev, int cold) const char fw_c_s7395[] = "dvb-usb-lme2510c-s7395.fw"; const char fw_c_lg[] = "dvb-usb-lme2510c-lg.fw"; const char fw_c_s0194[] = "dvb-usb-lme2510c-s0194.fw"; + const char fw_c_rs2000[] = "dvb-usb-lme2510c-rs2000.fw"; const char fw_lg[] = "dvb-usb-lme2510-lg.fw"; const char fw_s0194[] = "dvb-usb-lme2510-s0194.fw"; const char *fw_lme; - int ret, cold_fw; + int ret = 0, cold_fw; cold = (cold > 0) ? (cold & 1) : 0; cold_fw = !cold; - if (le16_to_cpu(udev->descriptor.idProduct) == 0x1122) { + switch (le16_to_cpu(udev->descriptor.idProduct)) { + case 0x1122: switch (dvb_usb_lme2510_firmware) { default: dvb_usb_lme2510_firmware = TUNER_S0194; @@ -813,7 +843,8 @@ static int lme_firmware_switch(struct usb_device *udev, int cold) cold_fw = 0; break; } - } else { + break; + case 0x1120: switch (dvb_usb_lme2510_firmware) { default: dvb_usb_lme2510_firmware = TUNER_S7395; @@ -842,8 +873,17 @@ static int lme_firmware_switch(struct usb_device *udev, int cold) cold_fw = 0; break; } + break; + case 0x22f0: + fw_lme = fw_c_rs2000; + ret = request_firmware(&fw, fw_lme, &udev->dev); + dvb_usb_lme2510_firmware = TUNER_RS2000; + break; + default: + fw_lme = fw_c_s7395; } + if (cold_fw) { info("FRM Loading %s file", fw_lme); ret = lme2510_download_firmware(udev, fw); @@ -906,6 +946,29 @@ static struct stv0299_config sharp_z0194_config = { .set_symbol_rate = sharp_z0194a_set_symbol_rate, }; +static int dm04_rs2000_set_ts_param(struct dvb_frontend *fe, + int caller) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dvb_usb_device *d = adap->dev; + struct lme2510_state *st = d->priv; + + mutex_lock(&d->i2c_mutex); + if ((st->i2c_talk_onoff == 1) && (st->stream_on & 1)) { + st->i2c_talk_onoff = 0; + lme2510_stream_restart(d); + } + mutex_unlock(&d->i2c_mutex); + + return 0; +} + +static struct m88rs2000_config m88rs2000_config = { + .demod_addr = 0xd0, + .tuner_addr = 0xc0, + .set_ts_params = dm04_rs2000_set_ts_param, +}; + static int dm04_lme2510_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { @@ -915,8 +978,7 @@ static int dm04_lme2510_set_voltage(struct dvb_frontend *fe, static u8 rbuf[1]; int ret = 0, len = 3, rlen = 1; - if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0) - return -EAGAIN; + mutex_lock(&adap->dev->i2c_mutex); switch (voltage) { case SEC_VOLTAGE_18: @@ -937,12 +999,31 @@ static int dm04_lme2510_set_voltage(struct dvb_frontend *fe, return (ret < 0) ? -ENODEV : 0; } +static int dm04_rs2000_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct lme2510_state *st = adap->dev->priv; + + *strength = (u16)((u32)st->signal_level * 0xffff / 0x7f); + return 0; +} + +static int dm04_rs2000_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct lme2510_state *st = adap->dev->priv; + + *snr = (u16)((u32)st->signal_sn * 0xffff / 0xff); + return 0; +} + static int lme_name(struct dvb_usb_adapter *adap) { struct lme2510_state *st = adap->dev->priv; const char *desc = adap->dev->desc->name; char *fe_name[] = {"", " LG TDQY-P001F", " SHARP:BS2F7HZ7395", - " SHARP:BS2F7HZ0194"}; + " SHARP:BS2F7HZ0194", " RS2000"}; char *name = adap->fe_adap[0].fe->ops.info.name; strlcpy(name, desc, 128); @@ -958,60 +1039,82 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) int ret = 0; st->i2c_talk_onoff = 1; + switch (le16_to_cpu(adap->dev->udev->descriptor.idProduct)) { + case 0x1122: + case 0x1120: + st->i2c_gate = 4; + adap->fe_adap[0].fe = dvb_attach(tda10086_attach, + &tda10086_config, &adap->dev->i2c_adap); + if (adap->fe_adap[0].fe) { + info("TUN Found Frontend TDA10086"); + st->i2c_tuner_gate_w = 4; + st->i2c_tuner_gate_r = 4; + st->i2c_tuner_addr = 0xc0; + st->tuner_config = TUNER_LG; + if (dvb_usb_lme2510_firmware != TUNER_LG) { + dvb_usb_lme2510_firmware = TUNER_LG; + ret = lme_firmware_switch(adap->dev->udev, 1); + } + break; + } - st->i2c_gate = 4; - adap->fe_adap[0].fe = dvb_attach(tda10086_attach, &tda10086_config, - &adap->dev->i2c_adap); - - if (adap->fe_adap[0].fe) { - info("TUN Found Frontend TDA10086"); - st->i2c_tuner_gate_w = 4; - st->i2c_tuner_gate_r = 4; - st->i2c_tuner_addr = 0xc0; - st->tuner_config = TUNER_LG; - if (dvb_usb_lme2510_firmware != TUNER_LG) { - dvb_usb_lme2510_firmware = TUNER_LG; - ret = lme_firmware_switch(adap->dev->udev, 1); + st->i2c_gate = 4; + adap->fe_adap[0].fe = dvb_attach(stv0299_attach, + &sharp_z0194_config, &adap->dev->i2c_adap); + if (adap->fe_adap[0].fe) { + info("FE Found Stv0299"); + st->i2c_tuner_gate_w = 4; + st->i2c_tuner_gate_r = 5; + st->i2c_tuner_addr = 0xc0; + st->tuner_config = TUNER_S0194; + if (dvb_usb_lme2510_firmware != TUNER_S0194) { + dvb_usb_lme2510_firmware = TUNER_S0194; + ret = lme_firmware_switch(adap->dev->udev, 1); + } + break; } - goto end; - } - st->i2c_gate = 4; - adap->fe_adap[0].fe = dvb_attach(stv0299_attach, &sharp_z0194_config, + st->i2c_gate = 5; + adap->fe_adap[0].fe = dvb_attach(stv0288_attach, &lme_config, &adap->dev->i2c_adap); - if (adap->fe_adap[0].fe) { - info("FE Found Stv0299"); - st->i2c_tuner_gate_w = 4; - st->i2c_tuner_gate_r = 5; - st->i2c_tuner_addr = 0xc0; - st->tuner_config = TUNER_S0194; - if (dvb_usb_lme2510_firmware != TUNER_S0194) { - dvb_usb_lme2510_firmware = TUNER_S0194; - ret = lme_firmware_switch(adap->dev->udev, 1); + + if (adap->fe_adap[0].fe) { + info("FE Found Stv0288"); + st->i2c_tuner_gate_w = 4; + st->i2c_tuner_gate_r = 5; + st->i2c_tuner_addr = 0xc0; + st->tuner_config = TUNER_S7395; + if (dvb_usb_lme2510_firmware != TUNER_S7395) { + dvb_usb_lme2510_firmware = TUNER_S7395; + ret = lme_firmware_switch(adap->dev->udev, 1); + } + break; } - goto end; - } + case 0x22f0: + st->i2c_gate = 5; + adap->fe_adap[0].fe = dvb_attach(m88rs2000_attach, + &m88rs2000_config, &adap->dev->i2c_adap); - st->i2c_gate = 5; - adap->fe_adap[0].fe = dvb_attach(stv0288_attach, &lme_config, - &adap->dev->i2c_adap); - if (adap->fe_adap[0].fe) { - info("FE Found Stv0288"); - st->i2c_tuner_gate_w = 4; - st->i2c_tuner_gate_r = 5; - st->i2c_tuner_addr = 0xc0; - st->tuner_config = TUNER_S7395; - if (dvb_usb_lme2510_firmware != TUNER_S7395) { - dvb_usb_lme2510_firmware = TUNER_S7395; - ret = lme_firmware_switch(adap->dev->udev, 1); + if (adap->fe_adap[0].fe) { + info("FE Found M88RS2000"); + st->i2c_tuner_gate_w = 5; + st->i2c_tuner_gate_r = 5; + st->i2c_tuner_addr = 0xc0; + st->tuner_config = TUNER_RS2000; + adap->fe_adap[0].fe->ops.read_signal_strength = + dm04_rs2000_read_signal_strength; + adap->fe_adap[0].fe->ops.read_snr = + dm04_rs2000_read_snr; } - } else { - info("DM04 Not Supported"); - return -ENODEV; + break; } + if (adap->fe_adap[0].fe == NULL) { + info("DM04/QQBOX Not Powered up or not Supported"); + return -ENODEV; + } -end: if (ret) { + if (ret) { if (adap->fe_adap[0].fe) { dvb_frontend_detach(adap->fe_adap[0].fe); adap->fe_adap[0].fe = NULL; @@ -1028,7 +1131,7 @@ end: if (ret) { static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap) { struct lme2510_state *st = adap->dev->priv; - char *tun_msg[] = {"", "TDA8263", "IX2505V", "DVB_PLL_OPERA"}; + char *tun_msg[] = {"", "TDA8263", "IX2505V", "DVB_PLL_OPERA", "RS2000"}; int ret = 0; switch (st->tuner_config) { @@ -1047,6 +1150,9 @@ static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap) &adap->dev->i2c_adap, DVB_PLL_OPERA1)) ret = st->tuner_config; break; + case TUNER_RS2000: + ret = st->tuner_config; + break; default: break; } @@ -1075,10 +1181,9 @@ static int lme2510_powerup(struct dvb_usb_device *d, int onoff) static u8 lnb_on[] = LNB_ON; static u8 lnb_off[] = LNB_OFF; static u8 rbuf[1]; - int ret, len = 3, rlen = 1; + int ret = 0, len = 3, rlen = 1; - if (mutex_lock_interruptible(&d->i2c_mutex) < 0) - return -EAGAIN; + mutex_lock(&d->i2c_mutex); if (onoff) ret = lme2510_usb_talk(d, lnb_on, len, rbuf, rlen); @@ -1136,6 +1241,7 @@ static int lme2510_probe(struct usb_interface *intf, static struct usb_device_id lme2510_table[] = { { USB_DEVICE(0x3344, 0x1122) }, /* LME2510 */ { USB_DEVICE(0x3344, 0x1120) }, /* LME2510C */ + { USB_DEVICE(0x3344, 0x22f0) }, /* LME2510C RS2000 */ {} /* Terminating entry */ }; @@ -1153,7 +1259,7 @@ static struct dvb_usb_device_properties lme2510_properties = { DVB_USB_ADAP_NEED_PID_FILTERING| DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .streaming_ctrl = lme2510_streaming_ctrl, - .pid_filter_count = 15, + .pid_filter_count = 32, .pid_filter = lme2510_pid_filter, .pid_filter_ctrl = lme2510_pid_filter_ctrl, .frontend_attach = dm04_lme2510_frontend_attach, @@ -1204,7 +1310,7 @@ static struct dvb_usb_device_properties lme2510c_properties = { DVB_USB_ADAP_NEED_PID_FILTERING| DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .streaming_ctrl = lme2510_streaming_ctrl, - .pid_filter_count = 15, + .pid_filter_count = 32, .pid_filter = lme2510_pid_filter, .pid_filter_ctrl = lme2510_pid_filter_ctrl, .frontend_attach = dm04_lme2510_frontend_attach, @@ -1234,11 +1340,14 @@ static struct dvb_usb_device_properties lme2510c_properties = { .identify_state = lme2510_identify_state, .i2c_algo = &lme2510_i2c_algo, .generic_bulk_ctrl_endpoint = 0, - .num_device_descs = 1, + .num_device_descs = 2, .devices = { { "DM04_LME2510C_DVB-S", { &lme2510_table[1], NULL }, }, + { "DM04_LME2510C_DVB-S RS2000", + { &lme2510_table[2], NULL }, + }, } }; @@ -1295,5 +1404,5 @@ module_usb_driver(lme2510_driver); MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>"); MODULE_DESCRIPTION("LME2510(C) DVB-S USB2.0"); -MODULE_VERSION("1.91"); +MODULE_VERSION("1.99"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/lmedm04.h b/drivers/media/dvb/dvb-usb/lmedm04.h index ab21e2ef53fa..e9c207205c2f 100644 --- a/drivers/media/dvb/dvb-usb/lmedm04.h +++ b/drivers/media/dvb/dvb-usb/lmedm04.h @@ -41,6 +41,7 @@ #define LME_ST_ON_W {0x06, 0x00} #define LME_CLEAR_PID {0x03, 0x02, 0x20, 0xa0} #define LME_ZERO_PID {0x03, 0x06, 0x00, 0x00, 0x01, 0x00, 0x20, 0x9c} +#define LME_ALL_PIDS {0x03, 0x06, 0x00, 0xff, 0x01, 0x1f, 0x20, 0x81} /* LNB Voltage * 07 XX XX diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.c b/drivers/media/dvb/dvb-usb/mxl111sf.c index 38ef0253d3b5..81305de2fea5 100644 --- a/drivers/media/dvb/dvb-usb/mxl111sf.c +++ b/drivers/media/dvb/dvb-usb/mxl111sf.c @@ -351,15 +351,13 @@ static int mxl111sf_ep6_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) adap_state->ep6_clockphase, 0, 0); mxl_fail(ret); +#if 0 } else { ret = mxl111sf_disable_656_port(state); mxl_fail(ret); +#endif } - mxl111sf_read_reg(state, 0x12, &tmp); - tmp &= ~0x04; - mxl111sf_write_reg(state, 0x12, tmp); - return ret; } diff --git a/drivers/media/dvb/dvb-usb/rtl28xxu.c b/drivers/media/dvb/dvb-usb/rtl28xxu.c new file mode 100644 index 000000000000..8f4736a10fc8 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/rtl28xxu.c @@ -0,0 +1,982 @@ +/* + * Realtek RTL28xxU DVB USB driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "rtl28xxu.h" + +#include "rtl2830.h" + +#include "qt1010.h" +#include "mt2060.h" +#include "mxl5005s.h" + +/* debug */ +static int dvb_usb_rtl28xxu_debug; +module_param_named(debug, dvb_usb_rtl28xxu_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req) +{ + int ret; + unsigned int pipe; + u8 requesttype; + u8 *buf; + + buf = kmalloc(req->size, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto err; + } + + if (req->index & CMD_WR_FLAG) { + /* write */ + memcpy(buf, req->data, req->size); + requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); + pipe = usb_sndctrlpipe(d->udev, 0); + } else { + /* read */ + requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); + pipe = usb_rcvctrlpipe(d->udev, 0); + } + + ret = usb_control_msg(d->udev, pipe, 0, requesttype, req->value, + req->index, buf, req->size, 1000); + if (ret > 0) + ret = 0; + + deb_dump(0, requesttype, req->value, req->index, buf, req->size, + deb_xfer); + + /* read request, copy returned data to return buf */ + if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN)) + memcpy(req->data, buf, req->size); + + kfree(buf); + + if (ret) + goto err; + + return ret; +err: + deb_info("%s: failed=%d\n", __func__, ret); + return ret; +} + +static int rtl2831_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len) +{ + struct rtl28xxu_req req; + + if (reg < 0x3000) + req.index = CMD_USB_WR; + else if (reg < 0x4000) + req.index = CMD_SYS_WR; + else + req.index = CMD_IR_WR; + + req.value = reg; + req.size = len; + req.data = val; + + return rtl28xxu_ctrl_msg(d, &req); +} + +static int rtl2831_rd_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len) +{ + struct rtl28xxu_req req; + + if (reg < 0x3000) + req.index = CMD_USB_RD; + else if (reg < 0x4000) + req.index = CMD_SYS_RD; + else + req.index = CMD_IR_RD; + + req.value = reg; + req.size = len; + req.data = val; + + return rtl28xxu_ctrl_msg(d, &req); +} + +static int rtl2831_wr_reg(struct dvb_usb_device *d, u16 reg, u8 val) +{ + return rtl2831_wr_regs(d, reg, &val, 1); +} + +static int rtl2831_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val) +{ + return rtl2831_rd_regs(d, reg, val, 1); +} + +/* I2C */ +static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + int ret; + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct rtl28xxu_priv *priv = d->priv; + struct rtl28xxu_req req; + + /* + * It is not known which are real I2C bus xfer limits, but testing + * with RTL2831U + MT2060 gives max RD 24 and max WR 22 bytes. + * TODO: find out RTL2832U lens + */ + + /* + * I2C adapter logic looks rather complicated due to fact it handles + * three different access methods. Those methods are; + * 1) integrated demod access + * 2) old I2C access + * 3) new I2C access + * + * Used method is selected in order 1, 2, 3. Method 3 can handle all + * requests but there is two reasons why not use it always; + * 1) It is most expensive, usually two USB messages are needed + * 2) At least RTL2831U does not support it + * + * Method 3 is needed in case of I2C write+read (typical register read) + * where write is more than one byte. + */ + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + if (num == 2 && !(msg[0].flags & I2C_M_RD) && + (msg[1].flags & I2C_M_RD)) { + if (msg[0].len > 24 || msg[1].len > 24) { + /* TODO: check msg[0].len max */ + ret = -EOPNOTSUPP; + goto err_mutex_unlock; + } else if (msg[0].addr == 0x10) { + /* method 1 - integrated demod */ + req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1); + req.index = CMD_DEMOD_RD | priv->page; + req.size = msg[1].len; + req.data = &msg[1].buf[0]; + ret = rtl28xxu_ctrl_msg(d, &req); + } else if (msg[0].len < 2) { + /* method 2 - old I2C */ + req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1); + req.index = CMD_I2C_RD; + req.size = msg[1].len; + req.data = &msg[1].buf[0]; + ret = rtl28xxu_ctrl_msg(d, &req); + } else { + /* method 3 - new I2C */ + req.value = (msg[0].addr << 1); + req.index = CMD_I2C_DA_WR; + req.size = msg[0].len; + req.data = msg[0].buf; + ret = rtl28xxu_ctrl_msg(d, &req); + if (ret) + goto err_mutex_unlock; + + req.value = (msg[0].addr << 1); + req.index = CMD_I2C_DA_RD; + req.size = msg[1].len; + req.data = msg[1].buf; + ret = rtl28xxu_ctrl_msg(d, &req); + } + } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) { + if (msg[0].len > 22) { + /* TODO: check msg[0].len max */ + ret = -EOPNOTSUPP; + goto err_mutex_unlock; + } else if (msg[0].addr == 0x10) { + /* method 1 - integrated demod */ + if (msg[0].buf[0] == 0x00) { + /* save demod page for later demod access */ + priv->page = msg[0].buf[1]; + ret = 0; + } else { + req.value = (msg[0].buf[0] << 8) | + (msg[0].addr << 1); + req.index = CMD_DEMOD_WR | priv->page; + req.size = msg[0].len-1; + req.data = &msg[0].buf[1]; + ret = rtl28xxu_ctrl_msg(d, &req); + } + } else if (msg[0].len < 23) { + /* method 2 - old I2C */ + req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1); + req.index = CMD_I2C_WR; + req.size = msg[0].len-1; + req.data = &msg[0].buf[1]; + ret = rtl28xxu_ctrl_msg(d, &req); + } else { + /* method 3 - new I2C */ + req.value = (msg[0].addr << 1); + req.index = CMD_I2C_DA_WR; + req.size = msg[0].len; + req.data = msg[0].buf; + ret = rtl28xxu_ctrl_msg(d, &req); + } + } else { + ret = -EINVAL; + } + +err_mutex_unlock: + mutex_unlock(&d->i2c_mutex); + + return ret ? ret : num; +} + +static u32 rtl28xxu_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm rtl28xxu_i2c_algo = { + .master_xfer = rtl28xxu_i2c_xfer, + .functionality = rtl28xxu_i2c_func, +}; + +static struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = { + .i2c_addr = 0x10, /* 0x20 */ + .xtal = 28800000, + .ts_mode = 0, + .spec_inv = 1, + .if_dvbt = 36150000, + .vtop = 0x20, + .krf = 0x04, + .agc_targ_val = 0x2d, + +}; + +static struct rtl2830_config rtl28xxu_rtl2830_qt1010_config = { + .i2c_addr = 0x10, /* 0x20 */ + .xtal = 28800000, + .ts_mode = 0, + .spec_inv = 1, + .if_dvbt = 36125000, + .vtop = 0x20, + .krf = 0x04, + .agc_targ_val = 0x2d, +}; + +static struct rtl2830_config rtl28xxu_rtl2830_mxl5005s_config = { + .i2c_addr = 0x10, /* 0x20 */ + .xtal = 28800000, + .ts_mode = 0, + .spec_inv = 0, + .if_dvbt = 4570000, + .vtop = 0x3f, + .krf = 0x04, + .agc_targ_val = 0x3e, +}; + +static int rtl2831u_frontend_attach(struct dvb_usb_adapter *adap) +{ + int ret; + struct rtl28xxu_priv *priv = adap->dev->priv; + u8 buf[1]; + struct rtl2830_config *rtl2830_config; + /* open RTL2831U/RTL2830 I2C gate */ + struct rtl28xxu_req req_gate = { 0x0120, 0x0011, 0x0001, "\x08" }; + /* for MT2060 tuner probe */ + struct rtl28xxu_req req_mt2060 = { 0x00c0, CMD_I2C_RD, 1, buf }; + /* for QT1010 tuner probe */ + struct rtl28xxu_req req_qt1010 = { 0x0fc4, CMD_I2C_RD, 1, buf }; + + deb_info("%s:\n", __func__); + + /* + * RTL2831U GPIOs + * ========================================================= + * GPIO0 | tuner#0 | 0 off | 1 on | MXL5005S (?) + * GPIO2 | LED | 0 off | 1 on | + * GPIO4 | tuner#1 | 0 on | 1 off | MT2060 + */ + + /* GPIO direction */ + ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a); + if (ret) + goto err; + + /* enable as output GPIO0, GPIO2, GPIO4 */ + ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15); + if (ret) + goto err; + + /* + * Probe used tuner. We need to know used tuner before demod attach + * since there is some demod params needed to set according to tuner. + */ + + /* open demod I2C gate */ + ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate); + if (ret) + goto err; + + /* check QT1010 ID(?) register; reg=0f val=2c */ + ret = rtl28xxu_ctrl_msg(adap->dev, &req_qt1010); + if (ret == 0 && buf[0] == 0x2c) { + priv->tuner = TUNER_RTL2830_QT1010; + rtl2830_config = &rtl28xxu_rtl2830_qt1010_config; + deb_info("%s: QT1010\n", __func__); + goto found; + } else { + deb_info("%s: QT1010 probe failed=%d - %02x\n", + __func__, ret, buf[0]); + } + + /* open demod I2C gate */ + ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate); + if (ret) + goto err; + + /* check MT2060 ID register; reg=00 val=63 */ + ret = rtl28xxu_ctrl_msg(adap->dev, &req_mt2060); + if (ret == 0 && buf[0] == 0x63) { + priv->tuner = TUNER_RTL2830_MT2060; + rtl2830_config = &rtl28xxu_rtl2830_mt2060_config; + deb_info("%s: MT2060\n", __func__); + goto found; + } else { + deb_info("%s: MT2060 probe failed=%d - %02x\n", + __func__, ret, buf[0]); + } + + /* assume MXL5005S */ + ret = 0; + priv->tuner = TUNER_RTL2830_MXL5005S; + rtl2830_config = &rtl28xxu_rtl2830_mxl5005s_config; + deb_info("%s: MXL5005S\n", __func__); + goto found; + +found: + /* attach demodulator */ + adap->fe_adap[0].fe = dvb_attach(rtl2830_attach, rtl2830_config, + &adap->dev->i2c_adap); + if (adap->fe_adap[0].fe == NULL) { + ret = -ENODEV; + goto err; + } + + return ret; +err: + deb_info("%s: failed=%d\n", __func__, ret); + return ret; +} + +static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) +{ + int ret; + struct rtl28xxu_priv *priv = adap->dev->priv; + u8 buf[1]; + /* open RTL2832U/RTL2832 I2C gate */ + struct rtl28xxu_req req_gate_open = {0x0120, 0x0011, 0x0001, "\x18"}; + /* close RTL2832U/RTL2832 I2C gate */ + struct rtl28xxu_req req_gate_close = {0x0120, 0x0011, 0x0001, "\x10"}; + /* for FC2580 tuner probe */ + struct rtl28xxu_req req_fc2580 = {0x01ac, CMD_I2C_RD, 1, buf}; + + deb_info("%s:\n", __func__); + + /* GPIO direction */ + ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a); + if (ret) + goto err; + + /* enable as output GPIO0, GPIO2, GPIO4 */ + ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15); + if (ret) + goto err; + + ret = rtl2831_wr_reg(adap->dev, SYS_DEMOD_CTL, 0xe8); + if (ret) + goto err; + + /* + * Probe used tuner. We need to know used tuner before demod attach + * since there is some demod params needed to set according to tuner. + */ + + /* open demod I2C gate */ + ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_open); + if (ret) + goto err; + + /* check FC2580 ID register; reg=01 val=56 */ + ret = rtl28xxu_ctrl_msg(adap->dev, &req_fc2580); + if (ret == 0 && buf[0] == 0x56) { + priv->tuner = TUNER_RTL2832_FC2580; + deb_info("%s: FC2580\n", __func__); + goto found; + } else { + deb_info("%s: FC2580 probe failed=%d - %02x\n", + __func__, ret, buf[0]); + } + + /* close demod I2C gate */ + ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_close); + if (ret) + goto err; + + /* tuner not found */ + ret = -ENODEV; + goto err; + +found: + /* close demod I2C gate */ + ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_close); + if (ret) + goto err; + + /* attach demodulator */ + /* TODO: */ + + return ret; +err: + deb_info("%s: failed=%d\n", __func__, ret); + return ret; +} + +static struct qt1010_config rtl28xxu_qt1010_config = { + .i2c_address = 0x62, /* 0xc4 */ +}; + +static struct mt2060_config rtl28xxu_mt2060_config = { + .i2c_address = 0x60, /* 0xc0 */ + .clock_out = 0, +}; + +static struct mxl5005s_config rtl28xxu_mxl5005s_config = { + .i2c_address = 0x63, /* 0xc6 */ + .if_freq = IF_FREQ_4570000HZ, + .xtal_freq = CRYSTAL_FREQ_16000000HZ, + .agc_mode = MXL_SINGLE_AGC, + .tracking_filter = MXL_TF_C_H, + .rssi_enable = MXL_RSSI_ENABLE, + .cap_select = MXL_CAP_SEL_ENABLE, + .div_out = MXL_DIV_OUT_4, + .clock_out = MXL_CLOCK_OUT_DISABLE, + .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, + .top = MXL5005S_TOP_25P2, + .mod_mode = MXL_DIGITAL_MODE, + .if_mode = MXL_ZERO_IF, + .AgcMasterByte = 0x00, +}; + +static int rtl2831u_tuner_attach(struct dvb_usb_adapter *adap) +{ + int ret; + struct rtl28xxu_priv *priv = adap->dev->priv; + struct i2c_adapter *rtl2830_tuner_i2c; + struct dvb_frontend *fe; + + deb_info("%s:\n", __func__); + + /* use rtl2830 driver I2C adapter, for more info see rtl2830 driver */ + rtl2830_tuner_i2c = rtl2830_get_tuner_i2c_adapter(adap->fe_adap[0].fe); + + switch (priv->tuner) { + case TUNER_RTL2830_QT1010: + fe = dvb_attach(qt1010_attach, adap->fe_adap[0].fe, + rtl2830_tuner_i2c, &rtl28xxu_qt1010_config); + break; + case TUNER_RTL2830_MT2060: + fe = dvb_attach(mt2060_attach, adap->fe_adap[0].fe, + rtl2830_tuner_i2c, &rtl28xxu_mt2060_config, + 1220); + break; + case TUNER_RTL2830_MXL5005S: + fe = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, + rtl2830_tuner_i2c, &rtl28xxu_mxl5005s_config); + break; + default: + fe = NULL; + err("unknown tuner=%d", priv->tuner); + } + + if (fe == NULL) { + ret = -ENODEV; + goto err; + } + + return 0; +err: + deb_info("%s: failed=%d\n", __func__, ret); + return ret; +} + +static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) +{ + int ret; + struct rtl28xxu_priv *priv = adap->dev->priv; + struct dvb_frontend *fe; + + deb_info("%s:\n", __func__); + + switch (priv->tuner) { + case TUNER_RTL2832_FC2580: + /* TODO: */ + fe = NULL; + break; + default: + fe = NULL; + err("unknown tuner=%d", priv->tuner); + } + + if (fe == NULL) { + ret = -ENODEV; + goto err; + } + + return 0; +err: + deb_info("%s: failed=%d\n", __func__, ret); + return ret; +} + +static int rtl28xxu_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff) +{ + int ret; + u8 buf[2], gpio; + + deb_info("%s: onoff=%d\n", __func__, onoff); + + ret = rtl2831_rd_reg(adap->dev, SYS_GPIO_OUT_VAL, &gpio); + if (ret) + goto err; + + if (onoff) { + buf[0] = 0x00; + buf[1] = 0x00; + gpio |= 0x04; /* LED on */ + } else { + buf[0] = 0x10; /* stall EPA */ + buf[1] = 0x02; /* reset EPA */ + gpio &= (~0x04); /* LED off */ + } + + ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_VAL, gpio); + if (ret) + goto err; + + ret = rtl2831_wr_regs(adap->dev, USB_EPA_CTL, buf, 2); + if (ret) + goto err; + + return ret; +err: + deb_info("%s: failed=%d\n", __func__, ret); + return ret; +} + +static int rtl28xxu_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + int ret; + u8 gpio, sys0; + + deb_info("%s: onoff=%d\n", __func__, onoff); + + /* demod adc */ + ret = rtl2831_rd_reg(d, SYS_SYS0, &sys0); + if (ret) + goto err; + + /* tuner power, read GPIOs */ + ret = rtl2831_rd_reg(d, SYS_GPIO_OUT_VAL, &gpio); + if (ret) + goto err; + + deb_info("%s: RD SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, sys0, gpio); + + if (onoff) { + gpio |= 0x01; /* GPIO0 = 1 */ + gpio &= (~0x10); /* GPIO4 = 0 */ + sys0 = sys0 & 0x0f; + sys0 |= 0xe0; + } else { + gpio &= (~0x01); /* GPIO0 = 0 */ + gpio |= 0x10; /* GPIO4 = 1 */ + sys0 = sys0 & (~0xc0); + } + + deb_info("%s: WR SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, sys0, gpio); + + /* demod adc */ + ret = rtl2831_wr_reg(d, SYS_SYS0, sys0); + if (ret) + goto err; + + /* tuner power, write GPIOs */ + ret = rtl2831_wr_reg(d, SYS_GPIO_OUT_VAL, gpio); + if (ret) + goto err; + + return ret; +err: + deb_info("%s: failed=%d\n", __func__, ret); + return ret; +} + +static int rtl2831u_rc_query(struct dvb_usb_device *d) +{ + int ret, i; + struct rtl28xxu_priv *priv = d->priv; + u8 buf[5]; + u32 rc_code; + struct rtl28xxu_reg_val rc_nec_tab[] = { + { 0x3033, 0x80 }, + { 0x3020, 0x43 }, + { 0x3021, 0x16 }, + { 0x3022, 0x16 }, + { 0x3023, 0x5a }, + { 0x3024, 0x2d }, + { 0x3025, 0x16 }, + { 0x3026, 0x01 }, + { 0x3028, 0xb0 }, + { 0x3029, 0x04 }, + { 0x302c, 0x88 }, + { 0x302e, 0x13 }, + { 0x3030, 0xdf }, + { 0x3031, 0x05 }, + }; + + /* init remote controller */ + if (!priv->rc_active) { + for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) { + ret = rtl2831_wr_reg(d, rc_nec_tab[i].reg, + rc_nec_tab[i].val); + if (ret) + goto err; + } + priv->rc_active = true; + } + + ret = rtl2831_rd_regs(d, SYS_IRRC_RP, buf, 5); + if (ret) + goto err; + + if (buf[4] & 0x01) { + if (buf[2] == (u8) ~buf[3]) { + if (buf[0] == (u8) ~buf[1]) { + /* NEC standard (16 bit) */ + rc_code = buf[0] << 8 | buf[2]; + } else { + /* NEC extended (24 bit) */ + rc_code = buf[0] << 16 | + buf[1] << 8 | buf[2]; + } + } else { + /* NEC full (32 bit) */ + rc_code = buf[0] << 24 | buf[1] << 16 | + buf[2] << 8 | buf[3]; + } + + rc_keydown(d->rc_dev, rc_code, 0); + + ret = rtl2831_wr_reg(d, SYS_IRRC_SR, 1); + if (ret) + goto err; + + /* repeated intentionally to avoid extra keypress */ + ret = rtl2831_wr_reg(d, SYS_IRRC_SR, 1); + if (ret) + goto err; + } + + return ret; +err: + deb_info("%s: failed=%d\n", __func__, ret); + return ret; +} + +static int rtl2832u_rc_query(struct dvb_usb_device *d) +{ + int ret, i; + struct rtl28xxu_priv *priv = d->priv; + u8 buf[128]; + int len; + struct rtl28xxu_reg_val rc_nec_tab[] = { + { IR_RX_CTRL, 0x20 }, + { IR_RX_BUF_CTRL, 0x80 }, + { IR_RX_IF, 0xff }, + { IR_RX_IE, 0xff }, + { IR_MAX_DURATION0, 0xd0 }, + { IR_MAX_DURATION1, 0x07 }, + { IR_IDLE_LEN0, 0xc0 }, + { IR_IDLE_LEN1, 0x00 }, + { IR_GLITCH_LEN, 0x03 }, + { IR_RX_CLK, 0x09 }, + { IR_RX_CFG, 0x1c }, + { IR_MAX_H_TOL_LEN, 0x1e }, + { IR_MAX_L_TOL_LEN, 0x1e }, + { IR_RX_CTRL, 0x80 }, + }; + + /* init remote controller */ + if (!priv->rc_active) { + for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) { + ret = rtl2831_wr_reg(d, rc_nec_tab[i].reg, + rc_nec_tab[i].val); + if (ret) + goto err; + } + priv->rc_active = true; + } + + ret = rtl2831_rd_reg(d, IR_RX_IF, &buf[0]); + if (ret) + goto err; + + if (buf[0] != 0x83) + goto exit; + + ret = rtl2831_rd_reg(d, IR_RX_BC, &buf[0]); + if (ret) + goto err; + + len = buf[0]; + ret = rtl2831_rd_regs(d, IR_RX_BUF, buf, len); + + /* TODO: pass raw IR to Kernel IR decoder */ + + ret = rtl2831_wr_reg(d, IR_RX_IF, 0x03); + ret = rtl2831_wr_reg(d, IR_RX_BUF_CTRL, 0x80); + ret = rtl2831_wr_reg(d, IR_RX_CTRL, 0x80); + +exit: + return ret; +err: + deb_info("%s: failed=%d\n", __func__, ret); + return ret; +} + +enum rtl28xxu_usb_table_entry { + RTL2831U_0BDA_2831, + RTL2831U_14AA_0160, + RTL2831U_14AA_0161, +}; + +static struct usb_device_id rtl28xxu_table[] = { + /* RTL2831U */ + [RTL2831U_0BDA_2831] = { + USB_DEVICE(USB_VID_REALTEK, USB_PID_REALTEK_RTL2831U)}, + [RTL2831U_14AA_0160] = { + USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT)}, + [RTL2831U_14AA_0161] = { + USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT_2)}, + + /* RTL2832U */ + {} /* terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, rtl28xxu_table); + +static struct dvb_usb_device_properties rtl28xxu_properties[] = { + { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .no_reconnect = 1, + + .size_of_priv = sizeof(struct rtl28xxu_priv), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = { + { + .frontend_attach = rtl2831u_frontend_attach, + .tuner_attach = rtl2831u_tuner_attach, + .streaming_ctrl = rtl28xxu_streaming_ctrl, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x81, + .u = { + .bulk = { + .buffersize = 8*512, + } + } + } + } + } + } + }, + + .power_ctrl = rtl28xxu_power_ctrl, + + .rc.core = { + .protocol = RC_TYPE_NEC, + .module_name = "rtl28xxu", + .rc_query = rtl2831u_rc_query, + .rc_interval = 400, + .allowed_protos = RC_TYPE_NEC, + .rc_codes = RC_MAP_EMPTY, + }, + + .i2c_algo = &rtl28xxu_i2c_algo, + + .num_device_descs = 2, + .devices = { + { + .name = "Realtek RTL2831U reference design", + .warm_ids = { + &rtl28xxu_table[RTL2831U_0BDA_2831], + }, + }, + { + .name = "Freecom USB2.0 DVB-T", + .warm_ids = { + &rtl28xxu_table[RTL2831U_14AA_0160], + &rtl28xxu_table[RTL2831U_14AA_0161], + }, + }, + } + }, + { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .no_reconnect = 1, + + .size_of_priv = sizeof(struct rtl28xxu_priv), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = { + { + .frontend_attach = rtl2832u_frontend_attach, + .tuner_attach = rtl2832u_tuner_attach, + .streaming_ctrl = rtl28xxu_streaming_ctrl, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x81, + .u = { + .bulk = { + .buffersize = 8*512, + } + } + } + } + } + } + }, + + .power_ctrl = rtl28xxu_power_ctrl, + + .rc.core = { + .protocol = RC_TYPE_NEC, + .module_name = "rtl28xxu", + .rc_query = rtl2832u_rc_query, + .rc_interval = 400, + .allowed_protos = RC_TYPE_NEC, + .rc_codes = RC_MAP_EMPTY, + }, + + .i2c_algo = &rtl28xxu_i2c_algo, + + .num_device_descs = 0, /* disabled as no support for RTL2832 */ + .devices = { + { + .name = "Realtek RTL2832U reference design", + }, + } + }, + +}; + +static int rtl28xxu_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int ret, i; + int properties_count = ARRAY_SIZE(rtl28xxu_properties); + struct dvb_usb_device *d; + + deb_info("%s: interface=%d\n", __func__, + intf->cur_altsetting->desc.bInterfaceNumber); + + if (intf->cur_altsetting->desc.bInterfaceNumber != 0) + return 0; + + for (i = 0; i < properties_count; i++) { + ret = dvb_usb_device_init(intf, &rtl28xxu_properties[i], + THIS_MODULE, &d, adapter_nr); + if (ret == 0 || ret != -ENODEV) + break; + } + + if (ret) + goto err; + + /* init USB endpoints */ + ret = rtl2831_wr_reg(d, USB_SYSCTL_0, 0x09); + if (ret) + goto err; + + ret = rtl2831_wr_regs(d, USB_EPA_MAXPKT, "\x00\x02\x00\x00", 4); + if (ret) + goto err; + + ret = rtl2831_wr_regs(d, USB_EPA_FIFO_CFG, "\x14\x00\x00\x00", 4); + if (ret) + goto err; + + return ret; +err: + deb_info("%s: failed=%d\n", __func__, ret); + return ret; +} + +static struct usb_driver rtl28xxu_driver = { + .name = "dvb_usb_rtl28xxu", + .probe = rtl28xxu_probe, + .disconnect = dvb_usb_device_exit, + .id_table = rtl28xxu_table, +}; + +/* module stuff */ +static int __init rtl28xxu_module_init(void) +{ + int ret; + + deb_info("%s:\n", __func__); + + ret = usb_register(&rtl28xxu_driver); + if (ret) + err("usb_register failed=%d", ret); + + return ret; +} + +static void __exit rtl28xxu_module_exit(void) +{ + deb_info("%s:\n", __func__); + + /* deregister this driver from the USB subsystem */ + usb_deregister(&rtl28xxu_driver); +} + +module_init(rtl28xxu_module_init); +module_exit(rtl28xxu_module_exit); + +MODULE_DESCRIPTION("Realtek RTL28xxU DVB USB driver"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/rtl28xxu.h b/drivers/media/dvb/dvb-usb/rtl28xxu.h new file mode 100644 index 000000000000..90f3bb4f4c0e --- /dev/null +++ b/drivers/media/dvb/dvb-usb/rtl28xxu.h @@ -0,0 +1,264 @@ +/* + * Realtek RTL28xxU DVB USB driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef RTL28XXU_H +#define RTL28XXU_H + +#define DVB_USB_LOG_PREFIX "rtl28xxu" +#include "dvb-usb.h" + +#define deb_info(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x01, args) +#define deb_rc(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x02, args) +#define deb_xfer(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x04, args) +#define deb_reg(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x08, args) +#define deb_i2c(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x10, args) +#define deb_fw(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x20, args) + +#define deb_dump(r, t, v, i, b, l, func) { \ + int loop_; \ + func("%02x %02x %02x %02x %02x %02x %02x %02x", \ + t, r, v & 0xff, v >> 8, i & 0xff, i >> 8, l & 0xff, l >> 8); \ + if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \ + func(" >>> "); \ + else \ + func(" <<< "); \ + for (loop_ = 0; loop_ < l; loop_++) \ + func("%02x ", b[loop_]); \ + func("\n");\ +} + +/* + * USB commands + * (usb_control_msg() index parameter) + */ + +#define DEMOD 0x0000 +#define USB 0x0100 +#define SYS 0x0200 +#define I2C 0x0300 +#define I2C_DA 0x0600 + +#define CMD_WR_FLAG 0x0010 +#define CMD_DEMOD_RD 0x0000 +#define CMD_DEMOD_WR 0x0010 +#define CMD_USB_RD 0x0100 +#define CMD_USB_WR 0x0110 +#define CMD_SYS_RD 0x0200 +#define CMD_IR_RD 0x0201 +#define CMD_IR_WR 0x0211 +#define CMD_SYS_WR 0x0210 +#define CMD_I2C_RD 0x0300 +#define CMD_I2C_WR 0x0310 +#define CMD_I2C_DA_RD 0x0600 +#define CMD_I2C_DA_WR 0x0610 + + +struct rtl28xxu_priv { + u8 chip_id; + u8 tuner; + u8 page; /* integrated demod active register page */ + bool rc_active; +}; + +enum rtl28xxu_chip_id { + CHIP_ID_NONE, + CHIP_ID_RTL2831U, + CHIP_ID_RTL2832U, +}; + +enum rtl28xxu_tuner { + TUNER_NONE, + + TUNER_RTL2830_QT1010, + TUNER_RTL2830_MT2060, + TUNER_RTL2830_MXL5005S, + + TUNER_RTL2832_MT2266, + TUNER_RTL2832_FC2580, + TUNER_RTL2832_MT2063, + TUNER_RTL2832_MAX3543, + TUNER_RTL2832_TUA9001, + TUNER_RTL2832_MXL5007T, + TUNER_RTL2832_FC0012, + TUNER_RTL2832_E4000, + TUNER_RTL2832_TDA18272, + TUNER_RTL2832_FC0013, +}; + +struct rtl28xxu_req { + u16 value; + u16 index; + u16 size; + u8 *data; +}; + +struct rtl28xxu_reg_val { + u16 reg; + u8 val; +}; + +/* + * memory map + * + * 0x0000 DEMOD : demodulator + * 0x2000 USB : SIE, USB endpoint, debug, DMA + * 0x3000 SYS : system + * 0xfc00 RC : remote controller (not RTL2831U) + */ + +/* + * USB registers + */ +/* SIE Control Registers */ +#define USB_SYSCTL 0x2000 /* USB system control */ +#define USB_SYSCTL_0 0x2000 /* USB system control */ +#define USB_SYSCTL_1 0x2001 /* USB system control */ +#define USB_SYSCTL_2 0x2002 /* USB system control */ +#define USB_SYSCTL_3 0x2003 /* USB system control */ +#define USB_IRQSTAT 0x2008 /* SIE interrupt status */ +#define USB_IRQEN 0x200C /* SIE interrupt enable */ +#define USB_CTRL 0x2010 /* USB control */ +#define USB_STAT 0x2014 /* USB status */ +#define USB_DEVADDR 0x2018 /* USB device address */ +#define USB_TEST 0x201C /* USB test mode */ +#define USB_FRAME_NUMBER 0x2020 /* frame number */ +#define USB_FIFO_ADDR 0x2028 /* address of SIE FIFO RAM */ +#define USB_FIFO_CMD 0x202A /* SIE FIFO RAM access command */ +#define USB_FIFO_DATA 0x2030 /* SIE FIFO RAM data */ +/* Endpoint Registers */ +#define EP0_SETUPA 0x20F8 /* EP 0 setup packet lower byte */ +#define EP0_SETUPB 0x20FC /* EP 0 setup packet higher byte */ +#define USB_EP0_CFG 0x2104 /* EP 0 configure */ +#define USB_EP0_CTL 0x2108 /* EP 0 control */ +#define USB_EP0_STAT 0x210C /* EP 0 status */ +#define USB_EP0_IRQSTAT 0x2110 /* EP 0 interrupt status */ +#define USB_EP0_IRQEN 0x2114 /* EP 0 interrupt enable */ +#define USB_EP0_MAXPKT 0x2118 /* EP 0 max packet size */ +#define USB_EP0_BC 0x2120 /* EP 0 FIFO byte counter */ +#define USB_EPA_CFG 0x2144 /* EP A configure */ +#define USB_EPA_CFG_0 0x2144 /* EP A configure */ +#define USB_EPA_CFG_1 0x2145 /* EP A configure */ +#define USB_EPA_CFG_2 0x2146 /* EP A configure */ +#define USB_EPA_CFG_3 0x2147 /* EP A configure */ +#define USB_EPA_CTL 0x2148 /* EP A control */ +#define USB_EPA_CTL_0 0x2148 /* EP A control */ +#define USB_EPA_CTL_1 0x2149 /* EP A control */ +#define USB_EPA_CTL_2 0x214A /* EP A control */ +#define USB_EPA_CTL_3 0x214B /* EP A control */ +#define USB_EPA_STAT 0x214C /* EP A status */ +#define USB_EPA_IRQSTAT 0x2150 /* EP A interrupt status */ +#define USB_EPA_IRQEN 0x2154 /* EP A interrupt enable */ +#define USB_EPA_MAXPKT 0x2158 /* EP A max packet size */ +#define USB_EPA_MAXPKT_0 0x2158 /* EP A max packet size */ +#define USB_EPA_MAXPKT_1 0x2159 /* EP A max packet size */ +#define USB_EPA_MAXPKT_2 0x215A /* EP A max packet size */ +#define USB_EPA_MAXPKT_3 0x215B /* EP A max packet size */ +#define USB_EPA_FIFO_CFG 0x2160 /* EP A FIFO configure */ +#define USB_EPA_FIFO_CFG_0 0x2160 /* EP A FIFO configure */ +#define USB_EPA_FIFO_CFG_1 0x2161 /* EP A FIFO configure */ +#define USB_EPA_FIFO_CFG_2 0x2162 /* EP A FIFO configure */ +#define USB_EPA_FIFO_CFG_3 0x2163 /* EP A FIFO configure */ +/* Debug Registers */ +#define USB_PHYTSTDIS 0x2F04 /* PHY test disable */ +#define USB_TOUT_VAL 0x2F08 /* USB time-out time */ +#define USB_VDRCTRL 0x2F10 /* UTMI vendor signal control */ +#define USB_VSTAIN 0x2F14 /* UTMI vendor signal status in */ +#define USB_VLOADM 0x2F18 /* UTMI load vendor signal status in */ +#define USB_VSTAOUT 0x2F1C /* UTMI vendor signal status out */ +#define USB_UTMI_TST 0x2F80 /* UTMI test */ +#define USB_UTMI_STATUS 0x2F84 /* UTMI status */ +#define USB_TSTCTL 0x2F88 /* test control */ +#define USB_TSTCTL2 0x2F8C /* test control 2 */ +#define USB_PID_FORCE 0x2F90 /* force PID */ +#define USB_PKTERR_CNT 0x2F94 /* packet error counter */ +#define USB_RXERR_CNT 0x2F98 /* RX error counter */ +#define USB_MEM_BIST 0x2F9C /* MEM BIST test */ +#define USB_SLBBIST 0x2FA0 /* self-loop-back BIST */ +#define USB_CNTTEST 0x2FA4 /* counter test */ +#define USB_PHYTST 0x2FC0 /* USB PHY test */ +#define USB_DBGIDX 0x2FF0 /* select individual block debug signal */ +#define USB_DBGMUX 0x2FF4 /* debug signal module mux */ + +/* + * SYS registers + */ +/* demod control registers */ +#define SYS_SYS0 0x3000 /* include DEMOD_CTL, GPO, GPI, GPOE */ +#define SYS_DEMOD_CTL 0x3000 /* control register for DVB-T demodulator */ +/* GPIO registers */ +#define SYS_GPIO_OUT_VAL 0x3001 /* output value of GPIO */ +#define SYS_GPIO_IN_VAL 0x3002 /* input value of GPIO */ +#define SYS_GPIO_OUT_EN 0x3003 /* output enable of GPIO */ +#define SYS_SYS1 0x3004 /* include GPD, SYSINTE, SYSINTS, GP_CFG0 */ +#define SYS_GPIO_DIR 0x3004 /* direction control for GPIO */ +#define SYS_SYSINTE 0x3005 /* system interrupt enable */ +#define SYS_SYSINTS 0x3006 /* system interrupt status */ +#define SYS_GPIO_CFG0 0x3007 /* PAD configuration for GPIO0-GPIO3 */ +#define SYS_SYS2 0x3008 /* include GP_CFG1 and 3 reserved bytes */ +#define SYS_GPIO_CFG1 0x3008 /* PAD configuration for GPIO4 */ +#define SYS_DEMOD_CTL1 0x300B + +/* IrDA registers */ +#define SYS_IRRC_PSR 0x3020 /* IR protocol selection */ +#define SYS_IRRC_PER 0x3024 /* IR protocol extension */ +#define SYS_IRRC_SF 0x3028 /* IR sampling frequency */ +#define SYS_IRRC_DPIR 0x302C /* IR data package interval */ +#define SYS_IRRC_CR 0x3030 /* IR control */ +#define SYS_IRRC_RP 0x3034 /* IR read port */ +#define SYS_IRRC_SR 0x3038 /* IR status */ +/* I2C master registers */ +#define SYS_I2CCR 0x3040 /* I2C clock */ +#define SYS_I2CMCR 0x3044 /* I2C master control */ +#define SYS_I2CMSTR 0x3048 /* I2C master SCL timing */ +#define SYS_I2CMSR 0x304C /* I2C master status */ +#define SYS_I2CMFR 0x3050 /* I2C master FIFO */ + +/* + * IR registers + */ +#define IR_RX_BUF 0xFC00 +#define IR_RX_IE 0xFD00 +#define IR_RX_IF 0xFD01 +#define IR_RX_CTRL 0xFD02 +#define IR_RX_CFG 0xFD03 +#define IR_MAX_DURATION0 0xFD04 +#define IR_MAX_DURATION1 0xFD05 +#define IR_IDLE_LEN0 0xFD06 +#define IR_IDLE_LEN1 0xFD07 +#define IR_GLITCH_LEN 0xFD08 +#define IR_RX_BUF_CTRL 0xFD09 +#define IR_RX_BUF_DATA 0xFD0A +#define IR_RX_BC 0xFD0B +#define IR_RX_CLK 0xFD0C +#define IR_RX_C_COUNT_L 0xFD0D +#define IR_RX_C_COUNT_H 0xFD0E +#define IR_SUSPEND_CTRL 0xFD10 +#define IR_ERR_TOL_CTRL 0xFD11 +#define IR_UNIT_LEN 0xFD12 +#define IR_ERR_TOL_LEN 0xFD13 +#define IR_MAX_H_TOL_LEN 0xFD14 +#define IR_MAX_L_TOL_LEN 0xFD15 +#define IR_MASK_CTRL 0xFD16 +#define IR_MASK_DATA 0xFD17 +#define IR_RES_MASK_ADDR 0xFD18 +#define IR_RES_MASK_T_LEN 0xFD19 + +#endif diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index ebb5ed7a7783..21246707fbfb 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -425,6 +425,13 @@ config DVB_CXD2820R help Say Y when you want to support this frontend. +config DVB_RTL2830 + tristate "Realtek RTL2830 DVB-T" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + Say Y when you want to support this frontend. + comment "DVB-C (cable) frontends" depends on DVB_CORE @@ -698,6 +705,14 @@ config DVB_IT913X_FE A DVB-T tuner module. Say Y when you want to support this frontend. +config DVB_M88RS2000 + tristate "M88RS2000 DVB-S demodulator and tuner" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S tuner module. + Say Y when you want to support this frontend. + comment "Tools to develop new frontends" config DVB_DUMMY_FE diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 00a20636df62..86fa808bf589 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -2,8 +2,8 @@ # Makefile for the kernel DVB frontend device drivers. # -ccflags-y += -Idrivers/media/dvb/dvb-core/ -ccflags-y += -Idrivers/media/common/tuners/ +ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core/ +ccflags-y += -I$(srctree)/drivers/media/common/tuners/ stb0899-objs = stb0899_drv.o stb0899_algo.o stv0900-objs = stv0900_core.o stv0900_sw.o @@ -96,4 +96,6 @@ obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o obj-$(CONFIG_DVB_IT913X_FE) += it913x-fe.o obj-$(CONFIG_DVB_A8293) += a8293.o obj-$(CONFIG_DVB_TDA10071) += tda10071.o +obj-$(CONFIG_DVB_RTL2830) += rtl2830.o +obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o diff --git a/drivers/media/dvb/frontends/au8522_decoder.c b/drivers/media/dvb/frontends/au8522_decoder.c index 2b248c12f404..55b6390198e3 100644 --- a/drivers/media/dvb/frontends/au8522_decoder.c +++ b/drivers/media/dvb/frontends/au8522_decoder.c @@ -839,15 +839,4 @@ static struct i2c_driver au8522_driver = { .id_table = au8522_id, }; -static __init int init_au8522(void) -{ - return i2c_add_driver(&au8522_driver); -} - -static __exit void exit_au8522(void) -{ - i2c_del_driver(&au8522_driver); -} - -module_init(init_au8522); -module_exit(exit_au8522); +module_i2c_driver(au8522_driver); diff --git a/drivers/media/dvb/frontends/au8522_dig.c b/drivers/media/dvb/frontends/au8522_dig.c index c688b95df486..25f650934c73 100644 --- a/drivers/media/dvb/frontends/au8522_dig.c +++ b/drivers/media/dvb/frontends/au8522_dig.c @@ -588,11 +588,6 @@ static int au8522_set_frontend(struct dvb_frontend *fe) (state->current_modulation == c->modulation)) return 0; - au8522_enable_modulation(fe, c->modulation); - - /* Allow the demod to settle */ - msleep(100); - if (fe->ops.tuner_ops.set_params) { if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -604,6 +599,11 @@ static int au8522_set_frontend(struct dvb_frontend *fe) if (ret < 0) return ret; + /* Allow the tuner to settle */ + msleep(100); + + au8522_enable_modulation(fe, c->modulation); + state->current_frequency = c->frequency; return 0; diff --git a/drivers/media/dvb/frontends/cx22702.c b/drivers/media/dvb/frontends/cx22702.c index faba82485086..edc8eafc5c09 100644 --- a/drivers/media/dvb/frontends/cx22702.c +++ b/drivers/media/dvb/frontends/cx22702.c @@ -502,10 +502,26 @@ static int cx22702_read_signal_strength(struct dvb_frontend *fe, u16 *signal_strength) { struct cx22702_state *state = fe->demodulator_priv; + u8 reg23; - u16 rs_ber; - rs_ber = cx22702_readreg(state, 0x23); - *signal_strength = (rs_ber << 8) | rs_ber; + /* + * Experience suggests that the strength signal register works as + * follows: + * - In the absence of signal, value is 0xff. + * - In the presence of a weak signal, bit 7 is set, not sure what + * the lower 7 bits mean. + * - In the presence of a strong signal, the register holds a 7-bit + * value (bit 7 is cleared), with greater values standing for + * weaker signals. + */ + reg23 = cx22702_readreg(state, 0x23); + if (reg23 & 0x80) { + *signal_strength = 0; + } else { + reg23 = ~reg23 & 0x7f; + /* Scale to 16 bit */ + *signal_strength = (reg23 << 9) | (reg23 << 2) | (reg23 >> 5); + } return 0; } diff --git a/drivers/media/dvb/frontends/dib0090.c b/drivers/media/dvb/frontends/dib0090.c index 224d81e85091..d9fe60b4be48 100644 --- a/drivers/media/dvb/frontends/dib0090.c +++ b/drivers/media/dvb/frontends/dib0090.c @@ -519,7 +519,7 @@ static int dib0090_fw_identify(struct dvb_frontend *fe) return 0; identification_error: - return -EIO;; + return -EIO; } static void dib0090_reset_digital(struct dvb_frontend *fe, const struct dib0090_config *cfg) diff --git a/drivers/media/dvb/frontends/dib9000.c b/drivers/media/dvb/frontends/dib9000.c index 863ef3cfab9f..80848b4c15d4 100644 --- a/drivers/media/dvb/frontends/dib9000.c +++ b/drivers/media/dvb/frontends/dib9000.c @@ -33,7 +33,7 @@ struct i2c_device { /* lock */ #define DIB_LOCK struct mutex -#define DibAcquireLock(lock) do { if (mutex_lock_interruptible(lock) < 0) dprintk("could not get the lock"); } while (0) +#define DibAcquireLock(lock) mutex_lock_interruptible(lock) #define DibReleaseLock(lock) mutex_unlock(lock) #define DibInitLock(lock) mutex_init(lock) #define DibFreeLock(lock) @@ -446,7 +446,10 @@ static int dib9000_risc_mem_read(struct dib9000_state *state, u8 cmd, u8 * b, u1 if (!state->platform.risc.fw_is_running) return -EIO; - DibAcquireLock(&state->platform.risc.mem_lock); + if (DibAcquireLock(&state->platform.risc.mem_lock) < 0) { + dprintk("could not get the lock"); + return -EINTR; + } dib9000_risc_mem_setup(state, cmd | 0x80); dib9000_risc_mem_read_chunks(state, b, len); DibReleaseLock(&state->platform.risc.mem_lock); @@ -459,7 +462,10 @@ static int dib9000_risc_mem_write(struct dib9000_state *state, u8 cmd, const u8 if (!state->platform.risc.fw_is_running) return -EIO; - DibAcquireLock(&state->platform.risc.mem_lock); + if (DibAcquireLock(&state->platform.risc.mem_lock) < 0) { + dprintk("could not get the lock"); + return -EINTR; + } dib9000_risc_mem_setup(state, cmd); dib9000_risc_mem_write_chunks(state, b, m->size); DibReleaseLock(&state->platform.risc.mem_lock); @@ -531,7 +537,10 @@ static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data, if (!state->platform.risc.fw_is_running) return -EINVAL; - DibAcquireLock(&state->platform.risc.mbx_if_lock); + if (DibAcquireLock(&state->platform.risc.mbx_if_lock) < 0) { + dprintk("could not get the lock"); + return -EINTR; + } tmp = MAX_MAILBOX_TRY; do { size = dib9000_read_word_attr(state, 1043, attr) & 0xff; @@ -593,7 +602,10 @@ static u8 dib9000_mbx_read(struct dib9000_state *state, u16 * data, u8 risc_id, if (!state->platform.risc.fw_is_running) return 0; - DibAcquireLock(&state->platform.risc.mbx_if_lock); + if (DibAcquireLock(&state->platform.risc.mbx_if_lock) < 0) { + dprintk("could not get the lock"); + return 0; + } if (risc_id == 1) mc_base = 16; else @@ -701,7 +713,10 @@ static int dib9000_mbx_process(struct dib9000_state *state, u16 attr) if (!state->platform.risc.fw_is_running) return -1; - DibAcquireLock(&state->platform.risc.mbx_lock); + if (DibAcquireLock(&state->platform.risc.mbx_lock) < 0) { + dprintk("could not get the lock"); + return -1; + } if (dib9000_mbx_count(state, 1, attr)) /* 1=RiscB */ ret = dib9000_mbx_fetch_to_cache(state, attr); @@ -1178,7 +1193,10 @@ static int dib9000_fw_get_channel(struct dvb_frontend *fe) struct dibDVBTChannel *ch; int ret = 0; - DibAcquireLock(&state->platform.risc.mem_mbx_lock); + if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) { + dprintk("could not get the lock"); + return -EINTR; + } if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { ret = -EIO; goto error; @@ -1660,7 +1678,10 @@ static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2 p[12] = 0; } - DibAcquireLock(&state->platform.risc.mem_mbx_lock); + if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) { + dprintk("could not get the lock"); + return 0; + } dib9000_risc_mem_write(state, FE_MM_W_COMPONENT_ACCESS, p); @@ -1768,7 +1789,10 @@ int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) return 0; } - DibAcquireLock(&state->demod_lock); + if (DibAcquireLock(&state->demod_lock) < 0) { + dprintk("could not get the lock"); + return -EINTR; + } val = dib9000_read_word(state, 294 + 1) & 0xffef; val |= (onoff & 0x1) << 4; @@ -1800,7 +1824,10 @@ int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) return 0; } - DibAcquireLock(&state->demod_lock); + if (DibAcquireLock(&state->demod_lock) < 0) { + dprintk("could not get the lock"); + return -EINTR; + } dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff); ret = dib9000_write_word(state, 300 + 1 + id, onoff ? (1 << 13) | pid : 0); @@ -1848,7 +1875,10 @@ static int dib9000_sleep(struct dvb_frontend *fe) u8 index_frontend; int ret = 0; - DibAcquireLock(&state->demod_lock); + if (DibAcquireLock(&state->demod_lock) < 0) { + dprintk("could not get the lock"); + return -EINTR; + } for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ret = state->fe[index_frontend]->ops.sleep(state->fe[index_frontend]); if (ret < 0) @@ -1874,8 +1904,12 @@ static int dib9000_get_frontend(struct dvb_frontend *fe) fe_status_t stat; int ret = 0; - if (state->get_frontend_internal == 0) - DibAcquireLock(&state->demod_lock); + if (state->get_frontend_internal == 0) { + if (DibAcquireLock(&state->demod_lock) < 0) { + dprintk("could not get the lock"); + return -EINTR; + } + } for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat); @@ -1978,7 +2012,10 @@ static int dib9000_set_frontend(struct dvb_frontend *fe) } state->pid_ctrl_index = -1; /* postpone the pid filtering cmd */ - DibAcquireLock(&state->demod_lock); + if (DibAcquireLock(&state->demod_lock) < 0) { + dprintk("could not get the lock"); + return 0; + } fe->dtv_property_cache.delivery_system = SYS_DVBT; @@ -2138,7 +2175,10 @@ static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat) u8 index_frontend; u16 lock = 0, lock_slave = 0; - DibAcquireLock(&state->demod_lock); + if (DibAcquireLock(&state->demod_lock) < 0) { + dprintk("could not get the lock"); + return -EINTR; + } for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) lock_slave |= dib9000_read_lock(state->fe[index_frontend]); @@ -2168,8 +2208,15 @@ static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber) u16 *c; int ret = 0; - DibAcquireLock(&state->demod_lock); - DibAcquireLock(&state->platform.risc.mem_mbx_lock); + if (DibAcquireLock(&state->demod_lock) < 0) { + dprintk("could not get the lock"); + return -EINTR; + } + if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) { + dprintk("could not get the lock"); + ret = -EINTR; + goto error; + } if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { DibReleaseLock(&state->platform.risc.mem_mbx_lock); ret = -EIO; @@ -2196,7 +2243,10 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) u16 val; int ret = 0; - DibAcquireLock(&state->demod_lock); + if (DibAcquireLock(&state->demod_lock) < 0) { + dprintk("could not get the lock"); + return -EINTR; + } *strength = 0; for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { state->fe[index_frontend]->ops.read_signal_strength(state->fe[index_frontend], &val); @@ -2206,8 +2256,13 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) *strength += val; } - DibAcquireLock(&state->platform.risc.mem_mbx_lock); + if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) { + dprintk("could not get the lock"); + ret = -EINTR; + goto error; + } if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { + DibReleaseLock(&state->platform.risc.mem_mbx_lock); ret = -EIO; goto error; } @@ -2232,9 +2287,14 @@ static u32 dib9000_get_snr(struct dvb_frontend *fe) u32 n, s, exp; u16 val; - DibAcquireLock(&state->platform.risc.mem_mbx_lock); - if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) - return -EIO; + if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) { + dprintk("could not get the lock"); + return 0; + } + if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { + DibReleaseLock(&state->platform.risc.mem_mbx_lock); + return 0; + } dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2); DibReleaseLock(&state->platform.risc.mem_mbx_lock); @@ -2266,7 +2326,10 @@ static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr) u8 index_frontend; u32 snr_master; - DibAcquireLock(&state->demod_lock); + if (DibAcquireLock(&state->demod_lock) < 0) { + dprintk("could not get the lock"); + return -EINTR; + } snr_master = dib9000_get_snr(fe); for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) snr_master += dib9000_get_snr(state->fe[index_frontend]); @@ -2288,9 +2351,17 @@ static int dib9000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) u16 *c = (u16 *)state->i2c_read_buffer; int ret = 0; - DibAcquireLock(&state->demod_lock); - DibAcquireLock(&state->platform.risc.mem_mbx_lock); + if (DibAcquireLock(&state->demod_lock) < 0) { + dprintk("could not get the lock"); + return -EINTR; + } + if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) { + dprintk("could not get the lock"); + ret = -EINTR; + goto error; + } if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { + DibReleaseLock(&state->platform.risc.mem_mbx_lock); ret = -EIO; goto error; } diff --git a/drivers/media/dvb/frontends/drxd_hard.c b/drivers/media/dvb/frontends/drxd_hard.c index 7bf39cda83c5..f380eb43e9d5 100644 --- a/drivers/media/dvb/frontends/drxd_hard.c +++ b/drivers/media/dvb/frontends/drxd_hard.c @@ -101,9 +101,9 @@ struct SCfgAgc { struct SNoiseCal { int cpOpt; - u16 cpNexpOfs; - u16 tdCal2k; - u16 tdCal8k; + short cpNexpOfs; + short tdCal2k; + short tdCal8k; }; enum app_env { diff --git a/drivers/media/dvb/frontends/drxk.h b/drivers/media/dvb/frontends/drxk.h index 020981844a86..9d64e4fea066 100644 --- a/drivers/media/dvb/frontends/drxk.h +++ b/drivers/media/dvb/frontends/drxk.h @@ -7,15 +7,19 @@ /** * struct drxk_config - Configure the initial parameters for DRX-K * - * adr: I2C Address of the DRX-K - * parallel_ts: true means that the device uses parallel TS, + * @adr: I2C Address of the DRX-K + * @parallel_ts: True means that the device uses parallel TS, * Serial otherwise. - * single_master: Device is on the single master mode - * no_i2c_bridge: Don't switch the I2C bridge to talk with tuner - * antenna_gpio: GPIO bit used to control the antenna - * antenna_dvbt: GPIO bit for changing antenna to DVB-C. A value of 1 + * @dynamic_clk: True means that the clock will be dynamically + * adjusted. Static clock otherwise. + * @enable_merr_cfg: Enable SIO_PDR_PERR_CFG/SIO_PDR_MVAL_CFG. + * @single_master: Device is on the single master mode + * @no_i2c_bridge: Don't switch the I2C bridge to talk with tuner + * @antenna_gpio: GPIO bit used to control the antenna + * @antenna_dvbt: GPIO bit for changing antenna to DVB-C. A value of 1 * means that 1=DVBC, 0 = DVBT. Zero means the opposite. - * microcode_name: Name of the firmware file with the microcode + * @mpeg_out_clk_strength: DRXK Mpeg output clock drive strength. + * @microcode_name: Name of the firmware file with the microcode * * On the *_gpio vars, bit 0 is UIO-1, bit 1 is UIO-2 and bit 2 is * UIO-3. @@ -25,11 +29,14 @@ struct drxk_config { bool single_master; bool no_i2c_bridge; bool parallel_ts; + bool dynamic_clk; + bool enable_merr_cfg; bool antenna_dvbt; u16 antenna_gpio; - int chunk_size; + u8 mpeg_out_clk_strength; + int chunk_size; const char *microcode_name; }; diff --git a/drivers/media/dvb/frontends/drxk_hard.c b/drivers/media/dvb/frontends/drxk_hard.c index 5ab53795bd7a..36d11756492f 100644 --- a/drivers/media/dvb/frontends/drxk_hard.c +++ b/drivers/media/dvb/frontends/drxk_hard.c @@ -90,10 +90,6 @@ bool IsA1WithRomCode(struct drxk_state *state) #define DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH (0x03) #endif -#ifndef DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH -#define DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH (0x06) -#endif - #define DEFAULT_DRXK_MPEG_LOCK_TIMEOUT 700 #define DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT 500 @@ -649,9 +645,6 @@ static int init_state(struct drxk_state *state) u32 ulQual83 = DEFAULT_MER_83; u32 ulQual93 = DEFAULT_MER_93; - u32 ulDVBTStaticTSClock = 1; - u32 ulDVBCStaticTSClock = 1; - u32 ulMpegLockTimeOut = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT; u32 ulDemodLockTimeOut = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT; @@ -661,7 +654,6 @@ static int init_state(struct drxk_state *state) u32 ulGPIOCfg = 0x0113; u32 ulInvertTSClock = 0; u32 ulTSDataStrength = DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH; - u32 ulTSClockkStrength = DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH; u32 ulDVBTBitrate = 50000000; u32 ulDVBCBitrate = DRXK_QAM_SYMBOLRATE_MAX * 8; @@ -814,8 +806,7 @@ static int init_state(struct drxk_state *state) state->m_invertSTR = false; /* If TRUE; invert STR signals */ state->m_invertVAL = false; /* If TRUE; invert VAL signals */ state->m_invertCLK = (ulInvertTSClock != 0); /* If TRUE; invert CLK signals */ - state->m_DVBTStaticCLK = (ulDVBTStaticTSClock != 0); - state->m_DVBCStaticCLK = (ulDVBCStaticTSClock != 0); + /* If TRUE; static MPEG clockrate will be used; otherwise clockrate will adapt to the bitrate of the TS */ @@ -823,7 +814,6 @@ static int init_state(struct drxk_state *state) state->m_DVBCBitrate = ulDVBCBitrate; state->m_TSDataStrength = (ulTSDataStrength & 0x07); - state->m_TSClockkStrength = (ulTSClockkStrength & 0x07); /* Maximum bitrate in b/s in case static clockrate is selected */ state->m_mpegTsStaticBitrate = 19392658; @@ -1188,6 +1178,7 @@ static int MPEGTSConfigurePins(struct drxk_state *state, bool mpegEnable) int status = -1; u16 sioPdrMclkCfg = 0; u16 sioPdrMdxCfg = 0; + u16 err_cfg = 0; dprintk(1, ": mpeg %s, %s mode\n", mpegEnable ? "enable" : "disable", @@ -1253,12 +1244,17 @@ static int MPEGTSConfigurePins(struct drxk_state *state, bool mpegEnable) status = write16(state, SIO_PDR_MSTRT_CFG__A, sioPdrMdxCfg); if (status < 0) goto error; - status = write16(state, SIO_PDR_MERR_CFG__A, 0x0000); /* Disable */ + + if (state->enable_merr_cfg) + err_cfg = sioPdrMdxCfg; + + status = write16(state, SIO_PDR_MERR_CFG__A, err_cfg); if (status < 0) goto error; - status = write16(state, SIO_PDR_MVAL_CFG__A, 0x0000); /* Disable */ + status = write16(state, SIO_PDR_MVAL_CFG__A, err_cfg); if (status < 0) goto error; + if (state->m_enableParallel == true) { /* paralel -> enable MD1 to MD7 */ status = write16(state, SIO_PDR_MD1_CFG__A, sioPdrMdxCfg); @@ -6069,9 +6065,7 @@ static int init_drxk(struct drxk_state *state) if (status < 0) goto error; - if (!state->microcode_name) - load_microcode(state, "drxk_a3.mc"); - else + if (state->microcode_name) load_microcode(state, state->microcode_name); /* disable token-ring bus through OFDM block for possible ucode upload */ @@ -6322,15 +6316,12 @@ static int drxk_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_t switch (p->delivery_system) { case SYS_DVBC_ANNEX_A: case SYS_DVBC_ANNEX_C: + case SYS_DVBT: sets->min_delay_ms = 3000; sets->max_drift = 0; sets->step_size = 0; return 0; default: - /* - * For DVB-T, let it use the default DVB core way, that is: - * fepriv->step_size = fe->ops.info.frequency_stepsize * 2 - */ return -EINVAL; } } @@ -6390,6 +6381,21 @@ struct dvb_frontend *drxk_attach(const struct drxk_config *config, state->antenna_gpio = config->antenna_gpio; state->antenna_dvbt = config->antenna_dvbt; state->m_ChunkSize = config->chunk_size; + state->enable_merr_cfg = config->enable_merr_cfg; + + if (config->dynamic_clk) { + state->m_DVBTStaticCLK = 0; + state->m_DVBCStaticCLK = 0; + } else { + state->m_DVBTStaticCLK = 1; + state->m_DVBCStaticCLK = 1; + } + + + if (config->mpeg_out_clk_strength) + state->m_TSClockkStrength = config->mpeg_out_clk_strength & 0x07; + else + state->m_TSClockkStrength = 0x06; if (config->parallel_ts) state->m_enableParallel = true; diff --git a/drivers/media/dvb/frontends/drxk_hard.h b/drivers/media/dvb/frontends/drxk_hard.h index 3a58b73eb9b9..4bbf841de83a 100644 --- a/drivers/media/dvb/frontends/drxk_hard.h +++ b/drivers/media/dvb/frontends/drxk_hard.h @@ -332,6 +332,7 @@ struct drxk_state { u16 UIO_mask; /* Bits used by UIO */ + bool enable_merr_cfg; bool single_master; bool no_i2c_bridge; bool antenna_dvbt; diff --git a/drivers/media/dvb/frontends/it913x-fe-priv.h b/drivers/media/dvb/frontends/it913x-fe-priv.h index 93b086ea7e1c..eb6fd8aebdb3 100644 --- a/drivers/media/dvb/frontends/it913x-fe-priv.h +++ b/drivers/media/dvb/frontends/it913x-fe-priv.h @@ -201,6 +201,11 @@ fe_modulation_t fe_con[] = { QAM_64, }; +enum { + PRIORITY_HIGH = 0, /* High-priority stream */ + PRIORITY_LOW, /* Low-priority stream */ +}; + /* Standard demodulator functions */ static struct it913xset set_solo_fe[] = { {PRO_LINK, GPIOH5_EN, {0x01}, 0x01}, diff --git a/drivers/media/dvb/frontends/it913x-fe.c b/drivers/media/dvb/frontends/it913x-fe.c index ccc36bf2deb4..84df03c29179 100644 --- a/drivers/media/dvb/frontends/it913x-fe.c +++ b/drivers/media/dvb/frontends/it913x-fe.c @@ -57,6 +57,7 @@ struct it913x_fe_state { u32 frequency; fe_modulation_t constellation; fe_transmit_mode_t transmission_mode; + u8 priority; u32 crystalFrequency; u32 adcFrequency; u8 tuner_type; @@ -500,19 +501,87 @@ static int it913x_fe_read_status(struct dvb_frontend *fe, fe_status_t *status) return 0; } +/* FEC values based on fe_code_rate_t non supported values 0*/ +int it913x_qpsk_pval[] = {0, -93, -91, -90, 0, -89, -88}; +int it913x_16qam_pval[] = {0, -87, -85, -84, 0, -83, -82}; +int it913x_64qam_pval[] = {0, -82, -80, -78, 0, -77, -76}; + +static int it913x_get_signal_strength(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct it913x_fe_state *state = fe->demodulator_priv; + u8 code_rate; + int ret, temp; + u8 lna_gain_os; + + ret = it913x_read_reg_u8(state, VAR_P_INBAND); + if (ret < 0) + return ret; + + /* VHF/UHF gain offset */ + if (state->frequency < 300000000) + lna_gain_os = 7; + else + lna_gain_os = 14; + + temp = (ret - 100) - lna_gain_os; + + if (state->priority == PRIORITY_HIGH) + code_rate = p->code_rate_HP; + else + code_rate = p->code_rate_LP; + + if (code_rate >= ARRAY_SIZE(it913x_qpsk_pval)) + return -EINVAL; + + deb_info("Reg VAR_P_INBAND:%d Calc Offset Value:%d", ret, temp); + + /* Apply FEC offset values*/ + switch (p->modulation) { + case QPSK: + temp -= it913x_qpsk_pval[code_rate]; + break; + case QAM_16: + temp -= it913x_16qam_pval[code_rate]; + break; + case QAM_64: + temp -= it913x_64qam_pval[code_rate]; + break; + default: + return -EINVAL; + } + + if (temp < -15) + ret = 0; + else if ((-15 <= temp) && (temp < 0)) + ret = (2 * (temp + 15)) / 3; + else if ((0 <= temp) && (temp < 20)) + ret = 4 * temp + 10; + else if ((20 <= temp) && (temp < 35)) + ret = (2 * (temp - 20)) / 3 + 90; + else if (temp >= 35) + ret = 100; + + deb_info("Signal Strength :%d", ret); + + return ret; +} + static int it913x_fe_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { struct it913x_fe_state *state = fe->demodulator_priv; - int ret = it913x_read_reg_u8(state, SIGNAL_LEVEL); - /*SIGNAL_LEVEL always returns 100%! so using FE_HAS_SIGNAL as switch*/ - if (state->it913x_status & FE_HAS_SIGNAL) - ret = (ret * 0xff) / 0x64; - else - ret = 0x0; - ret |= ret << 0x8; - *strength = ret; - return 0; + int ret = 0; + if (state->config->read_slevel) { + if (state->it913x_status & FE_HAS_SIGNAL) + ret = it913x_read_reg_u8(state, SIGNAL_LEVEL); + } else + ret = it913x_get_signal_strength(fe); + + if (ret >= 0) + *strength = (u16)((u32)ret * 0xffff / 0x64); + + return (ret < 0) ? -ENODEV : 0; } static int it913x_fe_read_snr(struct dvb_frontend *fe, u16 *snr) @@ -606,6 +675,8 @@ static int it913x_fe_get_frontend(struct dvb_frontend *fe) if (reg[2] < 4) p->hierarchy = fe_hi[reg[2]]; + state->priority = reg[5]; + p->code_rate_HP = (reg[6] < 6) ? fe_code[reg[6]] : FEC_NONE; p->code_rate_LP = (reg[7] < 6) ? fe_code[reg[7]] : FEC_NONE; @@ -972,5 +1043,5 @@ static struct dvb_frontend_ops it913x_fe_ofdm_ops = { MODULE_DESCRIPTION("it913x Frontend and it9137 tuner"); MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com"); -MODULE_VERSION("1.13"); +MODULE_VERSION("1.15"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/it913x-fe.h b/drivers/media/dvb/frontends/it913x-fe.h index c4a908e354e0..07fa4594c12b 100644 --- a/drivers/media/dvb/frontends/it913x-fe.h +++ b/drivers/media/dvb/frontends/it913x-fe.h @@ -34,6 +34,8 @@ struct ite_config { u8 tuner_id_1; u8 dual_mode; u8 adf; + /* option to read SIGNAL_LEVEL */ + u8 read_slevel; }; #if defined(CONFIG_DVB_IT913X_FE) || (defined(CONFIG_DVB_IT913X_FE_MODULE) && \ @@ -168,6 +170,8 @@ static inline struct dvb_frontend *it913x_fe_attach( #define EST_SIGNAL_LEVEL 0x004a #define FREE_BAND 0x004b #define SUSPEND_FLAG 0x004c +#define VAR_P_INBAND 0x00f7 + /* Build in tuner types */ #define IT9137 0x38 #define IT9135_38 0x38 diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c index c990d35a13dc..e046622df0e4 100644 --- a/drivers/media/dvb/frontends/lgdt330x.c +++ b/drivers/media/dvb/frontends/lgdt330x.c @@ -104,8 +104,8 @@ static int i2c_write_demod_bytes (struct lgdt330x_state* state, * then reads the data returned for (len) bytes. */ -static u8 i2c_read_demod_bytes (struct lgdt330x_state* state, - enum I2C_REG reg, u8* buf, int len) +static int i2c_read_demod_bytes(struct lgdt330x_state *state, + enum I2C_REG reg, u8 *buf, int len) { u8 wr [] = { reg }; struct i2c_msg msg [] = { @@ -118,6 +118,8 @@ static u8 i2c_read_demod_bytes (struct lgdt330x_state* state, ret = i2c_transfer(state->i2c, msg, 2); if (ret != 2) { printk(KERN_WARNING "lgdt330x: %s: addr 0x%02x select 0x%02x error (ret == %i)\n", __func__, state->config->demod_address, reg, ret); + if (ret >= 0) + ret = -EIO; } else { ret = 0; } diff --git a/drivers/media/dvb/frontends/m88rs2000.c b/drivers/media/dvb/frontends/m88rs2000.c new file mode 100644 index 000000000000..045ee5a6f7ae --- /dev/null +++ b/drivers/media/dvb/frontends/m88rs2000.c @@ -0,0 +1,904 @@ +/* + Driver for M88RS2000 demodulator and tuner + + Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com) + Beta Driver + + Include various calculation code from DS3000 driver. + Copyright (C) 2009 Konstantin Dimitrov. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/jiffies.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/types.h> + + +#include "dvb_frontend.h" +#include "m88rs2000.h" + +struct m88rs2000_state { + struct i2c_adapter *i2c; + const struct m88rs2000_config *config; + struct dvb_frontend frontend; + u8 no_lock_count; + u32 tuner_frequency; + u32 symbol_rate; + fe_code_rate_t fec_inner; + u8 tuner_level; + int errmode; +}; + +static int m88rs2000_debug; + +module_param_named(debug, m88rs2000_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); + +#define dprintk(level, args...) do { \ + if (level & m88rs2000_debug) \ + printk(KERN_DEBUG "m88rs2000-fe: " args); \ +} while (0) + +#define deb_info(args...) dprintk(0x01, args) +#define info(format, arg...) \ + printk(KERN_INFO "m88rs2000-fe: " format "\n" , ## arg) + +static int m88rs2000_writereg(struct m88rs2000_state *state, u8 tuner, + u8 reg, u8 data) +{ + int ret; + u8 addr = (tuner == 0) ? state->config->tuner_addr : + state->config->demod_addr; + u8 buf[] = { reg, data }; + struct i2c_msg msg = { + .addr = addr, + .flags = 0, + .buf = buf, + .len = 2 + }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + deb_info("%s: writereg error (reg == 0x%02x, val == 0x%02x, " + "ret == %i)\n", __func__, reg, data, ret); + + return (ret != 1) ? -EREMOTEIO : 0; +} + +static int m88rs2000_demod_write(struct m88rs2000_state *state, u8 reg, u8 data) +{ + return m88rs2000_writereg(state, 1, reg, data); +} + +static int m88rs2000_tuner_write(struct m88rs2000_state *state, u8 reg, u8 data) +{ + m88rs2000_demod_write(state, 0x81, 0x84); + udelay(10); + return m88rs2000_writereg(state, 0, reg, data); + +} + +static int m88rs2000_write(struct dvb_frontend *fe, const u8 buf[], int len) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + + if (len != 2) + return -EINVAL; + + return m88rs2000_writereg(state, 1, buf[0], buf[1]); +} + +static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 tuner, u8 reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + u8 addr = (tuner == 0) ? state->config->tuner_addr : + state->config->demod_addr; + struct i2c_msg msg[] = { + { + .addr = addr, + .flags = 0, + .buf = b0, + .len = 1 + }, { + .addr = addr, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + deb_info("%s: readreg error (reg == 0x%02x, ret == %i)\n", + __func__, reg, ret); + + return b1[0]; +} + +static u8 m88rs2000_demod_read(struct m88rs2000_state *state, u8 reg) +{ + return m88rs2000_readreg(state, 1, reg); +} + +static u8 m88rs2000_tuner_read(struct m88rs2000_state *state, u8 reg) +{ + m88rs2000_demod_write(state, 0x81, 0x85); + udelay(10); + return m88rs2000_readreg(state, 0, reg); +} + +static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + int ret; + u32 temp; + u8 b[3]; + + if ((srate < 1000000) || (srate > 45000000)) + return -EINVAL; + + temp = srate / 1000; + temp *= 11831; + temp /= 68; + temp -= 3; + + b[0] = (u8) (temp >> 16) & 0xff; + b[1] = (u8) (temp >> 8) & 0xff; + b[2] = (u8) temp & 0xff; + ret = m88rs2000_demod_write(state, 0x93, b[2]); + ret |= m88rs2000_demod_write(state, 0x94, b[1]); + ret |= m88rs2000_demod_write(state, 0x95, b[0]); + + deb_info("m88rs2000: m88rs2000_set_symbolrate\n"); + return ret; +} + +static int m88rs2000_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *m) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + + int i; + u8 reg; + deb_info("%s\n", __func__); + m88rs2000_demod_write(state, 0x9a, 0x30); + reg = m88rs2000_demod_read(state, 0xb2); + reg &= 0x3f; + m88rs2000_demod_write(state, 0xb2, reg); + for (i = 0; i < m->msg_len; i++) + m88rs2000_demod_write(state, 0xb3 + i, m->msg[i]); + + reg = m88rs2000_demod_read(state, 0xb1); + reg &= 0x87; + reg |= ((m->msg_len - 1) << 3) | 0x07; + reg &= 0x7f; + m88rs2000_demod_write(state, 0xb1, reg); + + for (i = 0; i < 15; i++) { + if ((m88rs2000_demod_read(state, 0xb1) & 0x40) == 0x0) + break; + msleep(20); + } + + reg = m88rs2000_demod_read(state, 0xb1); + if ((reg & 0x40) > 0x0) { + reg &= 0x7f; + reg |= 0x40; + m88rs2000_demod_write(state, 0xb1, reg); + } + + reg = m88rs2000_demod_read(state, 0xb2); + reg &= 0x3f; + reg |= 0x80; + m88rs2000_demod_write(state, 0xb2, reg); + m88rs2000_demod_write(state, 0x9a, 0xb0); + + + return 0; +} + +static int m88rs2000_send_diseqc_burst(struct dvb_frontend *fe, + fe_sec_mini_cmd_t burst) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + u8 reg0, reg1; + deb_info("%s\n", __func__); + m88rs2000_demod_write(state, 0x9a, 0x30); + msleep(50); + reg0 = m88rs2000_demod_read(state, 0xb1); + reg1 = m88rs2000_demod_read(state, 0xb2); + /* TODO complete this section */ + m88rs2000_demod_write(state, 0xb2, reg1); + m88rs2000_demod_write(state, 0xb1, reg0); + m88rs2000_demod_write(state, 0x9a, 0xb0); + + return 0; +} + +static int m88rs2000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + u8 reg0, reg1; + m88rs2000_demod_write(state, 0x9a, 0x30); + reg0 = m88rs2000_demod_read(state, 0xb1); + reg1 = m88rs2000_demod_read(state, 0xb2); + + reg1 &= 0x3f; + + switch (tone) { + case SEC_TONE_ON: + reg0 |= 0x4; + reg0 &= 0xbc; + break; + case SEC_TONE_OFF: + reg1 |= 0x80; + break; + default: + break; + } + m88rs2000_demod_write(state, 0xb2, reg1); + m88rs2000_demod_write(state, 0xb1, reg0); + m88rs2000_demod_write(state, 0x9a, 0xb0); + return 0; +} + +struct inittab { + u8 cmd; + u8 reg; + u8 val; +}; + +struct inittab m88rs2000_setup[] = { + {DEMOD_WRITE, 0x9a, 0x30}, + {DEMOD_WRITE, 0x00, 0x01}, + {WRITE_DELAY, 0x19, 0x00}, + {DEMOD_WRITE, 0x00, 0x00}, + {DEMOD_WRITE, 0x9a, 0xb0}, + {DEMOD_WRITE, 0x81, 0xc1}, + {TUNER_WRITE, 0x42, 0x73}, + {TUNER_WRITE, 0x05, 0x07}, + {TUNER_WRITE, 0x20, 0x27}, + {TUNER_WRITE, 0x07, 0x02}, + {TUNER_WRITE, 0x11, 0xff}, + {TUNER_WRITE, 0x60, 0xf9}, + {TUNER_WRITE, 0x08, 0x01}, + {TUNER_WRITE, 0x00, 0x41}, + {DEMOD_WRITE, 0x81, 0x81}, + {DEMOD_WRITE, 0x86, 0xc6}, + {DEMOD_WRITE, 0x9a, 0x30}, + {DEMOD_WRITE, 0xf0, 0x22}, + {DEMOD_WRITE, 0xf1, 0xbf}, + {DEMOD_WRITE, 0xb0, 0x45}, + {DEMOD_WRITE, 0xb2, 0x01}, /* set voltage pin always set 1*/ + {DEMOD_WRITE, 0x9a, 0xb0}, + {0xff, 0xaa, 0xff} +}; + +struct inittab m88rs2000_shutdown[] = { + {DEMOD_WRITE, 0x9a, 0x30}, + {DEMOD_WRITE, 0xb0, 0x00}, + {DEMOD_WRITE, 0xf1, 0x89}, + {DEMOD_WRITE, 0x00, 0x01}, + {DEMOD_WRITE, 0x9a, 0xb0}, + {TUNER_WRITE, 0x00, 0x40}, + {DEMOD_WRITE, 0x81, 0x81}, + {0xff, 0xaa, 0xff} +}; + +struct inittab tuner_reset[] = { + {TUNER_WRITE, 0x42, 0x73}, + {TUNER_WRITE, 0x05, 0x07}, + {TUNER_WRITE, 0x20, 0x27}, + {TUNER_WRITE, 0x07, 0x02}, + {TUNER_WRITE, 0x11, 0xff}, + {TUNER_WRITE, 0x60, 0xf9}, + {TUNER_WRITE, 0x08, 0x01}, + {TUNER_WRITE, 0x00, 0x41}, + {0xff, 0xaa, 0xff} +}; + +struct inittab fe_reset[] = { + {DEMOD_WRITE, 0x00, 0x01}, + {DEMOD_WRITE, 0xf1, 0xbf}, + {DEMOD_WRITE, 0x00, 0x01}, + {DEMOD_WRITE, 0x20, 0x81}, + {DEMOD_WRITE, 0x21, 0x80}, + {DEMOD_WRITE, 0x10, 0x33}, + {DEMOD_WRITE, 0x11, 0x44}, + {DEMOD_WRITE, 0x12, 0x07}, + {DEMOD_WRITE, 0x18, 0x20}, + {DEMOD_WRITE, 0x28, 0x04}, + {DEMOD_WRITE, 0x29, 0x8e}, + {DEMOD_WRITE, 0x3b, 0xff}, + {DEMOD_WRITE, 0x32, 0x10}, + {DEMOD_WRITE, 0x33, 0x02}, + {DEMOD_WRITE, 0x34, 0x30}, + {DEMOD_WRITE, 0x35, 0xff}, + {DEMOD_WRITE, 0x38, 0x50}, + {DEMOD_WRITE, 0x39, 0x68}, + {DEMOD_WRITE, 0x3c, 0x7f}, + {DEMOD_WRITE, 0x3d, 0x0f}, + {DEMOD_WRITE, 0x45, 0x20}, + {DEMOD_WRITE, 0x46, 0x24}, + {DEMOD_WRITE, 0x47, 0x7c}, + {DEMOD_WRITE, 0x48, 0x16}, + {DEMOD_WRITE, 0x49, 0x04}, + {DEMOD_WRITE, 0x4a, 0x01}, + {DEMOD_WRITE, 0x4b, 0x78}, + {DEMOD_WRITE, 0X4d, 0xd2}, + {DEMOD_WRITE, 0x4e, 0x6d}, + {DEMOD_WRITE, 0x50, 0x30}, + {DEMOD_WRITE, 0x51, 0x30}, + {DEMOD_WRITE, 0x54, 0x7b}, + {DEMOD_WRITE, 0x56, 0x09}, + {DEMOD_WRITE, 0x58, 0x59}, + {DEMOD_WRITE, 0x59, 0x37}, + {DEMOD_WRITE, 0x63, 0xfa}, + {0xff, 0xaa, 0xff} +}; + +struct inittab fe_trigger[] = { + {DEMOD_WRITE, 0x97, 0x04}, + {DEMOD_WRITE, 0x99, 0x77}, + {DEMOD_WRITE, 0x9b, 0x64}, + {DEMOD_WRITE, 0x9e, 0x00}, + {DEMOD_WRITE, 0x9f, 0xf8}, + {DEMOD_WRITE, 0xa0, 0x20}, + {DEMOD_WRITE, 0xa1, 0xe0}, + {DEMOD_WRITE, 0xa3, 0x38}, + {DEMOD_WRITE, 0x98, 0xff}, + {DEMOD_WRITE, 0xc0, 0x0f}, + {DEMOD_WRITE, 0x89, 0x01}, + {DEMOD_WRITE, 0x00, 0x00}, + {WRITE_DELAY, 0x0a, 0x00}, + {DEMOD_WRITE, 0x00, 0x01}, + {DEMOD_WRITE, 0x00, 0x00}, + {DEMOD_WRITE, 0x9a, 0xb0}, + {0xff, 0xaa, 0xff} +}; + +static int m88rs2000_tab_set(struct m88rs2000_state *state, + struct inittab *tab) +{ + int ret = 0; + u8 i; + if (tab == NULL) + return -EINVAL; + + for (i = 0; i < 255; i++) { + switch (tab[i].cmd) { + case 0x01: + ret = m88rs2000_demod_write(state, tab[i].reg, + tab[i].val); + break; + case 0x02: + ret = m88rs2000_tuner_write(state, tab[i].reg, + tab[i].val); + break; + case 0x10: + if (tab[i].reg > 0) + mdelay(tab[i].reg); + break; + case 0xff: + if (tab[i].reg == 0xaa && tab[i].val == 0xff) + return 0; + case 0x00: + break; + default: + return -EINVAL; + } + if (ret < 0) + return -ENODEV; + } + return 0; +} + +static int m88rs2000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) +{ + deb_info("%s: %s\n", __func__, + volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : + volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); + + return 0; +} + +static int m88rs2000_startup(struct m88rs2000_state *state) +{ + int ret = 0; + u8 reg; + + reg = m88rs2000_tuner_read(state, 0x00); + if ((reg & 0x40) == 0) + ret = -ENODEV; + + return ret; +} + +static int m88rs2000_init(struct dvb_frontend *fe) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + int ret; + + deb_info("m88rs2000: init chip\n"); + /* Setup frontend from shutdown/cold */ + ret = m88rs2000_tab_set(state, m88rs2000_setup); + + return ret; +} + +static int m88rs2000_sleep(struct dvb_frontend *fe) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + int ret; + /* Shutdown the frondend */ + ret = m88rs2000_tab_set(state, m88rs2000_shutdown); + return ret; +} + +static int m88rs2000_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + u8 reg = m88rs2000_demod_read(state, 0x8c); + + *status = 0; + + if ((reg & 0x7) == 0x7) { + *status = FE_HAS_CARRIER | FE_HAS_SIGNAL | FE_HAS_VITERBI + | FE_HAS_LOCK; + if (state->config->set_ts_params) + state->config->set_ts_params(fe, CALL_IS_READ); + } + return 0; +} + +/* Extact code for these unknown but lmedm04 driver uses interupt callbacks */ + +static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + deb_info("m88rs2000_read_ber %d\n", *ber); + *ber = 0; + return 0; +} + +static int m88rs2000_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + *strength = 0; + return 0; +} + +static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + deb_info("m88rs2000_read_snr %d\n", *snr); + *snr = 0; + return 0; +} + +static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + deb_info("m88rs2000_read_ber %d\n", *ucblocks); + *ucblocks = 0; + return 0; +} + +static int m88rs2000_tuner_gate_ctrl(struct m88rs2000_state *state, u8 offset) +{ + int ret; + ret = m88rs2000_tuner_write(state, 0x51, 0x1f - offset); + ret |= m88rs2000_tuner_write(state, 0x51, 0x1f); + ret |= m88rs2000_tuner_write(state, 0x50, offset); + ret |= m88rs2000_tuner_write(state, 0x50, 0x00); + msleep(20); + return ret; +} + +static int m88rs2000_set_tuner_rf(struct dvb_frontend *fe) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + int reg; + reg = m88rs2000_tuner_read(state, 0x3d); + reg &= 0x7f; + if (reg < 0x16) + reg = 0xa1; + else if (reg == 0x16) + reg = 0x99; + else + reg = 0xf9; + + m88rs2000_tuner_write(state, 0x60, reg); + reg = m88rs2000_tuner_gate_ctrl(state, 0x08); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + return reg; +} + +static int m88rs2000_set_tuner(struct dvb_frontend *fe, u16 *offset) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct m88rs2000_state *state = fe->demodulator_priv; + int ret; + u32 frequency = c->frequency; + s32 offset_khz; + s32 tmp; + u32 symbol_rate = (c->symbol_rate / 1000); + u32 f3db, gdiv28; + u16 value, ndiv, lpf_coeff; + u8 lpf_mxdiv, mlpf_max, mlpf_min, nlpf; + u8 lo = 0x01, div4 = 0x0; + + /* Reset Tuner */ + ret = m88rs2000_tab_set(state, tuner_reset); + + /* Calculate frequency divider */ + if (frequency < 1060000) { + lo |= 0x10; + div4 = 0x1; + ndiv = (frequency * 14 * 4) / FE_CRYSTAL_KHZ; + } else + ndiv = (frequency * 14 * 2) / FE_CRYSTAL_KHZ; + ndiv = ndiv + ndiv % 2; + ndiv = ndiv - 1024; + + ret = m88rs2000_tuner_write(state, 0x10, 0x80 | lo); + + /* Set frequency divider */ + ret |= m88rs2000_tuner_write(state, 0x01, (ndiv >> 8) & 0xf); + ret |= m88rs2000_tuner_write(state, 0x02, ndiv & 0xff); + + ret |= m88rs2000_tuner_write(state, 0x03, 0x06); + ret |= m88rs2000_tuner_gate_ctrl(state, 0x10); + if (ret < 0) + return -ENODEV; + + /* Tuner Frequency Range */ + ret = m88rs2000_tuner_write(state, 0x10, lo); + + ret |= m88rs2000_tuner_gate_ctrl(state, 0x08); + + /* Tuner RF */ + ret |= m88rs2000_set_tuner_rf(fe); + + gdiv28 = (FE_CRYSTAL_KHZ / 1000 * 1694 + 500) / 1000; + ret |= m88rs2000_tuner_write(state, 0x04, gdiv28 & 0xff); + ret |= m88rs2000_tuner_gate_ctrl(state, 0x04); + if (ret < 0) + return -ENODEV; + + value = m88rs2000_tuner_read(state, 0x26); + + f3db = (symbol_rate * 135) / 200 + 2000; + f3db += FREQ_OFFSET_LOW_SYM_RATE; + if (f3db < 7000) + f3db = 7000; + if (f3db > 40000) + f3db = 40000; + + gdiv28 = gdiv28 * 207 / (value * 2 + 151); + mlpf_max = gdiv28 * 135 / 100; + mlpf_min = gdiv28 * 78 / 100; + if (mlpf_max > 63) + mlpf_max = 63; + + lpf_coeff = 2766; + + nlpf = (f3db * gdiv28 * 2 / lpf_coeff / + (FE_CRYSTAL_KHZ / 1000) + 1) / 2; + if (nlpf > 23) + nlpf = 23; + if (nlpf < 1) + nlpf = 1; + + lpf_mxdiv = (nlpf * (FE_CRYSTAL_KHZ / 1000) + * lpf_coeff * 2 / f3db + 1) / 2; + + if (lpf_mxdiv < mlpf_min) { + nlpf++; + lpf_mxdiv = (nlpf * (FE_CRYSTAL_KHZ / 1000) + * lpf_coeff * 2 / f3db + 1) / 2; + } + + if (lpf_mxdiv > mlpf_max) + lpf_mxdiv = mlpf_max; + + ret = m88rs2000_tuner_write(state, 0x04, lpf_mxdiv); + ret |= m88rs2000_tuner_write(state, 0x06, nlpf); + + ret |= m88rs2000_tuner_gate_ctrl(state, 0x04); + + ret |= m88rs2000_tuner_gate_ctrl(state, 0x01); + + msleep(80); + /* calculate offset assuming 96000kHz*/ + offset_khz = (ndiv - ndiv % 2 + 1024) * FE_CRYSTAL_KHZ + / 14 / (div4 + 1) / 2; + + offset_khz -= frequency; + + tmp = offset_khz; + tmp *= 65536; + + tmp = (2 * tmp + 96000) / (2 * 96000); + if (tmp < 0) + tmp += 65536; + + *offset = tmp & 0xffff; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return (ret < 0) ? -EINVAL : 0; +} + +static int m88rs2000_set_fec(struct m88rs2000_state *state, + fe_code_rate_t fec) +{ + int ret; + u16 fec_set; + switch (fec) { + /* This is not confirmed kept for reference */ +/* case FEC_1_2: + fec_set = 0x88; + break; + case FEC_2_3: + fec_set = 0x68; + break; + case FEC_3_4: + fec_set = 0x48; + break; + case FEC_5_6: + fec_set = 0x28; + break; + case FEC_7_8: + fec_set = 0x18; + break; */ + case FEC_AUTO: + default: + fec_set = 0x08; + } + ret = m88rs2000_demod_write(state, 0x76, fec_set); + + return 0; +} + + +static fe_code_rate_t m88rs2000_get_fec(struct m88rs2000_state *state) +{ + u8 reg; + m88rs2000_demod_write(state, 0x9a, 0x30); + reg = m88rs2000_demod_read(state, 0x76); + m88rs2000_demod_write(state, 0x9a, 0xb0); + + switch (reg) { + case 0x88: + return FEC_1_2; + case 0x68: + return FEC_2_3; + case 0x48: + return FEC_3_4; + case 0x28: + return FEC_5_6; + case 0x18: + return FEC_7_8; + case 0x08: + default: + break; + } + + return FEC_AUTO; +} + +static int m88rs2000_set_frontend(struct dvb_frontend *fe) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + fe_status_t status; + int i, ret; + u16 offset = 0; + u8 reg; + + state->no_lock_count = 0; + + if (c->delivery_system != SYS_DVBS) { + deb_info("%s: unsupported delivery " + "system selected (%d)\n", + __func__, c->delivery_system); + return -EOPNOTSUPP; + } + + /* Set Tuner */ + ret = m88rs2000_set_tuner(fe, &offset); + if (ret < 0) + return -ENODEV; + + ret = m88rs2000_demod_write(state, 0x9a, 0x30); + /* Unknown usually 0xc6 sometimes 0xc1 */ + reg = m88rs2000_demod_read(state, 0x86); + ret |= m88rs2000_demod_write(state, 0x86, reg); + /* Offset lower nibble always 0 */ + ret |= m88rs2000_demod_write(state, 0x9c, (offset >> 8)); + ret |= m88rs2000_demod_write(state, 0x9d, offset & 0xf0); + + + /* Reset Demod */ + ret = m88rs2000_tab_set(state, fe_reset); + if (ret < 0) + return -ENODEV; + + /* Unknown */ + reg = m88rs2000_demod_read(state, 0x70); + ret = m88rs2000_demod_write(state, 0x70, reg); + + /* Set FEC */ + ret |= m88rs2000_set_fec(state, c->fec_inner); + ret |= m88rs2000_demod_write(state, 0x85, 0x1); + ret |= m88rs2000_demod_write(state, 0x8a, 0xbf); + ret |= m88rs2000_demod_write(state, 0x8d, 0x1e); + ret |= m88rs2000_demod_write(state, 0x90, 0xf1); + ret |= m88rs2000_demod_write(state, 0x91, 0x08); + + if (ret < 0) + return -ENODEV; + + /* Set Symbol Rate */ + ret = m88rs2000_set_symbolrate(fe, c->symbol_rate); + if (ret < 0) + return -ENODEV; + + /* Set up Demod */ + ret = m88rs2000_tab_set(state, fe_trigger); + if (ret < 0) + return -ENODEV; + + for (i = 0; i < 25; i++) { + u8 reg = m88rs2000_demod_read(state, 0x8c); + if ((reg & 0x7) == 0x7) { + status = FE_HAS_LOCK; + break; + } + state->no_lock_count++; + if (state->no_lock_count > 15) { + reg = m88rs2000_demod_read(state, 0x70); + reg ^= 0x4; + m88rs2000_demod_write(state, 0x70, reg); + state->no_lock_count = 0; + } + if (state->no_lock_count == 20) + m88rs2000_set_tuner_rf(fe); + msleep(20); + } + + if (status & FE_HAS_LOCK) { + state->fec_inner = m88rs2000_get_fec(state); + /* Uknown suspect SNR level */ + reg = m88rs2000_demod_read(state, 0x65); + } + + state->tuner_frequency = c->frequency; + state->symbol_rate = c->symbol_rate; + return 0; +} + +static int m88rs2000_get_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct m88rs2000_state *state = fe->demodulator_priv; + c->fec_inner = state->fec_inner; + c->frequency = state->tuner_frequency; + c->symbol_rate = state->symbol_rate; + return 0; +} + +static int m88rs2000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + + if (enable) + m88rs2000_demod_write(state, 0x81, 0x84); + else + m88rs2000_demod_write(state, 0x81, 0x81); + udelay(10); + return 0; +} + +static void m88rs2000_release(struct dvb_frontend *fe) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops m88rs2000_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "M88RS2000 DVB-S", + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 1000, /* kHz for QPSK frontends */ + .frequency_tolerance = 5000, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .symbol_rate_tolerance = 500, /* ppm */ + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | + FE_CAN_QPSK | + FE_CAN_FEC_AUTO + }, + + .release = m88rs2000_release, + .init = m88rs2000_init, + .sleep = m88rs2000_sleep, + .write = m88rs2000_write, + .i2c_gate_ctrl = m88rs2000_i2c_gate_ctrl, + .read_status = m88rs2000_read_status, + .read_ber = m88rs2000_read_ber, + .read_signal_strength = m88rs2000_read_signal_strength, + .read_snr = m88rs2000_read_snr, + .read_ucblocks = m88rs2000_read_ucblocks, + .diseqc_send_master_cmd = m88rs2000_send_diseqc_msg, + .diseqc_send_burst = m88rs2000_send_diseqc_burst, + .set_tone = m88rs2000_set_tone, + .set_voltage = m88rs2000_set_voltage, + + .set_frontend = m88rs2000_set_frontend, + .get_frontend = m88rs2000_get_frontend, +}; + +struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config, + struct i2c_adapter *i2c) +{ + struct m88rs2000_state *state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct m88rs2000_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->tuner_frequency = 0; + state->symbol_rate = 0; + state->fec_inner = 0; + + if (m88rs2000_startup(state) < 0) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &m88rs2000_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + + return NULL; +} +EXPORT_SYMBOL(m88rs2000_attach); + +MODULE_DESCRIPTION("M88RS2000 DVB-S Demodulator driver"); +MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.13"); + diff --git a/drivers/media/dvb/frontends/m88rs2000.h b/drivers/media/dvb/frontends/m88rs2000.h new file mode 100644 index 000000000000..59acdb696873 --- /dev/null +++ b/drivers/media/dvb/frontends/m88rs2000.h @@ -0,0 +1,66 @@ +/* + Driver for M88RS2000 demodulator + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef M88RS2000_H +#define M88RS2000_H + +#include <linux/dvb/frontend.h> +#include "dvb_frontend.h" + +struct m88rs2000_config { + /* Demodulator i2c address */ + u8 demod_addr; + /* Tuner address */ + u8 tuner_addr; + + u8 *inittab; + + /* minimum delay before retuning */ + int min_delay_ms; + + int (*set_ts_params)(struct dvb_frontend *, int); +}; + +enum { + CALL_IS_SET_FRONTEND = 0x0, + CALL_IS_READ, +}; + +#if defined(CONFIG_DVB_M88RS2000) || (defined(CONFIG_DVB_M88RS2000_MODULE) && \ + defined(MODULE)) +extern struct dvb_frontend *m88rs2000_attach( + const struct m88rs2000_config *config, struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *m88rs2000_attach( + const struct m88rs2000_config *config, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_M88RS2000 */ + +#define FE_CRYSTAL_KHZ 27000 +#define FREQ_OFFSET_LOW_SYM_RATE 3000 + +enum { + DEMOD_WRITE = 0x1, + TUNER_WRITE, + WRITE_DELAY = 0x10, +}; +#endif /* M88RS2000_H */ diff --git a/drivers/media/dvb/frontends/rtl2830.c b/drivers/media/dvb/frontends/rtl2830.c new file mode 100644 index 000000000000..45196c5b0736 --- /dev/null +++ b/drivers/media/dvb/frontends/rtl2830.c @@ -0,0 +1,562 @@ +/* + * Realtek RTL2830 DVB-T demodulator driver + * + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +/* + * Driver implements own I2C-adapter for tuner I2C access. That's since chip + * have unusual I2C-gate control which closes gate automatically after each + * I2C transfer. Using own I2C adapter we can workaround that. + */ + +#include "rtl2830_priv.h" + +int rtl2830_debug; +module_param_named(debug, rtl2830_debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +/* write multiple hardware registers */ +static int rtl2830_wr(struct rtl2830_priv *priv, u8 reg, u8 *val, int len) +{ + int ret; + u8 buf[1+len]; + struct i2c_msg msg[1] = { + { + .addr = priv->cfg.i2c_addr, + .flags = 0, + .len = 1+len, + .buf = buf, + } + }; + + buf[0] = reg; + memcpy(&buf[1], val, len); + + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret == 1) { + ret = 0; + } else { + warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len); + ret = -EREMOTEIO; + } + return ret; +} + +/* read multiple hardware registers */ +static int rtl2830_rd(struct rtl2830_priv *priv, u8 reg, u8 *val, int len) +{ + int ret; + struct i2c_msg msg[2] = { + { + .addr = priv->cfg.i2c_addr, + .flags = 0, + .len = 1, + .buf = ®, + }, { + .addr = priv->cfg.i2c_addr, + .flags = I2C_M_RD, + .len = len, + .buf = val, + } + }; + + ret = i2c_transfer(priv->i2c, msg, 2); + if (ret == 2) { + ret = 0; + } else { + warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len); + ret = -EREMOTEIO; + } + return ret; +} + +/* write multiple registers */ +static int rtl2830_wr_regs(struct rtl2830_priv *priv, u16 reg, u8 *val, int len) +{ + int ret; + u8 reg2 = (reg >> 0) & 0xff; + u8 page = (reg >> 8) & 0xff; + + /* switch bank if needed */ + if (page != priv->page) { + ret = rtl2830_wr(priv, 0x00, &page, 1); + if (ret) + return ret; + + priv->page = page; + } + + return rtl2830_wr(priv, reg2, val, len); +} + +/* read multiple registers */ +static int rtl2830_rd_regs(struct rtl2830_priv *priv, u16 reg, u8 *val, int len) +{ + int ret; + u8 reg2 = (reg >> 0) & 0xff; + u8 page = (reg >> 8) & 0xff; + + /* switch bank if needed */ + if (page != priv->page) { + ret = rtl2830_wr(priv, 0x00, &page, 1); + if (ret) + return ret; + + priv->page = page; + } + + return rtl2830_rd(priv, reg2, val, len); +} + +#if 0 /* currently not used */ +/* write single register */ +static int rtl2830_wr_reg(struct rtl2830_priv *priv, u16 reg, u8 val) +{ + return rtl2830_wr_regs(priv, reg, &val, 1); +} +#endif + +/* read single register */ +static int rtl2830_rd_reg(struct rtl2830_priv *priv, u16 reg, u8 *val) +{ + return rtl2830_rd_regs(priv, reg, val, 1); +} + +/* write single register with mask */ +int rtl2830_wr_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 val, u8 mask) +{ + int ret; + u8 tmp; + + /* no need for read if whole reg is written */ + if (mask != 0xff) { + ret = rtl2830_rd_regs(priv, reg, &tmp, 1); + if (ret) + return ret; + + val &= mask; + tmp &= ~mask; + val |= tmp; + } + + return rtl2830_wr_regs(priv, reg, &val, 1); +} + +/* read single register with mask */ +int rtl2830_rd_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 *val, u8 mask) +{ + int ret, i; + u8 tmp; + + ret = rtl2830_rd_regs(priv, reg, &tmp, 1); + if (ret) + return ret; + + tmp &= mask; + + /* find position of the first bit */ + for (i = 0; i < 8; i++) { + if ((mask >> i) & 0x01) + break; + } + *val = tmp >> i; + + return 0; +} + +static int rtl2830_init(struct dvb_frontend *fe) +{ + struct rtl2830_priv *priv = fe->demodulator_priv; + int ret, i; + u64 num; + u8 buf[3], tmp; + u32 if_ctl; + struct rtl2830_reg_val_mask tab[] = { + { 0x00d, 0x01, 0x03 }, + { 0x00d, 0x10, 0x10 }, + { 0x104, 0x00, 0x1e }, + { 0x105, 0x80, 0x80 }, + { 0x110, 0x02, 0x03 }, + { 0x110, 0x08, 0x0c }, + { 0x17b, 0x00, 0x40 }, + { 0x17d, 0x05, 0x0f }, + { 0x17d, 0x50, 0xf0 }, + { 0x18c, 0x08, 0x0f }, + { 0x18d, 0x00, 0xc0 }, + { 0x188, 0x05, 0x0f }, + { 0x189, 0x00, 0xfc }, + { 0x2d5, 0x02, 0x02 }, + { 0x2f1, 0x02, 0x06 }, + { 0x2f1, 0x20, 0xf8 }, + { 0x16d, 0x00, 0x01 }, + { 0x1a6, 0x00, 0x80 }, + { 0x106, priv->cfg.vtop, 0x3f }, + { 0x107, priv->cfg.krf, 0x3f }, + { 0x112, 0x28, 0xff }, + { 0x103, priv->cfg.agc_targ_val, 0xff }, + { 0x00a, 0x02, 0x07 }, + { 0x140, 0x0c, 0x3c }, + { 0x140, 0x40, 0xc0 }, + { 0x15b, 0x05, 0x07 }, + { 0x15b, 0x28, 0x38 }, + { 0x15c, 0x05, 0x07 }, + { 0x15c, 0x28, 0x38 }, + { 0x115, priv->cfg.spec_inv, 0x01 }, + { 0x16f, 0x01, 0x07 }, + { 0x170, 0x18, 0x38 }, + { 0x172, 0x0f, 0x0f }, + { 0x173, 0x08, 0x38 }, + { 0x175, 0x01, 0x07 }, + { 0x176, 0x00, 0xc0 }, + }; + + for (i = 0; i < ARRAY_SIZE(tab); i++) { + ret = rtl2830_wr_reg_mask(priv, tab[i].reg, tab[i].val, + tab[i].mask); + if (ret) + goto err; + } + + ret = rtl2830_wr_regs(priv, 0x18f, "\x28\x00", 2); + if (ret) + goto err; + + ret = rtl2830_wr_regs(priv, 0x195, + "\x04\x06\x0a\x12\x0a\x12\x1e\x28", 8); + if (ret) + goto err; + + num = priv->cfg.if_dvbt % priv->cfg.xtal; + num *= 0x400000; + num = div_u64(num, priv->cfg.xtal); + num = -num; + if_ctl = num & 0x3fffff; + dbg("%s: if_ctl=%08x", __func__, if_ctl); + + ret = rtl2830_rd_reg_mask(priv, 0x119, &tmp, 0xc0); /* b[7:6] */ + if (ret) + goto err; + + buf[0] = tmp << 6; + buf[0] = (if_ctl >> 16) & 0x3f; + buf[1] = (if_ctl >> 8) & 0xff; + buf[2] = (if_ctl >> 0) & 0xff; + + ret = rtl2830_wr_regs(priv, 0x119, buf, 3); + if (ret) + goto err; + + /* TODO: spec init */ + + /* soft reset */ + ret = rtl2830_wr_reg_mask(priv, 0x101, 0x04, 0x04); + if (ret) + goto err; + + ret = rtl2830_wr_reg_mask(priv, 0x101, 0x00, 0x04); + if (ret) + goto err; + + priv->sleeping = false; + + return ret; +err: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static int rtl2830_sleep(struct dvb_frontend *fe) +{ + struct rtl2830_priv *priv = fe->demodulator_priv; + priv->sleeping = true; + return 0; +} + +int rtl2830_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms = 500; + s->step_size = fe->ops.info.frequency_stepsize * 2; + s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; + + return 0; +} + +static int rtl2830_set_frontend(struct dvb_frontend *fe) +{ + struct rtl2830_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i; + static u8 bw_params1[3][34] = { + { + 0x1f, 0xf0, 0x1f, 0xf0, 0x1f, 0xfa, 0x00, 0x17, 0x00, 0x41, + 0x00, 0x64, 0x00, 0x67, 0x00, 0x38, 0x1f, 0xde, 0x1f, 0x7a, + 0x1f, 0x47, 0x1f, 0x7c, 0x00, 0x30, 0x01, 0x4b, 0x02, 0x82, + 0x03, 0x73, 0x03, 0xcf, /* 6 MHz */ + }, { + 0x1f, 0xfa, 0x1f, 0xda, 0x1f, 0xc1, 0x1f, 0xb3, 0x1f, 0xca, + 0x00, 0x07, 0x00, 0x4d, 0x00, 0x6d, 0x00, 0x40, 0x1f, 0xca, + 0x1f, 0x4d, 0x1f, 0x2a, 0x1f, 0xb2, 0x00, 0xec, 0x02, 0x7e, + 0x03, 0xd0, 0x04, 0x53, /* 7 MHz */ + }, { + 0x00, 0x10, 0x00, 0x0e, 0x1f, 0xf7, 0x1f, 0xc9, 0x1f, 0xa0, + 0x1f, 0xa6, 0x1f, 0xec, 0x00, 0x4e, 0x00, 0x7d, 0x00, 0x3a, + 0x1f, 0x98, 0x1f, 0x10, 0x1f, 0x40, 0x00, 0x75, 0x02, 0x5f, + 0x04, 0x24, 0x04, 0xdb, /* 8 MHz */ + }, + }; + static u8 bw_params2[3][6] = { + {0xc3, 0x0c, 0x44, 0x33, 0x33, 0x30,}, /* 6 MHz */ + {0xb8, 0xe3, 0x93, 0x99, 0x99, 0x98,}, /* 7 MHz */ + {0xae, 0xba, 0xf3, 0x26, 0x66, 0x64,}, /* 8 MHz */ + }; + + + dbg("%s: frequency=%d bandwidth_hz=%d inversion=%d", __func__, + c->frequency, c->bandwidth_hz, c->inversion); + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + + switch (c->bandwidth_hz) { + case 6000000: + i = 0; + break; + case 7000000: + i = 1; + break; + case 8000000: + i = 2; + break; + default: + dbg("invalid bandwidth"); + return -EINVAL; + } + + ret = rtl2830_wr_reg_mask(priv, 0x008, i << 1, 0x06); + if (ret) + goto err; + + /* 1/2 split I2C write */ + ret = rtl2830_wr_regs(priv, 0x11c, &bw_params1[i][0], 17); + if (ret) + goto err; + + /* 2/2 split I2C write */ + ret = rtl2830_wr_regs(priv, 0x12d, &bw_params1[i][17], 17); + if (ret) + goto err; + + ret = rtl2830_wr_regs(priv, 0x19d, bw_params2[i], 6); + if (ret) + goto err; + + return ret; +err: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static int rtl2830_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct rtl2830_priv *priv = fe->demodulator_priv; + int ret; + u8 tmp; + *status = 0; + + if (priv->sleeping) + return 0; + + ret = rtl2830_rd_reg_mask(priv, 0x351, &tmp, 0x78); /* [6:3] */ + if (ret) + goto err; + + if (tmp == 11) { + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + } else if (tmp == 10) { + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI; + } + + return ret; +err: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static int rtl2830_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + *snr = 0; + return 0; +} + +static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + *ber = 0; + return 0; +} + +static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; + return 0; +} + +static int rtl2830_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + *strength = 0; + return 0; +} + +static struct dvb_frontend_ops rtl2830_ops; + +static u32 rtl2830_tuner_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static int rtl2830_tuner_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msg[], int num) +{ + struct rtl2830_priv *priv = i2c_get_adapdata(i2c_adap); + int ret; + + /* open i2c-gate */ + ret = rtl2830_wr_reg_mask(priv, 0x101, 0x08, 0x08); + if (ret) + goto err; + + ret = i2c_transfer(priv->i2c, msg, num); + if (ret < 0) + warn("tuner i2c failed=%d", ret); + + return ret; +err: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static struct i2c_algorithm rtl2830_tuner_i2c_algo = { + .master_xfer = rtl2830_tuner_i2c_xfer, + .functionality = rtl2830_tuner_i2c_func, +}; + +struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(struct dvb_frontend *fe) +{ + struct rtl2830_priv *priv = fe->demodulator_priv; + return &priv->tuner_i2c_adapter; +} +EXPORT_SYMBOL(rtl2830_get_tuner_i2c_adapter); + +static void rtl2830_release(struct dvb_frontend *fe) +{ + struct rtl2830_priv *priv = fe->demodulator_priv; + + i2c_del_adapter(&priv->tuner_i2c_adapter); + kfree(priv); +} + +struct dvb_frontend *rtl2830_attach(const struct rtl2830_config *cfg, + struct i2c_adapter *i2c) +{ + struct rtl2830_priv *priv = NULL; + int ret = 0; + u8 tmp; + + /* allocate memory for the internal state */ + priv = kzalloc(sizeof(struct rtl2830_priv), GFP_KERNEL); + if (priv == NULL) + goto err; + + /* setup the priv */ + priv->i2c = i2c; + memcpy(&priv->cfg, cfg, sizeof(struct rtl2830_config)); + + /* check if the demod is there */ + ret = rtl2830_rd_reg(priv, 0x000, &tmp); + if (ret) + goto err; + + /* create dvb_frontend */ + memcpy(&priv->fe.ops, &rtl2830_ops, sizeof(struct dvb_frontend_ops)); + priv->fe.demodulator_priv = priv; + + /* create tuner i2c adapter */ + strlcpy(priv->tuner_i2c_adapter.name, "RTL2830 tuner I2C adapter", + sizeof(priv->tuner_i2c_adapter.name)); + priv->tuner_i2c_adapter.algo = &rtl2830_tuner_i2c_algo; + priv->tuner_i2c_adapter.algo_data = NULL; + i2c_set_adapdata(&priv->tuner_i2c_adapter, priv); + if (i2c_add_adapter(&priv->tuner_i2c_adapter) < 0) { + err("tuner I2C bus could not be initialized"); + goto err; + } + + priv->sleeping = true; + + return &priv->fe; +err: + dbg("%s: failed=%d", __func__, ret); + kfree(priv); + return NULL; +} +EXPORT_SYMBOL(rtl2830_attach); + +static struct dvb_frontend_ops rtl2830_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Realtek RTL2830 (DVB-T)", + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_RECOVER | + FE_CAN_MUTE_TS + }, + + .release = rtl2830_release, + + .init = rtl2830_init, + .sleep = rtl2830_sleep, + + .get_tune_settings = rtl2830_get_tune_settings, + + .set_frontend = rtl2830_set_frontend, + + .read_status = rtl2830_read_status, + .read_snr = rtl2830_read_snr, + .read_ber = rtl2830_read_ber, + .read_ucblocks = rtl2830_read_ucblocks, + .read_signal_strength = rtl2830_read_signal_strength, +}; + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Realtek RTL2830 DVB-T demodulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/rtl2830.h b/drivers/media/dvb/frontends/rtl2830.h new file mode 100644 index 000000000000..1c6ee91749c2 --- /dev/null +++ b/drivers/media/dvb/frontends/rtl2830.h @@ -0,0 +1,97 @@ +/* + * Realtek RTL2830 DVB-T demodulator driver + * + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef RTL2830_H +#define RTL2830_H + +#include <linux/dvb/frontend.h> + +struct rtl2830_config { + /* + * Demodulator I2C address. + */ + u8 i2c_addr; + + /* + * Xtal frequency. + * Hz + * 4000000, 16000000, 25000000, 28800000 + */ + u32 xtal; + + /* + * TS output mode. + */ + u8 ts_mode; + + /* + * Spectrum inversion. + */ + bool spec_inv; + + /* + * IFs for all used modes. + * Hz + * 4570000, 4571429, 36000000, 36125000, 36166667, 44000000 + */ + u32 if_dvbt; + + /* + */ + u8 vtop; + + /* + */ + u8 krf; + + /* + */ + u8 agc_targ_val; +}; + +#if defined(CONFIG_DVB_RTL2830) || \ + (defined(CONFIG_DVB_RTL2830_MODULE) && defined(MODULE)) +extern struct dvb_frontend *rtl2830_attach( + const struct rtl2830_config *config, + struct i2c_adapter *i2c +); + +extern struct i2c_adapter *rtl2830_get_tuner_i2c_adapter( + struct dvb_frontend *fe +); +#else +static inline struct dvb_frontend *rtl2830_attach( + const struct rtl2830_config *config, + struct i2c_adapter *i2c +) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline struct i2c_adapter *rtl2830_get_tuner_i2c_adapter( + struct dvb_frontend *fe +) +{ + return NULL; +} +#endif + +#endif /* RTL2830_H */ diff --git a/drivers/media/dvb/frontends/rtl2830_priv.h b/drivers/media/dvb/frontends/rtl2830_priv.h new file mode 100644 index 000000000000..4a464761b5b8 --- /dev/null +++ b/drivers/media/dvb/frontends/rtl2830_priv.h @@ -0,0 +1,57 @@ +/* + * Realtek RTL2830 DVB-T demodulator driver + * + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef RTL2830_PRIV_H +#define RTL2830_PRIV_H + +#include "dvb_frontend.h" +#include "rtl2830.h" + +#define LOG_PREFIX "rtl2830" + +#undef dbg +#define dbg(f, arg...) \ + if (rtl2830_debug) \ + printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +#undef err +#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) +#undef info +#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +#undef warn +#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) + +struct rtl2830_priv { + struct i2c_adapter *i2c; + struct dvb_frontend fe; + struct rtl2830_config cfg; + struct i2c_adapter tuner_i2c_adapter; + + bool sleeping; + + u8 page; /* active register page */ +}; + +struct rtl2830_reg_val_mask { + u16 reg; + u8 val; + u8 mask; +}; + +#endif /* RTL2830_PRIV_H */ diff --git a/drivers/media/dvb/frontends/stb0899_drv.c b/drivers/media/dvb/frontends/stb0899_drv.c index 38565beafe23..dd08f4ac64a8 100644 --- a/drivers/media/dvb/frontends/stb0899_drv.c +++ b/drivers/media/dvb/frontends/stb0899_drv.c @@ -67,7 +67,7 @@ static const struct stb0899_tab stb0899_cn_tab[] = { * Crude linear extrapolation below -84.8dBm and above -8.0dBm. */ static const struct stb0899_tab stb0899_dvbsrf_tab[] = { - { -950, -128 }, + { -750, -128 }, { -748, -94 }, { -745, -92 }, { -735, -90 }, @@ -131,7 +131,7 @@ static const struct stb0899_tab stb0899_dvbs2rf_tab[] = { { -730, 13645 }, { -750, 13909 }, { -766, 14153 }, - { -999, 16383 } + { -950, 16383 } }; /* DVB-S2 Es/N0 quant in dB/100 vs read value * 100*/ @@ -964,6 +964,7 @@ static int stb0899_read_signal_strength(struct dvb_frontend *fe, u16 *strength) int val; u32 reg; + *strength = 0; switch (state->delsys) { case SYS_DVBS: case SYS_DSS: @@ -983,11 +984,11 @@ static int stb0899_read_signal_strength(struct dvb_frontend *fe, u16 *strength) break; case SYS_DVBS2: if (internal->lock) { - reg = STB0899_READ_S2REG(STB0899_DEMOD, IF_AGC_GAIN); + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_GAIN); val = STB0899_GETFIELD(IF_AGC_GAIN, reg); *strength = stb0899_table_lookup(stb0899_dvbs2rf_tab, ARRAY_SIZE(stb0899_dvbs2rf_tab) - 1, val); - *strength += 750; + *strength += 950; dprintk(state->verbose, FE_DEBUG, 1, "IF_AGC_GAIN = 0x%04x, C = %d * 0.1 dBm", val & 0x3fff, *strength); } @@ -1009,6 +1010,7 @@ static int stb0899_read_snr(struct dvb_frontend *fe, u16 *snr) u8 buf[2]; u32 reg; + *snr = 0; reg = stb0899_read_reg(state, STB0899_VSTATUS); switch (state->delsys) { case SYS_DVBS: @@ -1071,7 +1073,7 @@ static int stb0899_read_status(struct dvb_frontend *fe, enum fe_status *status) reg = stb0899_read_reg(state, STB0899_VSTATUS); if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) { dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_CARRIER | FE_HAS_LOCK"); - *status |= FE_HAS_CARRIER | FE_HAS_LOCK; + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; reg = stb0899_read_reg(state, STB0899_PLPARM); if (STB0899_GETFIELD(VITCURPUN, reg)) { diff --git a/drivers/media/dvb/frontends/stv0288.c b/drivers/media/dvb/frontends/stv0288.c index fb5548a82208..632b25156e4c 100644 --- a/drivers/media/dvb/frontends/stv0288.c +++ b/drivers/media/dvb/frontends/stv0288.c @@ -506,7 +506,7 @@ static int stv0288_set_frontend(struct dvb_frontend *fe) tda[1] = (unsigned char)tm; stv0288_writeregI(state, 0x2b, tda[1]); stv0288_writeregI(state, 0x2c, tda[2]); - udelay(30); + msleep(30); } state->tuner_frequency = c->frequency; state->fec_inner = FEC_AUTO; diff --git a/drivers/media/dvb/frontends/tda10071.c b/drivers/media/dvb/frontends/tda10071.c index a99205026751..c21bc92d2811 100644 --- a/drivers/media/dvb/frontends/tda10071.c +++ b/drivers/media/dvb/frontends/tda10071.c @@ -1215,7 +1215,7 @@ error: EXPORT_SYMBOL(tda10071_attach); static struct dvb_frontend_ops tda10071_ops = { - .delsys = { SYS_DVBT, SYS_DVBT2 }, + .delsys = { SYS_DVBS, SYS_DVBS2 }, .info = { .name = "NXP TDA10071", .frequency_min = 950000, diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c index 8418c02bcefe..7539a5d71029 100644 --- a/drivers/media/dvb/ngene/ngene-cards.c +++ b/drivers/media/dvb/ngene/ngene-cards.c @@ -216,6 +216,7 @@ static int demod_attach_drxk(struct ngene_channel *chan, struct drxk_config config; memset(&config, 0, sizeof(config)); + config.microcode_name = "drxk_a3.mc"; config.adr = 0x29 + (chan->number ^ 2); chan->fe = dvb_attach(drxk_attach, &config, i2c); diff --git a/drivers/media/dvb/pt1/pt1.c b/drivers/media/dvb/pt1/pt1.c index b81df5fafe26..15b35c4725f1 100644 --- a/drivers/media/dvb/pt1/pt1.c +++ b/drivers/media/dvb/pt1/pt1.c @@ -28,6 +28,7 @@ #include <linux/pci.h> #include <linux/kthread.h> #include <linux/freezer.h> +#include <linux/ratelimit.h> #include "dvbdev.h" #include "dvb_demux.h" @@ -77,6 +78,8 @@ struct pt1 { struct pt1_adapter *adaps[PT1_NR_ADAPS]; struct pt1_table *tables; struct task_struct *kthread; + int table_index; + int buf_index; struct mutex lock; int power; @@ -90,12 +93,12 @@ struct pt1_adapter { u8 *buf; int upacket_count; int packet_count; + int st_count; struct dvb_adapter adap; struct dvb_demux demux; int users; struct dmxdev dmxdev; - struct dvb_net net; struct dvb_frontend *fe; int (*orig_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage); @@ -119,7 +122,7 @@ static u32 pt1_read_reg(struct pt1 *pt1, int reg) return readl(pt1->regs + reg * 4); } -static int pt1_nr_tables = 64; +static int pt1_nr_tables = 8; module_param_named(nr_tables, pt1_nr_tables, int, 0); static void pt1_increment_table_count(struct pt1 *pt1) @@ -264,6 +267,7 @@ static int pt1_filter(struct pt1 *pt1, struct pt1_buffer_page *page) struct pt1_adapter *adap; int offset; u8 *buf; + int sc; if (!page->upackets[PT1_NR_UPACKETS - 1]) return 0; @@ -280,6 +284,16 @@ static int pt1_filter(struct pt1 *pt1, struct pt1_buffer_page *page) else if (!adap->upacket_count) continue; + if (upacket >> 24 & 1) + printk_ratelimited(KERN_INFO "earth-pt1: device " + "buffer overflowing. table[%d] buf[%d]\n", + pt1->table_index, pt1->buf_index); + sc = upacket >> 26 & 0x7; + if (adap->st_count != -1 && sc != ((adap->st_count + 1) & 0x7)) + printk_ratelimited(KERN_INFO "earth-pt1: data loss" + " in streamID(adapter)[%d]\n", index); + adap->st_count = sc; + buf = adap->buf; offset = adap->packet_count * 188 + adap->upacket_count * 3; buf[offset] = upacket >> 16; @@ -303,30 +317,25 @@ static int pt1_filter(struct pt1 *pt1, struct pt1_buffer_page *page) static int pt1_thread(void *data) { struct pt1 *pt1; - int table_index; - int buf_index; struct pt1_buffer_page *page; pt1 = data; set_freezable(); - table_index = 0; - buf_index = 0; - while (!kthread_should_stop()) { try_to_freeze(); - page = pt1->tables[table_index].bufs[buf_index].page; + page = pt1->tables[pt1->table_index].bufs[pt1->buf_index].page; if (!pt1_filter(pt1, page)) { schedule_timeout_interruptible((HZ + 999) / 1000); continue; } - if (++buf_index >= PT1_NR_BUFS) { + if (++pt1->buf_index >= PT1_NR_BUFS) { pt1_increment_table_count(pt1); - buf_index = 0; - if (++table_index >= pt1_nr_tables) - table_index = 0; + pt1->buf_index = 0; + if (++pt1->table_index >= pt1_nr_tables) + pt1->table_index = 0; } } @@ -477,21 +486,60 @@ err: return ret; } +static int pt1_start_polling(struct pt1 *pt1) +{ + int ret = 0; + + mutex_lock(&pt1->lock); + if (!pt1->kthread) { + pt1->kthread = kthread_run(pt1_thread, pt1, "earth-pt1"); + if (IS_ERR(pt1->kthread)) { + ret = PTR_ERR(pt1->kthread); + pt1->kthread = NULL; + } + } + mutex_unlock(&pt1->lock); + return ret; +} + static int pt1_start_feed(struct dvb_demux_feed *feed) { struct pt1_adapter *adap; adap = container_of(feed->demux, struct pt1_adapter, demux); - if (!adap->users++) + if (!adap->users++) { + int ret; + + ret = pt1_start_polling(adap->pt1); + if (ret) + return ret; pt1_set_stream(adap->pt1, adap->index, 1); + } return 0; } +static void pt1_stop_polling(struct pt1 *pt1) +{ + int i, count; + + mutex_lock(&pt1->lock); + for (i = 0, count = 0; i < PT1_NR_ADAPS; i++) + count += pt1->adaps[i]->users; + + if (count == 0 && pt1->kthread) { + kthread_stop(pt1->kthread); + pt1->kthread = NULL; + } + mutex_unlock(&pt1->lock); +} + static int pt1_stop_feed(struct dvb_demux_feed *feed) { struct pt1_adapter *adap; adap = container_of(feed->demux, struct pt1_adapter, demux); - if (!--adap->users) + if (!--adap->users) { pt1_set_stream(adap->pt1, adap->index, 0); + pt1_stop_polling(adap->pt1); + } return 0; } @@ -575,7 +623,6 @@ static int pt1_wakeup(struct dvb_frontend *fe) static void pt1_free_adapter(struct pt1_adapter *adap) { - dvb_net_release(&adap->net); adap->demux.dmx.close(&adap->demux.dmx); dvb_dmxdev_release(&adap->dmxdev); dvb_dmx_release(&adap->demux); @@ -616,6 +663,7 @@ pt1_alloc_adapter(struct pt1 *pt1) adap->buf = buf; adap->upacket_count = 0; adap->packet_count = 0; + adap->st_count = -1; dvb_adap = &adap->adap; dvb_adap->priv = adap; @@ -644,8 +692,6 @@ pt1_alloc_adapter(struct pt1 *pt1) if (ret < 0) goto err_dmx_release; - dvb_net_init(dvb_adap, &adap->net, &demux->dmx); - return adap; err_dmx_release: @@ -1020,7 +1066,8 @@ static void __devexit pt1_remove(struct pci_dev *pdev) pt1 = pci_get_drvdata(pdev); regs = pt1->regs; - kthread_stop(pt1->kthread); + if (pt1->kthread) + kthread_stop(pt1->kthread); pt1_cleanup_tables(pt1); pt1_cleanup_frontends(pt1); pt1_disable_ram(pt1); @@ -1043,7 +1090,6 @@ pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) void __iomem *regs; struct pt1 *pt1; struct i2c_adapter *i2c_adap; - struct task_struct *kthread; ret = pci_enable_device(pdev); if (ret < 0) @@ -1139,17 +1185,8 @@ pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (ret < 0) goto err_pt1_cleanup_frontends; - kthread = kthread_run(pt1_thread, pt1, "pt1"); - if (IS_ERR(kthread)) { - ret = PTR_ERR(kthread); - goto err_pt1_cleanup_tables; - } - - pt1->kthread = kthread; return 0; -err_pt1_cleanup_tables: - pt1_cleanup_tables(pt1); err_pt1_cleanup_frontends: pt1_cleanup_frontends(pt1); err_pt1_disable_ram: diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c index 7b42ace419d9..421cf73858d3 100644 --- a/drivers/media/media-devnode.c +++ b/drivers/media/media-devnode.c @@ -312,7 +312,7 @@ static void __exit media_devnode_exit(void) unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES); } -module_init(media_devnode_init) +subsys_initcall(media_devnode_init); module_exit(media_devnode_exit) MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index e954781c90bf..8db2d7f4b52a 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -43,7 +43,7 @@ config USB_DSBR config RADIO_MAXIRADIO tristate "Guillemot MAXI Radio FM 2000 radio" - depends on VIDEO_V4L2 && PCI + depends on VIDEO_V4L2 && PCI && SND ---help--- Choose Y here if you have this radio card. This card may also be found as Gemtek PCI FM. @@ -80,6 +80,16 @@ config RADIO_SI4713 To compile this driver as a module, choose M here: the module will be called radio-si4713. +config USB_KEENE + tristate "Keene FM Transmitter USB support" + depends on USB && VIDEO_V4L2 + ---help--- + Say Y here if you want to connect this type of FM transmitter + to your computer's USB port. + + To compile this driver as a module, choose M here: the + module will be called radio-keene. + config RADIO_TEA5764 tristate "TEA5764 I2C FM radio support" depends on I2C && VIDEO_V4L2 @@ -167,6 +177,10 @@ menuconfig V4L_RADIO_ISA_DRIVERS if V4L_RADIO_ISA_DRIVERS +config RADIO_ISA + depends on ISA + tristate + config RADIO_CADET tristate "ADS Cadet AM/FM Tuner" depends on ISA && VIDEO_V4L2 @@ -174,20 +188,13 @@ config RADIO_CADET Choose Y here if you have one of these AM/FM radio cards, and then fill in the port address below. - In order to control your radio card, you will need to use programs - that are compatible with the Video For Linux API. Information on - this API and pointers to "v4l" programs may be found at - <file:Documentation/video4linux/API.html>. - - Further documentation on this driver can be found on the WWW at - <http://linux.blackhawke.net/cadet/>. - To compile this driver as a module, choose M here: the module will be called radio-cadet. config RADIO_RTRACK tristate "AIMSlab RadioTrack (aka RadioReveal) support" depends on ISA && VIDEO_V4L2 + select RADIO_ISA ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address below. @@ -201,11 +208,7 @@ config RADIO_RTRACK You must also pass the module a suitable io parameter, 0x248 has been reported to be used by these cards. - In order to control your radio card, you will need to use programs - that are compatible with the Video For Linux API. Information on - this API and pointers to "v4l" programs may be found at - <file:Documentation/video4linux/API.html>. More information is - contained in the file + More information is contained in the file <file:Documentation/video4linux/radiotrack.txt>. To compile this driver as a module, choose M here: the @@ -214,7 +217,7 @@ config RADIO_RTRACK config RADIO_RTRACK_PORT hex "RadioTrack i/o port (0x20f or 0x30f)" depends on RADIO_RTRACK=y - default "20f" + default "30f" help Enter either 0x30f or 0x20f here. The card default is 0x30f, if you haven't changed the jumper setting on the card. @@ -222,14 +225,14 @@ config RADIO_RTRACK_PORT config RADIO_RTRACK2 tristate "AIMSlab RadioTrack II support" depends on ISA && VIDEO_V4L2 + select RADIO_ISA ---help--- Choose Y here if you have this FM radio card, and then fill in the port address below. - In order to control your radio card, you will need to use programs - that are compatible with the Video For Linux API. Information on - this API and pointers to "v4l" programs may be found at - <file:Documentation/video4linux/API.html>. + Note: this driver hasn't been tested since a long time due to lack + of hardware. If you have this hardware, then please contact the + linux-media mailinglist. To compile this driver as a module, choose M here: the module will be called radio-rtrack2. @@ -245,15 +248,11 @@ config RADIO_RTRACK2_PORT config RADIO_AZTECH tristate "Aztech/Packard Bell Radio" depends on ISA && VIDEO_V4L2 + select RADIO_ISA ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address below. - In order to control your radio card, you will need to use programs - that are compatible with the Video For Linux API. Information on - this API and pointers to "v4l" programs may be found at - <file:Documentation/video4linux/API.html>. - To compile this driver as a module, choose M here: the module will be called radio-aztech. @@ -269,6 +268,7 @@ config RADIO_AZTECH_PORT config RADIO_GEMTEK tristate "GemTek Radio card (or compatible) support" depends on ISA && VIDEO_V4L2 + select RADIO_ISA ---help--- Choose Y here if you have this FM radio card, and then fill in the I/O port address and settings below. The following cards either have @@ -278,23 +278,21 @@ config RADIO_GEMTEK - Typhoon Radio card (some models) - Hama Radio card - In order to control your radio card, you will need to use programs - that are compatible with the Video For Linux API. Information on - this API and pointers to "v4l" programs may be found at - <file:Documentation/video4linux/API.html>. - To compile this driver as a module, choose M here: the module will be called radio-gemtek. config RADIO_GEMTEK_PORT - hex "Fixed I/O port (0x20c, 0x30c, 0x24c, 0x34c, 0c24c or 0x28c)" + hex "Fixed I/O port (0x20c, 0x30c, 0x24c, 0x34c, 0x248 or 0x28c)" depends on RADIO_GEMTEK=y default "34c" help - Enter either 0x20c, 0x30c, 0x24c or 0x34c here. The card default is - 0x34c, if you haven't changed the jumper setting on the card. On - Sound Vision 16 Gold PnP with FM Radio (ESS1869+FM Gemtek), the I/O + Enter either 0x20c, 0x30c, 0x24c, 0x34c, 0x248 or 0x28c here. The + card default is 0x34c, if you haven't changed the jumper setting + on the card. + + On Sound Vision 16 Gold PnP with FM Radio (ESS1869+FM Gemtek), the I/O port is 0x20c, 0x248 or 0x28c. + If automatic I/O port probing is enabled this port will be used only in case of automatic probing failure, ie. as a fallback. @@ -318,11 +316,6 @@ config RADIO_MIROPCM20 sound card driver "Miro miroSOUND PCM1pro/PCM12/PCM20radio" as this is required for the radio-miropcm20. - In order to control your radio card, you will need to use programs - that are compatible with the Video For Linux API. Information on - this API and pointers to "v4l" programs may be found at - <file:Documentation/video4linux/API.html>. - To compile this driver as a module, choose M here: the module will be called radio-miropcm20. @@ -332,11 +325,6 @@ config RADIO_SF16FMI ---help--- Choose Y here if you have one of these FM radio cards. - In order to control your radio card, you will need to use programs - that are compatible with the Video For Linux API. Information on - this API and pointers to "v4l" programs may be found at - <file:Documentation/video4linux/API.html>. - To compile this driver as a module, choose M here: the module will be called radio-sf16fmi. @@ -346,50 +334,35 @@ config RADIO_SF16FMR2 ---help--- Choose Y here if you have one of these FM radio cards. - In order to control your radio card, you will need to use programs - that are compatible with the Video For Linux API. Information on - this API and pointers to "v4l" programs may be found on the WWW at - <http://roadrunner.swansea.uk.linux.org/v4l.shtml>. - To compile this driver as a module, choose M here: the module will be called radio-sf16fmr2. config RADIO_TERRATEC tristate "TerraTec ActiveRadio ISA Standalone" depends on ISA && VIDEO_V4L2 + select RADIO_ISA ---help--- - Choose Y here if you have this FM radio card, and then fill in the - port address below. (TODO) + Choose Y here if you have this FM radio card. - Note: This driver is in its early stages. Right now volume and - frequency control and muting works at least for me, but - unfortunately I have not found anybody who wants to use this card - with Linux. So if it is this what YOU are trying to do right now, - PLEASE DROP ME A NOTE!! Rolf Offermanns <rolf@offermanns.de>. - - In order to control your radio card, you will need to use programs - that are compatible with the Video For Linux API. Information on - this API and pointers to "v4l" programs may be found at - <file:Documentation/video4linux/API.html>. + Note: this driver hasn't been tested since a long time due to lack + of hardware. If you have this hardware, then please contact the + linux-media mailinglist. To compile this driver as a module, choose M here: the module will be called radio-terratec. -config RADIO_TERRATEC_PORT - hex "Terratec i/o port (normally 0x590)" - depends on RADIO_TERRATEC=y - default "590" - help - Fill in the I/O port of your TerraTec FM radio card. If unsure, go - with the default. - config RADIO_TRUST tristate "Trust FM radio card" depends on ISA && VIDEO_V4L2 + select RADIO_ISA help This is a driver for the Trust FM radio cards. Say Y if you have such a card and want to use it under Linux. + Note: this driver hasn't been tested since a long time due to lack + of hardware. If you have this hardware, then please contact the + linux-media mailinglist. + To compile this driver as a module, choose M here: the module will be called radio-trust. @@ -404,14 +377,14 @@ config RADIO_TRUST_PORT config RADIO_TYPHOON tristate "Typhoon Radio (a.k.a. EcoRadio)" depends on ISA && VIDEO_V4L2 + select RADIO_ISA ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address and the frequency used for muting below. - In order to control your radio card, you will need to use programs - that are compatible with the Video For Linux API. Information on - this API and pointers to "v4l" programs may be found at - <file:Documentation/video4linux/API.html>. + Note: this driver hasn't been tested since a long time due to lack + of hardware. If you have this hardware, then please contact the + linux-media mailinglist. To compile this driver as a module, choose M here: the module will be called radio-typhoon. @@ -438,14 +411,14 @@ config RADIO_TYPHOON_MUTEFREQ config RADIO_ZOLTRIX tristate "Zoltrix Radio" depends on ISA && VIDEO_V4L2 + select RADIO_ISA ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address below. - In order to control your radio card, you will need to use programs - that are compatible with the Video For Linux API. Information on - this API and pointers to "v4l" programs may be found at - <file:Documentation/video4linux/API.html>. + Note: this driver hasn't been tested since a long time due to lack + of hardware. If you have this hardware, then please contact the + linux-media mailinglist. To compile this driver as a module, choose M here: the module will be called radio-zoltrix. diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index 390daf94d847..ca8c7d134b95 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -2,6 +2,7 @@ # Makefile for the kernel character device drivers. # +obj-$(CONFIG_RADIO_ISA) += radio-isa.o obj-$(CONFIG_RADIO_AZTECH) += radio-aztech.o obj-$(CONFIG_RADIO_RTRACK2) += radio-rtrack2.o obj-$(CONFIG_RADIO_SF16FMI) += radio-sf16fmi.o @@ -20,6 +21,7 @@ obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_RADIO_SI470X) += si470x/ obj-$(CONFIG_USB_MR800) += radio-mr800.o +obj-$(CONFIG_USB_KEENE) += radio-keene.o obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o obj-$(CONFIG_RADIO_TEF6862) += tef6862.o diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c index 1c3f8440a55c..98e0c8c20312 100644 --- a/drivers/media/radio/radio-aimslab.c +++ b/drivers/media/radio/radio-aimslab.c @@ -1,16 +1,13 @@ -/* radiotrack (radioreveal) driver for Linux radio support - * (c) 1997 M. Kirkwood +/* + * AimsLab RadioTrack (aka RadioVeveal) driver + * + * Copyright 1997 M. Kirkwood + * + * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com> * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk> * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> * - * History: - * 1999-02-24 Russell Kroll <rkroll@exploits.org> - * Fine tuning/VIDEO_TUNER_LOW - * Frequency range expanded to start at 87 MHz - * - * TODO: Allow for more than one of these foolish entities :-) - * * Notes on the hardware (reverse engineered from other peoples' * reverse engineering of AIMS' code :-) * @@ -26,6 +23,7 @@ * wait(a_wee_while); * out(port, stop_changing_the_volume); * + * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool. */ #include <linux/module.h> /* Modules */ @@ -34,401 +32,179 @@ #include <linux/delay.h> /* msleep */ #include <linux/videodev2.h> /* kernel radio structs */ #include <linux/io.h> /* outb, outb_p */ +#include <linux/slab.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include "radio-isa.h" -MODULE_AUTHOR("M.Kirkwood"); +MODULE_AUTHOR("M. Kirkwood"); MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card."); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.0.3"); +MODULE_VERSION("1.0.0"); #ifndef CONFIG_RADIO_RTRACK_PORT #define CONFIG_RADIO_RTRACK_PORT -1 #endif -static int io = CONFIG_RADIO_RTRACK_PORT; -static int radio_nr = -1; +#define RTRACK_MAX 2 -module_param(io, int, 0); -MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)"); -module_param(radio_nr, int, 0); +static int io[RTRACK_MAX] = { [0] = CONFIG_RADIO_RTRACK_PORT, + [1 ... (RTRACK_MAX - 1)] = -1 }; +static int radio_nr[RTRACK_MAX] = { [0 ... (RTRACK_MAX - 1)] = -1 }; -struct rtrack -{ - struct v4l2_device v4l2_dev; - struct video_device vdev; - int port; +module_param_array(io, int, NULL, 0444); +MODULE_PARM_DESC(io, "I/O addresses of the RadioTrack card (0x20f or 0x30f)"); +module_param_array(radio_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_nr, "Radio device numbers"); + +struct rtrack { + struct radio_isa_card isa; int curvol; - unsigned long curfreq; - int muted; - int io; - struct mutex lock; }; -static struct rtrack rtrack_card; - -/* local things */ - -static void rt_decvol(struct rtrack *rt) -{ - outb(0x58, rt->io); /* volume down + sigstr + on */ - msleep(100); - outb(0xd8, rt->io); /* volume steady + sigstr + on */ -} - -static void rt_incvol(struct rtrack *rt) -{ - outb(0x98, rt->io); /* volume up + sigstr + on */ - msleep(100); - outb(0xd8, rt->io); /* volume steady + sigstr + on */ -} - -static void rt_mute(struct rtrack *rt) -{ - rt->muted = 1; - mutex_lock(&rt->lock); - outb(0xd0, rt->io); /* volume steady, off */ - mutex_unlock(&rt->lock); -} - -static int rt_setvol(struct rtrack *rt, int vol) +static struct radio_isa_card *rtrack_alloc(void) { - int i; - - mutex_lock(&rt->lock); - - if (vol == rt->curvol) { /* requested volume = current */ - if (rt->muted) { /* user is unmuting the card */ - rt->muted = 0; - outb(0xd8, rt->io); /* enable card */ - } - mutex_unlock(&rt->lock); - return 0; - } - - if (vol == 0) { /* volume = 0 means mute the card */ - outb(0x48, rt->io); /* volume down but still "on" */ - msleep(2000); /* make sure it's totally down */ - outb(0xd0, rt->io); /* volume steady, off */ - rt->curvol = 0; /* track the volume state! */ - mutex_unlock(&rt->lock); - return 0; - } + struct rtrack *rt = kzalloc(sizeof(struct rtrack), GFP_KERNEL); - rt->muted = 0; - if (vol > rt->curvol) - for (i = rt->curvol; i < vol; i++) - rt_incvol(rt); - else - for (i = rt->curvol; i > vol; i--) - rt_decvol(rt); - - rt->curvol = vol; - mutex_unlock(&rt->lock); - return 0; + if (rt) + rt->curvol = 0xff; + return rt ? &rt->isa : NULL; } -/* the 128+64 on these outb's is to keep the volume stable while tuning - * without them, the volume _will_ creep up with each frequency change - * and bit 4 (+16) is to keep the signal strength meter enabled +/* The 128+64 on these outb's is to keep the volume stable while tuning. + * Without them, the volume _will_ creep up with each frequency change + * and bit 4 (+16) is to keep the signal strength meter enabled. */ -static void send_0_byte(struct rtrack *rt) +static void send_0_byte(struct radio_isa_card *isa, int on) { - if (rt->curvol == 0 || rt->muted) { - outb_p(128+64+16+ 1, rt->io); /* wr-enable + data low */ - outb_p(128+64+16+2+1, rt->io); /* clock */ - } - else { - outb_p(128+64+16+8+ 1, rt->io); /* on + wr-enable + data low */ - outb_p(128+64+16+8+2+1, rt->io); /* clock */ - } + outb_p(128+64+16+on+1, isa->io); /* wr-enable + data low */ + outb_p(128+64+16+on+2+1, isa->io); /* clock */ msleep(1); } -static void send_1_byte(struct rtrack *rt) +static void send_1_byte(struct radio_isa_card *isa, int on) { - if (rt->curvol == 0 || rt->muted) { - outb_p(128+64+16+4 +1, rt->io); /* wr-enable+data high */ - outb_p(128+64+16+4+2+1, rt->io); /* clock */ - } - else { - outb_p(128+64+16+8+4 +1, rt->io); /* on+wr-enable+data high */ - outb_p(128+64+16+8+4+2+1, rt->io); /* clock */ - } - + outb_p(128+64+16+on+4+1, isa->io); /* wr-enable+data high */ + outb_p(128+64+16+on+4+2+1, isa->io); /* clock */ msleep(1); } -static int rt_setfreq(struct rtrack *rt, unsigned long freq) +static int rtrack_s_frequency(struct radio_isa_card *isa, u32 freq) { + int on = v4l2_ctrl_g_ctrl(isa->mute) ? 0 : 8; int i; - mutex_lock(&rt->lock); /* Stop other ops interfering */ - - rt->curfreq = freq; - - /* now uses VIDEO_TUNER_LOW for fine tuning */ - freq += 171200; /* Add 10.7 MHz IF */ freq /= 800; /* Convert to 50 kHz units */ - send_0_byte(rt); /* 0: LSB of frequency */ + send_0_byte(isa, on); /* 0: LSB of frequency */ for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ if (freq & (1 << i)) - send_1_byte(rt); + send_1_byte(isa, on); else - send_0_byte(rt); - - send_0_byte(rt); /* 14: test bit - always 0 */ - send_0_byte(rt); /* 15: test bit - always 0 */ - - send_0_byte(rt); /* 16: band data 0 - always 0 */ - send_0_byte(rt); /* 17: band data 1 - always 0 */ - send_0_byte(rt); /* 18: band data 2 - always 0 */ - send_0_byte(rt); /* 19: time base - always 0 */ - - send_0_byte(rt); /* 20: spacing (0 = 25 kHz) */ - send_1_byte(rt); /* 21: spacing (1 = 25 kHz) */ - send_0_byte(rt); /* 22: spacing (0 = 25 kHz) */ - send_1_byte(rt); /* 23: AM/FM (FM = 1, always) */ - - if (rt->curvol == 0 || rt->muted) - outb(0xd0, rt->io); /* volume steady + sigstr */ - else - outb(0xd8, rt->io); /* volume steady + sigstr + on */ - - mutex_unlock(&rt->lock); - - return 0; -} - -static int rt_getsigstr(struct rtrack *rt) -{ - int sig = 1; - - mutex_lock(&rt->lock); - if (inb(rt->io) & 2) /* bit set = no signal present */ - sig = 0; - mutex_unlock(&rt->lock); - return sig; -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *v) -{ - strlcpy(v->driver, "radio-aimslab", sizeof(v->driver)); - strlcpy(v->card, "RadioTrack", sizeof(v->card)); - strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - return 0; -} - -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - struct rtrack *rt = video_drvdata(file); + send_0_byte(isa, on); - if (v->index > 0) - return -EINVAL; + send_0_byte(isa, on); /* 14: test bit - always 0 */ + send_0_byte(isa, on); /* 15: test bit - always 0 */ - strlcpy(v->name, "FM", sizeof(v->name)); - v->type = V4L2_TUNER_RADIO; - v->rangelow = 87 * 16000; - v->rangehigh = 108 * 16000; - v->rxsubchans = V4L2_TUNER_SUB_MONO; - v->capability = V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal = 0xffff * rt_getsigstr(rt); - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - return v->index ? -EINVAL : 0; -} - -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct rtrack *rt = video_drvdata(file); - - if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) - return -EINVAL; - rt_setfreq(rt, f->frequency); - return 0; -} + send_0_byte(isa, on); /* 16: band data 0 - always 0 */ + send_0_byte(isa, on); /* 17: band data 1 - always 0 */ + send_0_byte(isa, on); /* 18: band data 2 - always 0 */ + send_0_byte(isa, on); /* 19: time base - always 0 */ -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct rtrack *rt = video_drvdata(file); + send_0_byte(isa, on); /* 20: spacing (0 = 25 kHz) */ + send_1_byte(isa, on); /* 21: spacing (1 = 25 kHz) */ + send_0_byte(isa, on); /* 22: spacing (0 = 25 kHz) */ + send_1_byte(isa, on); /* 23: AM/FM (FM = 1, always) */ - if (f->tuner != 0) - return -EINVAL; - f->type = V4L2_TUNER_RADIO; - f->frequency = rt->curfreq; + outb(0xd0 + on, isa->io); /* volume steady + sigstr */ return 0; } -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) +static u32 rtrack_g_signal(struct radio_isa_card *isa) { - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff); - } - return -EINVAL; + /* bit set = no signal present */ + return 0xffff * !(inb(isa->io) & 2); } -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) +static int rtrack_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) { - struct rtrack *rt = video_drvdata(file); + struct rtrack *rt = container_of(isa, struct rtrack, isa); + int curvol = rt->curvol; - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value = rt->muted; - return 0; - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = rt->curvol; + if (mute) { + outb(0xd0, isa->io); /* volume steady + sigstr + off */ return 0; } - return -EINVAL; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct rtrack *rt = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) - rt_mute(rt); - else - rt_setvol(rt, rt->curvol); - return 0; - case V4L2_CID_AUDIO_VOLUME: - rt_setvol(rt, ctrl->value); - return 0; + if (vol == 0) { /* volume = 0 means mute the card */ + outb(0x48, isa->io); /* volume down but still "on" */ + msleep(curvol * 3); /* make sure it's totally down */ + } else if (curvol < vol) { + outb(0x98, isa->io); /* volume up + sigstr + on */ + for (; curvol < vol; curvol++) + udelay(3000); + } else if (curvol > vol) { + outb(0x58, isa->io); /* volume down + sigstr + on */ + for (; curvol > vol; curvol--) + udelay(3000); } - return -EINVAL; -} - -static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; + outb(0xd8, isa->io); /* volume steady + sigstr + on */ + rt->curvol = vol; return 0; } -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +/* Mute card - prevents noisy bootups */ +static int rtrack_initialize(struct radio_isa_card *isa) { - return i ? -EINVAL : 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - a->index = 0; - strlcpy(a->name, "Radio", sizeof(a->name)); - a->capability = V4L2_AUDCAP_STEREO; + /* this ensures that the volume is all the way up */ + outb(0x90, isa->io); /* volume up but still "on" */ + msleep(3000); /* make sure it's totally up */ + outb(0xc0, isa->io); /* steady volume, mute card */ return 0; } -static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - return a->index ? -EINVAL : 0; -} - -static const struct v4l2_file_operations rtrack_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = video_ioctl2, +static const struct radio_isa_ops rtrack_ops = { + .alloc = rtrack_alloc, + .init = rtrack_initialize, + .s_mute_volume = rtrack_s_mute_volume, + .s_frequency = rtrack_s_frequency, + .g_signal = rtrack_g_signal, }; -static const struct v4l2_ioctl_ops rtrack_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, +static const int rtrack_ioports[] = { 0x20f, 0x30f }; + +static struct radio_isa_driver rtrack_driver = { + .driver = { + .match = radio_isa_match, + .probe = radio_isa_probe, + .remove = radio_isa_remove, + .driver = { + .name = "radio-aimslab", + }, + }, + .io_params = io, + .radio_nr_params = radio_nr, + .io_ports = rtrack_ioports, + .num_of_io_ports = ARRAY_SIZE(rtrack_ioports), + .region_size = 2, + .card = "AIMSlab RadioTrack/RadioReveal", + .ops = &rtrack_ops, + .has_stereo = true, + .max_volume = 0xff, }; static int __init rtrack_init(void) { - struct rtrack *rt = &rtrack_card; - struct v4l2_device *v4l2_dev = &rt->v4l2_dev; - int res; - - strlcpy(v4l2_dev->name, "rtrack", sizeof(v4l2_dev->name)); - rt->io = io; - - if (rt->io == -1) { - v4l2_err(v4l2_dev, "you must set an I/O address with io=0x20f or 0x30f\n"); - return -EINVAL; - } - - if (!request_region(rt->io, 2, "rtrack")) { - v4l2_err(v4l2_dev, "port 0x%x already in use\n", rt->io); - return -EBUSY; - } - - res = v4l2_device_register(NULL, v4l2_dev); - if (res < 0) { - release_region(rt->io, 2); - v4l2_err(v4l2_dev, "could not register v4l2_device\n"); - return res; - } - - strlcpy(rt->vdev.name, v4l2_dev->name, sizeof(rt->vdev.name)); - rt->vdev.v4l2_dev = v4l2_dev; - rt->vdev.fops = &rtrack_fops; - rt->vdev.ioctl_ops = &rtrack_ioctl_ops; - rt->vdev.release = video_device_release_empty; - video_set_drvdata(&rt->vdev, rt); - - /* Set up the I/O locking */ - - mutex_init(&rt->lock); - - /* mute card - prevents noisy bootups */ - - /* this ensures that the volume is all the way down */ - outb(0x48, rt->io); /* volume down but still "on" */ - msleep(2000); /* make sure it's totally down */ - outb(0xc0, rt->io); /* steady volume, mute card */ - - if (video_register_device(&rt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { - v4l2_device_unregister(&rt->v4l2_dev); - release_region(rt->io, 2); - return -EINVAL; - } - v4l2_info(v4l2_dev, "AIMSlab RadioTrack/RadioReveal card driver.\n"); - - return 0; + return isa_register_driver(&rtrack_driver.driver, RTRACK_MAX); } static void __exit rtrack_exit(void) { - struct rtrack *rt = &rtrack_card; - - video_unregister_device(&rt->vdev); - v4l2_device_unregister(&rt->v4l2_dev); - release_region(rt->io, 2); + isa_unregister_driver(&rtrack_driver.driver); } module_init(rtrack_init); module_exit(rtrack_exit); - diff --git a/drivers/media/radio/radio-aztech.c b/drivers/media/radio/radio-aztech.c index eed7b0840734..177bcbd7a7c1 100644 --- a/drivers/media/radio/radio-aztech.c +++ b/drivers/media/radio/radio-aztech.c @@ -1,5 +1,7 @@ -/* radio-aztech.c - Aztech radio card driver for Linux 2.2 +/* + * radio-aztech.c - Aztech radio card driver * + * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@xs4all.nl> * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> * Adapted to support the Video for Linux API by * Russell Kroll <rkroll@exploits.org>. Based on original tuner code by: @@ -10,19 +12,7 @@ * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) * William McGrath (wmcgrath@twilight.vtc.vsc.edu) * - * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/ - * along with more information on the card itself. - * - * History: - * 1999-02-24 Russell Kroll <rkroll@exploits.org> - * Fine tuning/VIDEO_TUNER_LOW - * Range expanded to 87-108 MHz (from 87.9-107.8) - * - * Notable changes from the original source: - * - includes stripped down to the essentials - * - for loops used as delays replaced with udelay() - * - #defines removed, changed to static values - * - tuning structure changed - no more character arrays, other changes + * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool. */ #include <linux/module.h> /* Modules */ @@ -31,126 +21,72 @@ #include <linux/delay.h> /* udelay */ #include <linux/videodev2.h> /* kernel radio structs */ #include <linux/io.h> /* outb, outb_p */ +#include <linux/slab.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include "radio-isa.h" MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); MODULE_DESCRIPTION("A driver for the Aztech radio card."); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.0.3"); +MODULE_VERSION("1.0.0"); /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */ - #ifndef CONFIG_RADIO_AZTECH_PORT #define CONFIG_RADIO_AZTECH_PORT -1 #endif -static int io = CONFIG_RADIO_AZTECH_PORT; -static int radio_nr = -1; -static int radio_wait_time = 1000; +#define AZTECH_MAX 2 -module_param(io, int, 0); -module_param(radio_nr, int, 0); -MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)"); +static int io[AZTECH_MAX] = { [0] = CONFIG_RADIO_AZTECH_PORT, + [1 ... (AZTECH_MAX - 1)] = -1 }; +static int radio_nr[AZTECH_MAX] = { [0 ... (AZTECH_MAX - 1)] = -1 }; +static const int radio_wait_time = 1000; -struct aztech -{ - struct v4l2_device v4l2_dev; - struct video_device vdev; - int io; +module_param_array(io, int, NULL, 0444); +MODULE_PARM_DESC(io, "I/O addresses of the Aztech card (0x350 or 0x358)"); +module_param_array(radio_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_nr, "Radio device numbers"); + +struct aztech { + struct radio_isa_card isa; int curvol; - unsigned long curfreq; - int stereo; - struct mutex lock; }; -static struct aztech aztech_card; - -static int volconvert(int level) -{ - level >>= 14; /* Map 16bits down to 2 bit */ - level &= 3; - - /* convert to card-friendly values */ - switch (level) { - case 0: - return 0; - case 1: - return 1; - case 2: - return 4; - case 3: - return 5; - } - return 0; /* Quieten gcc */ -} - static void send_0_byte(struct aztech *az) { udelay(radio_wait_time); - outb_p(2 + volconvert(az->curvol), az->io); - outb_p(64 + 2 + volconvert(az->curvol), az->io); + outb_p(2 + az->curvol, az->isa.io); + outb_p(64 + 2 + az->curvol, az->isa.io); } static void send_1_byte(struct aztech *az) { - udelay (radio_wait_time); - outb_p(128 + 2 + volconvert(az->curvol), az->io); - outb_p(128 + 64 + 2 + volconvert(az->curvol), az->io); -} - -static int az_setvol(struct aztech *az, int vol) -{ - mutex_lock(&az->lock); - outb(volconvert(vol), az->io); - mutex_unlock(&az->lock); - return 0; -} - -/* thanks to Michael Dwyer for giving me a dose of clues in - * the signal strength department.. - * - * This card has a stereo bit - bit 0 set = mono, not set = stereo - * It also has a "signal" bit - bit 1 set = bad signal, not set = good - * - */ - -static int az_getsigstr(struct aztech *az) -{ - int sig = 1; - - mutex_lock(&az->lock); - if (inb(az->io) & 2) /* bit set = no signal present */ - sig = 0; - mutex_unlock(&az->lock); - return sig; + udelay(radio_wait_time); + outb_p(128 + 2 + az->curvol, az->isa.io); + outb_p(128 + 64 + 2 + az->curvol, az->isa.io); } -static int az_getstereo(struct aztech *az) +static struct radio_isa_card *aztech_alloc(void) { - int stereo = 1; + struct aztech *az = kzalloc(sizeof(*az), GFP_KERNEL); - mutex_lock(&az->lock); - if (inb(az->io) & 1) /* bit set = mono */ - stereo = 0; - mutex_unlock(&az->lock); - return stereo; + return az ? &az->isa : NULL; } -static int az_setfreq(struct aztech *az, unsigned long frequency) +static int aztech_s_frequency(struct radio_isa_card *isa, u32 freq) { + struct aztech *az = container_of(isa, struct aztech, isa); int i; - mutex_lock(&az->lock); - - az->curfreq = frequency; - frequency += 171200; /* Add 10.7 MHz IF */ - frequency /= 800; /* Convert to 50 kHz units */ + freq += 171200; /* Add 10.7 MHz IF */ + freq /= 800; /* Convert to 50 kHz units */ send_0_byte(az); /* 0: LSB of frequency */ for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ - if (frequency & (1 << i)) + if (freq & (1 << i)) send_1_byte(az); else send_0_byte(az); @@ -158,7 +94,7 @@ static int az_setfreq(struct aztech *az, unsigned long frequency) send_0_byte(az); /* 14: test bit - always 0 */ send_0_byte(az); /* 15: test bit - always 0 */ send_0_byte(az); /* 16: band data 0 - always 0 */ - if (az->stereo) /* 17: stereo (1 to enable) */ + if (isa->stereo) /* 17: stereo (1 to enable) */ send_1_byte(az); else send_0_byte(az); @@ -173,225 +109,77 @@ static int az_setfreq(struct aztech *az, unsigned long frequency) /* latch frequency */ udelay(radio_wait_time); - outb_p(128 + 64 + volconvert(az->curvol), az->io); - - mutex_unlock(&az->lock); - - return 0; -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *v) -{ - strlcpy(v->driver, "radio-aztech", sizeof(v->driver)); - strlcpy(v->card, "Aztech Radio", sizeof(v->card)); - strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - return 0; -} - -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - struct aztech *az = video_drvdata(file); - - if (v->index > 0) - return -EINVAL; - - strlcpy(v->name, "FM", sizeof(v->name)); - v->type = V4L2_TUNER_RADIO; - - v->rangelow = 87 * 16000; - v->rangehigh = 108 * 16000; - v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - v->capability = V4L2_TUNER_CAP_LOW; - if (az_getstereo(az)) - v->audmode = V4L2_TUNER_MODE_STEREO; - else - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal = 0xFFFF * az_getsigstr(az); - - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - return v->index ? -EINVAL : 0; -} + outb_p(128 + 64 + az->curvol, az->isa.io); -static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; return 0; } -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - return i ? -EINVAL : 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - a->index = 0; - strlcpy(a->name, "Radio", sizeof(a->name)); - a->capability = V4L2_AUDCAP_STEREO; - return 0; -} - -static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) +/* thanks to Michael Dwyer for giving me a dose of clues in + * the signal strength department.. + * + * This card has a stereo bit - bit 0 set = mono, not set = stereo + */ +static u32 aztech_g_rxsubchans(struct radio_isa_card *isa) { - return a->index ? -EINVAL : 0; + if (inb(isa->io) & 1) + return V4L2_TUNER_SUB_MONO; + return V4L2_TUNER_SUB_STEREO; } -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) +static int aztech_s_stereo(struct radio_isa_card *isa, bool stereo) { - struct aztech *az = video_drvdata(file); - - if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) - return -EINVAL; - az_setfreq(az, f->frequency); - return 0; + return aztech_s_frequency(isa, isa->freq); } -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) +static int aztech_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) { - struct aztech *az = video_drvdata(file); + struct aztech *az = container_of(isa, struct aztech, isa); - if (f->tuner != 0) - return -EINVAL; - f->type = V4L2_TUNER_RADIO; - f->frequency = az->curfreq; + if (mute) + vol = 0; + az->curvol = (vol & 1) + ((vol & 2) << 1); + outb(az->curvol, isa->io); return 0; } -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff); - } - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct aztech *az = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (az->curvol == 0) - ctrl->value = 1; - else - ctrl->value = 0; - return 0; - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = az->curvol * 6554; - return 0; - } - return -EINVAL; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct aztech *az = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) - az_setvol(az, 0); - else - az_setvol(az, az->curvol); - return 0; - case V4L2_CID_AUDIO_VOLUME: - az_setvol(az, ctrl->value); - return 0; - } - return -EINVAL; -} - -static const struct v4l2_file_operations aztech_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = video_ioctl2, +static const struct radio_isa_ops aztech_ops = { + .alloc = aztech_alloc, + .s_mute_volume = aztech_s_mute_volume, + .s_frequency = aztech_s_frequency, + .s_stereo = aztech_s_stereo, + .g_rxsubchans = aztech_g_rxsubchans, }; -static const struct v4l2_ioctl_ops aztech_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, +static const int aztech_ioports[] = { 0x350, 0x358 }; + +static struct radio_isa_driver aztech_driver = { + .driver = { + .match = radio_isa_match, + .probe = radio_isa_probe, + .remove = radio_isa_remove, + .driver = { + .name = "radio-aztech", + }, + }, + .io_params = io, + .radio_nr_params = radio_nr, + .io_ports = aztech_ioports, + .num_of_io_ports = ARRAY_SIZE(aztech_ioports), + .region_size = 2, + .card = "Aztech Radio", + .ops = &aztech_ops, + .has_stereo = true, + .max_volume = 3, }; static int __init aztech_init(void) { - struct aztech *az = &aztech_card; - struct v4l2_device *v4l2_dev = &az->v4l2_dev; - int res; - - strlcpy(v4l2_dev->name, "aztech", sizeof(v4l2_dev->name)); - az->io = io; - - if (az->io == -1) { - v4l2_err(v4l2_dev, "you must set an I/O address with io=0x350 or 0x358\n"); - return -EINVAL; - } - - if (!request_region(az->io, 2, "aztech")) { - v4l2_err(v4l2_dev, "port 0x%x already in use\n", az->io); - return -EBUSY; - } - - res = v4l2_device_register(NULL, v4l2_dev); - if (res < 0) { - release_region(az->io, 2); - v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); - return res; - } - - mutex_init(&az->lock); - strlcpy(az->vdev.name, v4l2_dev->name, sizeof(az->vdev.name)); - az->vdev.v4l2_dev = v4l2_dev; - az->vdev.fops = &aztech_fops; - az->vdev.ioctl_ops = &aztech_ioctl_ops; - az->vdev.release = video_device_release_empty; - video_set_drvdata(&az->vdev, az); - /* mute card - prevents noisy bootups */ - outb(0, az->io); - - if (video_register_device(&az->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { - v4l2_device_unregister(v4l2_dev); - release_region(az->io, 2); - return -EINVAL; - } - - v4l2_info(v4l2_dev, "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n"); - return 0; + return isa_register_driver(&aztech_driver.driver, AZTECH_MAX); } static void __exit aztech_exit(void) { - struct aztech *az = &aztech_card; - - video_unregister_device(&az->vdev); - v4l2_device_unregister(&az->v4l2_dev); - release_region(az->io, 2); + isa_unregister_driver(&aztech_driver.driver); } module_init(aztech_init); diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c index 36ce0611c037..2e639ce6f256 100644 --- a/drivers/media/radio/radio-gemtek.c +++ b/drivers/media/radio/radio-gemtek.c @@ -1,4 +1,7 @@ -/* GemTek radio card driver for Linux (C) 1998 Jonas Munsin <jmunsin@iki.fi> +/* + * GemTek radio card driver + * + * Copyright 1998 Jonas Munsin <jmunsin@iki.fi> * * GemTek hasn't released any specs on the card, so the protocol had to * be reverse engineered with dosemu. @@ -11,9 +14,12 @@ * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk> * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> * - * TODO: Allow for more than one of these foolish entities :-) - * + * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com> * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> + * + * Note: this card seems to swap the left and right audio channels! + * + * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool. */ #include <linux/module.h> /* Modules */ @@ -23,8 +29,10 @@ #include <linux/videodev2.h> /* kernel radio structs */ #include <linux/mutex.h> #include <linux/io.h> /* outb, outb_p */ +#include <linux/slab.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-device.h> +#include "radio-isa.h" /* * Module info. @@ -33,7 +41,7 @@ MODULE_AUTHOR("Jonas Munsin, Pekka Seppänen <pexu@kapsi.fi>"); MODULE_DESCRIPTION("A driver for the GemTek Radio card."); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.0.4"); +MODULE_VERSION("1.0.0"); /* * Module params. @@ -46,45 +54,29 @@ MODULE_VERSION("0.0.4"); #define CONFIG_RADIO_GEMTEK_PROBE 1 #endif -static int io = CONFIG_RADIO_GEMTEK_PORT; -static bool probe = CONFIG_RADIO_GEMTEK_PROBE; -static bool hardmute; -static bool shutdown = 1; -static bool keepmuted = 1; -static bool initmute = 1; -static int radio_nr = -1; +#define GEMTEK_MAX 4 -module_param(io, int, 0444); -MODULE_PARM_DESC(io, "Force I/O port for the GemTek Radio card if automatic " - "probing is disabled or fails. The most common I/O ports are: 0x20c " - "0x30c, 0x24c or 0x34c (0x20c, 0x248 and 0x28c have been reported to " - "work for the combined sound/radiocard)."); +static bool probe = CONFIG_RADIO_GEMTEK_PROBE; +static bool hardmute; +static int io[GEMTEK_MAX] = { [0] = CONFIG_RADIO_GEMTEK_PORT, + [1 ... (GEMTEK_MAX - 1)] = -1 }; +static int radio_nr[GEMTEK_MAX] = { [0 ... (GEMTEK_MAX - 1)] = -1 }; module_param(probe, bool, 0444); -MODULE_PARM_DESC(probe, "Enable automatic device probing. Note: only the most " - "common I/O ports used by the card are probed."); +MODULE_PARM_DESC(probe, "Enable automatic device probing."); module_param(hardmute, bool, 0644); -MODULE_PARM_DESC(hardmute, "Enable `hard muting' by shutting down PLL, may " +MODULE_PARM_DESC(hardmute, "Enable 'hard muting' by shutting down PLL, may " "reduce static noise."); -module_param(shutdown, bool, 0644); -MODULE_PARM_DESC(shutdown, "Enable shutting down PLL and muting line when " - "module is unloaded."); - -module_param(keepmuted, bool, 0644); -MODULE_PARM_DESC(keepmuted, "Keep card muted even when frequency is changed."); - -module_param(initmute, bool, 0444); -MODULE_PARM_DESC(initmute, "Mute card when module is loaded."); - -module_param(radio_nr, int, 0444); +module_param_array(io, int, NULL, 0444); +MODULE_PARM_DESC(io, "Force I/O ports for the GemTek Radio card if automatic " + "probing is disabled or fails. The most common I/O ports are: 0x20c " + "0x30c, 0x24c or 0x34c (0x20c, 0x248 and 0x28c have been reported to " + "work for the combined sound/radiocard)."); -/* - * Functions for controlling the card. - */ -#define GEMTEK_LOWFREQ (87*16000) -#define GEMTEK_HIGHFREQ (108*16000) +module_param_array(radio_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_nr, "Radio device numbers"); /* * Frequency calculation constants. Intermediate frequency 10.52 MHz (nominal @@ -108,18 +100,11 @@ module_param(radio_nr, int, 0444); #define LONG_DELAY 75 /* usec */ struct gemtek { - struct v4l2_device v4l2_dev; - struct video_device vdev; - struct mutex lock; - unsigned long lastfreq; - int muted; - int verified; - int io; + struct radio_isa_card isa; + bool muted; u32 bu2614data; }; -static struct gemtek gemtek_card; - #define BU2614_FREQ_BITS 16 /* D0..D15, Frequency data */ #define BU2614_PORT_BITS 3 /* P0..P2, Output port control data */ #define BU2614_VOID_BITS 4 /* unused */ @@ -166,31 +151,24 @@ static struct gemtek gemtek_card; */ static void gemtek_bu2614_transmit(struct gemtek *gt) { + struct radio_isa_card *isa = >->isa; int i, bit, q, mute; - mutex_lock(>->lock); - mute = gt->muted ? GEMTEK_MT : 0x00; - outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io); - udelay(SHORT_DELAY); - outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io); + outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, isa->io); udelay(LONG_DELAY); for (i = 0, q = gt->bu2614data; i < 32; i++, q >>= 1) { bit = (q & 1) ? GEMTEK_DA : 0; - outb_p(mute | GEMTEK_CE | bit, gt->io); + outb_p(mute | GEMTEK_CE | bit, isa->io); udelay(SHORT_DELAY); - outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, gt->io); + outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, isa->io); udelay(SHORT_DELAY); } - outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io); + outb_p(mute | GEMTEK_DA | GEMTEK_CK, isa->io); udelay(SHORT_DELAY); - outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io); - udelay(LONG_DELAY); - - mutex_unlock(>->lock); } /* @@ -198,21 +176,27 @@ static void gemtek_bu2614_transmit(struct gemtek *gt) */ static unsigned long gemtek_convfreq(unsigned long freq) { - return ((freq<<FSCALE) + IF_OFFSET + REF_FREQ/2) / REF_FREQ; + return ((freq << FSCALE) + IF_OFFSET + REF_FREQ / 2) / REF_FREQ; +} + +static struct radio_isa_card *gemtek_alloc(void) +{ + struct gemtek *gt = kzalloc(sizeof(*gt), GFP_KERNEL); + + if (gt) + gt->muted = true; + return gt ? >->isa : NULL; } /* * Set FM-frequency. */ -static void gemtek_setfreq(struct gemtek *gt, unsigned long freq) +static int gemtek_s_frequency(struct radio_isa_card *isa, u32 freq) { - if (keepmuted && hardmute && gt->muted) - return; + struct gemtek *gt = container_of(isa, struct gemtek, isa); - freq = clamp_val(freq, GEMTEK_LOWFREQ, GEMTEK_HIGHFREQ); - - gt->lastfreq = freq; - gt->muted = 0; + if (hardmute && gt->muted) + return 0; gemtek_bu2614_set(gt, BU2614_PORT, 0); gemtek_bu2614_set(gt, BU2614_FMES, 0); @@ -220,23 +204,25 @@ static void gemtek_setfreq(struct gemtek *gt, unsigned long freq) gemtek_bu2614_set(gt, BU2614_SWAL, 0); gemtek_bu2614_set(gt, BU2614_FMUN, 1); /* GT bit set */ gemtek_bu2614_set(gt, BU2614_TEST, 0); - gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_STDF_3_125_KHZ); gemtek_bu2614_set(gt, BU2614_FREQ, gemtek_convfreq(freq)); - gemtek_bu2614_transmit(gt); + return 0; } /* * Set mute flag. */ -static void gemtek_mute(struct gemtek *gt) +static int gemtek_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) { + struct gemtek *gt = container_of(isa, struct gemtek, isa); int i; - gt->muted = 1; - + gt->muted = mute; if (hardmute) { + if (!mute) + return gemtek_s_frequency(isa, isa->freq); + /* Turn off PLL, disable data output */ gemtek_bu2614_set(gt, BU2614_PORT, 0); gemtek_bu2614_set(gt, BU2614_FMES, 0); /* CT bit off */ @@ -247,367 +233,85 @@ static void gemtek_mute(struct gemtek *gt) gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_PLL_OFF); gemtek_bu2614_set(gt, BU2614_FREQ, 0); gemtek_bu2614_transmit(gt); - return; + return 0; } - mutex_lock(>->lock); - /* Read bus contents (CE, CK and DA). */ - i = inb_p(gt->io); + i = inb_p(isa->io); /* Write it back with mute flag set. */ - outb_p((i >> 5) | GEMTEK_MT, gt->io); + outb_p((i >> 5) | (mute ? GEMTEK_MT : 0), isa->io); udelay(SHORT_DELAY); - - mutex_unlock(>->lock); -} - -/* - * Unset mute flag. - */ -static void gemtek_unmute(struct gemtek *gt) -{ - int i; - - gt->muted = 0; - if (hardmute) { - /* Turn PLL back on. */ - gemtek_setfreq(gt, gt->lastfreq); - return; - } - mutex_lock(>->lock); - - i = inb_p(gt->io); - outb_p(i >> 5, gt->io); - udelay(SHORT_DELAY); - - mutex_unlock(>->lock); + return 0; } -/* - * Get signal strength (= stereo status). - */ -static inline int gemtek_getsigstr(struct gemtek *gt) +static u32 gemtek_g_rxsubchans(struct radio_isa_card *isa) { - int sig; - - mutex_lock(>->lock); - sig = inb_p(gt->io) & GEMTEK_NS ? 0 : 1; - mutex_unlock(>->lock); - return sig; + if (inb_p(isa->io) & GEMTEK_NS) + return V4L2_TUNER_SUB_MONO; + return V4L2_TUNER_SUB_STEREO; } /* * Check if requested card acts like GemTek Radio card. */ -static int gemtek_verify(struct gemtek *gt, int port) +static bool gemtek_probe(struct radio_isa_card *isa, int io) { int i, q; - if (gt->verified == port) - return 1; - - mutex_lock(>->lock); - - q = inb_p(port); /* Read bus contents before probing. */ + q = inb_p(io); /* Read bus contents before probing. */ /* Try to turn on CE, CK and DA respectively and check if card responds properly. */ for (i = 0; i < 3; ++i) { - outb_p(1 << i, port); + outb_p(1 << i, io); udelay(SHORT_DELAY); - if ((inb_p(port) & (~GEMTEK_NS)) != (0x17 | (1 << (i + 5)))) { - mutex_unlock(>->lock); - return 0; - } + if ((inb_p(io) & ~GEMTEK_NS) != (0x17 | (1 << (i + 5)))) + return false; } - outb_p(q >> 5, port); /* Write bus contents back. */ + outb_p(q >> 5, io); /* Write bus contents back. */ udelay(SHORT_DELAY); - - mutex_unlock(>->lock); - gt->verified = port; - - return 1; -} - -/* - * Automatic probing for card. - */ -static int gemtek_probe(struct gemtek *gt) -{ - struct v4l2_device *v4l2_dev = >->v4l2_dev; - int ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c }; - int i; - - if (!probe) { - v4l2_info(v4l2_dev, "Automatic device probing disabled.\n"); - return -1; - } - - v4l2_info(v4l2_dev, "Automatic device probing enabled.\n"); - - for (i = 0; i < ARRAY_SIZE(ioports); ++i) { - v4l2_info(v4l2_dev, "Trying I/O port 0x%x...\n", ioports[i]); - - if (!request_region(ioports[i], 1, "gemtek-probe")) { - v4l2_warn(v4l2_dev, "I/O port 0x%x busy!\n", - ioports[i]); - continue; - } - - if (gemtek_verify(gt, ioports[i])) { - v4l2_info(v4l2_dev, "Card found from I/O port " - "0x%x!\n", ioports[i]); - - release_region(ioports[i], 1); - gt->io = ioports[i]; - return gt->io; - } - - release_region(ioports[i], 1); - } - - v4l2_err(v4l2_dev, "Automatic probing failed!\n"); - return -1; + return true; } -/* - * Video 4 Linux stuff. - */ - -static const struct v4l2_file_operations gemtek_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = video_ioctl2, +static const struct radio_isa_ops gemtek_ops = { + .alloc = gemtek_alloc, + .probe = gemtek_probe, + .s_mute_volume = gemtek_s_mute_volume, + .s_frequency = gemtek_s_frequency, + .g_rxsubchans = gemtek_g_rxsubchans, }; -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *v) -{ - strlcpy(v->driver, "radio-gemtek", sizeof(v->driver)); - strlcpy(v->card, "GemTek", sizeof(v->card)); - strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - return 0; -} - -static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) -{ - struct gemtek *gt = video_drvdata(file); - - if (v->index > 0) - return -EINVAL; - - strlcpy(v->name, "FM", sizeof(v->name)); - v->type = V4L2_TUNER_RADIO; - v->rangelow = GEMTEK_LOWFREQ; - v->rangehigh = GEMTEK_HIGHFREQ; - v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; - v->signal = 0xffff * gemtek_getsigstr(gt); - if (v->signal) { - v->audmode = V4L2_TUNER_MODE_STEREO; - v->rxsubchans = V4L2_TUNER_SUB_STEREO; - } else { - v->audmode = V4L2_TUNER_MODE_MONO; - v->rxsubchans = V4L2_TUNER_SUB_MONO; - } - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) -{ - return (v->index != 0) ? -EINVAL : 0; -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct gemtek *gt = video_drvdata(file); - - if (f->tuner != 0) - return -EINVAL; - f->type = V4L2_TUNER_RADIO; - f->frequency = gt->lastfreq; - return 0; -} - -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct gemtek *gt = video_drvdata(file); - - if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) - return -EINVAL; - gemtek_setfreq(gt, f->frequency); - return 0; -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); - default: - return -EINVAL; - } -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct gemtek *gt = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value = gt->muted; - return 0; - } - return -EINVAL; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct gemtek *gt = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) - gemtek_mute(gt); - else - gemtek_unmute(gt); - return 0; - } - return -EINVAL; -} - -static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - return (i != 0) ? -EINVAL : 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - a->index = 0; - strlcpy(a->name, "Radio", sizeof(a->name)); - a->capability = V4L2_AUDCAP_STEREO; - return 0; -} - -static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - return (a->index != 0) ? -EINVAL : 0; -} - -static const struct v4l2_ioctl_ops gemtek_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl +static const int gemtek_ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c }; + +static struct radio_isa_driver gemtek_driver = { + .driver = { + .match = radio_isa_match, + .probe = radio_isa_probe, + .remove = radio_isa_remove, + .driver = { + .name = "radio-gemtek", + }, + }, + .io_params = io, + .radio_nr_params = radio_nr, + .io_ports = gemtek_ioports, + .num_of_io_ports = ARRAY_SIZE(gemtek_ioports), + .region_size = 1, + .card = "GemTek Radio", + .ops = &gemtek_ops, + .has_stereo = true, }; -/* - * Initialization / cleanup related stuff. - */ - static int __init gemtek_init(void) { - struct gemtek *gt = &gemtek_card; - struct v4l2_device *v4l2_dev = >->v4l2_dev; - int res; - - strlcpy(v4l2_dev->name, "gemtek", sizeof(v4l2_dev->name)); - - v4l2_info(v4l2_dev, "GemTek Radio card driver: v0.0.3\n"); - - mutex_init(>->lock); - - gt->verified = -1; - gt->io = io; - gemtek_probe(gt); - if (gt->io) { - if (!request_region(gt->io, 1, "gemtek")) { - v4l2_err(v4l2_dev, "I/O port 0x%x already in use.\n", gt->io); - return -EBUSY; - } - - if (!gemtek_verify(gt, gt->io)) - v4l2_warn(v4l2_dev, "Card at I/O port 0x%x does not " - "respond properly, check your " - "configuration.\n", gt->io); - else - v4l2_info(v4l2_dev, "Using I/O port 0x%x.\n", gt->io); - } else if (probe) { - v4l2_err(v4l2_dev, "Automatic probing failed and no " - "fixed I/O port defined.\n"); - return -ENODEV; - } else { - v4l2_err(v4l2_dev, "Automatic probing disabled but no fixed " - "I/O port defined."); - return -EINVAL; - } - - res = v4l2_device_register(NULL, v4l2_dev); - if (res < 0) { - v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); - release_region(gt->io, 1); - return res; - } - - strlcpy(gt->vdev.name, v4l2_dev->name, sizeof(gt->vdev.name)); - gt->vdev.v4l2_dev = v4l2_dev; - gt->vdev.fops = &gemtek_fops; - gt->vdev.ioctl_ops = &gemtek_ioctl_ops; - gt->vdev.release = video_device_release_empty; - video_set_drvdata(>->vdev, gt); - - /* Set defaults */ - gt->lastfreq = GEMTEK_LOWFREQ; - gt->bu2614data = 0; - - if (initmute) - gemtek_mute(gt); - - if (video_register_device(>->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { - v4l2_device_unregister(v4l2_dev); - release_region(gt->io, 1); - return -EBUSY; - } - - return 0; + gemtek_driver.probe = probe; + return isa_register_driver(&gemtek_driver.driver, GEMTEK_MAX); } -/* - * Module cleanup - */ static void __exit gemtek_exit(void) { - struct gemtek *gt = &gemtek_card; - struct v4l2_device *v4l2_dev = >->v4l2_dev; - - if (shutdown) { - hardmute = 1; /* Turn off PLL */ - gemtek_mute(gt); - } else { - v4l2_info(v4l2_dev, "Module unloaded but card not muted!\n"); - } - - video_unregister_device(>->vdev); - v4l2_device_unregister(>->v4l2_dev); - release_region(gt->io, 1); + hardmute = 1; /* Turn off PLL */ + isa_unregister_driver(&gemtek_driver.driver); } module_init(gemtek_init); diff --git a/drivers/media/radio/radio-isa.c b/drivers/media/radio/radio-isa.c new file mode 100644 index 000000000000..06f906351fad --- /dev/null +++ b/drivers/media/radio/radio-isa.c @@ -0,0 +1,340 @@ +/* + * Framework for ISA radio drivers. + * This takes care of all the V4L2 scaffolding, allowing the ISA drivers + * to concentrate on the actual hardware operation. + * + * Copyright (C) 2012 Hans Verkuil <hans.verkuil@cisco.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/videodev2.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> + +#include "radio-isa.h" + +MODULE_AUTHOR("Hans Verkuil"); +MODULE_DESCRIPTION("A framework for ISA radio drivers."); +MODULE_LICENSE("GPL"); + +#define FREQ_LOW (87U * 16000U) +#define FREQ_HIGH (108U * 16000U) + +static int radio_isa_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + struct radio_isa_card *isa = video_drvdata(file); + + strlcpy(v->driver, isa->drv->driver.driver.name, sizeof(v->driver)); + strlcpy(v->card, isa->drv->card, sizeof(v->card)); + snprintf(v->bus_info, sizeof(v->bus_info), "ISA:%s", isa->v4l2_dev.name); + + v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + v->device_caps = v->capabilities | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int radio_isa_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + struct radio_isa_card *isa = video_drvdata(file); + const struct radio_isa_ops *ops = isa->drv->ops; + + if (v->index > 0) + return -EINVAL; + + strlcpy(v->name, "FM", sizeof(v->name)); + v->type = V4L2_TUNER_RADIO; + v->rangelow = FREQ_LOW; + v->rangehigh = FREQ_HIGH; + v->capability = V4L2_TUNER_CAP_LOW; + if (isa->drv->has_stereo) + v->capability |= V4L2_TUNER_CAP_STEREO; + + if (ops->g_rxsubchans) + v->rxsubchans = ops->g_rxsubchans(isa); + else + v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + v->audmode = isa->stereo ? V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO; + if (ops->g_signal) + v->signal = ops->g_signal(isa); + else + v->signal = (v->rxsubchans & V4L2_TUNER_SUB_STEREO) ? + 0xffff : 0; + return 0; +} + +static int radio_isa_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + struct radio_isa_card *isa = video_drvdata(file); + const struct radio_isa_ops *ops = isa->drv->ops; + + if (v->index) + return -EINVAL; + if (ops->s_stereo) { + isa->stereo = (v->audmode == V4L2_TUNER_MODE_STEREO); + return ops->s_stereo(isa, isa->stereo); + } + return 0; +} + +static int radio_isa_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct radio_isa_card *isa = video_drvdata(file); + int res; + + if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) + return -EINVAL; + f->frequency = clamp(f->frequency, FREQ_LOW, FREQ_HIGH); + res = isa->drv->ops->s_frequency(isa, f->frequency); + if (res == 0) + isa->freq = f->frequency; + return res; +} + +static int radio_isa_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct radio_isa_card *isa = video_drvdata(file); + + if (f->tuner != 0) + return -EINVAL; + f->type = V4L2_TUNER_RADIO; + f->frequency = isa->freq; + return 0; +} + +static int radio_isa_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct radio_isa_card *isa = + container_of(ctrl->handler, struct radio_isa_card, hdl); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + return isa->drv->ops->s_mute_volume(isa, ctrl->val, + isa->volume ? isa->volume->val : 0); + } + return -EINVAL; +} + +static int radio_isa_log_status(struct file *file, void *priv) +{ + struct radio_isa_card *isa = video_drvdata(file); + + v4l2_info(&isa->v4l2_dev, "I/O Port = 0x%03x\n", isa->io); + v4l2_ctrl_handler_log_status(&isa->hdl, isa->v4l2_dev.name); + return 0; +} + +static int radio_isa_subscribe_event(struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + if (sub->type == V4L2_EVENT_CTRL) + return v4l2_event_subscribe(fh, sub, 0); + return -EINVAL; +} + +static const struct v4l2_ctrl_ops radio_isa_ctrl_ops = { + .s_ctrl = radio_isa_s_ctrl, +}; + +static const struct v4l2_file_operations radio_isa_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops radio_isa_ioctl_ops = { + .vidioc_querycap = radio_isa_querycap, + .vidioc_g_tuner = radio_isa_g_tuner, + .vidioc_s_tuner = radio_isa_s_tuner, + .vidioc_g_frequency = radio_isa_g_frequency, + .vidioc_s_frequency = radio_isa_s_frequency, + .vidioc_log_status = radio_isa_log_status, + .vidioc_subscribe_event = radio_isa_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +int radio_isa_match(struct device *pdev, unsigned int dev) +{ + struct radio_isa_driver *drv = pdev->platform_data; + + return drv->probe || drv->io_params[dev] >= 0; +} +EXPORT_SYMBOL_GPL(radio_isa_match); + +static bool radio_isa_valid_io(const struct radio_isa_driver *drv, int io) +{ + int i; + + for (i = 0; i < drv->num_of_io_ports; i++) + if (drv->io_ports[i] == io) + return true; + return false; +} + +int radio_isa_probe(struct device *pdev, unsigned int dev) +{ + struct radio_isa_driver *drv = pdev->platform_data; + const struct radio_isa_ops *ops = drv->ops; + struct v4l2_device *v4l2_dev; + struct radio_isa_card *isa; + int res; + + isa = drv->ops->alloc(); + if (isa == NULL) + return -ENOMEM; + dev_set_drvdata(pdev, isa); + isa->drv = drv; + isa->io = drv->io_params[dev]; + v4l2_dev = &isa->v4l2_dev; + strlcpy(v4l2_dev->name, dev_name(pdev), sizeof(v4l2_dev->name)); + + if (drv->probe && ops->probe) { + int i; + + for (i = 0; i < drv->num_of_io_ports; ++i) { + int io = drv->io_ports[i]; + + if (request_region(io, drv->region_size, v4l2_dev->name)) { + bool found = ops->probe(isa, io); + + release_region(io, drv->region_size); + if (found) { + isa->io = io; + break; + } + } + } + } + + if (!radio_isa_valid_io(drv, isa->io)) { + int i; + + if (isa->io < 0) + return -ENODEV; + v4l2_err(v4l2_dev, "you must set an I/O address with io=0x%03x", + drv->io_ports[0]); + for (i = 1; i < drv->num_of_io_ports; i++) + printk(KERN_CONT "/0x%03x", drv->io_ports[i]); + printk(KERN_CONT ".\n"); + kfree(isa); + return -EINVAL; + } + + if (!request_region(isa->io, drv->region_size, v4l2_dev->name)) { + v4l2_err(v4l2_dev, "port 0x%x already in use\n", isa->io); + kfree(isa); + return -EBUSY; + } + + res = v4l2_device_register(pdev, v4l2_dev); + if (res < 0) { + v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); + goto err_dev_reg; + } + + v4l2_ctrl_handler_init(&isa->hdl, 1); + isa->mute = v4l2_ctrl_new_std(&isa->hdl, &radio_isa_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + if (drv->max_volume) + isa->volume = v4l2_ctrl_new_std(&isa->hdl, &radio_isa_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, drv->max_volume, 1, + drv->max_volume); + v4l2_dev->ctrl_handler = &isa->hdl; + if (isa->hdl.error) { + res = isa->hdl.error; + v4l2_err(v4l2_dev, "Could not register controls\n"); + goto err_hdl; + } + if (drv->max_volume) + v4l2_ctrl_cluster(2, &isa->mute); + v4l2_dev->ctrl_handler = &isa->hdl; + + mutex_init(&isa->lock); + isa->vdev.lock = &isa->lock; + strlcpy(isa->vdev.name, v4l2_dev->name, sizeof(isa->vdev.name)); + isa->vdev.v4l2_dev = v4l2_dev; + isa->vdev.fops = &radio_isa_fops; + isa->vdev.ioctl_ops = &radio_isa_ioctl_ops; + isa->vdev.release = video_device_release_empty; + set_bit(V4L2_FL_USE_FH_PRIO, &isa->vdev.flags); + video_set_drvdata(&isa->vdev, isa); + isa->freq = FREQ_LOW; + isa->stereo = drv->has_stereo; + + if (ops->init) + res = ops->init(isa); + if (!res) + res = v4l2_ctrl_handler_setup(&isa->hdl); + if (!res) + res = ops->s_frequency(isa, isa->freq); + if (!res && ops->s_stereo) + res = ops->s_stereo(isa, isa->stereo); + if (res < 0) { + v4l2_err(v4l2_dev, "Could not setup card\n"); + goto err_node_reg; + } + res = video_register_device(&isa->vdev, VFL_TYPE_RADIO, + drv->radio_nr_params[dev]); + if (res < 0) { + v4l2_err(v4l2_dev, "Could not register device node\n"); + goto err_node_reg; + } + + v4l2_info(v4l2_dev, "Initialized radio card %s on port 0x%03x\n", + drv->card, isa->io); + return 0; + +err_node_reg: + v4l2_ctrl_handler_free(&isa->hdl); +err_hdl: + v4l2_device_unregister(&isa->v4l2_dev); +err_dev_reg: + release_region(isa->io, drv->region_size); + kfree(isa); + return res; +} +EXPORT_SYMBOL_GPL(radio_isa_probe); + +int radio_isa_remove(struct device *pdev, unsigned int dev) +{ + struct radio_isa_card *isa = dev_get_drvdata(pdev); + const struct radio_isa_ops *ops = isa->drv->ops; + + ops->s_mute_volume(isa, true, isa->volume ? isa->volume->cur.val : 0); + video_unregister_device(&isa->vdev); + v4l2_ctrl_handler_free(&isa->hdl); + v4l2_device_unregister(&isa->v4l2_dev); + release_region(isa->io, isa->drv->region_size); + v4l2_info(&isa->v4l2_dev, "Removed radio card %s\n", isa->drv->card); + kfree(isa); + return 0; +} +EXPORT_SYMBOL_GPL(radio_isa_remove); diff --git a/drivers/media/radio/radio-isa.h b/drivers/media/radio/radio-isa.h new file mode 100644 index 000000000000..8a0ea84d86de --- /dev/null +++ b/drivers/media/radio/radio-isa.h @@ -0,0 +1,105 @@ +/* + * Framework for ISA radio drivers. + * This takes care of all the V4L2 scaffolding, allowing the ISA drivers + * to concentrate on the actual hardware operation. + * + * Copyright (C) 2012 Hans Verkuil <hans.verkuil@cisco.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef _RADIO_ISA_H_ +#define _RADIO_ISA_H_ + +#include <linux/isa.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> + +struct radio_isa_driver; +struct radio_isa_ops; + +/* Core structure for radio ISA cards */ +struct radio_isa_card { + const struct radio_isa_driver *drv; + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; + struct video_device vdev; + struct mutex lock; + const struct radio_isa_ops *ops; + struct { /* mute/volume cluster */ + struct v4l2_ctrl *mute; + struct v4l2_ctrl *volume; + }; + /* I/O port */ + int io; + + /* Card is in stereo audio mode */ + bool stereo; + /* Current frequency */ + u32 freq; +}; + +struct radio_isa_ops { + /* Allocate and initialize a radio_isa_card struct */ + struct radio_isa_card *(*alloc)(void); + /* Probe whether a card is present at the given port */ + bool (*probe)(struct radio_isa_card *isa, int io); + /* Special card initialization can be done here, this is called after + * the standard controls are registered, but before they are setup, + * thus allowing drivers to add their own controls here. */ + int (*init)(struct radio_isa_card *isa); + /* Set mute and volume. */ + int (*s_mute_volume)(struct radio_isa_card *isa, bool mute, int volume); + /* Set frequency */ + int (*s_frequency)(struct radio_isa_card *isa, u32 freq); + /* Set stereo/mono audio mode */ + int (*s_stereo)(struct radio_isa_card *isa, bool stereo); + /* Get rxsubchans value for VIDIOC_G_TUNER */ + u32 (*g_rxsubchans)(struct radio_isa_card *isa); + /* Get the signal strength for VIDIOC_G_TUNER */ + u32 (*g_signal)(struct radio_isa_card *isa); +}; + +/* Top level structure needed to instantiate the cards */ +struct radio_isa_driver { + struct isa_driver driver; + const struct radio_isa_ops *ops; + /* The module_param_array with the specified I/O ports */ + int *io_params; + /* The module_param_array with the radio_nr values */ + int *radio_nr_params; + /* Whether we should probe for possible cards */ + bool probe; + /* The list of possible I/O ports */ + const int *io_ports; + /* The size of that list */ + int num_of_io_ports; + /* The region size to request */ + unsigned region_size; + /* The name of the card */ + const char *card; + /* Card can capture stereo audio */ + bool has_stereo; + /* The maximum volume for the volume control. If 0, then there + is no volume control possible. */ + int max_volume; +}; + +int radio_isa_match(struct device *pdev, unsigned int dev); +int radio_isa_probe(struct device *pdev, unsigned int dev); +int radio_isa_remove(struct device *pdev, unsigned int dev); + +#endif diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c new file mode 100644 index 000000000000..55bd1d2937c8 --- /dev/null +++ b/drivers/media/radio/radio-keene.c @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2012 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* kernel includes */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <linux/usb.h> +#include <linux/version.h> +#include <linux/mutex.h> + +/* driver and module definitions */ +MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>"); +MODULE_DESCRIPTION("Keene FM Transmitter driver"); +MODULE_LICENSE("GPL"); + +/* Actually, it advertises itself as a Logitech */ +#define USB_KEENE_VENDOR 0x046d +#define USB_KEENE_PRODUCT 0x0a0e + +/* Probably USB_TIMEOUT should be modified in module parameter */ +#define BUFFER_LENGTH 8 +#define USB_TIMEOUT 500 + +/* Frequency limits in MHz */ +#define FREQ_MIN 76U +#define FREQ_MAX 108U +#define FREQ_MUL 16000U + +/* USB Device ID List */ +static struct usb_device_id usb_keene_device_table[] = { + {USB_DEVICE_AND_INTERFACE_INFO(USB_KEENE_VENDOR, USB_KEENE_PRODUCT, + USB_CLASS_HID, 0, 0) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, usb_keene_device_table); + +struct keene_device { + struct usb_device *usbdev; + struct usb_interface *intf; + struct video_device vdev; + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; + struct mutex lock; + + u8 *buffer; + unsigned curfreq; + u8 tx; + u8 pa; + bool stereo; + bool muted; + bool preemph_75_us; +}; + +static inline struct keene_device *to_keene_dev(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct keene_device, v4l2_dev); +} + +/* Set frequency (if non-0), PA, mute and turn on/off the FM transmitter. */ +static int keene_cmd_main(struct keene_device *radio, unsigned freq, bool play) +{ + unsigned short freq_send = freq ? (freq - 76 * 16000) / 800 : 0; + int ret; + + radio->buffer[0] = 0x00; + radio->buffer[1] = 0x50; + radio->buffer[2] = (freq_send >> 8) & 0xff; + radio->buffer[3] = freq_send & 0xff; + radio->buffer[4] = radio->pa; + /* If bit 4 is set, then tune to the frequency. + If bit 3 is set, then unmute; if bit 2 is set, then mute. + If bit 1 is set, then enter idle mode; if bit 0 is set, + then enter transit mode. + */ + radio->buffer[5] = (radio->muted ? 4 : 8) | (play ? 1 : 2) | + (freq ? 0x10 : 0); + radio->buffer[6] = 0x00; + radio->buffer[7] = 0x00; + + ret = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), + 9, 0x21, 0x200, 2, radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); + + if (ret < 0) { + dev_warn(&radio->vdev.dev, "%s failed (%d)\n", __func__, ret); + return ret; + } + if (freq) + radio->curfreq = freq; + return 0; +} + +/* Set TX, stereo and preemphasis mode (50 us vs 75 us). */ +static int keene_cmd_set(struct keene_device *radio) +{ + int ret; + + radio->buffer[0] = 0x00; + radio->buffer[1] = 0x51; + radio->buffer[2] = radio->tx; + /* If bit 0 is set, then transmit mono, otherwise stereo. + If bit 2 is set, then enable 75 us preemphasis, otherwise + it is 50 us. */ + radio->buffer[3] = (!radio->stereo) | (radio->preemph_75_us ? 4 : 0); + radio->buffer[4] = 0x00; + radio->buffer[5] = 0x00; + radio->buffer[6] = 0x00; + radio->buffer[7] = 0x00; + + ret = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), + 9, 0x21, 0x200, 2, radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); + + if (ret < 0) { + dev_warn(&radio->vdev.dev, "%s failed (%d)\n", __func__, ret); + return ret; + } + return 0; +} + +/* Handle unplugging the device. + * We call video_unregister_device in any case. + * The last function called in this procedure is + * usb_keene_device_release. + */ +static void usb_keene_disconnect(struct usb_interface *intf) +{ + struct keene_device *radio = to_keene_dev(usb_get_intfdata(intf)); + + v4l2_device_get(&radio->v4l2_dev); + mutex_lock(&radio->lock); + usb_set_intfdata(intf, NULL); + video_unregister_device(&radio->vdev); + v4l2_device_disconnect(&radio->v4l2_dev); + mutex_unlock(&radio->lock); + v4l2_device_put(&radio->v4l2_dev); +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + struct keene_device *radio = video_drvdata(file); + + strlcpy(v->driver, "radio-keene", sizeof(v->driver)); + strlcpy(v->card, "Keene FM Transmitter", sizeof(v->card)); + usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); + v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_MODULATOR; + v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int vidioc_g_modulator(struct file *file, void *priv, + struct v4l2_modulator *v) +{ + struct keene_device *radio = video_drvdata(file); + + if (v->index > 0) + return -EINVAL; + + strlcpy(v->name, "FM", sizeof(v->name)); + v->rangelow = FREQ_MIN * FREQ_MUL; + v->rangehigh = FREQ_MAX * FREQ_MUL; + v->txsubchans = radio->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; + v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + return 0; +} + +static int vidioc_s_modulator(struct file *file, void *priv, + struct v4l2_modulator *v) +{ + struct keene_device *radio = video_drvdata(file); + + if (v->index > 0) + return -EINVAL; + + radio->stereo = (v->txsubchans == V4L2_TUNER_SUB_STEREO); + return keene_cmd_set(radio); +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct keene_device *radio = video_drvdata(file); + + if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) + return -EINVAL; + f->frequency = clamp(f->frequency, + FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL); + return keene_cmd_main(radio, f->frequency, true); +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct keene_device *radio = video_drvdata(file); + + if (f->tuner != 0) + return -EINVAL; + f->type = V4L2_TUNER_RADIO; + f->frequency = radio->curfreq; + return 0; +} + +static int keene_s_ctrl(struct v4l2_ctrl *ctrl) +{ + static const u8 db2tx[] = { + /* -15, -12, -9, -6, -3, 0 dB */ + 0x03, 0x13, 0x02, 0x12, 0x22, 0x32, + /* 3, 6, 9, 12, 15, 18 dB */ + 0x21, 0x31, 0x20, 0x30, 0x40, 0x50 + }; + struct keene_device *radio = + container_of(ctrl->handler, struct keene_device, hdl); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + radio->muted = ctrl->val; + return keene_cmd_main(radio, 0, true); + + case V4L2_CID_TUNE_POWER_LEVEL: + /* To go from dBuV to the register value we apply the + following formula: */ + radio->pa = (ctrl->val - 71) * 100 / 62; + return keene_cmd_main(radio, 0, true); + + case V4L2_CID_TUNE_PREEMPHASIS: + radio->preemph_75_us = ctrl->val == V4L2_PREEMPHASIS_75_uS; + return keene_cmd_set(radio); + + case V4L2_CID_AUDIO_COMPRESSION_GAIN: + radio->tx = db2tx[(ctrl->val - ctrl->minimum) / ctrl->step]; + return keene_cmd_set(radio); + } + return -EINVAL; +} + +static int vidioc_subscribe_event(struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_CTRL: + return v4l2_event_subscribe(fh, sub, 0); + default: + return -EINVAL; + } +} + + +/* File system interface */ +static const struct v4l2_file_operations usb_keene_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ctrl_ops keene_ctrl_ops = { + .s_ctrl = keene_s_ctrl, +}; + +static const struct v4l2_ioctl_ops usb_keene_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_modulator = vidioc_g_modulator, + .vidioc_s_modulator = vidioc_s_modulator, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = vidioc_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static void usb_keene_video_device_release(struct v4l2_device *v4l2_dev) +{ + struct keene_device *radio = to_keene_dev(v4l2_dev); + + /* free rest memory */ + v4l2_ctrl_handler_free(&radio->hdl); + kfree(radio->buffer); + kfree(radio); +} + +/* check if the device is present and register with v4l and usb if it is */ +static int usb_keene_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct keene_device *radio; + struct v4l2_ctrl_handler *hdl; + int retval = 0; + + /* + * The Keene FM transmitter USB device has the same USB ID as + * the Logitech AudioHub Speaker, but it should ignore the hid. + * Check if the name is that of the Keene device. + * If not, then someone connected the AudioHub and we shouldn't + * attempt to handle this driver. + * For reference: the product name of the AudioHub is + * "AudioHub Speaker". + */ + if (dev->product && strcmp(dev->product, "B-LINK USB Audio ")) + return -ENODEV; + + radio = kzalloc(sizeof(struct keene_device), GFP_KERNEL); + if (radio) + radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL); + + if (!radio || !radio->buffer) { + dev_err(&intf->dev, "kmalloc for keene_device failed\n"); + kfree(radio); + retval = -ENOMEM; + goto err; + } + + hdl = &radio->hdl; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &keene_ctrl_ops, V4L2_CID_AUDIO_MUTE, + 0, 1, 1, 0); + v4l2_ctrl_new_std_menu(hdl, &keene_ctrl_ops, V4L2_CID_TUNE_PREEMPHASIS, + V4L2_PREEMPHASIS_75_uS, 1, V4L2_PREEMPHASIS_50_uS); + v4l2_ctrl_new_std(hdl, &keene_ctrl_ops, V4L2_CID_TUNE_POWER_LEVEL, + 84, 118, 1, 118); + v4l2_ctrl_new_std(hdl, &keene_ctrl_ops, V4L2_CID_AUDIO_COMPRESSION_GAIN, + -15, 18, 3, 0); + radio->pa = 118; + radio->tx = 0x32; + radio->stereo = true; + radio->curfreq = 95.16 * FREQ_MUL; + if (hdl->error) { + retval = hdl->error; + + v4l2_ctrl_handler_free(hdl); + goto err_v4l2; + } + retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); + if (retval < 0) { + dev_err(&intf->dev, "couldn't register v4l2_device\n"); + goto err_v4l2; + } + + mutex_init(&radio->lock); + + radio->v4l2_dev.ctrl_handler = hdl; + radio->v4l2_dev.release = usb_keene_video_device_release; + strlcpy(radio->vdev.name, radio->v4l2_dev.name, + sizeof(radio->vdev.name)); + radio->vdev.v4l2_dev = &radio->v4l2_dev; + radio->vdev.fops = &usb_keene_fops; + radio->vdev.ioctl_ops = &usb_keene_ioctl_ops; + radio->vdev.lock = &radio->lock; + radio->vdev.release = video_device_release_empty; + + radio->usbdev = interface_to_usbdev(intf); + radio->intf = intf; + usb_set_intfdata(intf, &radio->v4l2_dev); + + video_set_drvdata(&radio->vdev, radio); + set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags); + + retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, -1); + if (retval < 0) { + dev_err(&intf->dev, "could not register video device\n"); + goto err_vdev; + } + v4l2_ctrl_handler_setup(hdl); + dev_info(&intf->dev, "V4L2 device registered as %s\n", + video_device_node_name(&radio->vdev)); + return 0; + +err_vdev: + v4l2_device_unregister(&radio->v4l2_dev); +err_v4l2: + kfree(radio->buffer); + kfree(radio); +err: + return retval; +} + +/* USB subsystem interface */ +static struct usb_driver usb_keene_driver = { + .name = "radio-keene", + .probe = usb_keene_probe, + .disconnect = usb_keene_disconnect, + .id_table = usb_keene_device_table, +}; + +static int __init keene_init(void) +{ + int retval = usb_register(&usb_keene_driver); + + if (retval) + pr_err(KBUILD_MODNAME + ": usb_register failed. Error number %d\n", retval); + + return retval; +} + +static void __exit keene_exit(void) +{ + usb_deregister(&usb_keene_driver); +} + +module_init(keene_init); +module_exit(keene_exit); + diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c index f872a54cf3d9..740a3d5520c7 100644 --- a/drivers/media/radio/radio-maxiradio.c +++ b/drivers/media/radio/radio-maxiradio.c @@ -42,67 +42,37 @@ #include <linux/videodev2.h> #include <linux/io.h> #include <linux/slab.h> +#include <sound/tea575x-tuner.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> - -#define DRIVER_VERSION "0.7.8" - +#include <media/v4l2-fh.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net"); -MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio."); +MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000."); MODULE_LICENSE("GPL"); -MODULE_VERSION(DRIVER_VERSION); +MODULE_VERSION("1.0.0"); static int radio_nr = -1; -module_param(radio_nr, int, 0); - -static int debug; - -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "activates debug info"); - -#define dprintk(dev, num, fmt, arg...) \ - v4l2_dbg(num, debug, &dev->v4l2_dev, fmt, ## arg) - -#ifndef PCI_VENDOR_ID_GUILLEMOT -#define PCI_VENDOR_ID_GUILLEMOT 0x5046 -#endif - -#ifndef PCI_DEVICE_ID_GUILLEMOT -#define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001 -#endif - +module_param(radio_nr, int, 0644); +MODULE_PARM_DESC(radio_nr, "Radio device number"); /* TEA5757 pin mappings */ static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16; -#define FREQ_LO (87 * 16000) -#define FREQ_HI (108 * 16000) - -#define FREQ_IF 171200 /* 10.7*16000 */ -#define FREQ_STEP 200 /* 12.5*16 */ - -/* (x==fmhz*16*1000) -> bits */ -#define FREQ2BITS(x) \ - ((((unsigned int)(x) + FREQ_IF + (FREQ_STEP << 1)) / (FREQ_STEP << 2)) << 2) - -#define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF) +static atomic_t maxiradio_instance = ATOMIC_INIT(0); +#define PCI_VENDOR_ID_GUILLEMOT 0x5046 +#define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001 struct maxiradio { + struct snd_tea575x tea; struct v4l2_device v4l2_dev; - struct video_device vdev; struct pci_dev *pdev; u16 io; /* base of radio io */ - u16 muted; /* VIDEO_AUDIO_MUTE */ - u16 stereo; /* VIDEO_TUNER_STEREO_ON */ - u16 tuned; /* signal strength (0 or 0xffff) */ - - unsigned long freq; - - struct mutex lock; }; static inline struct maxiradio *to_maxiradio(struct v4l2_device *v4l2_dev) @@ -110,259 +80,41 @@ static inline struct maxiradio *to_maxiradio(struct v4l2_device *v4l2_dev) return container_of(v4l2_dev, struct maxiradio, v4l2_dev); } -static void outbit(unsigned long bit, u16 io) -{ - int val = power | wren | (bit ? data : 0); - - outb(val, io); - udelay(4); - outb(val | clk, io); - udelay(4); - outb(val, io); - udelay(4); -} - -static void turn_power(struct maxiradio *dev, int p) -{ - if (p != 0) { - dprintk(dev, 1, "Radio powered on\n"); - outb(power, dev->io); - } else { - dprintk(dev, 1, "Radio powered off\n"); - outb(0, dev->io); - } -} - -static void set_freq(struct maxiradio *dev, u32 freq) -{ - unsigned long int si; - int bl; - int io = dev->io; - int val = FREQ2BITS(freq); - - /* TEA5757 shift register bits (see pdf) */ - - outbit(0, io); /* 24 search */ - outbit(1, io); /* 23 search up/down */ - - outbit(0, io); /* 22 stereo/mono */ - - outbit(0, io); /* 21 band */ - outbit(0, io); /* 20 band (only 00=FM works I think) */ - - outbit(0, io); /* 19 port ? */ - outbit(0, io); /* 18 port ? */ - - outbit(0, io); /* 17 search level */ - outbit(0, io); /* 16 search level */ - - si = 0x8000; - for (bl = 1; bl <= 16; bl++) { - outbit(val & si, io); - si >>= 1; - } - - dprintk(dev, 1, "Radio freq set to %d.%02d MHz\n", - freq / 16000, - freq % 16000 * 100 / 16000); - - turn_power(dev, 1); -} - -static int get_stereo(u16 io) +static void maxiradio_tea575x_set_pins(struct snd_tea575x *tea, u8 pins) { - outb(power,io); - udelay(4); + struct maxiradio *dev = tea->private_data; + u8 bits = 0; - return !(inb(io) & mo_st); -} + bits |= (pins & TEA575X_DATA) ? data : 0; + bits |= (pins & TEA575X_CLK) ? clk : 0; + bits |= (pins & TEA575X_WREN) ? wren : 0; + bits |= power; -static int get_tune(u16 io) -{ - outb(power+clk,io); - udelay(4); - - return !(inb(io) & mo_st); -} - - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *v) -{ - struct maxiradio *dev = video_drvdata(file); - - strlcpy(v->driver, "radio-maxiradio", sizeof(v->driver)); - strlcpy(v->card, "Maxi Radio FM2000 radio", sizeof(v->card)); - snprintf(v->bus_info, sizeof(v->bus_info), "PCI:%s", pci_name(dev->pdev)); - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - return 0; + outb(bits, dev->io); } -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) +/* Note: this card cannot read out the data of the shift registers, + only the mono/stereo pin works. */ +static u8 maxiradio_tea575x_get_pins(struct snd_tea575x *tea) { - struct maxiradio *dev = video_drvdata(file); - - if (v->index > 0) - return -EINVAL; - - mutex_lock(&dev->lock); - strlcpy(v->name, "FM", sizeof(v->name)); - v->type = V4L2_TUNER_RADIO; - v->rangelow = FREQ_LO; - v->rangehigh = FREQ_HI; - v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - v->capability = V4L2_TUNER_CAP_LOW; - if (get_stereo(dev->io)) - v->audmode = V4L2_TUNER_MODE_STEREO; - else - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal = 0xffff * get_tune(dev->io); - mutex_unlock(&dev->lock); + struct maxiradio *dev = tea->private_data; + u8 bits = inb(dev->io); - return 0; + return ((bits & data) ? TEA575X_DATA : 0) | + ((bits & mo_st) ? TEA575X_MOST : 0); } -static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) +static void maxiradio_tea575x_set_direction(struct snd_tea575x *tea, bool output) { - return v->index ? -EINVAL : 0; } -static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - return i ? -EINVAL : 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - a->index = 0; - strlcpy(a->name, "Radio", sizeof(a->name)); - a->capability = V4L2_AUDCAP_STEREO; - return 0; -} - - -static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - return a->index ? -EINVAL : 0; -} - -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct maxiradio *dev = video_drvdata(file); - - if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) - return -EINVAL; - if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) { - dprintk(dev, 1, "radio freq (%d.%02d MHz) out of range (%d-%d)\n", - f->frequency / 16000, - f->frequency % 16000 * 100 / 16000, - FREQ_LO / 16000, FREQ_HI / 16000); - - return -EINVAL; - } - - mutex_lock(&dev->lock); - dev->freq = f->frequency; - set_freq(dev, dev->freq); - msleep(125); - mutex_unlock(&dev->lock); - - return 0; -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct maxiradio *dev = video_drvdata(file); - - if (f->tuner != 0) - return -EINVAL; - f->type = V4L2_TUNER_RADIO; - f->frequency = dev->freq; - - dprintk(dev, 4, "radio freq is %d.%02d MHz", - f->frequency / 16000, - f->frequency % 16000 * 100 / 16000); - - return 0; -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - } - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct maxiradio *dev = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value = dev->muted; - return 0; - } - - return -EINVAL; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct maxiradio *dev = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - mutex_lock(&dev->lock); - dev->muted = ctrl->value; - if (dev->muted) - turn_power(dev, 0); - else - set_freq(dev, dev->freq); - mutex_unlock(&dev->lock); - return 0; - } - - return -EINVAL; -} - -static const struct v4l2_file_operations maxiradio_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = video_ioctl2, +static struct snd_tea575x_ops maxiradio_tea_ops = { + .set_pins = maxiradio_tea575x_set_pins, + .get_pins = maxiradio_tea575x_get_pins, + .set_direction = maxiradio_tea575x_set_direction, }; -static const struct v4l2_ioctl_ops maxiradio_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, -}; - -static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +static int __devinit maxiradio_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct maxiradio *dev; struct v4l2_device *v4l2_dev; @@ -375,63 +127,60 @@ static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_d } v4l2_dev = &dev->v4l2_dev; - mutex_init(&dev->lock); - dev->pdev = pdev; - dev->muted = 1; - dev->freq = FREQ_LO; - - strlcpy(v4l2_dev->name, "maxiradio", sizeof(v4l2_dev->name)); + v4l2_device_set_name(v4l2_dev, "maxiradio", &maxiradio_instance); retval = v4l2_device_register(&pdev->dev, v4l2_dev); if (retval < 0) { v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); goto errfr; } + dev->tea.private_data = dev; + dev->tea.ops = &maxiradio_tea_ops; + /* The data pin cannot be read. This may be a hardware limitation, or + we just don't know how to read it. */ + dev->tea.cannot_read_data = true; + dev->tea.v4l2_dev = v4l2_dev; + dev->tea.radio_nr = radio_nr; + strlcpy(dev->tea.card, "Maxi Radio FM2000", sizeof(dev->tea.card)); + snprintf(dev->tea.bus_info, sizeof(dev->tea.bus_info), + "PCI:%s", pci_name(pdev)); + + retval = -ENODEV; if (!request_region(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0), "Maxi Radio FM 2000")) { - v4l2_err(v4l2_dev, "can't reserve I/O ports\n"); - goto err_out; + pci_resource_len(pdev, 0), v4l2_dev->name)) { + dev_err(&pdev->dev, "can't reserve I/O ports\n"); + goto err_hdl; } if (pci_enable_device(pdev)) goto err_out_free_region; dev->io = pci_resource_start(pdev, 0); - strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); - dev->vdev.v4l2_dev = v4l2_dev; - dev->vdev.fops = &maxiradio_fops; - dev->vdev.ioctl_ops = &maxiradio_ioctl_ops; - dev->vdev.release = video_device_release_empty; - video_set_drvdata(&dev->vdev, dev); - - if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { - v4l2_err(v4l2_dev, "can't register device!"); + if (snd_tea575x_init(&dev->tea)) { + printk(KERN_ERR "radio-maxiradio: Unable to detect TEA575x tuner\n"); goto err_out_free_region; } - - v4l2_info(v4l2_dev, "version " DRIVER_VERSION "\n"); - - v4l2_info(v4l2_dev, "found Guillemot MAXI Radio device (io = 0x%x)\n", - dev->io); return 0; err_out_free_region: release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); -err_out: +err_hdl: v4l2_device_unregister(v4l2_dev); errfr: kfree(dev); - return -ENODEV; + return retval; } -static void __devexit maxiradio_remove_one(struct pci_dev *pdev) +static void __devexit maxiradio_remove(struct pci_dev *pdev) { struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); struct maxiradio *dev = to_maxiradio(v4l2_dev); - video_unregister_device(&dev->vdev); - v4l2_device_unregister(&dev->v4l2_dev); + snd_tea575x_exit(&dev->tea); + /* Turn off power */ + outb(0, dev->io); + v4l2_device_unregister(v4l2_dev); release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); } @@ -446,19 +195,19 @@ MODULE_DEVICE_TABLE(pci, maxiradio_pci_tbl); static struct pci_driver maxiradio_driver = { .name = "radio-maxiradio", .id_table = maxiradio_pci_tbl, - .probe = maxiradio_init_one, - .remove = __devexit_p(maxiradio_remove_one), + .probe = maxiradio_probe, + .remove = __devexit_p(maxiradio_remove), }; -static int __init maxiradio_radio_init(void) +static int __init maxiradio_init(void) { return pci_register_driver(&maxiradio_driver); } -static void __exit maxiradio_radio_exit(void) +static void __exit maxiradio_exit(void) { pci_unregister_driver(&maxiradio_driver); } -module_init(maxiradio_radio_init); -module_exit(maxiradio_radio_exit); +module_init(maxiradio_init); +module_exit(maxiradio_exit); diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c index 3628be617ee9..b275c5d0fe9a 100644 --- a/drivers/media/radio/radio-rtrack2.c +++ b/drivers/media/radio/radio-rtrack2.c @@ -1,11 +1,12 @@ -/* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff +/* + * RadioTrack II driver + * Copyright 1998 Ben Pfaff * * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk> * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> * - * TODO: Allow for more than one of these foolish entities :-) - * + * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com> * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> */ @@ -18,323 +19,120 @@ #include <linux/io.h> /* outb, outb_p */ #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> +#include "radio-isa.h" MODULE_AUTHOR("Ben Pfaff"); MODULE_DESCRIPTION("A driver for the RadioTrack II radio card."); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.0.3"); +MODULE_VERSION("0.1.99"); #ifndef CONFIG_RADIO_RTRACK2_PORT #define CONFIG_RADIO_RTRACK2_PORT -1 #endif -static int io = CONFIG_RADIO_RTRACK2_PORT; -static int radio_nr = -1; - -module_param(io, int, 0); -MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20c or 0x30c)"); -module_param(radio_nr, int, 0); - -struct rtrack2 -{ - struct v4l2_device v4l2_dev; - struct video_device vdev; - int io; - unsigned long curfreq; - int muted; - struct mutex lock; -}; +#define RTRACK2_MAX 2 -static struct rtrack2 rtrack2_card; +static int io[RTRACK2_MAX] = { [0] = CONFIG_RADIO_RTRACK2_PORT, + [1 ... (RTRACK2_MAX - 1)] = -1 }; +static int radio_nr[RTRACK2_MAX] = { [0 ... (RTRACK2_MAX - 1)] = -1 }; +module_param_array(io, int, NULL, 0444); +MODULE_PARM_DESC(io, "I/O addresses of the RadioTrack card (0x20f or 0x30f)"); +module_param_array(radio_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_nr, "Radio device numbers"); -/* local things */ - -static void rt_mute(struct rtrack2 *dev) -{ - if (dev->muted) - return; - mutex_lock(&dev->lock); - outb(1, dev->io); - mutex_unlock(&dev->lock); - dev->muted = 1; -} - -static void rt_unmute(struct rtrack2 *dev) +static struct radio_isa_card *rtrack2_alloc(void) { - if(dev->muted == 0) - return; - mutex_lock(&dev->lock); - outb(0, dev->io); - mutex_unlock(&dev->lock); - dev->muted = 0; + return kzalloc(sizeof(struct radio_isa_card), GFP_KERNEL); } -static void zero(struct rtrack2 *dev) +static void zero(struct radio_isa_card *isa) { - outb_p(1, dev->io); - outb_p(3, dev->io); - outb_p(1, dev->io); + outb_p(1, isa->io); + outb_p(3, isa->io); + outb_p(1, isa->io); } -static void one(struct rtrack2 *dev) +static void one(struct radio_isa_card *isa) { - outb_p(5, dev->io); - outb_p(7, dev->io); - outb_p(5, dev->io); + outb_p(5, isa->io); + outb_p(7, isa->io); + outb_p(5, isa->io); } -static int rt_setfreq(struct rtrack2 *dev, unsigned long freq) +static int rtrack2_s_frequency(struct radio_isa_card *isa, u32 freq) { int i; - mutex_lock(&dev->lock); - dev->curfreq = freq; freq = freq / 200 + 856; - outb_p(0xc8, dev->io); - outb_p(0xc9, dev->io); - outb_p(0xc9, dev->io); + outb_p(0xc8, isa->io); + outb_p(0xc9, isa->io); + outb_p(0xc9, isa->io); for (i = 0; i < 10; i++) - zero(dev); + zero(isa); for (i = 14; i >= 0; i--) if (freq & (1 << i)) - one(dev); + one(isa); else - zero(dev); - - outb_p(0xc8, dev->io); - if (!dev->muted) - outb_p(0, dev->io); - - mutex_unlock(&dev->lock); - return 0; -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *v) -{ - strlcpy(v->driver, "radio-rtrack2", sizeof(v->driver)); - strlcpy(v->card, "RadioTrack II", sizeof(v->card)); - strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - return 0; -} + zero(isa); -static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - return v->index ? -EINVAL : 0; -} - -static int rt_getsigstr(struct rtrack2 *dev) -{ - int sig = 1; - - mutex_lock(&dev->lock); - if (inb(dev->io) & 2) /* bit set = no signal present */ - sig = 0; - mutex_unlock(&dev->lock); - return sig; -} - -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - struct rtrack2 *rt = video_drvdata(file); - - if (v->index > 0) - return -EINVAL; - - strlcpy(v->name, "FM", sizeof(v->name)); - v->type = V4L2_TUNER_RADIO; - v->rangelow = 88 * 16000; - v->rangehigh = 108 * 16000; - v->rxsubchans = V4L2_TUNER_SUB_MONO; - v->capability = V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal = 0xFFFF * rt_getsigstr(rt); + outb_p(0xc8, isa->io); + if (!v4l2_ctrl_g_ctrl(isa->mute)) + outb_p(0, isa->io); return 0; } -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) +static u32 rtrack2_g_signal(struct radio_isa_card *isa) { - struct rtrack2 *rt = video_drvdata(file); - - if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) - return -EINVAL; - rt_setfreq(rt, f->frequency); - return 0; + /* bit set = no signal present */ + return (inb(isa->io) & 2) ? 0 : 0xffff; } -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) +static int rtrack2_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) { - struct rtrack2 *rt = video_drvdata(file); - - if (f->tuner != 0) - return -EINVAL; - f->type = V4L2_TUNER_RADIO; - f->frequency = rt->curfreq; + outb(mute, isa->io); return 0; } -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535, 65535); - } - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct rtrack2 *rt = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value = rt->muted; - return 0; - case V4L2_CID_AUDIO_VOLUME: - if (rt->muted) - ctrl->value = 0; - else - ctrl->value = 65535; - return 0; - } - return -EINVAL; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct rtrack2 *rt = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) - rt_mute(rt); - else - rt_unmute(rt); - return 0; - case V4L2_CID_AUDIO_VOLUME: - if (ctrl->value) - rt_unmute(rt); - else - rt_mute(rt); - return 0; - } - return -EINVAL; -} - -static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - return i ? -EINVAL : 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - a->index = 0; - strlcpy(a->name, "Radio", sizeof(a->name)); - a->capability = V4L2_AUDCAP_STEREO; - return 0; -} - -static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - return a->index ? -EINVAL : 0; -} - -static const struct v4l2_file_operations rtrack2_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = video_ioctl2, +static const struct radio_isa_ops rtrack2_ops = { + .alloc = rtrack2_alloc, + .s_mute_volume = rtrack2_s_mute_volume, + .s_frequency = rtrack2_s_frequency, + .g_signal = rtrack2_g_signal, }; -static const struct v4l2_ioctl_ops rtrack2_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, +static const int rtrack2_ioports[] = { 0x20f, 0x30f }; + +static struct radio_isa_driver rtrack2_driver = { + .driver = { + .match = radio_isa_match, + .probe = radio_isa_probe, + .remove = radio_isa_remove, + .driver = { + .name = "radio-rtrack2", + }, + }, + .io_params = io, + .radio_nr_params = radio_nr, + .io_ports = rtrack2_ioports, + .num_of_io_ports = ARRAY_SIZE(rtrack2_ioports), + .region_size = 4, + .card = "AIMSlab RadioTrack II", + .ops = &rtrack2_ops, + .has_stereo = true, }; static int __init rtrack2_init(void) { - struct rtrack2 *dev = &rtrack2_card; - struct v4l2_device *v4l2_dev = &dev->v4l2_dev; - int res; - - strlcpy(v4l2_dev->name, "rtrack2", sizeof(v4l2_dev->name)); - dev->io = io; - if (dev->io == -1) { - v4l2_err(v4l2_dev, "You must set an I/O address with io=0x20c or io=0x30c\n"); - return -EINVAL; - } - if (!request_region(dev->io, 4, "rtrack2")) { - v4l2_err(v4l2_dev, "port 0x%x already in use\n", dev->io); - return -EBUSY; - } - - res = v4l2_device_register(NULL, v4l2_dev); - if (res < 0) { - release_region(dev->io, 4); - v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); - return res; - } - - strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); - dev->vdev.v4l2_dev = v4l2_dev; - dev->vdev.fops = &rtrack2_fops; - dev->vdev.ioctl_ops = &rtrack2_ioctl_ops; - dev->vdev.release = video_device_release_empty; - video_set_drvdata(&dev->vdev, dev); - - /* mute card - prevents noisy bootups */ - outb(1, dev->io); - dev->muted = 1; - - mutex_init(&dev->lock); - if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { - v4l2_device_unregister(v4l2_dev); - release_region(dev->io, 4); - return -EINVAL; - } - - v4l2_info(v4l2_dev, "AIMSlab Radiotrack II card driver.\n"); - - return 0; + return isa_register_driver(&rtrack2_driver.driver, RTRACK2_MAX); } static void __exit rtrack2_exit(void) { - struct rtrack2 *dev = &rtrack2_card; - - video_unregister_device(&dev->vdev); - v4l2_device_unregister(&dev->v4l2_dev); - release_region(dev->io, 4); + isa_unregister_driver(&rtrack2_driver.driver); } module_init(rtrack2_init); diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c index 7ab9afadf29b..7c69214334bf 100644 --- a/drivers/media/radio/radio-sf16fmr2.c +++ b/drivers/media/radio/radio-sf16fmr2.c @@ -9,16 +9,23 @@ #include <linux/delay.h> #include <linux/module.h> /* Modules */ #include <linux/init.h> /* Initdata */ +#include <linux/slab.h> #include <linux/ioport.h> /* request_region */ #include <linux/io.h> /* outb, outb_p */ +#include <linux/isa.h> #include <sound/tea575x-tuner.h> MODULE_AUTHOR("Ondrej Zary"); MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver"); MODULE_LICENSE("GPL"); +static int radio_nr = -1; +module_param(radio_nr, int, 0444); +MODULE_PARM_DESC(radio_nr, "Radio device number"); + struct fmr2 { int io; + struct v4l2_device v4l2_dev; struct snd_tea575x tea; struct v4l2_ctrl *volume; struct v4l2_ctrl *balance; @@ -26,7 +33,6 @@ struct fmr2 { /* the port is hardwired so no need to support multiple cards */ #define FMR2_PORT 0x384 -static struct fmr2 fmr2_card; /* TEA575x tuner pins */ #define STR_DATA (1 << 0) @@ -180,26 +186,46 @@ static int fmr2_tea_ext_init(struct snd_tea575x *tea) return 0; } -static int __init fmr2_init(void) +static int __devinit fmr2_probe(struct device *pdev, unsigned int dev) { - struct fmr2 *fmr2 = &fmr2_card; + struct fmr2 *fmr2; + int err; + + fmr2 = kzalloc(sizeof(*fmr2), GFP_KERNEL); + if (fmr2 == NULL) + return -ENOMEM; + strlcpy(fmr2->v4l2_dev.name, dev_name(pdev), + sizeof(fmr2->v4l2_dev.name)); fmr2->io = FMR2_PORT; - if (!request_region(fmr2->io, 2, "SF16-FMR2")) { + if (!request_region(fmr2->io, 2, fmr2->v4l2_dev.name)) { printk(KERN_ERR "radio-sf16fmr2: I/O port 0x%x already in use\n", fmr2->io); + kfree(fmr2); return -EBUSY; } + dev_set_drvdata(pdev, fmr2); + err = v4l2_device_register(pdev, &fmr2->v4l2_dev); + if (err < 0) { + v4l2_err(&fmr2->v4l2_dev, "Could not register v4l2_device\n"); + release_region(fmr2->io, 2); + kfree(fmr2); + return err; + } + fmr2->tea.v4l2_dev = &fmr2->v4l2_dev; fmr2->tea.private_data = fmr2; + fmr2->tea.radio_nr = radio_nr; fmr2->tea.ops = &fmr2_tea_ops; fmr2->tea.ext_init = fmr2_tea_ext_init; strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card)); - strcpy(fmr2->tea.bus_info, "ISA"); + snprintf(fmr2->tea.bus_info, sizeof(fmr2->tea.bus_info), "ISA:%s", + fmr2->v4l2_dev.name); if (snd_tea575x_init(&fmr2->tea)) { printk(KERN_ERR "radio-sf16fmr2: Unable to detect TEA575x tuner\n"); release_region(fmr2->io, 2); + kfree(fmr2); return -ENODEV; } @@ -207,12 +233,33 @@ static int __init fmr2_init(void) return 0; } -static void __exit fmr2_exit(void) +static int __exit fmr2_remove(struct device *pdev, unsigned int dev) { - struct fmr2 *fmr2 = &fmr2_card; + struct fmr2 *fmr2 = dev_get_drvdata(pdev); snd_tea575x_exit(&fmr2->tea); release_region(fmr2->io, 2); + v4l2_device_unregister(&fmr2->v4l2_dev); + kfree(fmr2); + return 0; +} + +struct isa_driver fmr2_driver = { + .probe = fmr2_probe, + .remove = fmr2_remove, + .driver = { + .name = "radio-sf16fmr2", + }, +}; + +static int __init fmr2_init(void) +{ + return isa_register_driver(&fmr2_driver, 1); +} + +static void __exit fmr2_exit(void) +{ + isa_unregister_driver(&fmr2_driver); } module_init(fmr2_init); diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c index db20904d01f0..6b1fae32b483 100644 --- a/drivers/media/radio/radio-tea5764.c +++ b/drivers/media/radio/radio-tea5764.c @@ -575,21 +575,7 @@ static struct i2c_driver tea5764_i2c_driver = { .id_table = tea5764_id, }; -/* init the driver */ -static int __init tea5764_init(void) -{ - int ret = i2c_add_driver(&tea5764_i2c_driver); - - printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ": " - DRIVER_DESC "\n"); - return ret; -} - -/* cleanup the driver */ -static void __exit tea5764_exit(void) -{ - i2c_del_driver(&tea5764_i2c_driver); -} +module_i2c_driver(tea5764_i2c_driver); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); @@ -600,6 +586,3 @@ module_param(use_xtal, int, 0); MODULE_PARM_DESC(use_xtal, "Chip have a xtal connected in board"); module_param(radio_nr, int, 0); MODULE_PARM_DESC(radio_nr, "video4linux device number to use"); - -module_init(tea5764_init); -module_exit(tea5764_exit); diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c index f2ed9cc3cf3b..be10a802e3a9 100644 --- a/drivers/media/radio/radio-terratec.c +++ b/drivers/media/radio/radio-terratec.c @@ -16,11 +16,7 @@ * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); * Volume Control is done digitally * - * there is a I2C controlled RDS decoder (SAA6588) onboard, which i would like to support someday - * (as soon i have understand how to get started :) - * If you can help me out with that, please contact me!! - * - * + * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com> * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> */ @@ -30,43 +26,24 @@ #include <linux/videodev2.h> /* kernel radio structs */ #include <linux/mutex.h> #include <linux/io.h> /* outb, outb_p */ +#include <linux/slab.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> +#include "radio-isa.h" -MODULE_AUTHOR("R.OFFERMANNS & others"); +MODULE_AUTHOR("R. Offermans & others"); MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card."); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.0.3"); - -#ifndef CONFIG_RADIO_TERRATEC_PORT -#define CONFIG_RADIO_TERRATEC_PORT 0x590 -#endif +MODULE_VERSION("0.1.99"); -static int io = CONFIG_RADIO_TERRATEC_PORT; +/* Note: there seems to be only one possible port (0x590), but without + hardware this is hard to verify. For now, this is the only one we will + support. */ +static int io = 0x590; static int radio_nr = -1; -module_param(io, int, 0); -MODULE_PARM_DESC(io, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)"); -module_param(radio_nr, int, 0); - -static struct v4l2_queryctrl radio_qctrl[] = { - { - .id = V4L2_CID_AUDIO_MUTE, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .default_value = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_AUDIO_VOLUME, - .name = "Volume", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0xff, - .type = V4L2_CTRL_TYPE_INTEGER, - } -}; +module_param(radio_nr, int, 0444); +MODULE_PARM_DESC(radio_nr, "Radio device number"); #define WRT_DIS 0x00 #define CLK_OFF 0x00 @@ -76,63 +53,24 @@ static struct v4l2_queryctrl radio_qctrl[] = { #define CLK_ON 0x08 #define WRT_EN 0x10 -struct terratec +static struct radio_isa_card *terratec_alloc(void) { - struct v4l2_device v4l2_dev; - struct video_device vdev; - int io; - int curvol; - unsigned long curfreq; - int muted; - struct mutex lock; -}; - -static struct terratec terratec_card; - -/* local things */ + return kzalloc(sizeof(struct radio_isa_card), GFP_KERNEL); +} -static void tt_write_vol(struct terratec *tt, int volume) +static int terratec_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) { int i; - volume = volume + (volume * 32); /* change both channels */ - mutex_lock(&tt->lock); + if (mute) + vol = 0; + vol = vol + (vol * 32); /* change both channels */ for (i = 0; i < 8; i++) { - if (volume & (0x80 >> i)) - outb(0x80, tt->io + 1); + if (vol & (0x80 >> i)) + outb(0x80, isa->io + 1); else - outb(0x00, tt->io + 1); - } - mutex_unlock(&tt->lock); -} - - - -static void tt_mute(struct terratec *tt) -{ - tt->muted = 1; - tt_write_vol(tt, 0); -} - -static int tt_setvol(struct terratec *tt, int vol) -{ - if (vol == tt->curvol) { /* requested volume = current */ - if (tt->muted) { /* user is unmuting the card */ - tt->muted = 0; - tt_write_vol(tt, vol); /* enable card */ - } - return 0; - } - - if (vol == 0) { /* volume = 0 means mute the card */ - tt_write_vol(tt, 0); /* "turn off card" by setting vol to 0 */ - tt->curvol = vol; /* track the volume state! */ - return 0; + outb(0x00, isa->io + 1); } - - tt->muted = 0; - tt_write_vol(tt, vol); - tt->curvol = vol; return 0; } @@ -140,20 +78,15 @@ static int tt_setvol(struct terratec *tt, int vol) /* this is the worst part in this driver */ /* many more or less strange things are going on here, but hey, it works :) */ -static int tt_setfreq(struct terratec *tt, unsigned long freq1) +static int terratec_s_frequency(struct radio_isa_card *isa, u32 freq) { - int freq; int i; int p; - int temp; + int temp; long rest; unsigned char buffer[25]; /* we have to bit shift 25 registers */ - mutex_lock(&tt->lock); - - tt->curfreq = freq1; - - freq = freq1 / 160; /* convert the freq. to a nice to handle value */ + freq = freq / 160; /* convert the freq. to a nice to handle value */ memset(buffer, 0, sizeof(buffer)); rest = freq * 10 + 10700; /* I once had understood what is going on here */ @@ -175,239 +108,61 @@ static int tt_setfreq(struct terratec *tt, unsigned long freq1) for (i = 24; i > -1; i--) { /* bit shift the values to the radiocard */ if (buffer[i] == 1) { - outb(WRT_EN | DATA, tt->io); - outb(WRT_EN | DATA | CLK_ON, tt->io); - outb(WRT_EN | DATA, tt->io); + outb(WRT_EN | DATA, isa->io); + outb(WRT_EN | DATA | CLK_ON, isa->io); + outb(WRT_EN | DATA, isa->io); } else { - outb(WRT_EN | 0x00, tt->io); - outb(WRT_EN | 0x00 | CLK_ON, tt->io); - } - } - outb(0x00, tt->io); - - mutex_unlock(&tt->lock); - - return 0; -} - -static int tt_getsigstr(struct terratec *tt) -{ - if (inb(tt->io) & 2) /* bit set = no signal present */ - return 0; - return 1; /* signal present */ -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *v) -{ - strlcpy(v->driver, "radio-terratec", sizeof(v->driver)); - strlcpy(v->card, "ActiveRadio", sizeof(v->card)); - strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - return 0; -} - -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - struct terratec *tt = video_drvdata(file); - - if (v->index > 0) - return -EINVAL; - - strlcpy(v->name, "FM", sizeof(v->name)); - v->type = V4L2_TUNER_RADIO; - v->rangelow = 87 * 16000; - v->rangehigh = 108 * 16000; - v->rxsubchans = V4L2_TUNER_SUB_MONO; - v->capability = V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal = 0xFFFF * tt_getsigstr(tt); - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - return v->index ? -EINVAL : 0; -} - -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct terratec *tt = video_drvdata(file); - - if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) - return -EINVAL; - tt_setfreq(tt, f->frequency); - return 0; -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct terratec *tt = video_drvdata(file); - - if (f->tuner != 0) - return -EINVAL; - f->type = V4L2_TUNER_RADIO; - f->frequency = tt->curfreq; - return 0; -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), sizeof(*qc)); - return 0; + outb(WRT_EN | 0x00, isa->io); + outb(WRT_EN | 0x00 | CLK_ON, isa->io); } } - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct terratec *tt = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (tt->muted) - ctrl->value = 1; - else - ctrl->value = 0; - return 0; - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = tt->curvol * 6554; - return 0; - } - return -EINVAL; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct terratec *tt = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) - tt_mute(tt); - else - tt_setvol(tt,tt->curvol); - return 0; - case V4L2_CID_AUDIO_VOLUME: - tt_setvol(tt,ctrl->value); - return 0; - } - return -EINVAL; -} - -static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - return i ? -EINVAL : 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - a->index = 0; - strlcpy(a->name, "Radio", sizeof(a->name)); - a->capability = V4L2_AUDCAP_STEREO; + outb(0x00, isa->io); return 0; } -static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) +static u32 terratec_g_signal(struct radio_isa_card *isa) { - return a->index ? -EINVAL : 0; + /* bit set = no signal present */ + return (inb(isa->io) & 2) ? 0 : 0xffff; } -static const struct v4l2_file_operations terratec_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = video_ioctl2, +static const struct radio_isa_ops terratec_ops = { + .alloc = terratec_alloc, + .s_mute_volume = terratec_s_mute_volume, + .s_frequency = terratec_s_frequency, + .g_signal = terratec_g_signal, }; -static const struct v4l2_ioctl_ops terratec_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, +static const int terratec_ioports[] = { 0x590 }; + +static struct radio_isa_driver terratec_driver = { + .driver = { + .match = radio_isa_match, + .probe = radio_isa_probe, + .remove = radio_isa_remove, + .driver = { + .name = "radio-terratec", + }, + }, + .io_params = &io, + .radio_nr_params = &radio_nr, + .io_ports = terratec_ioports, + .num_of_io_ports = ARRAY_SIZE(terratec_ioports), + .region_size = 2, + .card = "TerraTec ActiveRadio", + .ops = &terratec_ops, + .has_stereo = true, + .max_volume = 10, }; static int __init terratec_init(void) { - struct terratec *tt = &terratec_card; - struct v4l2_device *v4l2_dev = &tt->v4l2_dev; - int res; - - strlcpy(v4l2_dev->name, "terratec", sizeof(v4l2_dev->name)); - tt->io = io; - if (tt->io == -1) { - v4l2_err(v4l2_dev, "you must set an I/O address with io=0x590 or 0x591\n"); - return -EINVAL; - } - if (!request_region(tt->io, 2, "terratec")) { - v4l2_err(v4l2_dev, "port 0x%x already in use\n", io); - return -EBUSY; - } - - res = v4l2_device_register(NULL, v4l2_dev); - if (res < 0) { - release_region(tt->io, 2); - v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); - return res; - } - - strlcpy(tt->vdev.name, v4l2_dev->name, sizeof(tt->vdev.name)); - tt->vdev.v4l2_dev = v4l2_dev; - tt->vdev.fops = &terratec_fops; - tt->vdev.ioctl_ops = &terratec_ioctl_ops; - tt->vdev.release = video_device_release_empty; - video_set_drvdata(&tt->vdev, tt); - - mutex_init(&tt->lock); - - /* mute card - prevents noisy bootups */ - tt_write_vol(tt, 0); - - if (video_register_device(&tt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { - v4l2_device_unregister(&tt->v4l2_dev); - release_region(tt->io, 2); - return -EINVAL; - } - - v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver.\n"); - return 0; + return isa_register_driver(&terratec_driver.driver, 1); } static void __exit terratec_exit(void) { - struct terratec *tt = &terratec_card; - struct v4l2_device *v4l2_dev = &tt->v4l2_dev; - - video_unregister_device(&tt->vdev); - v4l2_device_unregister(&tt->v4l2_dev); - release_region(tt->io, 2); - v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver unloaded.\n"); + isa_unregister_driver(&terratec_driver.driver); } module_init(terratec_init); diff --git a/drivers/media/radio/radio-trust.c b/drivers/media/radio/radio-trust.c index b3f45a019d82..26a8c6002121 100644 --- a/drivers/media/radio/radio-trust.c +++ b/drivers/media/radio/radio-trust.c @@ -21,13 +21,15 @@ #include <linux/ioport.h> #include <linux/videodev2.h> #include <linux/io.h> +#include <linux/slab.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> +#include "radio-isa.h" MODULE_AUTHOR("Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); MODULE_DESCRIPTION("A driver for the Trust FM Radio card."); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.0.3"); +MODULE_VERSION("0.1.99"); /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */ @@ -35,39 +37,38 @@ MODULE_VERSION("0.0.3"); #define CONFIG_RADIO_TRUST_PORT -1 #endif -static int io = CONFIG_RADIO_TRUST_PORT; -static int radio_nr = -1; +#define TRUST_MAX 2 -module_param(io, int, 0); -MODULE_PARM_DESC(io, "I/O address of the Trust FM Radio card (0x350 or 0x358)"); -module_param(radio_nr, int, 0); +static int io[TRUST_MAX] = { [0] = CONFIG_RADIO_TRUST_PORT, + [1 ... (TRUST_MAX - 1)] = -1 }; +static int radio_nr[TRUST_MAX] = { [0 ... (TRUST_MAX - 1)] = -1 }; + +module_param_array(io, int, NULL, 0444); +MODULE_PARM_DESC(io, "I/O addresses of the Trust FM Radio card (0x350 or 0x358)"); +module_param_array(radio_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_nr, "Radio device numbers"); struct trust { - struct v4l2_device v4l2_dev; - struct video_device vdev; - int io; + struct radio_isa_card isa; int ioval; - __u16 curvol; - __u16 curbass; - __u16 curtreble; - int muted; - unsigned long curfreq; - int curstereo; - int curmute; - struct mutex lock; }; -static struct trust trust_card; +static struct radio_isa_card *trust_alloc(void) +{ + struct trust *tr = kzalloc(sizeof(*tr), GFP_KERNEL); + + return tr ? &tr->isa : NULL; +} /* i2c addresses */ #define TDA7318_ADDR 0x88 #define TSA6060T_ADDR 0xc4 -#define TR_DELAY do { inb(tr->io); inb(tr->io); inb(tr->io); } while (0) -#define TR_SET_SCL outb(tr->ioval |= 2, tr->io) -#define TR_CLR_SCL outb(tr->ioval &= 0xfd, tr->io) -#define TR_SET_SDA outb(tr->ioval |= 1, tr->io) -#define TR_CLR_SDA outb(tr->ioval &= 0xfe, tr->io) +#define TR_DELAY do { inb(tr->isa.io); inb(tr->isa.io); inb(tr->isa.io); } while (0) +#define TR_SET_SCL outb(tr->ioval |= 2, tr->isa.io) +#define TR_CLR_SCL outb(tr->ioval &= 0xfd, tr->isa.io) +#define TR_SET_SDA outb(tr->ioval |= 1, tr->isa.io) +#define TR_CLR_SDA outb(tr->ioval &= 0xfe, tr->isa.io) static void write_i2c(struct trust *tr, int n, ...) { @@ -84,10 +85,10 @@ static void write_i2c(struct trust *tr, int n, ...) TR_CLR_SCL; TR_DELAY; - for(; n; n--) { + for (; n; n--) { val = va_arg(args, unsigned); - for(mask = 0x80; mask; mask >>= 1) { - if(val & mask) + for (mask = 0x80; mask; mask >>= 1) { + if (val & mask) TR_SET_SDA; else TR_CLR_SDA; @@ -115,317 +116,128 @@ static void write_i2c(struct trust *tr, int n, ...) va_end(args); } -static void tr_setvol(struct trust *tr, __u16 vol) +static int trust_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) { - mutex_lock(&tr->lock); - tr->curvol = vol / 2048; - write_i2c(tr, 2, TDA7318_ADDR, tr->curvol ^ 0x1f); - mutex_unlock(&tr->lock); -} + struct trust *tr = container_of(isa, struct trust, isa); -static int basstreble2chip[15] = { - 0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8 -}; - -static void tr_setbass(struct trust *tr, __u16 bass) -{ - mutex_lock(&tr->lock); - tr->curbass = bass / 4370; - write_i2c(tr, 2, TDA7318_ADDR, 0x60 | basstreble2chip[tr->curbass]); - mutex_unlock(&tr->lock); -} - -static void tr_settreble(struct trust *tr, __u16 treble) -{ - mutex_lock(&tr->lock); - tr->curtreble = treble / 4370; - write_i2c(tr, 2, TDA7318_ADDR, 0x70 | basstreble2chip[tr->curtreble]); - mutex_unlock(&tr->lock); + tr->ioval = (tr->ioval & 0xf7) | (mute << 3); + outb(tr->ioval, isa->io); + write_i2c(tr, 2, TDA7318_ADDR, vol ^ 0x1f); + return 0; } -static void tr_setstereo(struct trust *tr, int stereo) +static int trust_s_stereo(struct radio_isa_card *isa, bool stereo) { - mutex_lock(&tr->lock); - tr->curstereo = !!stereo; - tr->ioval = (tr->ioval & 0xfb) | (!tr->curstereo << 2); - outb(tr->ioval, tr->io); - mutex_unlock(&tr->lock); -} + struct trust *tr = container_of(isa, struct trust, isa); -static void tr_setmute(struct trust *tr, int mute) -{ - mutex_lock(&tr->lock); - tr->curmute = !!mute; - tr->ioval = (tr->ioval & 0xf7) | (tr->curmute << 3); - outb(tr->ioval, tr->io); - mutex_unlock(&tr->lock); + tr->ioval = (tr->ioval & 0xfb) | (!stereo << 2); + outb(tr->ioval, isa->io); + return 0; } -static int tr_getsigstr(struct trust *tr) +static u32 trust_g_signal(struct radio_isa_card *isa) { int i, v; - mutex_lock(&tr->lock); for (i = 0, v = 0; i < 100; i++) - v |= inb(tr->io); - mutex_unlock(&tr->lock); + v |= inb(isa->io); return (v & 1) ? 0 : 0xffff; } -static int tr_getstereo(struct trust *tr) -{ - /* don't know how to determine it, just return the setting */ - return tr->curstereo; -} - -static void tr_setfreq(struct trust *tr, unsigned long f) +static int trust_s_frequency(struct radio_isa_card *isa, u32 freq) { - mutex_lock(&tr->lock); - tr->curfreq = f; - f /= 160; /* Convert to 10 kHz units */ - f += 1070; /* Add 10.7 MHz IF */ - write_i2c(tr, 5, TSA6060T_ADDR, (f << 1) | 1, f >> 7, 0x60 | ((f >> 15) & 1), 0); - mutex_unlock(&tr->lock); -} + struct trust *tr = container_of(isa, struct trust, isa); -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *v) -{ - strlcpy(v->driver, "radio-trust", sizeof(v->driver)); - strlcpy(v->card, "Trust FM Radio", sizeof(v->card)); - strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + freq /= 160; /* Convert to 10 kHz units */ + freq += 1070; /* Add 10.7 MHz IF */ + write_i2c(tr, 5, TSA6060T_ADDR, (freq << 1) | 1, + freq >> 7, 0x60 | ((freq >> 15) & 1), 0); return 0; } -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - struct trust *tr = video_drvdata(file); - - if (v->index > 0) - return -EINVAL; - - strlcpy(v->name, "FM", sizeof(v->name)); - v->type = V4L2_TUNER_RADIO; - v->rangelow = 87.5 * 16000; - v->rangehigh = 108 * 16000; - v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - v->capability = V4L2_TUNER_CAP_LOW; - if (tr_getstereo(tr)) - v->audmode = V4L2_TUNER_MODE_STEREO; - else - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal = tr_getsigstr(tr); - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - struct trust *tr = video_drvdata(file); - - if (v->index) - return -EINVAL; - tr_setstereo(tr, v->audmode == V4L2_TUNER_MODE_STEREO); - return 0; -} - -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct trust *tr = video_drvdata(file); - - if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) - return -EINVAL; - tr_setfreq(tr, f->frequency); - return 0; -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct trust *tr = video_drvdata(file); - - if (f->tuner != 0) - return -EINVAL; - f->type = V4L2_TUNER_RADIO; - f->frequency = tr->curfreq; - return 0; -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 65535, 2048, 65535); - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill(qc, 0, 65535, 4370, 32768); - } - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct trust *tr = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value = tr->curmute; - return 0; - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = tr->curvol * 2048; - return 0; - case V4L2_CID_AUDIO_BASS: - ctrl->value = tr->curbass * 4370; - return 0; - case V4L2_CID_AUDIO_TREBLE: - ctrl->value = tr->curtreble * 4370; - return 0; - } - return -EINVAL; -} +static int basstreble2chip[15] = { + 0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8 +}; -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) +static int trust_s_ctrl(struct v4l2_ctrl *ctrl) { - struct trust *tr = video_drvdata(file); + struct radio_isa_card *isa = + container_of(ctrl->handler, struct radio_isa_card, hdl); + struct trust *tr = container_of(isa, struct trust, isa); switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - tr_setmute(tr, ctrl->value); - return 0; - case V4L2_CID_AUDIO_VOLUME: - tr_setvol(tr, ctrl->value); - return 0; case V4L2_CID_AUDIO_BASS: - tr_setbass(tr, ctrl->value); + write_i2c(tr, 2, TDA7318_ADDR, 0x60 | basstreble2chip[ctrl->val]); return 0; case V4L2_CID_AUDIO_TREBLE: - tr_settreble(tr, ctrl->value); + write_i2c(tr, 2, TDA7318_ADDR, 0x70 | basstreble2chip[ctrl->val]); return 0; } return -EINVAL; } -static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - return i ? -EINVAL : 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - a->index = 0; - strlcpy(a->name, "Radio", sizeof(a->name)); - a->capability = V4L2_AUDCAP_STEREO; - return 0; -} - -static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - return a->index ? -EINVAL : 0; -} - -static const struct v4l2_file_operations trust_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops trust_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, +static const struct v4l2_ctrl_ops trust_ctrl_ops = { + .s_ctrl = trust_s_ctrl, }; -static int __init trust_init(void) +static int trust_initialize(struct radio_isa_card *isa) { - struct trust *tr = &trust_card; - struct v4l2_device *v4l2_dev = &tr->v4l2_dev; - int res; + struct trust *tr = container_of(isa, struct trust, isa); - strlcpy(v4l2_dev->name, "trust", sizeof(v4l2_dev->name)); - tr->io = io; tr->ioval = 0xf; - mutex_init(&tr->lock); - - if (tr->io == -1) { - v4l2_err(v4l2_dev, "You must set an I/O address with io=0x0x350 or 0x358\n"); - return -EINVAL; - } - if (!request_region(tr->io, 2, "Trust FM Radio")) { - v4l2_err(v4l2_dev, "port 0x%x already in use\n", tr->io); - return -EBUSY; - } - - res = v4l2_device_register(NULL, v4l2_dev); - if (res < 0) { - release_region(tr->io, 2); - v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); - return res; - } - - strlcpy(tr->vdev.name, v4l2_dev->name, sizeof(tr->vdev.name)); - tr->vdev.v4l2_dev = v4l2_dev; - tr->vdev.fops = &trust_fops; - tr->vdev.ioctl_ops = &trust_ioctl_ops; - tr->vdev.release = video_device_release_empty; - video_set_drvdata(&tr->vdev, tr); - write_i2c(tr, 2, TDA7318_ADDR, 0x80); /* speaker att. LF = 0 dB */ write_i2c(tr, 2, TDA7318_ADDR, 0xa0); /* speaker att. RF = 0 dB */ write_i2c(tr, 2, TDA7318_ADDR, 0xc0); /* speaker att. LR = 0 dB */ write_i2c(tr, 2, TDA7318_ADDR, 0xe0); /* speaker att. RR = 0 dB */ write_i2c(tr, 2, TDA7318_ADDR, 0x40); /* stereo 1 input, gain = 18.75 dB */ - tr_setvol(tr, 0xffff); - tr_setbass(tr, 0x8000); - tr_settreble(tr, 0x8000); - tr_setstereo(tr, 1); - - /* mute card - prevents noisy bootups */ - tr_setmute(tr, 1); + v4l2_ctrl_new_std(&isa->hdl, &trust_ctrl_ops, + V4L2_CID_AUDIO_BASS, 0, 15, 1, 8); + v4l2_ctrl_new_std(&isa->hdl, &trust_ctrl_ops, + V4L2_CID_AUDIO_TREBLE, 0, 15, 1, 8); + return isa->hdl.error; +} - if (video_register_device(&tr->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { - v4l2_device_unregister(v4l2_dev); - release_region(tr->io, 2); - return -EINVAL; - } +static const struct radio_isa_ops trust_ops = { + .init = trust_initialize, + .alloc = trust_alloc, + .s_mute_volume = trust_s_mute_volume, + .s_frequency = trust_s_frequency, + .s_stereo = trust_s_stereo, + .g_signal = trust_g_signal, +}; - v4l2_info(v4l2_dev, "Trust FM Radio card driver v1.0.\n"); +static const int trust_ioports[] = { 0x350, 0x358 }; + +static struct radio_isa_driver trust_driver = { + .driver = { + .match = radio_isa_match, + .probe = radio_isa_probe, + .remove = radio_isa_remove, + .driver = { + .name = "radio-trust", + }, + }, + .io_params = io, + .radio_nr_params = radio_nr, + .io_ports = trust_ioports, + .num_of_io_ports = ARRAY_SIZE(trust_ioports), + .region_size = 2, + .card = "Trust FM Radio", + .ops = &trust_ops, + .has_stereo = true, + .max_volume = 31, +}; - return 0; +static int __init trust_init(void) +{ + return isa_register_driver(&trust_driver.driver, TRUST_MAX); } -static void __exit cleanup_trust_module(void) +static void __exit trust_exit(void) { - struct trust *tr = &trust_card; - - video_unregister_device(&tr->vdev); - v4l2_device_unregister(&tr->v4l2_dev); - release_region(tr->io, 2); + isa_unregister_driver(&trust_driver.driver); } module_init(trust_init); -module_exit(cleanup_trust_module); +module_exit(trust_exit); diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c index 398726abc0c8..eb72a4d13758 100644 --- a/drivers/media/radio/radio-typhoon.c +++ b/drivers/media/radio/radio-typhoon.c @@ -33,63 +33,53 @@ #include <linux/ioport.h> /* request_region */ #include <linux/videodev2.h> /* kernel radio structs */ #include <linux/io.h> /* outb, outb_p */ +#include <linux/slab.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> +#include "radio-isa.h" #define DRIVER_VERSION "0.1.2" MODULE_AUTHOR("Dr. Henrik Seidel"); MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio)."); MODULE_LICENSE("GPL"); -MODULE_VERSION(DRIVER_VERSION); +MODULE_VERSION("0.1.99"); #ifndef CONFIG_RADIO_TYPHOON_PORT #define CONFIG_RADIO_TYPHOON_PORT -1 #endif #ifndef CONFIG_RADIO_TYPHOON_MUTEFREQ -#define CONFIG_RADIO_TYPHOON_MUTEFREQ 0 +#define CONFIG_RADIO_TYPHOON_MUTEFREQ 87000 #endif -static int io = CONFIG_RADIO_TYPHOON_PORT; -static int radio_nr = -1; - -module_param(io, int, 0); -MODULE_PARM_DESC(io, "I/O address of the Typhoon card (0x316 or 0x336)"); - -module_param(radio_nr, int, 0); +#define TYPHOON_MAX 2 +static int io[TYPHOON_MAX] = { [0] = CONFIG_RADIO_TYPHOON_PORT, + [1 ... (TYPHOON_MAX - 1)] = -1 }; +static int radio_nr[TYPHOON_MAX] = { [0 ... (TYPHOON_MAX - 1)] = -1 }; static unsigned long mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ; + +module_param_array(io, int, NULL, 0444); +MODULE_PARM_DESC(io, "I/O addresses of the Typhoon card (0x316 or 0x336)"); +module_param_array(radio_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_nr, "Radio device numbers"); module_param(mutefreq, ulong, 0); MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)"); -#define BANNER "Typhoon Radio Card driver v" DRIVER_VERSION "\n" - struct typhoon { - struct v4l2_device v4l2_dev; - struct video_device vdev; - int io; - int curvol; + struct radio_isa_card isa; int muted; - unsigned long curfreq; - unsigned long mutefreq; - struct mutex lock; }; -static struct typhoon typhoon_card; - -static void typhoon_setvol_generic(struct typhoon *dev, int vol) +static struct radio_isa_card *typhoon_alloc(void) { - mutex_lock(&dev->lock); - vol >>= 14; /* Map 16 bit to 2 bit */ - vol &= 3; - outb_p(vol / 2, dev->io); /* Set the volume, high bit. */ - outb_p(vol % 2, dev->io + 2); /* Set the volume, low bit. */ - mutex_unlock(&dev->lock); + struct typhoon *ty = kzalloc(sizeof(*ty), GFP_KERNEL); + + return ty ? &ty->isa : NULL; } -static int typhoon_setfreq_generic(struct typhoon *dev, - unsigned long frequency) +static int typhoon_s_frequency(struct radio_isa_card *isa, u32 freq) { unsigned long outval; unsigned long x; @@ -105,302 +95,86 @@ static int typhoon_setfreq_generic(struct typhoon *dev, * */ - mutex_lock(&dev->lock); - x = frequency / 160; + x = freq / 160; outval = (x * x + 2500) / 5000; outval = (outval * x + 5000) / 10000; outval -= (10 * x * x + 10433) / 20866; outval += 4 * x - 11505; - outb_p((outval >> 8) & 0x01, dev->io + 4); - outb_p(outval >> 9, dev->io + 6); - outb_p(outval & 0xff, dev->io + 8); - mutex_unlock(&dev->lock); - - return 0; -} - -static int typhoon_setfreq(struct typhoon *dev, unsigned long frequency) -{ - typhoon_setfreq_generic(dev, frequency); - dev->curfreq = frequency; - return 0; -} - -static void typhoon_mute(struct typhoon *dev) -{ - if (dev->muted == 1) - return; - typhoon_setvol_generic(dev, 0); - typhoon_setfreq_generic(dev, dev->mutefreq); - dev->muted = 1; -} - -static void typhoon_unmute(struct typhoon *dev) -{ - if (dev->muted == 0) - return; - typhoon_setfreq_generic(dev, dev->curfreq); - typhoon_setvol_generic(dev, dev->curvol); - dev->muted = 0; -} - -static int typhoon_setvol(struct typhoon *dev, int vol) -{ - if (dev->muted && vol != 0) { /* user is unmuting the card */ - dev->curvol = vol; - typhoon_unmute(dev); - return 0; - } - if (vol == dev->curvol) /* requested volume == current */ - return 0; - - if (vol == 0) { /* volume == 0 means mute the card */ - typhoon_mute(dev); - dev->curvol = vol; - return 0; - } - typhoon_setvol_generic(dev, vol); - dev->curvol = vol; - return 0; -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *v) -{ - strlcpy(v->driver, "radio-typhoon", sizeof(v->driver)); - strlcpy(v->card, "Typhoon Radio", sizeof(v->card)); - strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - return 0; -} - -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - if (v->index > 0) - return -EINVAL; - - strlcpy(v->name, "FM", sizeof(v->name)); - v->type = V4L2_TUNER_RADIO; - v->rangelow = 87.5 * 16000; - v->rangehigh = 108 * 16000; - v->rxsubchans = V4L2_TUNER_SUB_MONO; - v->capability = V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal = 0xFFFF; /* We can't get the signal strength */ - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - return v->index ? -EINVAL : 0; -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct typhoon *dev = video_drvdata(file); - - if (f->tuner != 0) - return -EINVAL; - f->type = V4L2_TUNER_RADIO; - f->frequency = dev->curfreq; - return 0; -} - -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct typhoon *dev = video_drvdata(file); - - if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) - return -EINVAL; - dev->curfreq = f->frequency; - typhoon_setfreq(dev, dev->curfreq); + outb_p((outval >> 8) & 0x01, isa->io + 4); + outb_p(outval >> 9, isa->io + 6); + outb_p(outval & 0xff, isa->io + 8); return 0; } -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) +static int typhoon_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) { - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 65535, 16384, 65535); - } - return -EINVAL; -} + struct typhoon *ty = container_of(isa, struct typhoon, isa); -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct typhoon *dev = video_drvdata(file); + if (mute) + vol = 0; + vol >>= 14; /* Map 16 bit to 2 bit */ + vol &= 3; + outb_p(vol / 2, isa->io); /* Set the volume, high bit. */ + outb_p(vol % 2, isa->io + 2); /* Set the volume, low bit. */ - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value = dev->muted; - return 0; - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = dev->curvol; - return 0; + if (vol == 0 && !ty->muted) { + ty->muted = true; + return typhoon_s_frequency(isa, mutefreq << 4); } - return -EINVAL; -} - -static int vidioc_s_ctrl (struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct typhoon *dev = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) - typhoon_mute(dev); - else - typhoon_unmute(dev); - return 0; - case V4L2_CID_AUDIO_VOLUME: - typhoon_setvol(dev, ctrl->value); - return 0; + if (vol && ty->muted) { + ty->muted = false; + return typhoon_s_frequency(isa, isa->freq); } - return -EINVAL; -} - -static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; return 0; } -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - return i ? -EINVAL : 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - a->index = 0; - strlcpy(a->name, "Radio", sizeof(a->name)); - a->capability = V4L2_AUDCAP_STEREO; - return 0; -} - -static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - return a->index ? -EINVAL : 0; -} - -static int vidioc_log_status(struct file *file, void *priv) -{ - struct typhoon *dev = video_drvdata(file); - struct v4l2_device *v4l2_dev = &dev->v4l2_dev; - - v4l2_info(v4l2_dev, BANNER); -#ifdef MODULE - v4l2_info(v4l2_dev, "Load type: Driver loaded as a module\n\n"); -#else - v4l2_info(v4l2_dev, "Load type: Driver compiled into kernel\n\n"); -#endif - v4l2_info(v4l2_dev, "frequency = %lu kHz\n", dev->curfreq >> 4); - v4l2_info(v4l2_dev, "volume = %d\n", dev->curvol); - v4l2_info(v4l2_dev, "mute = %s\n", dev->muted ? "on" : "off"); - v4l2_info(v4l2_dev, "io = 0x%x\n", dev->io); - v4l2_info(v4l2_dev, "mute frequency = %lu kHz\n", dev->mutefreq >> 4); - return 0; -} - -static const struct v4l2_file_operations typhoon_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = video_ioctl2, +static const struct radio_isa_ops typhoon_ops = { + .alloc = typhoon_alloc, + .s_mute_volume = typhoon_s_mute_volume, + .s_frequency = typhoon_s_frequency, }; -static const struct v4l2_ioctl_ops typhoon_ioctl_ops = { - .vidioc_log_status = vidioc_log_status, - .vidioc_querycap = vidioc_querycap, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, +static const int typhoon_ioports[] = { 0x316, 0x336 }; + +static struct radio_isa_driver typhoon_driver = { + .driver = { + .match = radio_isa_match, + .probe = radio_isa_probe, + .remove = radio_isa_remove, + .driver = { + .name = "radio-typhoon", + }, + }, + .io_params = io, + .radio_nr_params = radio_nr, + .io_ports = typhoon_ioports, + .num_of_io_ports = ARRAY_SIZE(typhoon_ioports), + .region_size = 8, + .card = "Typhoon Radio", + .ops = &typhoon_ops, + .has_stereo = true, + .max_volume = 3, }; static int __init typhoon_init(void) { - struct typhoon *dev = &typhoon_card; - struct v4l2_device *v4l2_dev = &dev->v4l2_dev; - int res; - - strlcpy(v4l2_dev->name, "typhoon", sizeof(v4l2_dev->name)); - dev->io = io; - - if (dev->io == -1) { - v4l2_err(v4l2_dev, "You must set an I/O address with io=0x316 or io=0x336\n"); - return -EINVAL; - } - - if (mutefreq < 87000 || mutefreq > 108500) { - v4l2_err(v4l2_dev, "You must set a frequency (in kHz) used when muting the card,\n"); - v4l2_err(v4l2_dev, "e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108500)\n"); - return -EINVAL; - } - dev->curfreq = dev->mutefreq = mutefreq << 4; - - mutex_init(&dev->lock); - if (!request_region(dev->io, 8, "typhoon")) { - v4l2_err(v4l2_dev, "port 0x%x already in use\n", - dev->io); - return -EBUSY; - } - - res = v4l2_device_register(NULL, v4l2_dev); - if (res < 0) { - release_region(dev->io, 8); - v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); - return res; + if (mutefreq < 87000 || mutefreq > 108000) { + printk(KERN_ERR "%s: You must set a frequency (in kHz) used when muting the card,\n", + typhoon_driver.driver.driver.name); + printk(KERN_ERR "%s: e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108000)\n", + typhoon_driver.driver.driver.name); + return -ENODEV; } - v4l2_info(v4l2_dev, BANNER); - - strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); - dev->vdev.v4l2_dev = v4l2_dev; - dev->vdev.fops = &typhoon_fops; - dev->vdev.ioctl_ops = &typhoon_ioctl_ops; - dev->vdev.release = video_device_release_empty; - video_set_drvdata(&dev->vdev, dev); - - /* mute card - prevents noisy bootups */ - typhoon_mute(dev); - - if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { - v4l2_device_unregister(&dev->v4l2_dev); - release_region(dev->io, 8); - return -EINVAL; - } - v4l2_info(v4l2_dev, "port 0x%x.\n", dev->io); - v4l2_info(v4l2_dev, "mute frequency is %lu kHz.\n", mutefreq); - - return 0; + return isa_register_driver(&typhoon_driver.driver, TYPHOON_MAX); } static void __exit typhoon_exit(void) { - struct typhoon *dev = &typhoon_card; - - video_unregister_device(&dev->vdev); - v4l2_device_unregister(&dev->v4l2_dev); - release_region(dev->io, 8); + isa_unregister_driver(&typhoon_driver.driver); } + module_init(typhoon_init); module_exit(typhoon_exit); diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c index f5613b948203..026e88eef29c 100644 --- a/drivers/media/radio/radio-zoltrix.c +++ b/drivers/media/radio/radio-zoltrix.c @@ -1,5 +1,6 @@ -/* zoltrix radio plus driver for Linux radio support - * (c) 1998 C. van Schaik <carl@leg.uct.ac.za> +/* + * Zoltrix Radio Plus driver + * Copyright 1998 C. van Schaik <carl@leg.uct.ac.za> * * BUGS * Due to the inconsistency in reading from the signal flags @@ -27,6 +28,14 @@ * * 2006-07-24 - Converted to V4L2 API * by Mauro Carvalho Chehab <mchehab@infradead.org> + * + * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com> + * + * Note that this is the driver for the Zoltrix Radio Plus. + * This driver does not work for the Zoltrix Radio Plus 108 or the + * Zoltrix Radio Plus for Windows. + * + * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool. */ #include <linux/module.h> /* Modules */ @@ -36,82 +45,70 @@ #include <linux/videodev2.h> /* kernel radio structs */ #include <linux/mutex.h> #include <linux/io.h> /* outb, outb_p */ +#include <linux/slab.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> +#include "radio-isa.h" -MODULE_AUTHOR("C.van Schaik"); +MODULE_AUTHOR("C. van Schaik"); MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus."); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.0.3"); +MODULE_VERSION("0.1.99"); #ifndef CONFIG_RADIO_ZOLTRIX_PORT #define CONFIG_RADIO_ZOLTRIX_PORT -1 #endif -static int io = CONFIG_RADIO_ZOLTRIX_PORT; -static int radio_nr = -1; +#define ZOLTRIX_MAX 2 + +static int io[ZOLTRIX_MAX] = { [0] = CONFIG_RADIO_ZOLTRIX_PORT, + [1 ... (ZOLTRIX_MAX - 1)] = -1 }; +static int radio_nr[ZOLTRIX_MAX] = { [0 ... (ZOLTRIX_MAX - 1)] = -1 }; -module_param(io, int, 0); -MODULE_PARM_DESC(io, "I/O address of the Zoltrix Radio Plus (0x20c or 0x30c)"); -module_param(radio_nr, int, 0); +module_param_array(io, int, NULL, 0444); +MODULE_PARM_DESC(io, "I/O addresses of the Zoltrix Radio Plus card (0x20c or 0x30c)"); +module_param_array(radio_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_nr, "Radio device numbers"); struct zoltrix { - struct v4l2_device v4l2_dev; - struct video_device vdev; - int io; + struct radio_isa_card isa; int curvol; - unsigned long curfreq; - int muted; - unsigned int stereo; - struct mutex lock; + bool muted; }; -static struct zoltrix zoltrix_card; +static struct radio_isa_card *zoltrix_alloc(void) +{ + struct zoltrix *zol = kzalloc(sizeof(*zol), GFP_KERNEL); + + return zol ? &zol->isa : NULL; +} -static int zol_setvol(struct zoltrix *zol, int vol) +static int zoltrix_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) { - zol->curvol = vol; - if (zol->muted) - return 0; + struct zoltrix *zol = container_of(isa, struct zoltrix, isa); - mutex_lock(&zol->lock); - if (vol == 0) { - outb(0, zol->io); - outb(0, zol->io); - inb(zol->io + 3); /* Zoltrix needs to be read to confirm */ - mutex_unlock(&zol->lock); + zol->curvol = vol; + zol->muted = mute; + if (mute || vol == 0) { + outb(0, isa->io); + outb(0, isa->io); + inb(isa->io + 3); /* Zoltrix needs to be read to confirm */ return 0; } - outb(zol->curvol-1, zol->io); + outb(vol - 1, isa->io); msleep(10); - inb(zol->io + 2); - mutex_unlock(&zol->lock); + inb(isa->io + 2); return 0; } -static void zol_mute(struct zoltrix *zol) -{ - zol->muted = 1; - mutex_lock(&zol->lock); - outb(0, zol->io); - outb(0, zol->io); - inb(zol->io + 3); /* Zoltrix needs to be read to confirm */ - mutex_unlock(&zol->lock); -} - -static void zol_unmute(struct zoltrix *zol) -{ - zol->muted = 0; - zol_setvol(zol, zol->curvol); -} - -static int zol_setfreq(struct zoltrix *zol, unsigned long freq) +/* tunes the radio to the desired frequency */ +static int zoltrix_s_frequency(struct radio_isa_card *isa, u32 freq) { - /* tunes the radio to the desired frequency */ - struct v4l2_device *v4l2_dev = &zol->v4l2_dev; + struct zoltrix *zol = container_of(isa, struct zoltrix, isa); + struct v4l2_device *v4l2_dev = &isa->v4l2_dev; unsigned long long bitmask, f, m; - unsigned int stereo = zol->stereo; + bool stereo = isa->stereo; int i; if (freq == 0) { @@ -125,340 +122,125 @@ static int zol_setfreq(struct zoltrix *zol, unsigned long freq) bitmask = 0xc480402c10080000ull; i = 45; - mutex_lock(&zol->lock); - - zol->curfreq = freq; - - outb(0, zol->io); - outb(0, zol->io); - inb(zol->io + 3); /* Zoltrix needs to be read to confirm */ + outb(0, isa->io); + outb(0, isa->io); + inb(isa->io + 3); /* Zoltrix needs to be read to confirm */ - outb(0x40, zol->io); - outb(0xc0, zol->io); + outb(0x40, isa->io); + outb(0xc0, isa->io); bitmask = (bitmask ^ ((f & 0xff) << 47) ^ ((f & 0xff00) << 30) ^ (stereo << 31)); while (i--) { if ((bitmask & 0x8000000000000000ull) != 0) { - outb(0x80, zol->io); + outb(0x80, isa->io); udelay(50); - outb(0x00, zol->io); + outb(0x00, isa->io); udelay(50); - outb(0x80, zol->io); + outb(0x80, isa->io); udelay(50); } else { - outb(0xc0, zol->io); + outb(0xc0, isa->io); udelay(50); - outb(0x40, zol->io); + outb(0x40, isa->io); udelay(50); - outb(0xc0, zol->io); + outb(0xc0, isa->io); udelay(50); } bitmask *= 2; } /* termination sequence */ - outb(0x80, zol->io); - outb(0xc0, zol->io); - outb(0x40, zol->io); + outb(0x80, isa->io); + outb(0xc0, isa->io); + outb(0x40, isa->io); udelay(1000); - inb(zol->io + 2); - + inb(isa->io + 2); udelay(1000); - if (zol->muted) { - outb(0, zol->io); - outb(0, zol->io); - inb(zol->io + 3); - udelay(1000); - } - - mutex_unlock(&zol->lock); - - if (!zol->muted) - zol_setvol(zol, zol->curvol); - return 0; + return zoltrix_s_mute_volume(isa, zol->muted, zol->curvol); } /* Get signal strength */ -static int zol_getsigstr(struct zoltrix *zol) +static u32 zoltrix_g_rxsubchans(struct radio_isa_card *isa) { + struct zoltrix *zol = container_of(isa, struct zoltrix, isa); int a, b; - mutex_lock(&zol->lock); - outb(0x00, zol->io); /* This stuff I found to do nothing */ - outb(zol->curvol, zol->io); + outb(0x00, isa->io); /* This stuff I found to do nothing */ + outb(zol->curvol, isa->io); msleep(20); - a = inb(zol->io); + a = inb(isa->io); msleep(10); - b = inb(zol->io); + b = inb(isa->io); - mutex_unlock(&zol->lock); - - if (a != b) - return 0; - - /* I found this out by playing with a binary scanner on the card io */ - return a == 0xcf || a == 0xdf || a == 0xef; + return (a == b && a == 0xcf) ? + V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; } -static int zol_is_stereo(struct zoltrix *zol) +static u32 zoltrix_g_signal(struct radio_isa_card *isa) { - int x1, x2; - - mutex_lock(&zol->lock); + struct zoltrix *zol = container_of(isa, struct zoltrix, isa); + int a, b; - outb(0x00, zol->io); - outb(zol->curvol, zol->io); + outb(0x00, isa->io); /* This stuff I found to do nothing */ + outb(zol->curvol, isa->io); msleep(20); - x1 = inb(zol->io); + a = inb(isa->io); msleep(10); - x2 = inb(zol->io); - - mutex_unlock(&zol->lock); - - return x1 == x2 && x1 == 0xcf; -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *v) -{ - strlcpy(v->driver, "radio-zoltrix", sizeof(v->driver)); - strlcpy(v->card, "Zoltrix Radio", sizeof(v->card)); - strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - return 0; -} - -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - struct zoltrix *zol = video_drvdata(file); - - if (v->index > 0) - return -EINVAL; - - strlcpy(v->name, "FM", sizeof(v->name)); - v->type = V4L2_TUNER_RADIO; - v->rangelow = 88 * 16000; - v->rangehigh = 108 * 16000; - v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - v->capability = V4L2_TUNER_CAP_LOW; - if (zol_is_stereo(zol)) - v->audmode = V4L2_TUNER_MODE_STEREO; - else - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal = 0xFFFF * zol_getsigstr(zol); - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - return v->index ? -EINVAL : 0; -} - -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct zoltrix *zol = video_drvdata(file); - - if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) - return -EINVAL; - if (zol_setfreq(zol, f->frequency) != 0) - return -EINVAL; - return 0; -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct zoltrix *zol = video_drvdata(file); + b = inb(isa->io); - if (f->tuner != 0) - return -EINVAL; - f->type = V4L2_TUNER_RADIO; - f->frequency = zol->curfreq; - return 0; -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 65535, 4096, 65535); - } - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct zoltrix *zol = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value = zol->muted; - return 0; - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = zol->curvol * 4096; - return 0; - } - return -EINVAL; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct zoltrix *zol = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) - zol_mute(zol); - else { - zol_unmute(zol); - zol_setvol(zol, zol->curvol); - } - return 0; - case V4L2_CID_AUDIO_VOLUME: - zol_setvol(zol, ctrl->value / 4096); + if (a != b) return 0; - } - zol->stereo = 1; - if (zol_setfreq(zol, zol->curfreq) != 0) - return -EINVAL; -#if 0 -/* FIXME: Implement stereo/mono switch on V4L2 */ - if (v->mode & VIDEO_SOUND_STEREO) { - zol->stereo = 1; - zol_setfreq(zol, zol->curfreq); - } - if (v->mode & VIDEO_SOUND_MONO) { - zol->stereo = 0; - zol_setfreq(zol, zol->curfreq); - } -#endif - return -EINVAL; -} - -static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - return i ? -EINVAL : 0; -} -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - a->index = 0; - strlcpy(a->name, "Radio", sizeof(a->name)); - a->capability = V4L2_AUDCAP_STEREO; - return 0; + /* I found this out by playing with a binary scanner on the card io */ + return (a == 0xcf || a == 0xdf || a == 0xef) ? 0xffff : 0; } -static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) +static int zoltrix_s_stereo(struct radio_isa_card *isa, bool stereo) { - return a->index ? -EINVAL : 0; + return zoltrix_s_frequency(isa, isa->freq); } -static const struct v4l2_file_operations zoltrix_fops = -{ - .owner = THIS_MODULE, - .unlocked_ioctl = video_ioctl2, +static const struct radio_isa_ops zoltrix_ops = { + .alloc = zoltrix_alloc, + .s_mute_volume = zoltrix_s_mute_volume, + .s_frequency = zoltrix_s_frequency, + .s_stereo = zoltrix_s_stereo, + .g_rxsubchans = zoltrix_g_rxsubchans, + .g_signal = zoltrix_g_signal, }; -static const struct v4l2_ioctl_ops zoltrix_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, +static const int zoltrix_ioports[] = { 0x20c, 0x30c }; + +static struct radio_isa_driver zoltrix_driver = { + .driver = { + .match = radio_isa_match, + .probe = radio_isa_probe, + .remove = radio_isa_remove, + .driver = { + .name = "radio-zoltrix", + }, + }, + .io_params = io, + .radio_nr_params = radio_nr, + .io_ports = zoltrix_ioports, + .num_of_io_ports = ARRAY_SIZE(zoltrix_ioports), + .region_size = 2, + .card = "Zoltrix Radio Plus", + .ops = &zoltrix_ops, + .has_stereo = true, + .max_volume = 15, }; static int __init zoltrix_init(void) { - struct zoltrix *zol = &zoltrix_card; - struct v4l2_device *v4l2_dev = &zol->v4l2_dev; - int res; - - strlcpy(v4l2_dev->name, "zoltrix", sizeof(v4l2_dev->name)); - zol->io = io; - if (zol->io == -1) { - v4l2_err(v4l2_dev, "You must set an I/O address with io=0x20c or 0x30c\n"); - return -EINVAL; - } - if (zol->io != 0x20c && zol->io != 0x30c) { - v4l2_err(v4l2_dev, "invalid port, try 0x20c or 0x30c\n"); - return -ENXIO; - } - - if (!request_region(zol->io, 2, "zoltrix")) { - v4l2_err(v4l2_dev, "port 0x%x already in use\n", zol->io); - return -EBUSY; - } - - res = v4l2_device_register(NULL, v4l2_dev); - if (res < 0) { - release_region(zol->io, 2); - v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); - return res; - } - - mutex_init(&zol->lock); - - /* mute card - prevents noisy bootups */ - - /* this ensures that the volume is all the way down */ - - outb(0, zol->io); - outb(0, zol->io); - msleep(20); - inb(zol->io + 3); - - zol->curvol = 0; - zol->stereo = 1; - - strlcpy(zol->vdev.name, v4l2_dev->name, sizeof(zol->vdev.name)); - zol->vdev.v4l2_dev = v4l2_dev; - zol->vdev.fops = &zoltrix_fops; - zol->vdev.ioctl_ops = &zoltrix_ioctl_ops; - zol->vdev.release = video_device_release_empty; - video_set_drvdata(&zol->vdev, zol); - - if (video_register_device(&zol->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { - v4l2_device_unregister(v4l2_dev); - release_region(zol->io, 2); - return -EINVAL; - } - v4l2_info(v4l2_dev, "Zoltrix Radio Plus card driver.\n"); - - return 0; + return isa_register_driver(&zoltrix_driver.driver, ZOLTRIX_MAX); } static void __exit zoltrix_exit(void) { - struct zoltrix *zol = &zoltrix_card; - - video_unregister_device(&zol->vdev); - v4l2_device_unregister(&zol->v4l2_dev); - release_region(zol->io, 2); + isa_unregister_driver(&zoltrix_driver.driver); } module_init(zoltrix_init); diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c index b1193dfc5087..9474706350f8 100644 --- a/drivers/media/radio/saa7706h.c +++ b/drivers/media/radio/saa7706h.c @@ -434,18 +434,7 @@ static struct i2c_driver saa7706h_driver = { .id_table = saa7706h_id, }; -static __init int saa7706h_init(void) -{ - return i2c_add_driver(&saa7706h_driver); -} - -static __exit void saa7706h_exit(void) -{ - i2c_del_driver(&saa7706h_driver); -} - -module_init(saa7706h_init); -module_exit(saa7706h_exit); +module_i2c_driver(saa7706h_driver); MODULE_DESCRIPTION("SAA7706H Car Radio DSP driver"); MODULE_AUTHOR("Mocean Laboratories"); diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index fd3541b0e91c..9b546a5523f3 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -539,33 +539,7 @@ static struct i2c_driver si470x_i2c_driver = { .id_table = si470x_i2c_id, }; - - -/************************************************************************** - * Module Interface - **************************************************************************/ - -/* - * si470x_i2c_init - module init - */ -static int __init si470x_i2c_init(void) -{ - printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n"); - return i2c_add_driver(&si470x_i2c_driver); -} - - -/* - * si470x_i2c_exit - module exit - */ -static void __exit si470x_i2c_exit(void) -{ - i2c_del_driver(&si470x_i2c_driver); -} - - -module_init(si470x_i2c_init); -module_exit(si470x_i2c_exit); +module_i2c_driver(si470x_i2c_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR(DRIVER_AUTHOR); diff --git a/drivers/media/radio/si4713-i2c.c b/drivers/media/radio/si4713-i2c.c index 27aba936fb2b..b898c8925ab7 100644 --- a/drivers/media/radio/si4713-i2c.c +++ b/drivers/media/radio/si4713-i2c.c @@ -2106,17 +2106,4 @@ static struct i2c_driver si4713_i2c_driver = { .id_table = si4713_id, }; -/* Module Interface */ -static int __init si4713_module_init(void) -{ - return i2c_add_driver(&si4713_i2c_driver); -} - -static void __exit si4713_module_exit(void) -{ - i2c_del_driver(&si4713_i2c_driver); -} - -module_init(si4713_module_init); -module_exit(si4713_module_exit); - +module_i2c_driver(si4713_i2c_driver); diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c index 3408685b690c..6418c4c9faf1 100644 --- a/drivers/media/radio/tef6862.c +++ b/drivers/media/radio/tef6862.c @@ -215,20 +215,8 @@ static struct i2c_driver tef6862_driver = { .id_table = tef6862_id, }; -static __init int tef6862_init(void) -{ - return i2c_add_driver(&tef6862_driver); -} - -static __exit void tef6862_exit(void) -{ - i2c_del_driver(&tef6862_driver); -} - -module_init(tef6862_init); -module_exit(tef6862_exit); +module_i2c_driver(tef6862_driver); MODULE_DESCRIPTION("TEF6862 Car Radio Enhanced Selectivity Tuner"); MODULE_AUTHOR("Mocean Laboratories"); MODULE_LICENSE("GPL v2"); - diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 4df4affeea5f..a3fbb21350e9 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -266,4 +266,13 @@ config RC_LOOPBACK To compile this driver as a module, choose M here: the module will be called rc_loopback. +config IR_GPIO_CIR + tristate "GPIO IR remote control" + depends on RC_CORE + ---help--- + Say Y if you want to use GPIO based IR Receiver. + + To compile this driver as a module, choose M here: the module will + be called gpio-ir-recv. + endif #RC_CORE diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index fb3dee2dd845..29f364f88a94 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -26,3 +26,4 @@ obj-$(CONFIG_IR_REDRAT3) += redrat3.o obj-$(CONFIG_IR_STREAMZAP) += streamzap.o obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o +obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index 7f7079b12f23..392d4be91f8f 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -117,7 +117,7 @@ static u8 fintek_cir_reg_read(struct fintek_dev *fintek, u8 offset) static void cir_dump_regs(struct fintek_dev *fintek) { fintek_config_mode_enable(fintek); - fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + fintek_select_logical_dev(fintek, fintek->logical_dev_cir); pr_reg("%s: Dump CIR logical device registers:\n", FINTEK_DRIVER_NAME); pr_reg(" * CR CIR BASE ADDR: 0x%x\n", @@ -143,7 +143,7 @@ static int fintek_hw_detect(struct fintek_dev *fintek) u8 chip_major, chip_minor; u8 vendor_major, vendor_minor; u8 portsel, ir_class; - u16 vendor; + u16 vendor, chip; int ret = 0; fintek_config_mode_enable(fintek); @@ -176,6 +176,7 @@ static int fintek_hw_detect(struct fintek_dev *fintek) chip_major = fintek_cr_read(fintek, GCR_CHIP_ID_HI); chip_minor = fintek_cr_read(fintek, GCR_CHIP_ID_LO); + chip = chip_major << 8 | chip_minor; vendor_major = fintek_cr_read(fintek, GCR_VENDOR_ID_HI); vendor_minor = fintek_cr_read(fintek, GCR_VENDOR_ID_LO); @@ -192,6 +193,15 @@ static int fintek_hw_detect(struct fintek_dev *fintek) fintek->chip_major = chip_major; fintek->chip_minor = chip_minor; fintek->chip_vendor = vendor; + + /* + * Newer reviews of this chipset uses port 8 instead of 5 + */ + if ((chip != 0x0408) || (chip != 0x0804)) + fintek->logical_dev_cir = LOGICAL_DEV_CIR_REV2; + else + fintek->logical_dev_cir = LOGICAL_DEV_CIR_REV1; + spin_unlock_irqrestore(&fintek->fintek_lock, flags); return ret; @@ -200,7 +210,7 @@ static int fintek_hw_detect(struct fintek_dev *fintek) static void fintek_cir_ldev_init(struct fintek_dev *fintek) { /* Select CIR logical device and enable */ - fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + fintek_select_logical_dev(fintek, fintek->logical_dev_cir); fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN); /* Write allocated CIR address and IRQ information to hardware */ @@ -381,7 +391,7 @@ static irqreturn_t fintek_cir_isr(int irq, void *data) fit_dbg_verbose("%s firing", __func__); fintek_config_mode_enable(fintek); - fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + fintek_select_logical_dev(fintek, fintek->logical_dev_cir); fintek_config_mode_disable(fintek); /* @@ -422,7 +432,7 @@ static void fintek_enable_cir(struct fintek_dev *fintek) fintek_config_mode_enable(fintek); /* enable the CIR logical device */ - fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + fintek_select_logical_dev(fintek, fintek->logical_dev_cir); fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN); fintek_config_mode_disable(fintek); @@ -439,7 +449,7 @@ static void fintek_disable_cir(struct fintek_dev *fintek) fintek_config_mode_enable(fintek); /* disable the CIR logical device */ - fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + fintek_select_logical_dev(fintek, fintek->logical_dev_cir); fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN); fintek_config_mode_disable(fintek); @@ -611,7 +621,7 @@ static int fintek_suspend(struct pnp_dev *pdev, pm_message_t state) fintek_config_mode_enable(fintek); /* disable cir logical dev */ - fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + fintek_select_logical_dev(fintek, fintek->logical_dev_cir); fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN); fintek_config_mode_disable(fintek); @@ -634,7 +644,7 @@ static int fintek_resume(struct pnp_dev *pdev) /* Enable CIR logical device */ fintek_config_mode_enable(fintek); - fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + fintek_select_logical_dev(fintek, fintek->logical_dev_cir); fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN); fintek_config_mode_disable(fintek); diff --git a/drivers/media/rc/fintek-cir.h b/drivers/media/rc/fintek-cir.h index 1b10b2011f5e..82516a1d39b0 100644 --- a/drivers/media/rc/fintek-cir.h +++ b/drivers/media/rc/fintek-cir.h @@ -88,6 +88,7 @@ struct fintek_dev { u8 chip_major; u8 chip_minor; u16 chip_vendor; + u8 logical_dev_cir; /* hardware features */ bool hw_learning_capable; @@ -172,7 +173,8 @@ struct fintek_dev { #define LOGICAL_DEV_ENABLE 0x01 /* Logical device number of the CIR function */ -#define LOGICAL_DEV_CIR 0x05 +#define LOGICAL_DEV_CIR_REV1 0x05 +#define LOGICAL_DEV_CIR_REV2 0x08 /* CIR Logical Device (LDN 0x08) config registers */ #define CIR_CR_COMMAND_INDEX 0x04 diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c new file mode 100644 index 000000000000..0d875450c5ce --- /dev/null +++ b/drivers/media/rc/gpio-ir-recv.c @@ -0,0 +1,205 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/irq.h> +#include <media/rc-core.h> +#include <media/gpio-ir-recv.h> + +#define GPIO_IR_DRIVER_NAME "gpio-rc-recv" +#define GPIO_IR_DEVICE_NAME "gpio_ir_recv" + +struct gpio_rc_dev { + struct rc_dev *rcdev; + int gpio_nr; + bool active_low; +}; + +static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id) +{ + struct gpio_rc_dev *gpio_dev = dev_id; + int gval; + int rc = 0; + enum raw_event_type type = IR_SPACE; + + gval = gpio_get_value_cansleep(gpio_dev->gpio_nr); + + if (gval < 0) + goto err_get_value; + + if (gpio_dev->active_low) + gval = !gval; + + if (gval == 1) + type = IR_PULSE; + + rc = ir_raw_event_store_edge(gpio_dev->rcdev, type); + if (rc < 0) + goto err_get_value; + + ir_raw_event_handle(gpio_dev->rcdev); + +err_get_value: + return IRQ_HANDLED; +} + +static int __devinit gpio_ir_recv_probe(struct platform_device *pdev) +{ + struct gpio_rc_dev *gpio_dev; + struct rc_dev *rcdev; + const struct gpio_ir_recv_platform_data *pdata = + pdev->dev.platform_data; + int rc; + + if (!pdata) + return -EINVAL; + + if (pdata->gpio_nr < 0) + return -EINVAL; + + gpio_dev = kzalloc(sizeof(struct gpio_rc_dev), GFP_KERNEL); + if (!gpio_dev) + return -ENOMEM; + + rcdev = rc_allocate_device(); + if (!rcdev) { + rc = -ENOMEM; + goto err_allocate_device; + } + + rcdev->driver_type = RC_DRIVER_IR_RAW; + rcdev->allowed_protos = RC_TYPE_ALL; + rcdev->input_name = GPIO_IR_DEVICE_NAME; + rcdev->input_id.bustype = BUS_HOST; + rcdev->driver_name = GPIO_IR_DRIVER_NAME; + rcdev->map_name = RC_MAP_EMPTY; + + gpio_dev->rcdev = rcdev; + gpio_dev->gpio_nr = pdata->gpio_nr; + gpio_dev->active_low = pdata->active_low; + + rc = gpio_request(pdata->gpio_nr, "gpio-ir-recv"); + if (rc < 0) + goto err_gpio_request; + rc = gpio_direction_input(pdata->gpio_nr); + if (rc < 0) + goto err_gpio_direction_input; + + rc = rc_register_device(rcdev); + if (rc < 0) { + dev_err(&pdev->dev, "failed to register rc device\n"); + goto err_register_rc_device; + } + + platform_set_drvdata(pdev, gpio_dev); + + rc = request_any_context_irq(gpio_to_irq(pdata->gpio_nr), + gpio_ir_recv_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "gpio-ir-recv-irq", gpio_dev); + if (rc < 0) + goto err_request_irq; + + return 0; + +err_request_irq: + platform_set_drvdata(pdev, NULL); + rc_unregister_device(rcdev); +err_register_rc_device: +err_gpio_direction_input: + gpio_free(pdata->gpio_nr); +err_gpio_request: + rc_free_device(rcdev); + rcdev = NULL; +err_allocate_device: + kfree(gpio_dev); + return rc; +} + +static int __devexit gpio_ir_recv_remove(struct platform_device *pdev) +{ + struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev); + + free_irq(gpio_to_irq(gpio_dev->gpio_nr), gpio_dev); + platform_set_drvdata(pdev, NULL); + rc_unregister_device(gpio_dev->rcdev); + gpio_free(gpio_dev->gpio_nr); + rc_free_device(gpio_dev->rcdev); + kfree(gpio_dev); + return 0; +} + +#ifdef CONFIG_PM +static int gpio_ir_recv_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev); + + if (device_may_wakeup(dev)) + enable_irq_wake(gpio_to_irq(gpio_dev->gpio_nr)); + else + disable_irq(gpio_to_irq(gpio_dev->gpio_nr)); + + return 0; +} + +static int gpio_ir_recv_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev); + + if (device_may_wakeup(dev)) + disable_irq_wake(gpio_to_irq(gpio_dev->gpio_nr)); + else + enable_irq(gpio_to_irq(gpio_dev->gpio_nr)); + + return 0; +} + +static const struct dev_pm_ops gpio_ir_recv_pm_ops = { + .suspend = gpio_ir_recv_suspend, + .resume = gpio_ir_recv_resume, +}; +#endif + +static struct platform_driver gpio_ir_recv_driver = { + .probe = gpio_ir_recv_probe, + .remove = __devexit_p(gpio_ir_recv_remove), + .driver = { + .name = GPIO_IR_DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &gpio_ir_recv_pm_ops, +#endif + }, +}; + +static int __init gpio_ir_recv_init(void) +{ + return platform_driver_register(&gpio_ir_recv_driver); +} +module_init(gpio_ir_recv_init); + +static void __exit gpio_ir_recv_exit(void) +{ + platform_driver_unregister(&gpio_ir_recv_driver); +} +module_exit(gpio_ir_recv_exit); + +MODULE_DESCRIPTION("GPIO IR Receiver driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c index d5e2b50aff1f..dab98b37621a 100644 --- a/drivers/media/rc/ir-sony-decoder.c +++ b/drivers/media/rc/ir-sony-decoder.c @@ -130,7 +130,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) case 15: device = bitrev8((data->bits >> 0) & 0xFF); subdevice = 0; - function = bitrev8((data->bits >> 7) & 0xFD); + function = bitrev8((data->bits >> 7) & 0xFE); break; case 20: device = bitrev8((data->bits >> 5) & 0xF8); diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 36e4d5e8dd6a..49ce2662f56b 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -41,8 +41,11 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-imon-mce.o \ rc-imon-pad.o \ rc-iodata-bctv7e.o \ + rc-it913x-v1.o \ + rc-it913x-v2.o \ rc-kaiomy.o \ rc-kworld-315u.o \ + rc-kworld-pc150u.o \ rc-kworld-plus-tv-analog.o \ rc-leadtek-y04g0051.o \ rc-lirc.o \ diff --git a/drivers/media/rc/keymaps/rc-it913x-v1.c b/drivers/media/rc/keymaps/rc-it913x-v1.c new file mode 100644 index 000000000000..0ac775fd109d --- /dev/null +++ b/drivers/media/rc/keymaps/rc-it913x-v1.c @@ -0,0 +1,95 @@ +/* ITE Generic remotes Version 1 + * + * Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <media/rc-map.h> +#include <linux/module.h> + + +static struct rc_map_table it913x_v1_rc[] = { + /* Type 1 */ + { 0x61d601, KEY_VIDEO }, /* Source */ + { 0x61d602, KEY_3 }, + { 0x61d603, KEY_POWER }, /* ShutDown */ + { 0x61d604, KEY_1 }, + { 0x61d605, KEY_5 }, + { 0x61d606, KEY_6 }, + { 0x61d607, KEY_CHANNELDOWN }, /* CH- */ + { 0x61d608, KEY_2 }, + { 0x61d609, KEY_CHANNELUP }, /* CH+ */ + { 0x61d60a, KEY_9 }, + { 0x61d60b, KEY_ZOOM }, /* Zoom */ + { 0x61d60c, KEY_7 }, + { 0x61d60d, KEY_8 }, + { 0x61d60e, KEY_VOLUMEUP }, /* Vol+ */ + { 0x61d60f, KEY_4 }, + { 0x61d610, KEY_ESC }, /* [back up arrow] */ + { 0x61d611, KEY_0 }, + { 0x61d612, KEY_OK }, /* [enter arrow] */ + { 0x61d613, KEY_VOLUMEDOWN }, /* Vol- */ + { 0x61d614, KEY_RECORD }, /* Rec */ + { 0x61d615, KEY_STOP }, /* Stop */ + { 0x61d616, KEY_PLAY }, /* Play */ + { 0x61d617, KEY_MUTE }, /* Mute */ + { 0x61d618, KEY_UP }, + { 0x61d619, KEY_DOWN }, + { 0x61d61a, KEY_LEFT }, + { 0x61d61b, KEY_RIGHT }, + { 0x61d61c, KEY_RED }, + { 0x61d61d, KEY_GREEN }, + { 0x61d61e, KEY_YELLOW }, + { 0x61d61f, KEY_BLUE }, + { 0x61d643, KEY_POWER2 }, /* [red power button] */ + /* Type 2 - 20 buttons */ + { 0x807f0d, KEY_0 }, + { 0x807f04, KEY_1 }, + { 0x807f05, KEY_2 }, + { 0x807f06, KEY_3 }, + { 0x807f07, KEY_4 }, + { 0x807f08, KEY_5 }, + { 0x807f09, KEY_6 }, + { 0x807f0a, KEY_7 }, + { 0x807f1b, KEY_8 }, + { 0x807f1f, KEY_9 }, + { 0x807f12, KEY_POWER }, + { 0x807f01, KEY_MEDIA_REPEAT}, /* Recall */ + { 0x807f19, KEY_PAUSE }, /* Timeshift */ + { 0x807f1e, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ + { 0x807f03, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ + { 0x807f1a, KEY_CHANNELUP }, + { 0x807f02, KEY_CHANNELDOWN }, + { 0x807f0c, KEY_ZOOM }, + { 0x807f00, KEY_RECORD }, + { 0x807f0e, KEY_STOP }, +}; + +static struct rc_map_list it913x_v1_map = { + .map = { + .scan = it913x_v1_rc, + .size = ARRAY_SIZE(it913x_v1_rc), + .rc_type = RC_TYPE_NEC, + .name = RC_MAP_IT913X_V1, + } +}; + +static int __init init_rc_it913x_v1_map(void) +{ + return rc_map_register(&it913x_v1_map); +} + +static void __exit exit_rc_it913x_v1_map(void) +{ + rc_map_unregister(&it913x_v1_map); +} + +module_init(init_rc_it913x_v1_map) +module_exit(exit_rc_it913x_v1_map) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com"); diff --git a/drivers/media/rc/keymaps/rc-it913x-v2.c b/drivers/media/rc/keymaps/rc-it913x-v2.c new file mode 100644 index 000000000000..28e376e18b99 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-it913x-v2.c @@ -0,0 +1,94 @@ +/* ITE Generic remotes Version 2 + * + * Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <media/rc-map.h> +#include <linux/module.h> + + +static struct rc_map_table it913x_v2_rc[] = { + /* Type 1 */ + /* 9005 remote */ + { 0x807f12, KEY_POWER2 }, /* Power (RED POWER BUTTON)*/ + { 0x807f1a, KEY_VIDEO }, /* Source */ + { 0x807f1e, KEY_MUTE }, /* Mute */ + { 0x807f01, KEY_RECORD }, /* Record */ + { 0x807f02, KEY_CHANNELUP }, /* Channel+ */ + { 0x807f03, KEY_TIME }, /* TimeShift */ + { 0x807f04, KEY_VOLUMEUP }, /* Volume- */ + { 0x807f05, KEY_SCREEN }, /* FullScreen */ + { 0x807f06, KEY_VOLUMEDOWN }, /* Volume- */ + { 0x807f07, KEY_0 }, /* 0 */ + { 0x807f08, KEY_CHANNELDOWN }, /* Channel- */ + { 0x807f09, KEY_PREVIOUS }, /* Recall */ + { 0x807f0a, KEY_1 }, /* 1 */ + { 0x807f1b, KEY_2 }, /* 2 */ + { 0x807f1f, KEY_3 }, /* 3 */ + { 0x807f0c, KEY_4 }, /* 4 */ + { 0x807f0d, KEY_5 }, /* 5 */ + { 0x807f0e, KEY_6 }, /* 6 */ + { 0x807f00, KEY_7 }, /* 7 */ + { 0x807f0f, KEY_8 }, /* 8 */ + { 0x807f19, KEY_9 }, /* 9 */ + + /* Type 2 */ + /* keys stereo, snapshot unassigned */ + { 0x866b00, KEY_0 }, + { 0x866b1b, KEY_1 }, + { 0x866b02, KEY_2 }, + { 0x866b03, KEY_3 }, + { 0x866b04, KEY_4 }, + { 0x866b05, KEY_5 }, + { 0x866b06, KEY_6 }, + { 0x866b07, KEY_7 }, + { 0x866b08, KEY_8 }, + { 0x866b09, KEY_9 }, + { 0x866b12, KEY_POWER }, + { 0x866b13, KEY_MUTE }, + { 0x866b0a, KEY_PREVIOUS }, /* Recall */ + { 0x866b1e, KEY_PAUSE }, + { 0x866b0c, KEY_VOLUMEUP }, + { 0x866b18, KEY_VOLUMEDOWN }, + { 0x866b0b, KEY_CHANNELUP }, + { 0x866b18, KEY_CHANNELDOWN }, + { 0x866b10, KEY_ZOOM }, + { 0x866b1d, KEY_RECORD }, + { 0x866b0e, KEY_STOP }, + { 0x866b11, KEY_EPG}, + { 0x866b1a, KEY_FASTFORWARD }, + { 0x866b0f, KEY_REWIND }, + { 0x866b1c, KEY_TV }, + { 0x866b1b, KEY_TEXT }, + +}; + +static struct rc_map_list it913x_v2_map = { + .map = { + .scan = it913x_v2_rc, + .size = ARRAY_SIZE(it913x_v2_rc), + .rc_type = RC_TYPE_NEC, + .name = RC_MAP_IT913X_V2, + } +}; + +static int __init init_rc_it913x_v2_map(void) +{ + return rc_map_register(&it913x_v2_map); +} + +static void __exit exit_rc_it913x_v2_map(void) +{ + rc_map_unregister(&it913x_v2_map); +} + +module_init(init_rc_it913x_v2_map) +module_exit(exit_rc_it913x_v2_map) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com"); diff --git a/drivers/media/rc/keymaps/rc-kworld-pc150u.c b/drivers/media/rc/keymaps/rc-kworld-pc150u.c new file mode 100644 index 000000000000..233bb5ee087f --- /dev/null +++ b/drivers/media/rc/keymaps/rc-kworld-pc150u.c @@ -0,0 +1,102 @@ +/* kworld-pc150u.c - Keytable for kworld_pc150u Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Kyle Strickland + * (based on kworld-plus-tv-analog.c by + * Mauro Carvalho Chehab <mchehab@redhat.com>) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <media/rc-map.h> +#include <linux/module.h> + +/* Kworld PC150-U + Kyle Strickland <kyle@kyle.strickland.name> + */ + +static struct rc_map_table kworld_pc150u[] = { + { 0x0c, KEY_MEDIA }, /* Kworld key */ + { 0x16, KEY_EJECTCLOSECD }, /* -> ) */ + { 0x1d, KEY_POWER2 }, + + { 0x00, KEY_1 }, + { 0x01, KEY_2 }, + { 0x02, KEY_3 }, + { 0x03, KEY_4 }, + { 0x04, KEY_5 }, + { 0x05, KEY_6 }, + { 0x06, KEY_7 }, + { 0x07, KEY_8 }, + { 0x08, KEY_9 }, + { 0x0a, KEY_0 }, + + { 0x09, KEY_AGAIN }, + { 0x14, KEY_MUTE }, + + { 0x1e, KEY_LAST }, + { 0x17, KEY_ZOOM }, + { 0x1f, KEY_HOMEPAGE }, + { 0x0e, KEY_ESC }, + + { 0x20, KEY_UP }, + { 0x21, KEY_DOWN }, + { 0x42, KEY_LEFT }, + { 0x43, KEY_RIGHT }, + { 0x0b, KEY_ENTER }, + + { 0x10, KEY_CHANNELUP }, + { 0x11, KEY_CHANNELDOWN }, + + { 0x13, KEY_VOLUMEUP }, + { 0x12, KEY_VOLUMEDOWN }, + + { 0x19, KEY_TIME}, /* Timeshift */ + { 0x1a, KEY_STOP}, + { 0x1b, KEY_RECORD}, + { 0x4b, KEY_EMAIL}, + + { 0x40, KEY_REWIND}, + { 0x44, KEY_PLAYPAUSE}, + { 0x41, KEY_FORWARD}, + { 0x22, KEY_TEXT}, + + { 0x15, KEY_AUDIO}, /* ((*)) */ + { 0x0f, KEY_MODE}, /* display ratio */ + { 0x1c, KEY_SYSRQ}, /* snapshot */ + { 0x4a, KEY_SLEEP}, /* sleep timer */ + + { 0x48, KEY_SOUND}, /* switch theater mode */ + { 0x49, KEY_BLUE}, /* A */ + { 0x18, KEY_RED}, /* B */ + { 0x23, KEY_GREEN}, /* C */ +}; + +static struct rc_map_list kworld_pc150u_map = { + .map = { + .scan = kworld_pc150u, + .size = ARRAY_SIZE(kworld_pc150u), + .rc_type = RC_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_KWORLD_PC150U, + } +}; + +static int __init init_rc_map_kworld_pc150u(void) +{ + return rc_map_register(&kworld_pc150u_map); +} + +static void __exit exit_rc_map_kworld_pc150u(void) +{ + rc_map_unregister(&kworld_pc150u_map); +} + +module_init(init_rc_map_kworld_pc150u) +module_exit(exit_rc_map_kworld_pc150u) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kyle Strickland <kyle@kyle.strickland.name>"); diff --git a/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c b/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c index f3b86c8db679..8d4dae2e2ece 100644 --- a/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c +++ b/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c @@ -18,6 +18,8 @@ */ static struct rc_map_table nec_terratec_cinergy_xs[] = { + + /* Terratec Grey IR, with most keys in orange */ { 0x1441, KEY_HOME}, { 0x1401, KEY_POWER2}, @@ -78,6 +80,56 @@ static struct rc_map_table nec_terratec_cinergy_xs[] = { { 0x144e, KEY_REWIND}, { 0x144f, KEY_FASTFORWARD}, { 0x145c, KEY_NEXT}, + + /* Terratec Black IR, with most keys in black */ + { 0x04eb01, KEY_POWER2}, + + { 0x04eb02, KEY_1}, + { 0x04eb03, KEY_2}, + { 0x04eb04, KEY_3}, + { 0x04eb05, KEY_4}, + { 0x04eb06, KEY_5}, + { 0x04eb07, KEY_6}, + { 0x04eb08, KEY_7}, + { 0x04eb09, KEY_8}, + { 0x04eb0a, KEY_9}, + { 0x04eb0c, KEY_0}, + + { 0x04eb0b, KEY_TEXT}, /* TXT */ + { 0x04eb0d, KEY_REFRESH}, /* Refresh */ + + { 0x04eb0e, KEY_HOME}, + { 0x04eb0f, KEY_EPG}, + + { 0x04eb10, KEY_UP}, + { 0x04eb11, KEY_LEFT}, + { 0x04eb12, KEY_OK}, + { 0x04eb13, KEY_RIGHT}, + { 0x04eb14, KEY_DOWN}, + + { 0x04eb15, KEY_BACKSPACE}, + { 0x04eb16, KEY_INFO}, + + { 0x04eb17, KEY_RED}, + { 0x04eb18, KEY_GREEN}, + { 0x04eb19, KEY_YELLOW}, + { 0x04eb1a, KEY_BLUE}, + + { 0x04eb1c, KEY_VOLUMEUP}, + { 0x04eb1e, KEY_VOLUMEDOWN}, + + { 0x04eb1d, KEY_MUTE}, + + { 0x04eb1b, KEY_CHANNELUP}, + { 0x04eb1f, KEY_CHANNELDOWN}, + + { 0x04eb40, KEY_RECORD}, + { 0x04eb4c, KEY_PLAY}, + { 0x04eb58, KEY_PAUSE}, + + { 0x04eb54, KEY_REWIND}, + { 0x04eb48, KEY_STOP}, + { 0x04eb5c, KEY_NEXT}, }; static struct rc_map_list nec_terratec_cinergy_xs_map = { diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 21105bf9594d..e150a2e29a4b 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -361,6 +361,8 @@ static struct usb_device_id mceusb_dev_table[] = { { USB_DEVICE(VENDOR_FORMOSA, 0xe03c) }, /* Formosa Industrial Computing */ { USB_DEVICE(VENDOR_FORMOSA, 0xe03e) }, + /* Formosa Industrial Computing */ + { USB_DEVICE(VENDOR_FORMOSA, 0xe042) }, /* Fintek eHome Infrared Transceiver (HP branded) */ { USB_DEVICE(VENDOR_FINTEK, 0x5168) }, /* Fintek eHome Infrared Transceiver */ diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index b72f8580e317..96f0a8bb39ea 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -35,7 +35,7 @@ struct ir_raw_event_ctrl { struct list_head list; /* to keep track of raw clients */ struct task_struct *thread; spinlock_t lock; - struct kfifo kfifo; /* fifo for the pulse/space durations */ + struct kfifo_rec_ptr_1 kfifo; /* fifo for the pulse/space durations */ ktime_t last_event; /* when last event occurred */ enum raw_event_type last_type; /* last event type */ struct rc_dev *dev; /* pointer to the parent rc_dev */ diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index f6a930b70c69..6e16b09c24a9 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -1029,6 +1029,7 @@ EXPORT_SYMBOL_GPL(rc_free_device); int rc_register_device(struct rc_dev *dev) { + static bool raw_init = false; /* raw decoders loaded? */ static atomic_t devno = ATOMIC_INIT(0); struct rc_map *rc_map; const char *path; @@ -1103,6 +1104,12 @@ int rc_register_device(struct rc_dev *dev) kfree(path); if (dev->driver_type == RC_DRIVER_IR_RAW) { + /* Load raw decoders, if they aren't already */ + if (!raw_init) { + IR_dprintk(1, "Loading raw decoders\n"); + ir_raw_init(); + raw_init = true; + } rc = ir_raw_event_register(dev); if (rc < 0) goto out_input; @@ -1176,8 +1183,6 @@ static int __init rc_core_init(void) return rc; } - /* Initialize/load the decoders/keymap code that will be used */ - ir_raw_init(); rc_map_register(&empty_map); return 0; diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 9adada0d7447..f2479c5c0eb2 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -273,6 +273,16 @@ config VIDEO_ADV7180 To compile this driver as a module, choose M here: the module will be called adv7180. +config VIDEO_ADV7183 + tristate "Analog Devices ADV7183 decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + V4l2 subdevice driver for the Analog Devices + ADV7183 video decoder. + + To compile this driver as a module, choose M here: the + module will be called adv7183. + config VIDEO_BT819 tristate "BT819A VideoStream decoder" depends on VIDEO_V4L2 && I2C @@ -459,6 +469,9 @@ config VIDEO_AK881X comment "Camera sensor devices" +config VIDEO_APTINA_PLL + tristate + config VIDEO_OV7670 tristate "OmniVision OV7670 sensor support" depends on I2C && VIDEO_V4L2 @@ -467,9 +480,28 @@ config VIDEO_OV7670 OV7670 VGA camera. It currently only works with the M88ALP01 controller. +config VIDEO_VS6624 + tristate "ST VS6624 sensor support" + depends on VIDEO_V4L2 && I2C + ---help--- + This is a Video4Linux2 sensor-level driver for the ST VS6624 + camera. + + To compile this driver as a module, choose M here: the + module will be called vs6624. + +config VIDEO_MT9M032 + tristate "MT9M032 camera sensor support" + depends on I2C && VIDEO_V4L2 + select VIDEO_APTINA_PLL + ---help--- + This driver supports MT9M032 camera sensors from Aptina, monochrome + models only. + config VIDEO_MT9P031 tristate "Aptina MT9P031 support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select VIDEO_APTINA_PLL ---help--- This is a Video4Linux2 sensor-level driver for the Aptina (Micron) mt9p031 5 Mpixel camera. @@ -851,6 +883,8 @@ source "drivers/media/video/davinci/Kconfig" source "drivers/media/video/omap/Kconfig" +source "drivers/media/video/blackfin/Kconfig" + config VIDEO_SH_VOU tristate "SuperH VOU video output driver" depends on VIDEO_DEV && ARCH_SHMOBILE @@ -1087,7 +1121,7 @@ config VIDEO_MX2_HOSTSUPPORT config VIDEO_MX2 tristate "i.MX27/i.MX25 Camera Sensor Interface driver" depends on VIDEO_DEV && SOC_CAMERA && (MACH_MX27 || ARCH_MX25) - select VIDEOBUF_DMA_CONTIG + select VIDEOBUF2_DMA_CONTIG select VIDEO_MX2_HOSTSUPPORT ---help--- This is a v4l2 driver for the i.MX27 and the i.MX25 Camera Sensor @@ -1116,7 +1150,8 @@ config VIDEO_ATMEL_ISI config VIDEO_S5P_MIPI_CSIS tristate "Samsung S5P and EXYNOS4 MIPI CSI receiver driver" - depends on VIDEO_V4L2 && PM_RUNTIME && PLAT_S5P && VIDEO_V4L2_SUBDEV_API + depends on VIDEO_V4L2 && PM_RUNTIME && PLAT_S5P + depends on VIDEO_V4L2_SUBDEV_API && REGULATOR ---help--- This is a v4l2 driver for Samsung S5P/EXYNOS4 MIPI-CSI receiver. @@ -1176,4 +1211,14 @@ config VIDEO_SAMSUNG_S5P_MFC help MFC 5.1 driver for V4L2. +config VIDEO_MX2_EMMAPRP + tristate "MX2 eMMa-PrP support" + depends on VIDEO_DEV && VIDEO_V4L2 && SOC_IMX27 + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + MX2X chips have a PrP that can be used to process buffers from + memory to memory. Operations include resizing and format + conversion. + endif # V4L_MEM2MEM_DRIVERS diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 354138804cda..a6282a3a6a82 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -12,16 +12,19 @@ omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \ v4l2-event.o v4l2-ctrls.o v4l2-subdev.o +ifeq ($(CONFIG_COMPAT),y) + videodev-objs += v4l2-compat-ioctl32.o +endif # V4L2 core modules obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-int-device.o -ifeq ($(CONFIG_COMPAT),y) - obj-$(CONFIG_VIDEO_DEV) += v4l2-compat-ioctl32.o -endif - obj-$(CONFIG_VIDEO_V4L2_COMMON) += v4l2-common.o +# Helper modules + +obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o + # All i2c modules must come first: obj-$(CONFIG_VIDEO_TUNER) += tuner.o @@ -40,8 +43,10 @@ obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o +obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o +obj-$(CONFIG_VIDEO_VS6624) += vs6624.o obj-$(CONFIG_VIDEO_BT819) += bt819.o obj-$(CONFIG_VIDEO_BT856) += bt856.o obj-$(CONFIG_VIDEO_BT866) += bt866.o @@ -65,6 +70,7 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o +obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o @@ -177,6 +183,8 @@ obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o +obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o + obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/ @@ -184,6 +192,8 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5p-tv/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/ +obj-$(CONFIG_BLACKFIN) += blackfin/ + obj-$(CONFIG_ARCH_DAVINCI) += davinci/ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o @@ -199,6 +209,6 @@ obj-y += davinci/ obj-$(CONFIG_ARCH_OMAP) += omap/ -ccflags-y += -Idrivers/media/dvb/dvb-core -ccflags-y += -Idrivers/media/dvb/frontends -ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core +ccflags-y += -I$(srctree)/drivers/media/dvb/frontends +ccflags-y += -I$(srctree)/drivers/media/common/tuners diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c index 12eedf4d515a..5b045b4a66fe 100644 --- a/drivers/media/video/adp1653.c +++ b/drivers/media/video/adp1653.c @@ -33,7 +33,6 @@ #include <linux/delay.h> #include <linux/module.h> #include <linux/i2c.h> -#include <linux/module.h> #include <linux/slab.h> #include <linux/version.h> #include <media/adp1653.h> @@ -482,24 +481,7 @@ static struct i2c_driver adp1653_i2c_driver = { .id_table = adp1653_id_table, }; -static int __init adp1653_init(void) -{ - int rval; - - rval = i2c_add_driver(&adp1653_i2c_driver); - if (rval) - printk(KERN_ALERT "%s: failed at i2c_add_driver\n", __func__); - - return rval; -} - -static void __exit adp1653_exit(void) -{ - i2c_del_driver(&adp1653_i2c_driver); -} - -module_init(adp1653_init); -module_exit(adp1653_exit); +module_i2c_driver(adp1653_i2c_driver); MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>"); MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver"); diff --git a/drivers/media/video/adv7170.c b/drivers/media/video/adv7170.c index 879f1d839760..6bc01fb98ff8 100644 --- a/drivers/media/video/adv7170.c +++ b/drivers/media/video/adv7170.c @@ -407,15 +407,4 @@ static struct i2c_driver adv7170_driver = { .id_table = adv7170_id, }; -static __init int init_adv7170(void) -{ - return i2c_add_driver(&adv7170_driver); -} - -static __exit void exit_adv7170(void) -{ - i2c_del_driver(&adv7170_driver); -} - -module_init(init_adv7170); -module_exit(exit_adv7170); +module_i2c_driver(adv7170_driver); diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c index 206078eca853..c7640fab5730 100644 --- a/drivers/media/video/adv7175.c +++ b/drivers/media/video/adv7175.c @@ -457,15 +457,4 @@ static struct i2c_driver adv7175_driver = { .id_table = adv7175_id, }; -static __init int init_adv7175(void) -{ - return i2c_add_driver(&adv7175_driver); -} - -static __exit void exit_adv7175(void) -{ - i2c_del_driver(&adv7175_driver); -} - -module_init(init_adv7175); -module_exit(exit_adv7175); +module_i2c_driver(adv7175_driver); diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c index d2138d06bcad..b8b6c4b0cad4 100644 --- a/drivers/media/video/adv7180.c +++ b/drivers/media/video/adv7180.c @@ -444,20 +444,8 @@ static struct i2c_driver adv7180_driver = { .id_table = adv7180_id, }; -static __init int adv7180_init(void) -{ - return i2c_add_driver(&adv7180_driver); -} - -static __exit void adv7180_exit(void) -{ - i2c_del_driver(&adv7180_driver); -} - -module_init(adv7180_init); -module_exit(adv7180_exit); +module_i2c_driver(adv7180_driver); MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver"); MODULE_AUTHOR("Mocean Laboratories"); MODULE_LICENSE("GPL v2"); - diff --git a/drivers/media/video/adv7183.c b/drivers/media/video/adv7183.c new file mode 100644 index 000000000000..e1d4c89d7140 --- /dev/null +++ b/drivers/media/video/adv7183.c @@ -0,0 +1,699 @@ +/* + * adv7183.c Analog Devices ADV7183 video decoder driver + * + * Copyright (c) 2011 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +#include <media/adv7183.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> + +#include "adv7183_regs.h" + +struct adv7183 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + + v4l2_std_id std; /* Current set standard */ + u32 input; + u32 output; + unsigned reset_pin; + unsigned oe_pin; + struct v4l2_mbus_framefmt fmt; +}; + +/* EXAMPLES USING 27 MHz CLOCK + * Mode 1 CVBS Input (Composite Video on AIN5) + * All standards are supported through autodetect, 8-bit, 4:2:2, ITU-R BT.656 output on P15 to P8. + */ +static const unsigned char adv7183_init_regs[] = { + ADV7183_IN_CTRL, 0x04, /* CVBS input on AIN5 */ + ADV7183_DIGI_CLAMP_CTRL_1, 0x00, /* Slow down digital clamps */ + ADV7183_SHAP_FILT_CTRL, 0x41, /* Set CSFM to SH1 */ + ADV7183_ADC_CTRL, 0x16, /* Power down ADC 1 and ADC 2 */ + ADV7183_CTI_DNR_CTRL_4, 0x04, /* Set DNR threshold to 4 for flat response */ + /* ADI recommended programming sequence */ + ADV7183_ADI_CTRL, 0x80, + ADV7183_CTI_DNR_CTRL_4, 0x20, + 0x52, 0x18, + 0x58, 0xED, + 0x77, 0xC5, + 0x7C, 0x93, + 0x7D, 0x00, + 0xD0, 0x48, + 0xD5, 0xA0, + 0xD7, 0xEA, + ADV7183_SD_SATURATION_CR, 0x3E, + ADV7183_PAL_V_END, 0x3E, + ADV7183_PAL_F_TOGGLE, 0x0F, + ADV7183_ADI_CTRL, 0x00, +}; + +static inline struct adv7183 *to_adv7183(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7183, sd); +} +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct adv7183, hdl)->sd; +} + +static inline int adv7183_read(struct v4l2_subdev *sd, unsigned char reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +static inline int adv7183_write(struct v4l2_subdev *sd, unsigned char reg, + unsigned char value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int adv7183_writeregs(struct v4l2_subdev *sd, + const unsigned char *regs, unsigned int num) +{ + unsigned char reg, data; + unsigned int cnt = 0; + + if (num & 0x1) { + v4l2_err(sd, "invalid regs array\n"); + return -1; + } + + while (cnt < num) { + reg = *regs++; + data = *regs++; + cnt += 2; + + adv7183_write(sd, reg, data); + } + return 0; +} + +static int adv7183_log_status(struct v4l2_subdev *sd) +{ + struct adv7183 *decoder = to_adv7183(sd); + + v4l2_info(sd, "adv7183: Input control = 0x%02x\n", + adv7183_read(sd, ADV7183_IN_CTRL)); + v4l2_info(sd, "adv7183: Video selection = 0x%02x\n", + adv7183_read(sd, ADV7183_VD_SEL)); + v4l2_info(sd, "adv7183: Output control = 0x%02x\n", + adv7183_read(sd, ADV7183_OUT_CTRL)); + v4l2_info(sd, "adv7183: Extended output control = 0x%02x\n", + adv7183_read(sd, ADV7183_EXT_OUT_CTRL)); + v4l2_info(sd, "adv7183: Autodetect enable = 0x%02x\n", + adv7183_read(sd, ADV7183_AUTO_DET_EN)); + v4l2_info(sd, "adv7183: Contrast = 0x%02x\n", + adv7183_read(sd, ADV7183_CONTRAST)); + v4l2_info(sd, "adv7183: Brightness = 0x%02x\n", + adv7183_read(sd, ADV7183_BRIGHTNESS)); + v4l2_info(sd, "adv7183: Hue = 0x%02x\n", + adv7183_read(sd, ADV7183_HUE)); + v4l2_info(sd, "adv7183: Default value Y = 0x%02x\n", + adv7183_read(sd, ADV7183_DEF_Y)); + v4l2_info(sd, "adv7183: Default value C = 0x%02x\n", + adv7183_read(sd, ADV7183_DEF_C)); + v4l2_info(sd, "adv7183: ADI control = 0x%02x\n", + adv7183_read(sd, ADV7183_ADI_CTRL)); + v4l2_info(sd, "adv7183: Power Management = 0x%02x\n", + adv7183_read(sd, ADV7183_POW_MANAGE)); + v4l2_info(sd, "adv7183: Status 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_STATUS_1), + adv7183_read(sd, ADV7183_STATUS_2), + adv7183_read(sd, ADV7183_STATUS_3)); + v4l2_info(sd, "adv7183: Ident = 0x%02x\n", + adv7183_read(sd, ADV7183_IDENT)); + v4l2_info(sd, "adv7183: Analog clamp control = 0x%02x\n", + adv7183_read(sd, ADV7183_ANAL_CLAMP_CTRL)); + v4l2_info(sd, "adv7183: Digital clamp control 1 = 0x%02x\n", + adv7183_read(sd, ADV7183_DIGI_CLAMP_CTRL_1)); + v4l2_info(sd, "adv7183: Shaping filter control 1 and 2 = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_SHAP_FILT_CTRL), + adv7183_read(sd, ADV7183_SHAP_FILT_CTRL_2)); + v4l2_info(sd, "adv7183: Comb filter control = 0x%02x\n", + adv7183_read(sd, ADV7183_COMB_FILT_CTRL)); + v4l2_info(sd, "adv7183: ADI control 2 = 0x%02x\n", + adv7183_read(sd, ADV7183_ADI_CTRL_2)); + v4l2_info(sd, "adv7183: Pixel delay control = 0x%02x\n", + adv7183_read(sd, ADV7183_PIX_DELAY_CTRL)); + v4l2_info(sd, "adv7183: Misc gain control = 0x%02x\n", + adv7183_read(sd, ADV7183_MISC_GAIN_CTRL)); + v4l2_info(sd, "adv7183: AGC mode control = 0x%02x\n", + adv7183_read(sd, ADV7183_AGC_MODE_CTRL)); + v4l2_info(sd, "adv7183: Chroma gain control 1 and 2 = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_1), + adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_2)); + v4l2_info(sd, "adv7183: Luma gain control 1 and 2 = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_1), + adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_2)); + v4l2_info(sd, "adv7183: Vsync field control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_VS_FIELD_CTRL_1), + adv7183_read(sd, ADV7183_VS_FIELD_CTRL_2), + adv7183_read(sd, ADV7183_VS_FIELD_CTRL_3)); + v4l2_info(sd, "adv7183: Hsync positon control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_HS_POS_CTRL_1), + adv7183_read(sd, ADV7183_HS_POS_CTRL_2), + adv7183_read(sd, ADV7183_HS_POS_CTRL_3)); + v4l2_info(sd, "adv7183: Polarity = 0x%02x\n", + adv7183_read(sd, ADV7183_POLARITY)); + v4l2_info(sd, "adv7183: ADC control = 0x%02x\n", + adv7183_read(sd, ADV7183_ADC_CTRL)); + v4l2_info(sd, "adv7183: SD offset Cb and Cr = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_SD_OFFSET_CB), + adv7183_read(sd, ADV7183_SD_OFFSET_CR)); + v4l2_info(sd, "adv7183: SD saturation Cb and Cr = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_SD_SATURATION_CB), + adv7183_read(sd, ADV7183_SD_SATURATION_CR)); + v4l2_info(sd, "adv7183: Drive strength = 0x%02x\n", + adv7183_read(sd, ADV7183_DRIVE_STR)); + v4l2_ctrl_handler_log_status(&decoder->hdl, sd->name); + return 0; +} + +static int adv7183_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct adv7183 *decoder = to_adv7183(sd); + + *std = decoder->std; + return 0; +} + +static int adv7183_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7183 *decoder = to_adv7183(sd); + int reg; + + reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF; + if (std == V4L2_STD_PAL_60) + reg |= 0x60; + else if (std == V4L2_STD_NTSC_443) + reg |= 0x70; + else if (std == V4L2_STD_PAL_N) + reg |= 0x90; + else if (std == V4L2_STD_PAL_M) + reg |= 0xA0; + else if (std == V4L2_STD_PAL_Nc) + reg |= 0xC0; + else if (std & V4L2_STD_PAL) + reg |= 0x80; + else if (std & V4L2_STD_NTSC) + reg |= 0x50; + else if (std & V4L2_STD_SECAM) + reg |= 0xE0; + else + return -EINVAL; + adv7183_write(sd, ADV7183_IN_CTRL, reg); + + decoder->std = std; + + return 0; +} + +static int adv7183_reset(struct v4l2_subdev *sd, u32 val) +{ + int reg; + + reg = adv7183_read(sd, ADV7183_POW_MANAGE) | 0x80; + adv7183_write(sd, ADV7183_POW_MANAGE, reg); + /* wait 5ms before any further i2c writes are performed */ + usleep_range(5000, 10000); + return 0; +} + +static int adv7183_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct adv7183 *decoder = to_adv7183(sd); + int reg; + + if ((input > ADV7183_COMPONENT1) || (output > ADV7183_16BIT_OUT)) + return -EINVAL; + + if (input != decoder->input) { + decoder->input = input; + reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF0; + switch (input) { + case ADV7183_COMPOSITE1: + reg |= 0x1; + break; + case ADV7183_COMPOSITE2: + reg |= 0x2; + break; + case ADV7183_COMPOSITE3: + reg |= 0x3; + break; + case ADV7183_COMPOSITE4: + reg |= 0x4; + break; + case ADV7183_COMPOSITE5: + reg |= 0x5; + break; + case ADV7183_COMPOSITE6: + reg |= 0xB; + break; + case ADV7183_COMPOSITE7: + reg |= 0xC; + break; + case ADV7183_COMPOSITE8: + reg |= 0xD; + break; + case ADV7183_COMPOSITE9: + reg |= 0xE; + break; + case ADV7183_COMPOSITE10: + reg |= 0xF; + break; + case ADV7183_SVIDEO0: + reg |= 0x6; + break; + case ADV7183_SVIDEO1: + reg |= 0x7; + break; + case ADV7183_SVIDEO2: + reg |= 0x8; + break; + case ADV7183_COMPONENT0: + reg |= 0x9; + break; + case ADV7183_COMPONENT1: + reg |= 0xA; + break; + default: + break; + } + adv7183_write(sd, ADV7183_IN_CTRL, reg); + } + + if (output != decoder->output) { + decoder->output = output; + reg = adv7183_read(sd, ADV7183_OUT_CTRL) & 0xC0; + switch (output) { + case ADV7183_16BIT_OUT: + reg |= 0x9; + break; + default: + reg |= 0xC; + break; + } + adv7183_write(sd, ADV7183_OUT_CTRL, reg); + } + + return 0; +} + +static int adv7183_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + int val = ctrl->val; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (val < 0) + val = 127 - val; + adv7183_write(sd, ADV7183_BRIGHTNESS, val); + break; + case V4L2_CID_CONTRAST: + adv7183_write(sd, ADV7183_CONTRAST, val); + break; + case V4L2_CID_SATURATION: + adv7183_write(sd, ADV7183_SD_SATURATION_CB, val >> 8); + adv7183_write(sd, ADV7183_SD_SATURATION_CR, (val & 0xFF)); + break; + case V4L2_CID_HUE: + adv7183_write(sd, ADV7183_SD_OFFSET_CB, val >> 8); + adv7183_write(sd, ADV7183_SD_OFFSET_CR, (val & 0xFF)); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int adv7183_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct adv7183 *decoder = to_adv7183(sd); + int reg; + + /* enable autodetection block */ + reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF; + adv7183_write(sd, ADV7183_IN_CTRL, reg); + + /* wait autodetection switch */ + mdelay(10); + + /* get autodetection result */ + reg = adv7183_read(sd, ADV7183_STATUS_1); + switch ((reg >> 0x4) & 0x7) { + case 0: + *std = V4L2_STD_NTSC; + break; + case 1: + *std = V4L2_STD_NTSC_443; + break; + case 2: + *std = V4L2_STD_PAL_M; + break; + case 3: + *std = V4L2_STD_PAL_60; + break; + case 4: + *std = V4L2_STD_PAL; + break; + case 5: + *std = V4L2_STD_SECAM; + break; + case 6: + *std = V4L2_STD_PAL_Nc; + break; + case 7: + *std = V4L2_STD_SECAM; + break; + default: + *std = V4L2_STD_UNKNOWN; + break; + } + + /* after std detection, write back user set std */ + adv7183_s_std(sd, decoder->std); + return 0; +} + +static int adv7183_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + int reg; + + *status = V4L2_IN_ST_NO_SIGNAL; + reg = adv7183_read(sd, ADV7183_STATUS_1); + if (reg < 0) + return reg; + if (reg & 0x1) + *status = 0; + return 0; +} + +static int adv7183_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, + enum v4l2_mbus_pixelcode *code) +{ + if (index > 0) + return -EINVAL; + + *code = V4L2_MBUS_FMT_UYVY8_2X8; + return 0; +} + +static int adv7183_try_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct adv7183 *decoder = to_adv7183(sd); + + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + if (decoder->std & V4L2_STD_525_60) { + fmt->field = V4L2_FIELD_SEQ_TB; + fmt->width = 720; + fmt->height = 480; + } else { + fmt->field = V4L2_FIELD_SEQ_BT; + fmt->width = 720; + fmt->height = 576; + } + return 0; +} + +static int adv7183_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct adv7183 *decoder = to_adv7183(sd); + + adv7183_try_mbus_fmt(sd, fmt); + decoder->fmt = *fmt; + return 0; +} + +static int adv7183_g_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct adv7183 *decoder = to_adv7183(sd); + + *fmt = decoder->fmt; + return 0; +} + +static int adv7183_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct adv7183 *decoder = to_adv7183(sd); + + if (enable) + gpio_direction_output(decoder->oe_pin, 0); + else + gpio_direction_output(decoder->oe_pin, 1); + udelay(1); + return 0; +} + +static int adv7183_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + int rev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* 0x11 for adv7183, 0x13 for adv7183b */ + rev = adv7183_read(sd, ADV7183_IDENT); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7183, rev); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int adv7183_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = adv7183_read(sd, reg->reg & 0xff); + reg->size = 1; + return 0; +} + +static int adv7183_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + adv7183_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +static const struct v4l2_ctrl_ops adv7183_ctrl_ops = { + .s_ctrl = adv7183_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops adv7183_core_ops = { + .log_status = adv7183_log_status, + .g_std = adv7183_g_std, + .s_std = adv7183_s_std, + .reset = adv7183_reset, + .g_chip_ident = adv7183_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = adv7183_g_register, + .s_register = adv7183_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops adv7183_video_ops = { + .s_routing = adv7183_s_routing, + .querystd = adv7183_querystd, + .g_input_status = adv7183_g_input_status, + .enum_mbus_fmt = adv7183_enum_mbus_fmt, + .try_mbus_fmt = adv7183_try_mbus_fmt, + .s_mbus_fmt = adv7183_s_mbus_fmt, + .g_mbus_fmt = adv7183_g_mbus_fmt, + .s_stream = adv7183_s_stream, +}; + +static const struct v4l2_subdev_ops adv7183_ops = { + .core = &adv7183_core_ops, + .video = &adv7183_video_ops, +}; + +static int adv7183_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7183 *decoder; + struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *hdl; + int ret; + struct v4l2_mbus_framefmt fmt; + const unsigned *pin_array; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + pin_array = client->dev.platform_data; + if (pin_array == NULL) + return -EINVAL; + + decoder = kzalloc(sizeof(struct adv7183), GFP_KERNEL); + if (decoder == NULL) + return -ENOMEM; + + decoder->reset_pin = pin_array[0]; + decoder->oe_pin = pin_array[1]; + + if (gpio_request(decoder->reset_pin, "ADV7183 Reset")) { + v4l_err(client, "failed to request GPIO %d\n", decoder->reset_pin); + ret = -EBUSY; + goto err_free_decoder; + } + + if (gpio_request(decoder->oe_pin, "ADV7183 Output Enable")) { + v4l_err(client, "failed to request GPIO %d\n", decoder->oe_pin); + ret = -EBUSY; + goto err_free_reset; + } + + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &adv7183_ops); + + hdl = &decoder->hdl; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, + V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x80); + v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, + V4L2_CID_SATURATION, 0, 0xFFFF, 1, 0x8080); + v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, + V4L2_CID_HUE, 0, 0xFFFF, 1, 0x8080); + /* hook the control handler into the driver */ + sd->ctrl_handler = hdl; + if (hdl->error) { + ret = hdl->error; + + v4l2_ctrl_handler_free(hdl); + goto err_free_oe; + } + + /* v4l2 doesn't support an autodetect standard, pick PAL as default */ + decoder->std = V4L2_STD_PAL; + decoder->input = ADV7183_COMPOSITE4; + decoder->output = ADV7183_8BIT_OUT; + + gpio_direction_output(decoder->oe_pin, 1); + /* reset chip */ + gpio_direction_output(decoder->reset_pin, 0); + /* reset pulse width at least 5ms */ + mdelay(10); + gpio_direction_output(decoder->reset_pin, 1); + /* wait 5ms before any further i2c writes are performed */ + mdelay(5); + + adv7183_writeregs(sd, adv7183_init_regs, ARRAY_SIZE(adv7183_init_regs)); + adv7183_s_std(sd, decoder->std); + fmt.width = 720; + fmt.height = 576; + adv7183_s_mbus_fmt(sd, &fmt); + + /* initialize the hardware to the default control values */ + ret = v4l2_ctrl_handler_setup(hdl); + if (ret) { + v4l2_ctrl_handler_free(hdl); + goto err_free_oe; + } + + return 0; +err_free_oe: + gpio_free(decoder->oe_pin); +err_free_reset: + gpio_free(decoder->reset_pin); +err_free_decoder: + kfree(decoder); + return ret; +} + +static int adv7183_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7183 *decoder = to_adv7183(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + gpio_free(decoder->oe_pin); + gpio_free(decoder->reset_pin); + kfree(decoder); + return 0; +} + +static const struct i2c_device_id adv7183_id[] = { + {"adv7183", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, adv7183_id); + +static struct i2c_driver adv7183_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7183", + }, + .probe = adv7183_probe, + .remove = __devexit_p(adv7183_remove), + .id_table = adv7183_id, +}; + +static __init int adv7183_init(void) +{ + return i2c_add_driver(&adv7183_driver); +} + +static __exit void adv7183_exit(void) +{ + i2c_del_driver(&adv7183_driver); +} + +module_init(adv7183_init); +module_exit(adv7183_exit); + +MODULE_DESCRIPTION("Analog Devices ADV7183 video decoder driver"); +MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/adv7183_regs.h b/drivers/media/video/adv7183_regs.h new file mode 100644 index 000000000000..4a5b7d211d2f --- /dev/null +++ b/drivers/media/video/adv7183_regs.h @@ -0,0 +1,107 @@ +/* + * adv7183 - Analog Devices ADV7183 video decoder registers + * + * Copyright (c) 2011 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _ADV7183_REGS_H_ +#define _ADV7183_REGS_H_ + +#define ADV7183_IN_CTRL 0x00 /* Input control */ +#define ADV7183_VD_SEL 0x01 /* Video selection */ +#define ADV7183_OUT_CTRL 0x03 /* Output control */ +#define ADV7183_EXT_OUT_CTRL 0x04 /* Extended output control */ +#define ADV7183_AUTO_DET_EN 0x07 /* Autodetect enable */ +#define ADV7183_CONTRAST 0x08 /* Contrast */ +#define ADV7183_BRIGHTNESS 0x0A /* Brightness */ +#define ADV7183_HUE 0x0B /* Hue */ +#define ADV7183_DEF_Y 0x0C /* Default value Y */ +#define ADV7183_DEF_C 0x0D /* Default value C */ +#define ADV7183_ADI_CTRL 0x0E /* ADI control */ +#define ADV7183_POW_MANAGE 0x0F /* Power Management */ +#define ADV7183_STATUS_1 0x10 /* Status 1 */ +#define ADV7183_IDENT 0x11 /* Ident */ +#define ADV7183_STATUS_2 0x12 /* Status 2 */ +#define ADV7183_STATUS_3 0x13 /* Status 3 */ +#define ADV7183_ANAL_CLAMP_CTRL 0x14 /* Analog clamp control */ +#define ADV7183_DIGI_CLAMP_CTRL_1 0x15 /* Digital clamp control 1 */ +#define ADV7183_SHAP_FILT_CTRL 0x17 /* Shaping filter control */ +#define ADV7183_SHAP_FILT_CTRL_2 0x18 /* Shaping filter control 2 */ +#define ADV7183_COMB_FILT_CTRL 0x19 /* Comb filter control */ +#define ADV7183_ADI_CTRL_2 0x1D /* ADI control 2 */ +#define ADV7183_PIX_DELAY_CTRL 0x27 /* Pixel delay control */ +#define ADV7183_MISC_GAIN_CTRL 0x2B /* Misc gain control */ +#define ADV7183_AGC_MODE_CTRL 0x2C /* AGC mode control */ +#define ADV7183_CHRO_GAIN_CTRL_1 0x2D /* Chroma gain control 1 */ +#define ADV7183_CHRO_GAIN_CTRL_2 0x2E /* Chroma gain control 2 */ +#define ADV7183_LUMA_GAIN_CTRL_1 0x2F /* Luma gain control 1 */ +#define ADV7183_LUMA_GAIN_CTRL_2 0x30 /* Luma gain control 2 */ +#define ADV7183_VS_FIELD_CTRL_1 0x31 /* Vsync field control 1 */ +#define ADV7183_VS_FIELD_CTRL_2 0x32 /* Vsync field control 2 */ +#define ADV7183_VS_FIELD_CTRL_3 0x33 /* Vsync field control 3 */ +#define ADV7183_HS_POS_CTRL_1 0x34 /* Hsync positon control 1 */ +#define ADV7183_HS_POS_CTRL_2 0x35 /* Hsync positon control 2 */ +#define ADV7183_HS_POS_CTRL_3 0x36 /* Hsync positon control 3 */ +#define ADV7183_POLARITY 0x37 /* Polarity */ +#define ADV7183_NTSC_COMB_CTRL 0x38 /* NTSC comb control */ +#define ADV7183_PAL_COMB_CTRL 0x39 /* PAL comb control */ +#define ADV7183_ADC_CTRL 0x3A /* ADC control */ +#define ADV7183_MAN_WIN_CTRL 0x3D /* Manual window control */ +#define ADV7183_RESAMPLE_CTRL 0x41 /* Resample control */ +#define ADV7183_GEMSTAR_CTRL_1 0x48 /* Gemstar ctrl 1 */ +#define ADV7183_GEMSTAR_CTRL_2 0x49 /* Gemstar ctrl 2 */ +#define ADV7183_GEMSTAR_CTRL_3 0x4A /* Gemstar ctrl 3 */ +#define ADV7183_GEMSTAR_CTRL_4 0x4B /* Gemstar ctrl 4 */ +#define ADV7183_GEMSTAR_CTRL_5 0x4C /* Gemstar ctrl 5 */ +#define ADV7183_CTI_DNR_CTRL_1 0x4D /* CTI DNR ctrl 1 */ +#define ADV7183_CTI_DNR_CTRL_2 0x4E /* CTI DNR ctrl 2 */ +#define ADV7183_CTI_DNR_CTRL_4 0x50 /* CTI DNR ctrl 4 */ +#define ADV7183_LOCK_CNT 0x51 /* Lock count */ +#define ADV7183_FREE_LINE_LEN 0x8F /* Free-Run line length 1 */ +#define ADV7183_VBI_INFO 0x90 /* VBI info */ +#define ADV7183_WSS_1 0x91 /* WSS 1 */ +#define ADV7183_WSS_2 0x92 /* WSS 2 */ +#define ADV7183_EDTV_1 0x93 /* EDTV 1 */ +#define ADV7183_EDTV_2 0x94 /* EDTV 2 */ +#define ADV7183_EDTV_3 0x95 /* EDTV 3 */ +#define ADV7183_CGMS_1 0x96 /* CGMS 1 */ +#define ADV7183_CGMS_2 0x97 /* CGMS 2 */ +#define ADV7183_CGMS_3 0x98 /* CGMS 3 */ +#define ADV7183_CCAP_1 0x99 /* CCAP 1 */ +#define ADV7183_CCAP_2 0x9A /* CCAP 2 */ +#define ADV7183_LETTERBOX_1 0x9B /* Letterbox 1 */ +#define ADV7183_LETTERBOX_2 0x9C /* Letterbox 2 */ +#define ADV7183_LETTERBOX_3 0x9D /* Letterbox 3 */ +#define ADV7183_CRC_EN 0xB2 /* CRC enable */ +#define ADV7183_ADC_SWITCH_1 0xC3 /* ADC switch 1 */ +#define ADV7183_ADC_SWITCH_2 0xC4 /* ADC swithc 2 */ +#define ADV7183_LETTERBOX_CTRL_1 0xDC /* Letterbox control 1 */ +#define ADV7183_LETTERBOX_CTRL_2 0xDD /* Letterbox control 2 */ +#define ADV7183_SD_OFFSET_CB 0xE1 /* SD offset Cb */ +#define ADV7183_SD_OFFSET_CR 0xE2 /* SD offset Cr */ +#define ADV7183_SD_SATURATION_CB 0xE3 /* SD saturation Cb */ +#define ADV7183_SD_SATURATION_CR 0xE4 /* SD saturation Cr */ +#define ADV7183_NTSC_V_BEGIN 0xE5 /* NTSC V bit begin */ +#define ADV7183_NTSC_V_END 0xE6 /* NTSC V bit end */ +#define ADV7183_NTSC_F_TOGGLE 0xE7 /* NTSC F bit toggle */ +#define ADV7183_PAL_V_BEGIN 0xE8 /* PAL V bit begin */ +#define ADV7183_PAL_V_END 0xE9 /* PAL V bit end */ +#define ADV7183_PAL_F_TOGGLE 0xEA /* PAL F bit toggle */ +#define ADV7183_DRIVE_STR 0xF4 /* Drive strength */ +#define ADV7183_IF_COMP_CTRL 0xF8 /* IF comp control */ +#define ADV7183_VS_MODE_CTRL 0xF9 /* VS mode control */ + +#endif diff --git a/drivers/media/video/adv7343.c b/drivers/media/video/adv7343.c index 021fab23070d..119b60401bf3 100644 --- a/drivers/media/video/adv7343.c +++ b/drivers/media/video/adv7343.c @@ -475,15 +475,4 @@ static struct i2c_driver adv7343_driver = { .id_table = adv7343_id, }; -static __init int init_adv7343(void) -{ - return i2c_add_driver(&adv7343_driver); -} - -static __exit void exit_adv7343(void) -{ - i2c_del_driver(&adv7343_driver); -} - -module_init(init_adv7343); -module_exit(exit_adv7343); +module_i2c_driver(adv7343_driver); diff --git a/drivers/media/video/ak881x.c b/drivers/media/video/ak881x.c index 53c496c00fb6..ba674656b10d 100644 --- a/drivers/media/video/ak881x.c +++ b/drivers/media/video/ak881x.c @@ -352,18 +352,7 @@ static struct i2c_driver ak881x_i2c_driver = { .id_table = ak881x_id, }; -static int __init ak881x_module_init(void) -{ - return i2c_add_driver(&ak881x_i2c_driver); -} - -static void __exit ak881x_module_exit(void) -{ - i2c_del_driver(&ak881x_i2c_driver); -} - -module_init(ak881x_module_init); -module_exit(ak881x_module_exit); +module_i2c_driver(ak881x_i2c_driver); MODULE_DESCRIPTION("TV-output driver for ak8813/ak8814"); MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); diff --git a/drivers/media/video/aptina-pll.c b/drivers/media/video/aptina-pll.c new file mode 100644 index 000000000000..0bd3813bb59d --- /dev/null +++ b/drivers/media/video/aptina-pll.c @@ -0,0 +1,174 @@ +/* + * Aptina Sensor PLL Configuration + * + * Copyright (C) 2012 Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <linux/device.h> +#include <linux/gcd.h> +#include <linux/kernel.h> +#include <linux/lcm.h> +#include <linux/module.h> + +#include "aptina-pll.h" + +int aptina_pll_calculate(struct device *dev, + const struct aptina_pll_limits *limits, + struct aptina_pll *pll) +{ + unsigned int mf_min; + unsigned int mf_max; + unsigned int p1_min; + unsigned int p1_max; + unsigned int p1; + unsigned int div; + + dev_dbg(dev, "PLL: ext clock %u pix clock %u\n", + pll->ext_clock, pll->pix_clock); + + if (pll->ext_clock < limits->ext_clock_min || + pll->ext_clock > limits->ext_clock_max) { + dev_err(dev, "pll: invalid external clock frequency.\n"); + return -EINVAL; + } + + if (pll->pix_clock == 0 || pll->pix_clock > limits->pix_clock_max) { + dev_err(dev, "pll: invalid pixel clock frequency.\n"); + return -EINVAL; + } + + /* Compute the multiplier M and combined N*P1 divisor. */ + div = gcd(pll->pix_clock, pll->ext_clock); + pll->m = pll->pix_clock / div; + div = pll->ext_clock / div; + + /* We now have the smallest M and N*P1 values that will result in the + * desired pixel clock frequency, but they might be out of the valid + * range. Compute the factor by which we should multiply them given the + * following constraints: + * + * - minimum/maximum multiplier + * - minimum/maximum multiplier output clock frequency assuming the + * minimum/maximum N value + * - minimum/maximum combined N*P1 divisor + */ + mf_min = DIV_ROUND_UP(limits->m_min, pll->m); + mf_min = max(mf_min, limits->out_clock_min / + (pll->ext_clock / limits->n_min * pll->m)); + mf_min = max(mf_min, limits->n_min * limits->p1_min / div); + mf_max = limits->m_max / pll->m; + mf_max = min(mf_max, limits->out_clock_max / + (pll->ext_clock / limits->n_max * pll->m)); + mf_max = min(mf_max, DIV_ROUND_UP(limits->n_max * limits->p1_max, div)); + + dev_dbg(dev, "pll: mf min %u max %u\n", mf_min, mf_max); + if (mf_min > mf_max) { + dev_err(dev, "pll: no valid combined N*P1 divisor.\n"); + return -EINVAL; + } + + /* + * We're looking for the highest acceptable P1 value for which a + * multiplier factor MF exists that fulfills the following conditions: + * + * 1. p1 is in the [p1_min, p1_max] range given by the limits and is + * even + * 2. mf is in the [mf_min, mf_max] range computed above + * 3. div * mf is a multiple of p1, in order to compute + * n = div * mf / p1 + * m = pll->m * mf + * 4. the internal clock frequency, given by ext_clock / n, is in the + * [int_clock_min, int_clock_max] range given by the limits + * 5. the output clock frequency, given by ext_clock / n * m, is in the + * [out_clock_min, out_clock_max] range given by the limits + * + * The first naive approach is to iterate over all p1 values acceptable + * according to (1) and all mf values acceptable according to (2), and + * stop at the first combination that fulfills (3), (4) and (5). This + * has a O(n^2) complexity. + * + * Instead of iterating over all mf values in the [mf_min, mf_max] range + * we can compute the mf increment between two acceptable values + * according to (3) with + * + * mf_inc = p1 / gcd(div, p1) (6) + * + * and round the minimum up to the nearest multiple of mf_inc. This will + * restrict the number of mf values to be checked. + * + * Furthermore, conditions (4) and (5) only restrict the range of + * acceptable p1 and mf values by modifying the minimum and maximum + * limits. (5) can be expressed as + * + * ext_clock / (div * mf / p1) * m * mf >= out_clock_min + * ext_clock / (div * mf / p1) * m * mf <= out_clock_max + * + * or + * + * p1 >= out_clock_min * div / (ext_clock * m) (7) + * p1 <= out_clock_max * div / (ext_clock * m) + * + * Similarly, (4) can be expressed as + * + * mf >= ext_clock * p1 / (int_clock_max * div) (8) + * mf <= ext_clock * p1 / (int_clock_min * div) + * + * We can thus iterate over the restricted p1 range defined by the + * combination of (1) and (7), and then compute the restricted mf range + * defined by the combination of (2), (6) and (8). If the resulting mf + * range is not empty, any value in the mf range is acceptable. We thus + * select the mf lwoer bound and the corresponding p1 value. + */ + if (limits->p1_min == 0) { + dev_err(dev, "pll: P1 minimum value must be >0.\n"); + return -EINVAL; + } + + p1_min = max(limits->p1_min, DIV_ROUND_UP(limits->out_clock_min * div, + pll->ext_clock * pll->m)); + p1_max = min(limits->p1_max, limits->out_clock_max * div / + (pll->ext_clock * pll->m)); + + for (p1 = p1_max & ~1; p1 >= p1_min; p1 -= 2) { + unsigned int mf_inc = p1 / gcd(div, p1); + unsigned int mf_high; + unsigned int mf_low; + + mf_low = max(roundup(mf_min, mf_inc), + DIV_ROUND_UP(pll->ext_clock * p1, + limits->int_clock_max * div)); + mf_high = min(mf_max, pll->ext_clock * p1 / + (limits->int_clock_min * div)); + + if (mf_low > mf_high) + continue; + + pll->n = div * mf_low / p1; + pll->m *= mf_low; + pll->p1 = p1; + dev_dbg(dev, "PLL: N %u M %u P1 %u\n", pll->n, pll->m, pll->p1); + return 0; + } + + dev_err(dev, "pll: no valid N and P1 divisors found.\n"); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(aptina_pll_calculate); + +MODULE_DESCRIPTION("Aptina PLL Helpers"); +MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/aptina-pll.h b/drivers/media/video/aptina-pll.h new file mode 100644 index 000000000000..b370e341e75d --- /dev/null +++ b/drivers/media/video/aptina-pll.h @@ -0,0 +1,56 @@ +/* + * Aptina Sensor PLL Configuration + * + * Copyright (C) 2012 Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef __APTINA_PLL_H +#define __APTINA_PLL_H + +struct aptina_pll { + unsigned int ext_clock; + unsigned int pix_clock; + + unsigned int n; + unsigned int m; + unsigned int p1; +}; + +struct aptina_pll_limits { + unsigned int ext_clock_min; + unsigned int ext_clock_max; + unsigned int int_clock_min; + unsigned int int_clock_max; + unsigned int out_clock_min; + unsigned int out_clock_max; + unsigned int pix_clock_max; + + unsigned int n_min; + unsigned int n_max; + unsigned int m_min; + unsigned int m_max; + unsigned int p1_min; + unsigned int p1_max; +}; + +struct device; + +int aptina_pll_calculate(struct device *dev, + const struct aptina_pll_limits *limits, + struct aptina_pll *pll); + +#endif /* __APTINA_PLL_H */ diff --git a/drivers/media/video/as3645a.c b/drivers/media/video/as3645a.c index f241702a0f36..7a3371f044fc 100644 --- a/drivers/media/video/as3645a.c +++ b/drivers/media/video/as3645a.c @@ -881,24 +881,7 @@ static struct i2c_driver as3645a_i2c_driver = { .id_table = as3645a_id_table, }; -static int __init as3645a_init(void) -{ - int rval; - - rval = i2c_add_driver(&as3645a_i2c_driver); - if (rval) - pr_err("%s: Failed to register the driver\n", AS3645A_NAME); - - return rval; -} - -static void __exit as3645a_exit(void) -{ - i2c_del_driver(&as3645a_i2c_driver); -} - -module_init(as3645a_init); -module_exit(as3645a_exit); +module_i2c_driver(as3645a_i2c_driver); MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); MODULE_DESCRIPTION("LED flash driver for AS3645A, LM3555 and their clones"); diff --git a/drivers/media/video/blackfin/Kconfig b/drivers/media/video/blackfin/Kconfig new file mode 100644 index 000000000000..ecd5323768b7 --- /dev/null +++ b/drivers/media/video/blackfin/Kconfig @@ -0,0 +1,10 @@ +config VIDEO_BLACKFIN_CAPTURE + tristate "Blackfin Video Capture Driver" + depends on VIDEO_V4L2 && BLACKFIN && I2C + select VIDEOBUF2_DMA_CONTIG + help + V4L2 bridge driver for Blackfin video capture device. + Choose PPI or EPPI as its interface. + + To compile this driver as a module, choose M here: the + module will be called bfin_video_capture. diff --git a/drivers/media/video/blackfin/Makefile b/drivers/media/video/blackfin/Makefile new file mode 100644 index 000000000000..aa3a0a216387 --- /dev/null +++ b/drivers/media/video/blackfin/Makefile @@ -0,0 +1,2 @@ +bfin_video_capture-objs := bfin_capture.o ppi.o +obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_video_capture.o diff --git a/drivers/media/video/blackfin/bfin_capture.c b/drivers/media/video/blackfin/bfin_capture.c new file mode 100644 index 000000000000..514fcf742f5a --- /dev/null +++ b/drivers/media/video/blackfin/bfin_capture.c @@ -0,0 +1,1059 @@ +/* + * Analog Devices video capture driver + * + * Copyright (c) 2011 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/time.h> +#include <linux/types.h> + +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-dma-contig.h> + +#include <asm/dma.h> + +#include <media/blackfin/bfin_capture.h> +#include <media/blackfin/ppi.h> + +#define CAPTURE_DRV_NAME "bfin_capture" +#define BCAP_MIN_NUM_BUF 2 + +struct bcap_format { + char *desc; + u32 pixelformat; + enum v4l2_mbus_pixelcode mbus_code; + int bpp; /* bits per pixel */ +}; + +struct bcap_buffer { + struct vb2_buffer vb; + struct list_head list; +}; + +struct bcap_device { + /* capture device instance */ + struct v4l2_device v4l2_dev; + /* v4l2 control handler */ + struct v4l2_ctrl_handler ctrl_handler; + /* device node data */ + struct video_device *video_dev; + /* sub device instance */ + struct v4l2_subdev *sd; + /* capture config */ + struct bfin_capture_config *cfg; + /* ppi interface */ + struct ppi_if *ppi; + /* current input */ + unsigned int cur_input; + /* current selected standard */ + v4l2_std_id std; + /* used to store pixel format */ + struct v4l2_pix_format fmt; + /* bits per pixel*/ + int bpp; + /* used to store sensor supported format */ + struct bcap_format *sensor_formats; + /* number of sensor formats array */ + int num_sensor_formats; + /* pointing to current video buffer */ + struct bcap_buffer *cur_frm; + /* pointing to next video buffer */ + struct bcap_buffer *next_frm; + /* buffer queue used in videobuf2 */ + struct vb2_queue buffer_queue; + /* allocator-specific contexts for each plane */ + struct vb2_alloc_ctx *alloc_ctx; + /* queue of filled frames */ + struct list_head dma_queue; + /* used in videobuf2 callback */ + spinlock_t lock; + /* used to access capture device */ + struct mutex mutex; + /* used to wait ppi to complete one transfer */ + struct completion comp; + /* prepare to stop */ + bool stop; +}; + +struct bcap_fh { + struct v4l2_fh fh; + /* indicates whether this file handle is doing IO */ + bool io_allowed; +}; + +static const struct bcap_format bcap_formats[] = { + { + .desc = "YCbCr 4:2:2 Interleaved UYVY", + .pixelformat = V4L2_PIX_FMT_UYVY, + .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, + .bpp = 16, + }, + { + .desc = "YCbCr 4:2:2 Interleaved YUYV", + .pixelformat = V4L2_PIX_FMT_YUYV, + .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, + .bpp = 16, + }, + { + .desc = "RGB 565", + .pixelformat = V4L2_PIX_FMT_RGB565, + .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, + .bpp = 16, + }, + { + .desc = "RGB 444", + .pixelformat = V4L2_PIX_FMT_RGB444, + .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, + .bpp = 16, + }, + +}; +#define BCAP_MAX_FMTS ARRAY_SIZE(bcap_formats) + +static irqreturn_t bcap_isr(int irq, void *dev_id); + +static struct bcap_buffer *to_bcap_vb(struct vb2_buffer *vb) +{ + return container_of(vb, struct bcap_buffer, vb); +} + +static int bcap_init_sensor_formats(struct bcap_device *bcap_dev) +{ + enum v4l2_mbus_pixelcode code; + struct bcap_format *sf; + unsigned int num_formats = 0; + int i, j; + + while (!v4l2_subdev_call(bcap_dev->sd, video, + enum_mbus_fmt, num_formats, &code)) + num_formats++; + if (!num_formats) + return -ENXIO; + + sf = kzalloc(num_formats * sizeof(*sf), GFP_KERNEL); + if (!sf) + return -ENOMEM; + + for (i = 0; i < num_formats; i++) { + v4l2_subdev_call(bcap_dev->sd, video, + enum_mbus_fmt, i, &code); + for (j = 0; j < BCAP_MAX_FMTS; j++) + if (code == bcap_formats[j].mbus_code) + break; + if (j == BCAP_MAX_FMTS) { + /* we don't allow this sensor working with our bridge */ + kfree(sf); + return -EINVAL; + } + sf[i] = bcap_formats[j]; + } + bcap_dev->sensor_formats = sf; + bcap_dev->num_sensor_formats = num_formats; + return 0; +} + +static void bcap_free_sensor_formats(struct bcap_device *bcap_dev) +{ + bcap_dev->num_sensor_formats = 0; + kfree(bcap_dev->sensor_formats); + bcap_dev->sensor_formats = NULL; +} + +static int bcap_open(struct file *file) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct video_device *vfd = bcap_dev->video_dev; + struct bcap_fh *bcap_fh; + + if (!bcap_dev->sd) { + v4l2_err(&bcap_dev->v4l2_dev, "No sub device registered\n"); + return -ENODEV; + } + + bcap_fh = kzalloc(sizeof(*bcap_fh), GFP_KERNEL); + if (!bcap_fh) { + v4l2_err(&bcap_dev->v4l2_dev, + "unable to allocate memory for file handle object\n"); + return -ENOMEM; + } + + v4l2_fh_init(&bcap_fh->fh, vfd); + + /* store pointer to v4l2_fh in private_data member of file */ + file->private_data = &bcap_fh->fh; + v4l2_fh_add(&bcap_fh->fh); + bcap_fh->io_allowed = false; + return 0; +} + +static int bcap_release(struct file *file) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct v4l2_fh *fh = file->private_data; + struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh); + + /* if this instance is doing IO */ + if (bcap_fh->io_allowed) + vb2_queue_release(&bcap_dev->buffer_queue); + + file->private_data = NULL; + v4l2_fh_del(&bcap_fh->fh); + v4l2_fh_exit(&bcap_fh->fh); + kfree(bcap_fh); + return 0; +} + +static int bcap_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + return vb2_mmap(&bcap_dev->buffer_queue, vma); +} + +#ifndef CONFIG_MMU +static unsigned long bcap_get_unmapped_area(struct file *file, + unsigned long addr, + unsigned long len, + unsigned long pgoff, + unsigned long flags) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + return vb2_get_unmapped_area(&bcap_dev->buffer_queue, + addr, + len, + pgoff, + flags); +} +#endif + +static unsigned int bcap_poll(struct file *file, poll_table *wait) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + return vb2_poll(&bcap_dev->buffer_queue, file, wait); +} + +static int bcap_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); + + if (*nbuffers < BCAP_MIN_NUM_BUF) + *nbuffers = BCAP_MIN_NUM_BUF; + + *nplanes = 1; + sizes[0] = bcap_dev->fmt.sizeimage; + alloc_ctxs[0] = bcap_dev->alloc_ctx; + + return 0; +} + +static int bcap_buffer_init(struct vb2_buffer *vb) +{ + struct bcap_buffer *buf = to_bcap_vb(vb); + + INIT_LIST_HEAD(&buf->list); + return 0; +} + +static int bcap_buffer_prepare(struct vb2_buffer *vb) +{ + struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue); + struct bcap_buffer *buf = to_bcap_vb(vb); + unsigned long size; + + size = bcap_dev->fmt.sizeimage; + if (vb2_plane_size(vb, 0) < size) { + v4l2_err(&bcap_dev->v4l2_dev, "buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + vb2_set_plane_payload(&buf->vb, 0, size); + + return 0; +} + +static void bcap_buffer_queue(struct vb2_buffer *vb) +{ + struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue); + struct bcap_buffer *buf = to_bcap_vb(vb); + unsigned long flags; + + spin_lock_irqsave(&bcap_dev->lock, flags); + list_add_tail(&buf->list, &bcap_dev->dma_queue); + spin_unlock_irqrestore(&bcap_dev->lock, flags); +} + +static void bcap_buffer_cleanup(struct vb2_buffer *vb) +{ + struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue); + struct bcap_buffer *buf = to_bcap_vb(vb); + unsigned long flags; + + spin_lock_irqsave(&bcap_dev->lock, flags); + list_del_init(&buf->list); + spin_unlock_irqrestore(&bcap_dev->lock, flags); +} + +static void bcap_lock(struct vb2_queue *vq) +{ + struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); + mutex_lock(&bcap_dev->mutex); +} + +static void bcap_unlock(struct vb2_queue *vq) +{ + struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); + mutex_unlock(&bcap_dev->mutex); +} + +static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); + struct ppi_if *ppi = bcap_dev->ppi; + struct ppi_params params; + int ret; + + /* enable streamon on the sub device */ + ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 1); + if (ret && (ret != -ENOIOCTLCMD)) { + v4l2_err(&bcap_dev->v4l2_dev, "stream on failed in subdev\n"); + return ret; + } + + /* set ppi params */ + params.width = bcap_dev->fmt.width; + params.height = bcap_dev->fmt.height; + params.bpp = bcap_dev->bpp; + params.ppi_control = bcap_dev->cfg->ppi_control; + params.int_mask = bcap_dev->cfg->int_mask; + params.blank_clocks = bcap_dev->cfg->blank_clocks; + ret = ppi->ops->set_params(ppi, ¶ms); + if (ret < 0) { + v4l2_err(&bcap_dev->v4l2_dev, + "Error in setting ppi params\n"); + return ret; + } + + /* attach ppi DMA irq handler */ + ret = ppi->ops->attach_irq(ppi, bcap_isr); + if (ret < 0) { + v4l2_err(&bcap_dev->v4l2_dev, + "Error in attaching interrupt handler\n"); + return ret; + } + + INIT_COMPLETION(bcap_dev->comp); + bcap_dev->stop = false; + return 0; +} + +static int bcap_stop_streaming(struct vb2_queue *vq) +{ + struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); + struct ppi_if *ppi = bcap_dev->ppi; + int ret; + + if (!vb2_is_streaming(vq)) + return 0; + + bcap_dev->stop = true; + wait_for_completion(&bcap_dev->comp); + ppi->ops->stop(ppi); + ppi->ops->detach_irq(ppi); + ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 0); + if (ret && (ret != -ENOIOCTLCMD)) + v4l2_err(&bcap_dev->v4l2_dev, + "stream off failed in subdev\n"); + + /* release all active buffers */ + while (!list_empty(&bcap_dev->dma_queue)) { + bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next, + struct bcap_buffer, list); + list_del(&bcap_dev->next_frm->list); + vb2_buffer_done(&bcap_dev->next_frm->vb, VB2_BUF_STATE_ERROR); + } + return 0; +} + +static struct vb2_ops bcap_video_qops = { + .queue_setup = bcap_queue_setup, + .buf_init = bcap_buffer_init, + .buf_prepare = bcap_buffer_prepare, + .buf_cleanup = bcap_buffer_cleanup, + .buf_queue = bcap_buffer_queue, + .wait_prepare = bcap_unlock, + .wait_finish = bcap_lock, + .start_streaming = bcap_start_streaming, + .stop_streaming = bcap_stop_streaming, +}; + +static int bcap_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req_buf) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct vb2_queue *vq = &bcap_dev->buffer_queue; + struct v4l2_fh *fh = file->private_data; + struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh); + + if (vb2_is_busy(vq)) + return -EBUSY; + + bcap_fh->io_allowed = true; + + return vb2_reqbufs(vq, req_buf); +} + +static int bcap_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + return vb2_querybuf(&bcap_dev->buffer_queue, buf); +} + +static int bcap_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct v4l2_fh *fh = file->private_data; + struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh); + + if (!bcap_fh->io_allowed) + return -EBUSY; + + return vb2_qbuf(&bcap_dev->buffer_queue, buf); +} + +static int bcap_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct v4l2_fh *fh = file->private_data; + struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh); + + if (!bcap_fh->io_allowed) + return -EBUSY; + + return vb2_dqbuf(&bcap_dev->buffer_queue, + buf, file->f_flags & O_NONBLOCK); +} + +static irqreturn_t bcap_isr(int irq, void *dev_id) +{ + struct ppi_if *ppi = dev_id; + struct bcap_device *bcap_dev = ppi->priv; + struct timeval timevalue; + struct vb2_buffer *vb = &bcap_dev->cur_frm->vb; + dma_addr_t addr; + + spin_lock(&bcap_dev->lock); + + if (bcap_dev->cur_frm != bcap_dev->next_frm) { + do_gettimeofday(&timevalue); + vb->v4l2_buf.timestamp = timevalue; + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + bcap_dev->cur_frm = bcap_dev->next_frm; + } + + ppi->ops->stop(ppi); + + if (bcap_dev->stop) { + complete(&bcap_dev->comp); + } else { + if (!list_empty(&bcap_dev->dma_queue)) { + bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next, + struct bcap_buffer, list); + list_del(&bcap_dev->next_frm->list); + addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->next_frm->vb, 0); + ppi->ops->update_addr(ppi, (unsigned long)addr); + } + ppi->ops->start(ppi); + } + + spin_unlock(&bcap_dev->lock); + + return IRQ_HANDLED; +} + +static int bcap_streamon(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct bcap_fh *fh = file->private_data; + struct ppi_if *ppi = bcap_dev->ppi; + dma_addr_t addr; + int ret; + + if (!fh->io_allowed) + return -EBUSY; + + /* call streamon to start streaming in videobuf */ + ret = vb2_streamon(&bcap_dev->buffer_queue, buf_type); + if (ret) + return ret; + + /* if dma queue is empty, return error */ + if (list_empty(&bcap_dev->dma_queue)) { + v4l2_err(&bcap_dev->v4l2_dev, "dma queue is empty\n"); + ret = -EINVAL; + goto err; + } + + /* get the next frame from the dma queue */ + bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next, + struct bcap_buffer, list); + bcap_dev->cur_frm = bcap_dev->next_frm; + /* remove buffer from the dma queue */ + list_del(&bcap_dev->cur_frm->list); + addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0); + /* update DMA address */ + ppi->ops->update_addr(ppi, (unsigned long)addr); + /* enable ppi */ + ppi->ops->start(ppi); + + return 0; +err: + vb2_streamoff(&bcap_dev->buffer_queue, buf_type); + return ret; +} + +static int bcap_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct bcap_fh *fh = file->private_data; + + if (!fh->io_allowed) + return -EBUSY; + + return vb2_streamoff(&bcap_dev->buffer_queue, buf_type); +} + +static int bcap_querystd(struct file *file, void *priv, v4l2_std_id *std) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + return v4l2_subdev_call(bcap_dev->sd, video, querystd, std); +} + +static int bcap_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + *std = bcap_dev->std; + return 0; +} + +static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + int ret; + + if (vb2_is_busy(&bcap_dev->buffer_queue)) + return -EBUSY; + + ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, *std); + if (ret < 0) + return ret; + + bcap_dev->std = *std; + return 0; +} + +static int bcap_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct bfin_capture_config *config = bcap_dev->cfg; + int ret; + u32 status; + + if (input->index >= config->num_inputs) + return -EINVAL; + + *input = config->inputs[input->index]; + /* get input status */ + ret = v4l2_subdev_call(bcap_dev->sd, video, g_input_status, &status); + if (!ret) + input->status = status; + return 0; +} + +static int bcap_g_input(struct file *file, void *priv, unsigned int *index) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + *index = bcap_dev->cur_input; + return 0; +} + +static int bcap_s_input(struct file *file, void *priv, unsigned int index) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct bfin_capture_config *config = bcap_dev->cfg; + struct bcap_route *route; + int ret; + + if (vb2_is_busy(&bcap_dev->buffer_queue)) + return -EBUSY; + + if (index >= config->num_inputs) + return -EINVAL; + + route = &config->routes[index]; + ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing, + route->input, route->output, 0); + if ((ret < 0) && (ret != -ENOIOCTLCMD)) { + v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n"); + return ret; + } + bcap_dev->cur_input = index; + return 0; +} + +static int bcap_try_format(struct bcap_device *bcap, + struct v4l2_pix_format *pixfmt, + enum v4l2_mbus_pixelcode *mbus_code, + int *bpp) +{ + struct bcap_format *sf = bcap->sensor_formats; + struct bcap_format *fmt = NULL; + struct v4l2_mbus_framefmt mbus_fmt; + int ret, i; + + for (i = 0; i < bcap->num_sensor_formats; i++) { + fmt = &sf[i]; + if (pixfmt->pixelformat == fmt->pixelformat) + break; + } + if (i == bcap->num_sensor_formats) + fmt = &sf[0]; + + if (mbus_code) + *mbus_code = fmt->mbus_code; + if (bpp) + *bpp = fmt->bpp; + v4l2_fill_mbus_format(&mbus_fmt, pixfmt, fmt->mbus_code); + ret = v4l2_subdev_call(bcap->sd, video, + try_mbus_fmt, &mbus_fmt); + if (ret < 0) + return ret; + v4l2_fill_pix_format(pixfmt, &mbus_fmt); + pixfmt->bytesperline = pixfmt->width * fmt->bpp / 8; + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; + return 0; +} + +static int bcap_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct bcap_format *sf = bcap_dev->sensor_formats; + + if (fmt->index >= bcap_dev->num_sensor_formats) + return -EINVAL; + + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + strlcpy(fmt->description, + sf[fmt->index].desc, + sizeof(fmt->description)); + fmt->pixelformat = sf[fmt->index].pixelformat; + return 0; +} + +static int bcap_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + + return bcap_try_format(bcap_dev, pixfmt, NULL, NULL); +} + +static int bcap_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + fmt->fmt.pix = bcap_dev->fmt; + return 0; +} + +static int bcap_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct v4l2_mbus_framefmt mbus_fmt; + enum v4l2_mbus_pixelcode mbus_code; + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + int ret, bpp; + + if (vb2_is_busy(&bcap_dev->buffer_queue)) + return -EBUSY; + + /* see if format works */ + ret = bcap_try_format(bcap_dev, pixfmt, &mbus_code, &bpp); + if (ret < 0) + return ret; + + v4l2_fill_mbus_format(&mbus_fmt, pixfmt, mbus_code); + ret = v4l2_subdev_call(bcap_dev->sd, video, s_mbus_fmt, &mbus_fmt); + if (ret < 0) + return ret; + bcap_dev->fmt = *pixfmt; + bcap_dev->bpp = bpp; + return 0; +} + +static int bcap_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); + strlcpy(cap->bus_info, "Blackfin Platform", sizeof(cap->bus_info)); + strlcpy(cap->card, bcap_dev->cfg->card_name, sizeof(cap->card)); + return 0; +} + +static int bcap_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + return v4l2_subdev_call(bcap_dev->sd, video, g_parm, a); +} + +static int bcap_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + return v4l2_subdev_call(bcap_dev->sd, video, s_parm, a); +} + +static int bcap_g_chip_ident(struct file *file, void *priv, + struct v4l2_dbg_chip_ident *chip) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && + chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + + return v4l2_subdev_call(bcap_dev->sd, core, + g_chip_ident, chip); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int bcap_dbg_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + return v4l2_subdev_call(bcap_dev->sd, core, + g_register, reg); +} + +static int bcap_dbg_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + return v4l2_subdev_call(bcap_dev->sd, core, + s_register, reg); +} +#endif + +static int bcap_log_status(struct file *file, void *priv) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + /* status for sub devices */ + v4l2_device_call_all(&bcap_dev->v4l2_dev, 0, core, log_status); + return 0; +} + +static const struct v4l2_ioctl_ops bcap_ioctl_ops = { + .vidioc_querycap = bcap_querycap, + .vidioc_g_fmt_vid_cap = bcap_g_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = bcap_enum_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = bcap_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = bcap_try_fmt_vid_cap, + .vidioc_enum_input = bcap_enum_input, + .vidioc_g_input = bcap_g_input, + .vidioc_s_input = bcap_s_input, + .vidioc_querystd = bcap_querystd, + .vidioc_s_std = bcap_s_std, + .vidioc_g_std = bcap_g_std, + .vidioc_reqbufs = bcap_reqbufs, + .vidioc_querybuf = bcap_querybuf, + .vidioc_qbuf = bcap_qbuf, + .vidioc_dqbuf = bcap_dqbuf, + .vidioc_streamon = bcap_streamon, + .vidioc_streamoff = bcap_streamoff, + .vidioc_g_parm = bcap_g_parm, + .vidioc_s_parm = bcap_s_parm, + .vidioc_g_chip_ident = bcap_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = bcap_dbg_g_register, + .vidioc_s_register = bcap_dbg_s_register, +#endif + .vidioc_log_status = bcap_log_status, +}; + +static struct v4l2_file_operations bcap_fops = { + .owner = THIS_MODULE, + .open = bcap_open, + .release = bcap_release, + .unlocked_ioctl = video_ioctl2, + .mmap = bcap_mmap, +#ifndef CONFIG_MMU + .get_unmapped_area = bcap_get_unmapped_area, +#endif + .poll = bcap_poll +}; + +static int __devinit bcap_probe(struct platform_device *pdev) +{ + struct bcap_device *bcap_dev; + struct video_device *vfd; + struct i2c_adapter *i2c_adap; + struct bfin_capture_config *config; + struct vb2_queue *q; + int ret; + + config = pdev->dev.platform_data; + if (!config) { + v4l2_err(pdev->dev.driver, "Unable to get board config\n"); + return -ENODEV; + } + + bcap_dev = kzalloc(sizeof(*bcap_dev), GFP_KERNEL); + if (!bcap_dev) { + v4l2_err(pdev->dev.driver, "Unable to alloc bcap_dev\n"); + return -ENOMEM; + } + + bcap_dev->cfg = config; + + bcap_dev->ppi = ppi_create_instance(config->ppi_info); + if (!bcap_dev->ppi) { + v4l2_err(pdev->dev.driver, "Unable to create ppi\n"); + ret = -ENODEV; + goto err_free_dev; + } + bcap_dev->ppi->priv = bcap_dev; + + bcap_dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(bcap_dev->alloc_ctx)) { + ret = PTR_ERR(bcap_dev->alloc_ctx); + goto err_free_ppi; + } + + vfd = video_device_alloc(); + if (!vfd) { + ret = -ENOMEM; + v4l2_err(pdev->dev.driver, "Unable to alloc video device\n"); + goto err_cleanup_ctx; + } + + /* initialize field of video device */ + vfd->release = video_device_release; + vfd->fops = &bcap_fops; + vfd->ioctl_ops = &bcap_ioctl_ops; + vfd->tvnorms = 0; + vfd->v4l2_dev = &bcap_dev->v4l2_dev; + set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); + strncpy(vfd->name, CAPTURE_DRV_NAME, sizeof(vfd->name)); + bcap_dev->video_dev = vfd; + + ret = v4l2_device_register(&pdev->dev, &bcap_dev->v4l2_dev); + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register v4l2 device\n"); + goto err_release_vdev; + } + v4l2_info(&bcap_dev->v4l2_dev, "v4l2 device registered\n"); + + bcap_dev->v4l2_dev.ctrl_handler = &bcap_dev->ctrl_handler; + ret = v4l2_ctrl_handler_init(&bcap_dev->ctrl_handler, 0); + if (ret) { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to init control handler\n"); + goto err_unreg_v4l2; + } + + spin_lock_init(&bcap_dev->lock); + /* initialize queue */ + q = &bcap_dev->buffer_queue; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP; + q->drv_priv = bcap_dev; + q->buf_struct_size = sizeof(struct bcap_buffer); + q->ops = &bcap_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + + vb2_queue_init(q); + + mutex_init(&bcap_dev->mutex); + init_completion(&bcap_dev->comp); + + /* init video dma queues */ + INIT_LIST_HEAD(&bcap_dev->dma_queue); + + vfd->lock = &bcap_dev->mutex; + + /* register video device */ + ret = video_register_device(bcap_dev->video_dev, VFL_TYPE_GRABBER, -1); + if (ret) { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to register video device\n"); + goto err_free_handler; + } + video_set_drvdata(bcap_dev->video_dev, bcap_dev); + v4l2_info(&bcap_dev->v4l2_dev, "video device registered as: %s\n", + video_device_node_name(vfd)); + + /* load up the subdevice */ + i2c_adap = i2c_get_adapter(config->i2c_adapter_id); + if (!i2c_adap) { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to find i2c adapter\n"); + goto err_unreg_vdev; + + } + bcap_dev->sd = v4l2_i2c_new_subdev_board(&bcap_dev->v4l2_dev, + i2c_adap, + &config->board_info, + NULL); + if (bcap_dev->sd) { + int i; + /* update tvnorms from the sub devices */ + for (i = 0; i < config->num_inputs; i++) + vfd->tvnorms |= config->inputs[i].std; + } else { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to register sub device\n"); + goto err_unreg_vdev; + } + + v4l2_info(&bcap_dev->v4l2_dev, "v4l2 sub device registered\n"); + + /* now we can probe the default state */ + if (vfd->tvnorms) { + v4l2_std_id std; + ret = v4l2_subdev_call(bcap_dev->sd, core, g_std, &std); + if (ret) { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to get std\n"); + goto err_unreg_vdev; + } + bcap_dev->std = std; + } + ret = bcap_init_sensor_formats(bcap_dev); + if (ret) { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to create sensor formats table\n"); + goto err_unreg_vdev; + } + return 0; +err_unreg_vdev: + video_unregister_device(bcap_dev->video_dev); + bcap_dev->video_dev = NULL; +err_free_handler: + v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler); +err_unreg_v4l2: + v4l2_device_unregister(&bcap_dev->v4l2_dev); +err_release_vdev: + if (bcap_dev->video_dev) + video_device_release(bcap_dev->video_dev); +err_cleanup_ctx: + vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx); +err_free_ppi: + ppi_delete_instance(bcap_dev->ppi); +err_free_dev: + kfree(bcap_dev); + return ret; +} + +static int __devexit bcap_remove(struct platform_device *pdev) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct bcap_device *bcap_dev = container_of(v4l2_dev, + struct bcap_device, v4l2_dev); + + bcap_free_sensor_formats(bcap_dev); + video_unregister_device(bcap_dev->video_dev); + v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler); + v4l2_device_unregister(v4l2_dev); + vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx); + ppi_delete_instance(bcap_dev->ppi); + kfree(bcap_dev); + return 0; +} + +static struct platform_driver bcap_driver = { + .driver = { + .name = CAPTURE_DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = bcap_probe, + .remove = __devexit_p(bcap_remove), +}; + +static __init int bcap_init(void) +{ + return platform_driver_register(&bcap_driver); +} + +static __exit void bcap_exit(void) +{ + platform_driver_unregister(&bcap_driver); +} + +module_init(bcap_init); +module_exit(bcap_exit); + +MODULE_DESCRIPTION("Analog Devices blackfin video capture driver"); +MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/blackfin/ppi.c b/drivers/media/video/blackfin/ppi.c new file mode 100644 index 000000000000..d29592186b02 --- /dev/null +++ b/drivers/media/video/blackfin/ppi.c @@ -0,0 +1,271 @@ +/* + * ppi.c Analog Devices Parallel Peripheral Interface driver + * + * Copyright (c) 2011 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/slab.h> + +#include <asm/bfin_ppi.h> +#include <asm/blackfin.h> +#include <asm/cacheflush.h> +#include <asm/dma.h> +#include <asm/portmux.h> + +#include <media/blackfin/ppi.h> + +static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler); +static void ppi_detach_irq(struct ppi_if *ppi); +static int ppi_start(struct ppi_if *ppi); +static int ppi_stop(struct ppi_if *ppi); +static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params); +static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr); + +static const struct ppi_ops ppi_ops = { + .attach_irq = ppi_attach_irq, + .detach_irq = ppi_detach_irq, + .start = ppi_start, + .stop = ppi_stop, + .set_params = ppi_set_params, + .update_addr = ppi_update_addr, +}; + +static irqreturn_t ppi_irq_err(int irq, void *dev_id) +{ + struct ppi_if *ppi = dev_id; + const struct ppi_info *info = ppi->info; + + switch (info->type) { + case PPI_TYPE_PPI: + { + struct bfin_ppi_regs *reg = info->base; + unsigned short status; + + /* register on bf561 is cleared when read + * others are W1C + */ + status = bfin_read16(®->status); + bfin_write16(®->status, 0xff00); + break; + } + case PPI_TYPE_EPPI: + { + struct bfin_eppi_regs *reg = info->base; + bfin_write16(®->status, 0xffff); + break; + } + default: + break; + } + + return IRQ_HANDLED; +} + +static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler) +{ + const struct ppi_info *info = ppi->info; + int ret; + + ret = request_dma(info->dma_ch, "PPI_DMA"); + + if (ret) { + pr_err("Unable to allocate DMA channel for PPI\n"); + return ret; + } + set_dma_callback(info->dma_ch, handler, ppi); + + if (ppi->err_int) { + ret = request_irq(info->irq_err, ppi_irq_err, 0, "PPI ERROR", ppi); + if (ret) { + pr_err("Unable to allocate IRQ for PPI\n"); + free_dma(info->dma_ch); + } + } + return ret; +} + +static void ppi_detach_irq(struct ppi_if *ppi) +{ + const struct ppi_info *info = ppi->info; + + if (ppi->err_int) + free_irq(info->irq_err, ppi); + free_dma(info->dma_ch); +} + +static int ppi_start(struct ppi_if *ppi) +{ + const struct ppi_info *info = ppi->info; + + /* enable DMA */ + enable_dma(info->dma_ch); + + /* enable PPI */ + ppi->ppi_control |= PORT_EN; + switch (info->type) { + case PPI_TYPE_PPI: + { + struct bfin_ppi_regs *reg = info->base; + bfin_write16(®->control, ppi->ppi_control); + break; + } + case PPI_TYPE_EPPI: + { + struct bfin_eppi_regs *reg = info->base; + bfin_write32(®->control, ppi->ppi_control); + break; + } + default: + return -EINVAL; + } + + SSYNC(); + return 0; +} + +static int ppi_stop(struct ppi_if *ppi) +{ + const struct ppi_info *info = ppi->info; + + /* disable PPI */ + ppi->ppi_control &= ~PORT_EN; + switch (info->type) { + case PPI_TYPE_PPI: + { + struct bfin_ppi_regs *reg = info->base; + bfin_write16(®->control, ppi->ppi_control); + break; + } + case PPI_TYPE_EPPI: + { + struct bfin_eppi_regs *reg = info->base; + bfin_write32(®->control, ppi->ppi_control); + break; + } + default: + return -EINVAL; + } + + /* disable DMA */ + clear_dma_irqstat(info->dma_ch); + disable_dma(info->dma_ch); + + SSYNC(); + return 0; +} + +static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) +{ + const struct ppi_info *info = ppi->info; + int dma32 = 0; + int dma_config, bytes_per_line, lines_per_frame; + + bytes_per_line = params->width * params->bpp / 8; + lines_per_frame = params->height; + if (params->int_mask == 0xFFFFFFFF) + ppi->err_int = false; + else + ppi->err_int = true; + + dma_config = (DMA_FLOW_STOP | WNR | RESTART | DMA2D | DI_EN); + ppi->ppi_control = params->ppi_control & ~PORT_EN; + switch (info->type) { + case PPI_TYPE_PPI: + { + struct bfin_ppi_regs *reg = info->base; + + if (params->ppi_control & DMA32) + dma32 = 1; + + bfin_write16(®->control, ppi->ppi_control); + bfin_write16(®->count, bytes_per_line - 1); + bfin_write16(®->frame, lines_per_frame); + break; + } + case PPI_TYPE_EPPI: + { + struct bfin_eppi_regs *reg = info->base; + + if ((params->ppi_control & PACK_EN) + || (params->ppi_control & 0x38000) > DLEN_16) + dma32 = 1; + + bfin_write32(®->control, ppi->ppi_control); + bfin_write16(®->line, bytes_per_line + params->blank_clocks); + bfin_write16(®->frame, lines_per_frame); + bfin_write16(®->hdelay, 0); + bfin_write16(®->vdelay, 0); + bfin_write16(®->hcount, bytes_per_line); + bfin_write16(®->vcount, lines_per_frame); + break; + } + default: + return -EINVAL; + } + + if (dma32) { + dma_config |= WDSIZE_32; + set_dma_x_count(info->dma_ch, bytes_per_line >> 2); + set_dma_x_modify(info->dma_ch, 4); + set_dma_y_modify(info->dma_ch, 4); + } else { + dma_config |= WDSIZE_16; + set_dma_x_count(info->dma_ch, bytes_per_line >> 1); + set_dma_x_modify(info->dma_ch, 2); + set_dma_y_modify(info->dma_ch, 2); + } + set_dma_y_count(info->dma_ch, lines_per_frame); + set_dma_config(info->dma_ch, dma_config); + + SSYNC(); + return 0; +} + +static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr) +{ + set_dma_start_addr(ppi->info->dma_ch, addr); +} + +struct ppi_if *ppi_create_instance(const struct ppi_info *info) +{ + struct ppi_if *ppi; + + if (!info || !info->pin_req) + return NULL; + + if (peripheral_request_list(info->pin_req, KBUILD_MODNAME)) { + pr_err("request peripheral failed\n"); + return NULL; + } + + ppi = kzalloc(sizeof(*ppi), GFP_KERNEL); + if (!ppi) { + peripheral_free_list(info->pin_req); + pr_err("unable to allocate memory for ppi handle\n"); + return NULL; + } + ppi->ops = &ppi_ops; + ppi->info = info; + + pr_info("ppi probe success\n"); + return ppi; +} + +void ppi_delete_instance(struct ppi_if *ppi) +{ + peripheral_free_list(ppi->info->pin_req); + kfree(ppi); +} diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c index 859eabf57978..377bf05b1efd 100644 --- a/drivers/media/video/bt819.c +++ b/drivers/media/video/bt819.c @@ -514,15 +514,4 @@ static struct i2c_driver bt819_driver = { .id_table = bt819_id, }; -static __init int init_bt819(void) -{ - return i2c_add_driver(&bt819_driver); -} - -static __exit void exit_bt819(void) -{ - i2c_del_driver(&bt819_driver); -} - -module_init(init_bt819); -module_exit(exit_bt819); +module_i2c_driver(bt819_driver); diff --git a/drivers/media/video/bt856.c b/drivers/media/video/bt856.c index a43059d4c799..7e5bd365c239 100644 --- a/drivers/media/video/bt856.c +++ b/drivers/media/video/bt856.c @@ -270,15 +270,4 @@ static struct i2c_driver bt856_driver = { .id_table = bt856_id, }; -static __init int init_bt856(void) -{ - return i2c_add_driver(&bt856_driver); -} - -static __exit void exit_bt856(void) -{ - i2c_del_driver(&bt856_driver); -} - -module_init(init_bt856); -module_exit(exit_bt856); +module_i2c_driver(bt856_driver); diff --git a/drivers/media/video/bt866.c b/drivers/media/video/bt866.c index 4e5dcea0501d..905320b67a1c 100644 --- a/drivers/media/video/bt866.c +++ b/drivers/media/video/bt866.c @@ -240,15 +240,4 @@ static struct i2c_driver bt866_driver = { .id_table = bt866_id, }; -static __init int init_bt866(void) -{ - return i2c_add_driver(&bt866_driver); -} - -static __exit void exit_bt866(void) -{ - i2c_del_driver(&bt866_driver); -} - -module_init(init_bt866); -module_exit(exit_bt866); +module_i2c_driver(bt866_driver); diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 76c301f05095..e581b37be789 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -2035,11 +2035,7 @@ static int bttv_log_status(struct file *file, void *f) struct bttv_fh *fh = f; struct bttv *btv = fh->btv; - pr_info("%d: ======== START STATUS CARD #%d ========\n", - btv->c.nr, btv->c.nr); bttv_call_all(btv, core, log_status); - pr_info("%d: ======== END STATUS CARD #%d ========\n", - btv->c.nr, btv->c.nr); return 0; } diff --git a/drivers/media/video/cs5345.c b/drivers/media/video/cs5345.c index 1d64af9adf71..c8581e26fa9c 100644 --- a/drivers/media/video/cs5345.c +++ b/drivers/media/video/cs5345.c @@ -249,15 +249,4 @@ static struct i2c_driver cs5345_driver = { .id_table = cs5345_id, }; -static __init int init_cs5345(void) -{ - return i2c_add_driver(&cs5345_driver); -} - -static __exit void exit_cs5345(void) -{ - i2c_del_driver(&cs5345_driver); -} - -module_init(init_cs5345); -module_exit(exit_cs5345); +module_i2c_driver(cs5345_driver); diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c index 51c5b9ad67d8..b293912206eb 100644 --- a/drivers/media/video/cs53l32a.c +++ b/drivers/media/video/cs53l32a.c @@ -248,15 +248,4 @@ static struct i2c_driver cs53l32a_driver = { .id_table = cs53l32a_id, }; -static __init int init_cs53l32a(void) -{ - return i2c_add_driver(&cs53l32a_driver); -} - -static __exit void exit_cs53l32a(void) -{ - i2c_del_driver(&cs53l32a_driver); -} - -module_init(init_cs53l32a); -module_exit(exit_cs53l32a); +module_i2c_driver(cs53l32a_driver); diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 349bd9c2aff5..b55d57cc1a1c 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -38,7 +38,7 @@ #include "cx18-ioctl.h" #include "cx18-controls.h" #include "tuner-xc2028.h" - +#include <linux/dma-mapping.h> #include <media/tveeprom.h> /* If you have already X v4l cards, then set this to X. This way @@ -75,7 +75,7 @@ static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; static unsigned cardtype_c = 1; static unsigned tuner_c = 1; -static bool radio_c = 1; +static unsigned radio_c = 1; static char pal[] = "--"; static char secam[] = "--"; static char ntsc[] = "-"; @@ -110,7 +110,7 @@ static int retry_mmio = 1; int cx18_debug; module_param_array(tuner, int, &tuner_c, 0644); -module_param_array(radio, bool, &radio_c, 0644); +module_param_array(radio, int, &radio_c, 0644); module_param_array(cardtype, int, &cardtype_c, 0644); module_param_string(pal, pal, sizeof(pal), 0644); module_param_string(secam, secam, sizeof(secam), 0644); @@ -812,7 +812,7 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev, CX18_ERR("Can't enable device %d!\n", cx->instance); return -EIO; } - if (pci_set_dma_mask(pci_dev, 0xffffffff)) { + if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) { CX18_ERR("No suitable DMA available, card %d\n", cx->instance); return -EIO; } diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index b9a94fc5146d..7a37e0ee136f 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -44,8 +44,6 @@ #include <linux/slab.h> #include <asm/byteorder.h> -#include <linux/dvb/video.h> -#include <linux/dvb/audio.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-device.h> diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 66b1c15c3541..be49f68ddf37 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -1085,8 +1085,6 @@ static int cx18_log_status(struct file *file, void *fh) struct v4l2_audio audin; int i; - CX18_INFO("================= START STATUS CARD #%d " - "=================\n", cx->instance); CX18_INFO("Version: %s Card: %s\n", CX18_VERSION, cx->card_name); if (cx->hw_flags & CX18_HW_TVEEPROM) { struct tveeprom tv; @@ -1120,8 +1118,6 @@ static int cx18_log_status(struct file *file, void *fh) CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n", (long long)cx->mpg_data_received, (long long)cx->vbi_data_inserted); - CX18_INFO("================== END STATUS CARD #%d " - "==================\n", cx->instance); return 0; } diff --git a/drivers/media/video/cx231xx/cx231xx-417.c b/drivers/media/video/cx231xx/cx231xx-417.c index f8f0e59cd583..d4327dab5a36 100644 --- a/drivers/media/video/cx231xx/cx231xx-417.c +++ b/drivers/media/video/cx231xx/cx231xx-417.c @@ -1686,7 +1686,6 @@ static struct v4l2_capability pvr_capability = { .capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE), - .reserved = {0, 0, 0, 0} }; static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c index 875a7ce94736..8ed460d692e0 100644 --- a/drivers/media/video/cx231xx/cx231xx-cards.c +++ b/drivers/media/video/cx231xx/cx231xx-cards.c @@ -861,7 +861,6 @@ void cx231xx_release_resources(struct cx231xx *dev) kfree(dev->sliced_cc_mode.alt_max_pkt_size); kfree(dev->ts1_mode.alt_max_pkt_size); kfree(dev); - dev = NULL; } /* diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c index 829a41b0c9ef..7f916f0685e9 100644 --- a/drivers/media/video/cx231xx/cx231xx-video.c +++ b/drivers/media/video/cx231xx/cx231xx-video.c @@ -2319,8 +2319,7 @@ static int cx231xx_v4l2_close(struct file *filp) if (dev->state & DEV_DISCONNECTED) { if (atomic_read(&dev->devlist_count) > 0) { cx231xx_release_resources(dev); - kfree(dev); - dev = NULL; + fh->dev = NULL; return 0; } return 0; @@ -2350,8 +2349,7 @@ static int cx231xx_v4l2_close(struct file *filp) free the remaining resources */ if (dev->state & DEV_DISCONNECTED) { cx231xx_release_resources(dev); - kfree(dev); - dev = NULL; + fh->dev = NULL; return 0; } diff --git a/drivers/media/video/cx25821/cx25821-core.c b/drivers/media/video/cx25821/cx25821-core.c index f617474f9073..7930ca58349f 100644 --- a/drivers/media/video/cx25821/cx25821-core.c +++ b/drivers/media/video/cx25821/cx25821-core.c @@ -1474,8 +1474,13 @@ static DEFINE_PCI_DEVICE_TABLE(cx25821_pci_tbl) = { .device = 0x8210, .subvendor = 0x14f1, .subdevice = 0x0920, - }, - { + }, { + /* CX25821 No Brand */ + .vendor = 0x14f1, + .device = 0x8210, + .subvendor = 0x0000, + .subdevice = 0x0000, + }, { /* --- end of list --- */ } }; diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 05247d4c340a..fc1ff69cffd0 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -5301,15 +5301,4 @@ static struct i2c_driver cx25840_driver = { .id_table = cx25840_id, }; -static __init int init_cx25840(void) -{ - return i2c_add_driver(&cx25840_driver); -} - -static __exit void exit_cx25840(void) -{ - i2c_del_driver(&cx25840_driver); -} - -module_init(init_cx25840); -module_exit(exit_cx25840); +module_i2c_driver(cx25840_driver); diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h index 25036cb11bea..8bcac65f9294 100644 --- a/drivers/media/video/davinci/vpif.h +++ b/drivers/media/video/davinci/vpif.h @@ -18,8 +18,6 @@ #include <linux/io.h> #include <linux/videodev2.h> -#include <mach/hardware.h> -#include <mach/dm646x.h> #include <media/davinci/vpif_types.h> /* Maximum channel allowed */ diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c index 286f02910044..7fa34b4fae26 100644 --- a/drivers/media/video/davinci/vpif_display.c +++ b/drivers/media/video/davinci/vpif_display.c @@ -39,8 +39,6 @@ #include <media/v4l2-ioctl.h> #include <media/v4l2-chip-ident.h> -#include <mach/dm646x.h> - #include "vpif_display.h" #include "vpif.h" diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 4561cd89938d..9fd8cc7dbb23 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -353,6 +353,44 @@ static struct em28xx_reg_seq hauppauge_930c_digital[] = { }; #endif +/* 1b80:e425 MaxMedia UB425-TC + * GPIO_6 - demod reset, 0=active + * GPIO_7 - LED, 0=active + */ +static struct em28xx_reg_seq maxmedia_ub425_tc[] = { + {EM2874_R80_GPIO, 0x83, 0xff, 100}, + {EM2874_R80_GPIO, 0xc3, 0xff, 100}, /* GPIO_6 = 1 */ + {EM2874_R80_GPIO, 0x43, 0xff, 000}, /* GPIO_7 = 0 */ + {-1, -1, -1, -1}, +}; + +/* 2304:0242 PCTV QuatroStick (510e) + * GPIO_2: decoder reset, 0=active + * GPIO_4: decoder suspend, 0=active + * GPIO_6: demod reset, 0=active + * GPIO_7: LED, 1=active + */ +static struct em28xx_reg_seq pctv_510e[] = { + {EM2874_R80_GPIO, 0x10, 0xff, 100}, + {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */ + {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */ + { -1, -1, -1, -1}, +}; + +/* 2013:0251 PCTV QuatroStick nano (520e) + * GPIO_2: decoder reset, 0=active + * GPIO_4: decoder suspend, 0=active + * GPIO_6: demod reset, 0=active + * GPIO_7: LED, 1=active + */ +static struct em28xx_reg_seq pctv_520e[] = { + {EM2874_R80_GPIO, 0x10, 0xff, 100}, + {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */ + {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */ + {EM2874_R80_GPIO, 0xd4, 0xff, 000}, /* GPIO_7 = 1 */ + { -1, -1, -1, -1}, +}; + /* * Board definitions */ @@ -1908,6 +1946,41 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + /* 1b80:e425 MaxMedia UB425-TC + * Empia EM2874B + Micronas DRX 3913KA2 + NXP TDA18271HDC2 */ + [EM2874_BOARD_MAXMEDIA_UB425_TC] = { + .name = "MaxMedia UB425-TC", + .tuner_type = TUNER_ABSENT, + .tuner_gpio = maxmedia_ub425_tc, + .has_dvb = 1, + .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, + }, + /* 2304:0242 PCTV QuatroStick (510e) + * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */ + [EM2884_BOARD_PCTV_510E] = { + .name = "PCTV QuatroStick (510e)", + .tuner_type = TUNER_ABSENT, + .tuner_gpio = pctv_510e, + .has_dvb = 1, + .ir_codes = RC_MAP_PINNACLE_PCTV_HD, + .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, + }, + /* 2013:0251 PCTV QuatroStick nano (520e) + * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */ + [EM2884_BOARD_PCTV_520E] = { + .name = "PCTV QuatroStick nano (520e)", + .tuner_type = TUNER_ABSENT, + .tuner_gpio = pctv_520e, + .has_dvb = 1, + .ir_codes = RC_MAP_PINNACLE_PCTV_HD, + .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -2059,6 +2132,12 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2860_BOARD_HT_VIDBOX_NW03 }, { USB_DEVICE(0x1b80, 0xe309), /* Sveon STV40 */ .driver_info = EM2860_BOARD_EASYCAP }, + { USB_DEVICE(0x1b80, 0xe425), + .driver_info = EM2874_BOARD_MAXMEDIA_UB425_TC }, + { USB_DEVICE(0x2304, 0x0242), + .driver_info = EM2884_BOARD_PCTV_510E }, + { USB_DEVICE(0x2013, 0x0251), + .driver_info = EM2884_BOARD_PCTV_520E }, { }, }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); @@ -3122,7 +3201,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, int i, nr; const int ifnum = interface->altsetting[0].desc.bInterfaceNumber; char *speed; - char descr[255] = ""; udev = usb_get_dev(interface_to_usbdev(interface)); @@ -3227,21 +3305,11 @@ static int em28xx_usb_probe(struct usb_interface *interface, speed = "unknown"; } - if (udev->manufacturer) - strlcpy(descr, udev->manufacturer, sizeof(descr)); - - if (udev->product) { - if (*descr) - strlcat(descr, " ", sizeof(descr)); - strlcat(descr, udev->product, sizeof(descr)); - } - - if (*descr) - strlcat(descr, " ", sizeof(descr)); - printk(KERN_INFO DRIVER_NAME - ": New device %s@ %s Mbps (%04x:%04x, interface %d, class %d)\n", - descr, + ": New device %s %s @ %s Mbps " + "(%04x:%04x, interface %d, class %d)\n", + udev->manufacturer ? udev->manufacturer : "", + udev->product ? udev->product : "", speed, le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct), @@ -3307,6 +3375,17 @@ static int em28xx_usb_probe(struct usb_interface *interface, goto unlock_and_free; } + if (has_dvb) { + /* pre-allocate DVB isoc transfer buffers */ + retval = em28xx_alloc_isoc(dev, EM28XX_DIGITAL_MODE, + EM28XX_DVB_MAX_PACKETS, + EM28XX_DVB_NUM_BUFS, + dev->dvb_max_pkt_size); + if (retval) { + goto unlock_and_free; + } + } + request_modules(dev); /* Should be the last thing to do, to avoid newer udev's to @@ -3379,7 +3458,7 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) video_device_node_name(dev->vdev)); dev->state |= DEV_MISCONFIGURED; - em28xx_uninit_isoc(dev); + em28xx_uninit_isoc(dev, dev->mode); dev->state |= DEV_DISCONNECTED; wake_up_interruptible(&dev->wait_frame); wake_up_interruptible(&dev->wait_stream); @@ -3388,6 +3467,9 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) em28xx_release_resources(dev); } + /* free DVB isoc buffers */ + em28xx_uninit_isoc(dev, EM28XX_DIGITAL_MODE); + mutex_unlock(&dev->lock); em28xx_close_extension(dev); diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 0aacc96f9a23..53a9fb91e97e 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -666,6 +666,7 @@ int em28xx_capture_start(struct em28xx *dev, int start) return rc; } +EXPORT_SYMBOL_GPL(em28xx_capture_start); int em28xx_vbi_supported(struct em28xx *dev) { @@ -961,146 +962,192 @@ static void em28xx_irq_callback(struct urb *urb) /* * Stop and Deallocate URBs */ -void em28xx_uninit_isoc(struct em28xx *dev) +void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) { struct urb *urb; + struct em28xx_usb_isoc_bufs *isoc_bufs; int i; - em28xx_isocdbg("em28xx: called em28xx_uninit_isoc\n"); + em28xx_isocdbg("em28xx: called em28xx_uninit_isoc in mode %d\n", mode); + + if (mode == EM28XX_DIGITAL_MODE) + isoc_bufs = &dev->isoc_ctl.digital_bufs; + else + isoc_bufs = &dev->isoc_ctl.analog_bufs; dev->isoc_ctl.nfields = -1; - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - urb = dev->isoc_ctl.urb[i]; + for (i = 0; i < isoc_bufs->num_bufs; i++) { + urb = isoc_bufs->urb[i]; if (urb) { if (!irqs_disabled()) usb_kill_urb(urb); else usb_unlink_urb(urb); - if (dev->isoc_ctl.transfer_buffer[i]) { + if (isoc_bufs->transfer_buffer[i]) { usb_free_coherent(dev->udev, urb->transfer_buffer_length, - dev->isoc_ctl.transfer_buffer[i], + isoc_bufs->transfer_buffer[i], urb->transfer_dma); } usb_free_urb(urb); - dev->isoc_ctl.urb[i] = NULL; + isoc_bufs->urb[i] = NULL; } - dev->isoc_ctl.transfer_buffer[i] = NULL; + isoc_bufs->transfer_buffer[i] = NULL; } - kfree(dev->isoc_ctl.urb); - kfree(dev->isoc_ctl.transfer_buffer); + kfree(isoc_bufs->urb); + kfree(isoc_bufs->transfer_buffer); - dev->isoc_ctl.urb = NULL; - dev->isoc_ctl.transfer_buffer = NULL; - dev->isoc_ctl.num_bufs = 0; + isoc_bufs->urb = NULL; + isoc_bufs->transfer_buffer = NULL; + isoc_bufs->num_bufs = 0; em28xx_capture_start(dev, 0); } EXPORT_SYMBOL_GPL(em28xx_uninit_isoc); /* - * Allocate URBs and start IRQ + * Allocate URBs */ -int em28xx_init_isoc(struct em28xx *dev, int max_packets, - int num_bufs, int max_pkt_size, - int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) +int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, + int max_packets, int num_bufs, int max_pkt_size) { - struct em28xx_dmaqueue *dma_q = &dev->vidq; - struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; + struct em28xx_usb_isoc_bufs *isoc_bufs; int i; int sb_size, pipe; struct urb *urb; int j, k; - int rc; - em28xx_isocdbg("em28xx: called em28xx_prepare_isoc\n"); + em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode); + + if (mode == EM28XX_DIGITAL_MODE) + isoc_bufs = &dev->isoc_ctl.digital_bufs; + else + isoc_bufs = &dev->isoc_ctl.analog_bufs; /* De-allocates all pending stuff */ - em28xx_uninit_isoc(dev); + em28xx_uninit_isoc(dev, mode); - dev->isoc_ctl.isoc_copy = isoc_copy; - dev->isoc_ctl.num_bufs = num_bufs; + isoc_bufs->num_bufs = num_bufs; - dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); - if (!dev->isoc_ctl.urb) { + isoc_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!isoc_bufs->urb) { em28xx_errdev("cannot alloc memory for usb buffers\n"); return -ENOMEM; } - dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs, - GFP_KERNEL); - if (!dev->isoc_ctl.transfer_buffer) { + isoc_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs, + GFP_KERNEL); + if (!isoc_bufs->transfer_buffer) { em28xx_errdev("cannot allocate memory for usb transfer\n"); - kfree(dev->isoc_ctl.urb); + kfree(isoc_bufs->urb); return -ENOMEM; } - dev->isoc_ctl.max_pkt_size = max_pkt_size; + isoc_bufs->max_pkt_size = max_pkt_size; + isoc_bufs->num_packets = max_packets; dev->isoc_ctl.vid_buf = NULL; dev->isoc_ctl.vbi_buf = NULL; - sb_size = max_packets * dev->isoc_ctl.max_pkt_size; + sb_size = isoc_bufs->num_packets * isoc_bufs->max_pkt_size; /* allocate urbs and transfer buffers */ - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - urb = usb_alloc_urb(max_packets, GFP_KERNEL); + for (i = 0; i < isoc_bufs->num_bufs; i++) { + urb = usb_alloc_urb(isoc_bufs->num_packets, GFP_KERNEL); if (!urb) { em28xx_err("cannot alloc isoc_ctl.urb %i\n", i); - em28xx_uninit_isoc(dev); + em28xx_uninit_isoc(dev, mode); return -ENOMEM; } - dev->isoc_ctl.urb[i] = urb; + isoc_bufs->urb[i] = urb; - dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev, + isoc_bufs->transfer_buffer[i] = usb_alloc_coherent(dev->udev, sb_size, GFP_KERNEL, &urb->transfer_dma); - if (!dev->isoc_ctl.transfer_buffer[i]) { + if (!isoc_bufs->transfer_buffer[i]) { em28xx_err("unable to allocate %i bytes for transfer" " buffer %i%s\n", sb_size, i, in_interrupt() ? " while in int" : ""); - em28xx_uninit_isoc(dev); + em28xx_uninit_isoc(dev, mode); return -ENOMEM; } - memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + memset(isoc_bufs->transfer_buffer[i], 0, sb_size); /* FIXME: this is a hack - should be 'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK' should also be using 'desc.bInterval' */ pipe = usb_rcvisocpipe(dev->udev, - dev->mode == EM28XX_ANALOG_MODE ? + mode == EM28XX_ANALOG_MODE ? EM28XX_EP_ANALOG : EM28XX_EP_DIGITAL); usb_fill_int_urb(urb, dev->udev, pipe, - dev->isoc_ctl.transfer_buffer[i], sb_size, + isoc_bufs->transfer_buffer[i], sb_size, em28xx_irq_callback, dev, 1); - urb->number_of_packets = max_packets; + urb->number_of_packets = isoc_bufs->num_packets; urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; k = 0; - for (j = 0; j < max_packets; j++) { + for (j = 0; j < isoc_bufs->num_packets; j++) { urb->iso_frame_desc[j].offset = k; urb->iso_frame_desc[j].length = - dev->isoc_ctl.max_pkt_size; - k += dev->isoc_ctl.max_pkt_size; + isoc_bufs->max_pkt_size; + k += isoc_bufs->max_pkt_size; } } + return 0; +} +EXPORT_SYMBOL_GPL(em28xx_alloc_isoc); + +/* + * Allocate URBs and start IRQ + */ +int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, + int max_packets, int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) +{ + struct em28xx_dmaqueue *dma_q = &dev->vidq; + struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; + struct em28xx_usb_isoc_bufs *isoc_bufs; + int i; + int rc; + int alloc; + + em28xx_isocdbg("em28xx: called em28xx_init_isoc in mode %d\n", mode); + + dev->isoc_ctl.isoc_copy = isoc_copy; + + if (mode == EM28XX_DIGITAL_MODE) { + isoc_bufs = &dev->isoc_ctl.digital_bufs; + /* no need to free/alloc isoc buffers in digital mode */ + alloc = 0; + } else { + isoc_bufs = &dev->isoc_ctl.analog_bufs; + alloc = 1; + } + + if (alloc) { + rc = em28xx_alloc_isoc(dev, mode, max_packets, + num_bufs, max_pkt_size); + if (rc) + return rc; + } + init_waitqueue_head(&dma_q->wq); init_waitqueue_head(&vbi_dma_q->wq); em28xx_capture_start(dev, 1); /* submit urbs and enables IRQ */ - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); + for (i = 0; i < isoc_bufs->num_bufs; i++) { + rc = usb_submit_urb(isoc_bufs->urb[i], GFP_ATOMIC); if (rc) { em28xx_err("submit of urb %i failed (error=%i)\n", i, rc); - em28xx_uninit_isoc(dev); + em28xx_uninit_isoc(dev, mode); return rc; } } diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index aabbf4854f66..503a8d5b5382 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -61,9 +61,6 @@ if (debug >= level) \ printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \ } while (0) -#define EM28XX_DVB_NUM_BUFS 5 -#define EM28XX_DVB_MAX_PACKETS 64 - struct em28xx_dvb { struct dvb_frontend *fe[2]; @@ -172,20 +169,21 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) max_dvb_packet_size = dev->dvb_max_pkt_size; if (max_dvb_packet_size < 0) return max_dvb_packet_size; - dprintk(1, "Using %d buffers each with %d bytes\n", + dprintk(1, "Using %d buffers each with %d x %d bytes\n", EM28XX_DVB_NUM_BUFS, + EM28XX_DVB_MAX_PACKETS, max_dvb_packet_size); - return em28xx_init_isoc(dev, EM28XX_DVB_MAX_PACKETS, - EM28XX_DVB_NUM_BUFS, max_dvb_packet_size, - em28xx_dvb_isoc_copy); + return em28xx_init_isoc(dev, EM28XX_DIGITAL_MODE, + EM28XX_DVB_MAX_PACKETS, EM28XX_DVB_NUM_BUFS, + max_dvb_packet_size, em28xx_dvb_isoc_copy); } static int em28xx_stop_streaming(struct em28xx_dvb *dvb) { struct em28xx *dev = dvb->adapter.priv; - em28xx_uninit_isoc(dev); + em28xx_capture_start(dev, 0); em28xx_set_mode(dev, EM28XX_SUSPEND); @@ -327,6 +325,19 @@ struct drxk_config hauppauge_930c_drxk = { .chunk_size = 56, }; +struct drxk_config maxmedia_ub425_tc_drxk = { + .adr = 0x29, + .single_master = 1, + .no_i2c_bridge = 1, +}; + +struct drxk_config pctv_520e_drxk = { + .adr = 0x29, + .single_master = 1, + .microcode_name = "dvb-demod-drxk-pctv.fw", + .chunk_size = 58, +}; + static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) { struct em28xx_dvb *dvb = fe->sec_priv; @@ -460,6 +471,33 @@ static void terratec_h5_init(struct em28xx *dev) em28xx_gpio_set(dev, terratec_h5_end); }; +static void pctv_520e_init(struct em28xx *dev) +{ + /* + * Init TDA8295(?) analog demodulator. Looks like I2C traffic to + * digital demodulator and tuner are routed via TDA8295. + */ + int i; + struct { + unsigned char r[4]; + int len; + } regs[] = { + {{ 0x06, 0x02, 0x00, 0x31 }, 4}, + {{ 0x01, 0x02 }, 2}, + {{ 0x01, 0x02, 0x00, 0xc6 }, 4}, + {{ 0x01, 0x00 }, 2}, + {{ 0x01, 0x00, 0xff, 0xaf }, 4}, + {{ 0x01, 0x00, 0x03, 0xa0 }, 4}, + {{ 0x01, 0x00 }, 2}, + {{ 0x01, 0x00, 0x73, 0xaf }, 4}, + }; + + dev->i2c_client.addr = 0x82 >> 1; /* 0x41 */ + + for (i = 0; i < ARRAY_SIZE(regs); i++) + i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len); +}; + static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe) { /* Values extracted from a USB trace of the Terratec Windows driver */ @@ -938,6 +976,48 @@ static int em28xx_dvb_init(struct em28xx *dev) dvb_attach(a8293_attach, dvb->fe[0], &dev->i2c_adap, &em28xx_a8293_config); break; + case EM2874_BOARD_MAXMEDIA_UB425_TC: + /* attach demodulator */ + dvb->fe[0] = dvb_attach(drxk_attach, &maxmedia_ub425_tc_drxk, + &dev->i2c_adap); + + if (dvb->fe[0]) { + /* disable I2C-gate */ + dvb->fe[0]->ops.i2c_gate_ctrl = NULL; + + /* attach tuner */ + if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0], + &dev->i2c_adap, 0x60)) { + dvb_frontend_detach(dvb->fe[0]); + result = -EINVAL; + goto out_free; + } + } + + /* TODO: we need drx-3913k firmware in order to support DVB-T */ + em28xx_info("MaxMedia UB425-TC: only DVB-C supported by that " \ + "driver version\n"); + + break; + case EM2884_BOARD_PCTV_510E: + case EM2884_BOARD_PCTV_520E: + pctv_520e_init(dev); + + /* attach demodulator */ + dvb->fe[0] = dvb_attach(drxk_attach, &pctv_520e_drxk, + &dev->i2c_adap); + + if (dvb->fe[0]) { + /* attach tuner */ + if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, + &dev->i2c_adap, + &em28xx_cxd2820r_tda18271_config)) { + dvb_frontend_detach(dvb->fe[0]); + result = -EINVAL; + goto out_free; + } + } + break; default: em28xx_errdev("/2: The frontend of your DVB/ATSC card" " isn't supported yet\n"); diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index 36f5a9bc8b76..a88e169dba23 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -41,14 +41,6 @@ static unsigned int i2c_debug; module_param(i2c_debug, int, 0644); MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); - -#define dprintk1(lvl, fmt, args...) \ -do { \ - if (i2c_debug >= lvl) { \ - printk(fmt, ##args); \ - } \ -} while (0) - #define dprintk2(lvl, fmt, args...) \ do { \ if (i2c_debug >= lvl) { \ diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 613300b51a9e..324b695c0724 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -760,17 +760,19 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, goto fail; } - if (!dev->isoc_ctl.num_bufs) + if (!dev->isoc_ctl.analog_bufs.num_bufs) urb_init = 1; if (urb_init) { if (em28xx_vbi_supported(dev) == 1) - rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS, + rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, + EM28XX_NUM_PACKETS, EM28XX_NUM_BUFS, dev->max_pkt_size, em28xx_isoc_copy_vbi); else - rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS, + rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, + EM28XX_NUM_PACKETS, EM28XX_NUM_BUFS, dev->max_pkt_size, em28xx_isoc_copy); @@ -2267,7 +2269,7 @@ static int em28xx_v4l2_close(struct file *filp) v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); /* do this before setting alternate! */ - em28xx_uninit_isoc(dev); + em28xx_uninit_isoc(dev, EM28XX_ANALOG_MODE); em28xx_set_mode(dev, EM28XX_SUSPEND); /* set alternate 0 */ diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 22e252bcc41e..2868b19f8b54 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -125,6 +125,9 @@ #define EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C 81 #define EM2884_BOARD_CINERGY_HTC_STICK 82 #define EM2860_BOARD_HT_VIDBOX_NW03 83 +#define EM2874_BOARD_MAXMEDIA_UB425_TC 84 +#define EM2884_BOARD_PCTV_510E 85 +#define EM2884_BOARD_PCTV_520E 86 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -151,12 +154,14 @@ /* number of buffers for isoc transfers */ #define EM28XX_NUM_BUFS 5 +#define EM28XX_DVB_NUM_BUFS 5 /* number of packets for each buffer windows requests only 64 packets .. so we better do the same this is what I found out for all alternate numbers there! */ #define EM28XX_NUM_PACKETS 64 +#define EM28XX_DVB_MAX_PACKETS 64 #define EM28XX_INTERLACED_DEFAULT 1 @@ -197,10 +202,13 @@ enum em28xx_mode { struct em28xx; -struct em28xx_usb_isoc_ctl { +struct em28xx_usb_isoc_bufs { /* max packet size of isoc transaction */ int max_pkt_size; + /* number of packets in each buffer */ + int num_packets; + /* number of allocated urbs */ int num_bufs; @@ -209,6 +217,14 @@ struct em28xx_usb_isoc_ctl { /* transfer buffers for isoc transfer */ char **transfer_buffer; +}; + +struct em28xx_usb_isoc_ctl { + /* isoc transfer buffers for analog mode */ + struct em28xx_usb_isoc_bufs analog_bufs; + + /* isoc transfer buffers for digital mode */ + struct em28xx_usb_isoc_bufs digital_bufs; /* Last buffer command and region */ u8 cmd; @@ -600,9 +616,6 @@ struct em28xx { unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ int dvb_alt; /* alternate for DVB */ unsigned int dvb_max_pkt_size; /* wMaxPacketSize for DVB */ - struct urb *urb[EM28XX_NUM_BUFS]; /* urb for isoc transfers */ - char *transfer_buffer[EM28XX_NUM_BUFS]; /* transfer buffers for isoc - transfer */ char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */ /* helper funcs that call usb_control_msg */ @@ -676,10 +689,12 @@ int em28xx_vbi_supported(struct em28xx *dev); int em28xx_set_outfmt(struct em28xx *dev); int em28xx_resolution_set(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); -int em28xx_init_isoc(struct em28xx *dev, int max_packets, - int num_bufs, int max_pkt_size, +int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, + int max_packets, int num_bufs, int max_pkt_size); +int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, + int max_packets, int num_bufs, int max_pkt_size, int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); -void em28xx_uninit_isoc(struct em28xx *dev); +void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode); int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev); int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode); int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio); diff --git a/drivers/media/video/gspca/gl860/Makefile b/drivers/media/video/gspca/gl860/Makefile index f511eccdfd9c..773ea3426561 100644 --- a/drivers/media/video/gspca/gl860/Makefile +++ b/drivers/media/video/gspca/gl860/Makefile @@ -6,5 +6,5 @@ gspca_gl860-objs := gl860.o \ gl860-ov9655.o \ gl860-mi2020.o -ccflags-y += -Idrivers/media/video/gspca +ccflags-y += -I$(srctree)/drivers/media/video/gspca diff --git a/drivers/media/video/gspca/m5602/Makefile b/drivers/media/video/gspca/m5602/Makefile index 7f52961f439c..575b75bacb62 100644 --- a/drivers/media/video/gspca/m5602/Makefile +++ b/drivers/media/video/gspca/m5602/Makefile @@ -8,4 +8,4 @@ gspca_m5602-objs := m5602_core.o \ m5602_s5k83a.o \ m5602_s5k4aa.o -ccflags-y += -Idrivers/media/video/gspca +ccflags-y += -I$(srctree)/drivers/media/video/gspca diff --git a/drivers/media/video/gspca/ov534_9.c b/drivers/media/video/gspca/ov534_9.c index fbfa02affa13..e6601b886032 100644 --- a/drivers/media/video/gspca/ov534_9.c +++ b/drivers/media/video/gspca/ov534_9.c @@ -1107,16 +1107,34 @@ static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 val; + s8 sval; if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS)) return; - val = sd->ctrls[BRIGHTNESS].val; - if (val < 8) - val = 15 - val; /* f .. 8 */ - else - val = val - 8; /* 0 .. 7 */ - sccb_write(gspca_dev, 0x55, /* brtn - brightness adjustment */ - 0x0f | (val << 4)); + if (sd->sensor == SENSOR_OV562x) { + sval = sd->ctrls[BRIGHTNESS].val; + val = 0x76; + val += sval; + sccb_write(gspca_dev, 0x24, val); + val = 0x6a; + val += sval; + sccb_write(gspca_dev, 0x25, val); + if (sval < -40) + val = 0x71; + else if (sval < 20) + val = 0x94; + else + val = 0xe6; + sccb_write(gspca_dev, 0x26, val); + } else { + val = sd->ctrls[BRIGHTNESS].val; + if (val < 8) + val = 15 - val; /* f .. 8 */ + else + val = val - 8; /* 0 .. 7 */ + sccb_write(gspca_dev, 0x55, /* brtn - brightness adjustment */ + 0x0f | (val << 4)); + } } static void setcontrast(struct gspca_dev *gspca_dev) @@ -1339,7 +1357,16 @@ static int sd_init(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x56, 0x17); } else if ((sensor_id & 0xfff0) == 0x5620) { sd->sensor = SENSOR_OV562x; - + gspca_dev->ctrl_dis = (1 << CONTRAST) | + (1 << AUTOGAIN) | + (1 << EXPOSURE) | + (1 << SHARPNESS) | + (1 << SATUR) | + (1 << LIGHTFREQ); + + sd->ctrls[BRIGHTNESS].min = -90; + sd->ctrls[BRIGHTNESS].max = 90; + sd->ctrls[BRIGHTNESS].def = 0; gspca_dev->cam.cam_mode = ov562x_mode; gspca_dev->cam.nmodes = ARRAY_SIZE(ov562x_mode); @@ -1360,8 +1387,12 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - if (sd->sensor == SENSOR_OV971x || sd->sensor == SENSOR_OV562x) + if (sd->sensor == SENSOR_OV971x) return gspca_dev->usb_err; + else if (sd->sensor == SENSOR_OV562x) { + setbrightness(gspca_dev); + return gspca_dev->usb_err; + } switch (gspca_dev->curr_mode) { case QVGA_MODE: /* 320x240 */ sccb_w_array(gspca_dev, ov965x_start_1_vga, diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index 9db2b34d172c..30662fccb0cf 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -1,8 +1,8 @@ /* - * Pixart PAC7302 library - * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li + * Pixart PAC7302 driver * - * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * Copyright (C) 2008-2012 Jean-Francois Moine <http://moinejf.free.fr> + * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li * * Separated from Pixart PAC7311 library by Márton Németh * Camera button input handling by Márton Németh <nm127@freemail.hu> @@ -63,67 +63,61 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define MODULE_NAME "pac7302" - #include <linux/input.h> #include <media/v4l2-chip-ident.h> #include "gspca.h" +/* Include pac common sof detection functions */ +#include "pac_common.h" -MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); +MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, " + "Thomas Kaiser thomas@kaiser-linux.li"); MODULE_DESCRIPTION("Pixart PAC7302"); MODULE_LICENSE("GPL"); +enum e_ctrl { + BRIGHTNESS, + CONTRAST, + COLORS, + WHITE_BALANCE, + RED_BALANCE, + BLUE_BALANCE, + GAIN, + AUTOGAIN, + EXPOSURE, + VFLIP, + HFLIP, + NCTRLS /* number of controls */ +}; + /* specific webcam descriptor for pac7302 */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - unsigned char brightness; - unsigned char contrast; - unsigned char colors; - unsigned char white_balance; - unsigned char red_balance; - unsigned char blue_balance; - unsigned char gain; - unsigned char autogain; - unsigned short exposure; - __u8 hflip; - __u8 vflip; + struct gspca_ctrl ctrls[NCTRLS]; + u8 flags; #define FL_HFLIP 0x01 /* mirrored by default */ #define FL_VFLIP 0x02 /* vertical flipped by default */ u8 sof_read; - u8 autogain_ignore_frames; + s8 autogain_ignore_frames; atomic_t avg_lum; }; /* V4L2 controls supported by the driver */ -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); -static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); +static void setbrightcont(struct gspca_dev *gspca_dev); +static void setcolors(struct gspca_dev *gspca_dev); +static void setwhitebalance(struct gspca_dev *gspca_dev); +static void setredbalance(struct gspca_dev *gspca_dev); +static void setbluebalance(struct gspca_dev *gspca_dev); +static void setgain(struct gspca_dev *gspca_dev); +static void setexposure(struct gspca_dev *gspca_dev); +static void setautogain(struct gspca_dev *gspca_dev); +static void sethvflip(struct gspca_dev *gspca_dev); static const struct ctrl sd_ctrls[] = { - { +[BRIGHTNESS] = { { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, @@ -132,13 +126,11 @@ static const struct ctrl sd_ctrls[] = { #define BRIGHTNESS_MAX 0x20 .maximum = BRIGHTNESS_MAX, .step = 1, -#define BRIGHTNESS_DEF 0x10 - .default_value = BRIGHTNESS_DEF, + .default_value = 0x10, }, - .set = sd_setbrightness, - .get = sd_getbrightness, + .set_control = setbrightcont }, - { +[CONTRAST] = { { .id = V4L2_CID_CONTRAST, .type = V4L2_CTRL_TYPE_INTEGER, @@ -147,13 +139,11 @@ static const struct ctrl sd_ctrls[] = { #define CONTRAST_MAX 255 .maximum = CONTRAST_MAX, .step = 1, -#define CONTRAST_DEF 127 - .default_value = CONTRAST_DEF, + .default_value = 127, }, - .set = sd_setcontrast, - .get = sd_getcontrast, + .set_control = setbrightcont }, - { +[COLORS] = { { .id = V4L2_CID_SATURATION, .type = V4L2_CTRL_TYPE_INTEGER, @@ -162,13 +152,11 @@ static const struct ctrl sd_ctrls[] = { #define COLOR_MAX 255 .maximum = COLOR_MAX, .step = 1, -#define COLOR_DEF 127 - .default_value = COLOR_DEF, + .default_value = 127 }, - .set = sd_setcolors, - .get = sd_getcolors, + .set_control = setcolors }, - { +[WHITE_BALANCE] = { { .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -176,13 +164,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 255, .step = 1, -#define WHITEBALANCE_DEF 4 - .default_value = WHITEBALANCE_DEF, + .default_value = 4, }, - .set = sd_setwhitebalance, - .get = sd_getwhitebalance, + .set_control = setwhitebalance }, - { +[RED_BALANCE] = { { .id = V4L2_CID_RED_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -190,13 +176,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 3, .step = 1, -#define REDBALANCE_DEF 1 - .default_value = REDBALANCE_DEF, + .default_value = 1, }, - .set = sd_setredbalance, - .get = sd_getredbalance, + .set_control = setredbalance }, - { +[BLUE_BALANCE] = { { .id = V4L2_CID_BLUE_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -204,29 +188,25 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 3, .step = 1, -#define BLUEBALANCE_DEF 1 - .default_value = BLUEBALANCE_DEF, + .default_value = 1, }, - .set = sd_setbluebalance, - .get = sd_getbluebalance, + .set_control = setbluebalance }, - { +[GAIN] = { { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Gain", .minimum = 0, -#define GAIN_MAX 255 - .maximum = GAIN_MAX, + .maximum = 255, .step = 1, #define GAIN_DEF 127 #define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */ .default_value = GAIN_DEF, }, - .set = sd_setgain, - .get = sd_getgain, + .set_control = setgain }, - { +[EXPOSURE] = { { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -238,10 +218,9 @@ static const struct ctrl sd_ctrls[] = { #define EXPOSURE_KNEE 133 /* 66 ms / 15 fps */ .default_value = EXPOSURE_DEF, }, - .set = sd_setexposure, - .get = sd_getexposure, + .set_control = setexposure }, - { +[AUTOGAIN] = { { .id = V4L2_CID_AUTOGAIN, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -252,10 +231,9 @@ static const struct ctrl sd_ctrls[] = { #define AUTOGAIN_DEF 1 .default_value = AUTOGAIN_DEF, }, - .set = sd_setautogain, - .get = sd_getautogain, + .set_control = setautogain, }, - { +[HFLIP] = { { .id = V4L2_CID_HFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -263,13 +241,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define HFLIP_DEF 0 - .default_value = HFLIP_DEF, + .default_value = 0, }, - .set = sd_sethflip, - .get = sd_gethflip, + .set_control = sethvflip, }, - { +[VFLIP] = { { .id = V4L2_CID_VFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -277,11 +253,9 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define VFLIP_DEF 0 - .default_value = VFLIP_DEF, + .default_value = 0, }, - .set = sd_setvflip, - .get = sd_getvflip, + .set_control = sethvflip }, }; @@ -290,21 +264,21 @@ static const struct v4l2_pix_format vga_mode[] = { .bytesperline = 640, .sizeimage = 640 * 480 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, + }, }; #define LOAD_PAGE3 255 #define END_OF_SEQUENCE 0 /* pac 7302 */ -static const __u8 init_7302[] = { +static const u8 init_7302[] = { /* index,value */ 0xff, 0x01, /* page 1 */ 0x78, 0x00, /* deactivate */ 0xff, 0x01, 0x78, 0x40, /* led off */ }; -static const __u8 start_7302[] = { +static const u8 start_7302[] = { /* index, len, [value]* */ 0xff, 1, 0x00, /* page 0 */ 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80, @@ -319,7 +293,7 @@ static const __u8 start_7302[] = { 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11, 0x00, 0x54, 0x11, 0x55, 1, 0x00, - 0x62, 4, 0x10, 0x1e, 0x1e, 0x18, + 0x62, 4, 0x10, 0x1e, 0x1e, 0x18, 0x6b, 1, 0x00, 0x6e, 3, 0x08, 0x06, 0x00, 0x72, 3, 0x00, 0xff, 0x00, @@ -370,7 +344,7 @@ static const __u8 start_7302[] = { #define SKIP 0xaa /* page 3 - the value SKIP says skip the index - see reg_w_page() */ -static const __u8 page3_7302[] = { +static const u8 page3_7302[] = { 0x90, 0x40, 0x03, 0x00, 0xc0, 0x01, 0x14, 0x16, 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -394,7 +368,7 @@ static const __u8 page3_7302[] = { }; static void reg_w_buf(struct gspca_dev *gspca_dev, - __u8 index, + u8 index, const u8 *buffer, int len) { int ret; @@ -410,7 +384,7 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, index, gspca_dev->usb_buf, len, 500); if (ret < 0) { - pr_err("reg_w_buf failed index 0x%02x, error %d\n", + pr_err("reg_w_buf failed i: %02x error %d\n", index, ret); gspca_dev->usb_err = ret; } @@ -418,8 +392,8 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, static void reg_w(struct gspca_dev *gspca_dev, - __u8 index, - __u8 value) + u8 index, + u8 value) { int ret; @@ -433,14 +407,14 @@ static void reg_w(struct gspca_dev *gspca_dev, 0, index, gspca_dev->usb_buf, 1, 500); if (ret < 0) { - pr_err("reg_w() failed index 0x%02x, value 0x%02x, error %d\n", + pr_err("reg_w() failed i: %02x v: %02x error %d\n", index, value, ret); gspca_dev->usb_err = ret; } } static void reg_w_seq(struct gspca_dev *gspca_dev, - const __u8 *seq, int len) + const u8 *seq, int len) { while (--len >= 0) { reg_w(gspca_dev, seq[0], seq[1]); @@ -450,7 +424,7 @@ static void reg_w_seq(struct gspca_dev *gspca_dev, /* load the beginning of a page */ static void reg_w_page(struct gspca_dev *gspca_dev, - const __u8 *page, int len) + const u8 *page, int len) { int index; int ret = 0; @@ -468,7 +442,7 @@ static void reg_w_page(struct gspca_dev *gspca_dev, 0, index, gspca_dev->usb_buf, 1, 500); if (ret < 0) { - pr_err("reg_w_page() failed index 0x%02x, value 0x%02x, error %d\n", + pr_err("reg_w_page() failed i: %02x v: %02x error %d\n", index, page[index], ret); gspca_dev->usb_err = ret; break; @@ -478,8 +452,8 @@ static void reg_w_page(struct gspca_dev *gspca_dev, /* output a variable sequence */ static void reg_w_var(struct gspca_dev *gspca_dev, - const __u8 *seq, - const __u8 *page3, unsigned int page3_len) + const u8 *seq, + const u8 *page3, unsigned int page3_len) { int index, len; @@ -493,11 +467,13 @@ static void reg_w_var(struct gspca_dev *gspca_dev, reg_w_page(gspca_dev, page3, page3_len); break; default: +#ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { PDEBUG(D_ERR|D_STREAM, "Incorrect variable sequence"); return; } +#endif while (len > 0) { if (len < 8) { reg_w_buf(gspca_dev, @@ -524,21 +500,11 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; - PDEBUG(D_CONF, "Find Sensor PAC7302"); cam->cam_mode = vga_mode; /* only 640x480 */ cam->nmodes = ARRAY_SIZE(vga_mode); - sd->brightness = BRIGHTNESS_DEF; - sd->contrast = CONTRAST_DEF; - sd->colors = COLOR_DEF; - sd->white_balance = WHITEBALANCE_DEF; - sd->red_balance = REDBALANCE_DEF; - sd->blue_balance = BLUEBALANCE_DEF; - sd->gain = GAIN_DEF; - sd->exposure = EXPOSURE_DEF; - sd->autogain = AUTOGAIN_DEF; - sd->hflip = HFLIP_DEF; - sd->vflip = VFLIP_DEF; + gspca_dev->cam.ctrls = sd->ctrls; + sd->flags = id->driver_info; return 0; } @@ -548,19 +514,19 @@ static void setbrightcont(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int i, v; - static const __u8 max[10] = + static const u8 max[10] = {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb, 0xd4, 0xec}; - static const __u8 delta[10] = + static const u8 delta[10] = {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17, 0x11, 0x0b}; reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ for (i = 0; i < 10; i++) { v = max[i]; - v += (sd->brightness - BRIGHTNESS_MAX) + v += (sd->ctrls[BRIGHTNESS].val - BRIGHTNESS_MAX) * 150 / BRIGHTNESS_MAX; /* 200 ? */ - v -= delta[i] * sd->contrast / CONTRAST_MAX; + v -= delta[i] * sd->ctrls[CONTRAST].val / CONTRAST_MAX; if (v < 0) v = 0; else if (v > 0xff) @@ -584,12 +550,11 @@ static void setcolors(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x11, 0x01); reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ for (i = 0; i < 9; i++) { - v = a[i] * sd->colors / COLOR_MAX + b[i]; + v = a[i] * sd->ctrls[COLORS].val / COLOR_MAX + b[i]; reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07); reg_w(gspca_dev, 0x0f + 2 * i + 1, v); } reg_w(gspca_dev, 0xdc, 0x01); - PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors); } static void setwhitebalance(struct gspca_dev *gspca_dev) @@ -597,10 +562,9 @@ static void setwhitebalance(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - reg_w(gspca_dev, 0xc6, sd->white_balance); + reg_w(gspca_dev, 0xc6, sd->ctrls[WHITE_BALANCE].val); reg_w(gspca_dev, 0xdc, 0x01); - PDEBUG(D_CONF|D_STREAM, "white_balance: %i", sd->white_balance); } static void setredbalance(struct gspca_dev *gspca_dev) @@ -608,10 +572,9 @@ static void setredbalance(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - reg_w(gspca_dev, 0xc5, sd->red_balance); + reg_w(gspca_dev, 0xc5, sd->ctrls[RED_BALANCE].val); reg_w(gspca_dev, 0xdc, 0x01); - PDEBUG(D_CONF|D_STREAM, "red_balance: %i", sd->red_balance); } static void setbluebalance(struct gspca_dev *gspca_dev) @@ -619,10 +582,9 @@ static void setbluebalance(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - reg_w(gspca_dev, 0xc7, sd->blue_balance); + reg_w(gspca_dev, 0xc7, sd->ctrls[BLUE_BALANCE].val); reg_w(gspca_dev, 0xdc, 0x01); - PDEBUG(D_CONF|D_STREAM, "blue_balance: %i", sd->blue_balance); } static void setgain(struct gspca_dev *gspca_dev) @@ -630,7 +592,7 @@ static void setgain(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - reg_w(gspca_dev, 0x10, sd->gain >> 3); + reg_w(gspca_dev, 0x10, sd->ctrls[GAIN].val >> 3); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); @@ -639,13 +601,13 @@ static void setgain(struct gspca_dev *gspca_dev) static void setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 clockdiv; - __u16 exposure; + u8 clockdiv; + u16 exposure; /* register 2 of frame 3 contains the clock divider configuring the no fps according to the formula: 90 / reg. sd->exposure is the desired exposure time in 0.5 ms. */ - clockdiv = (90 * sd->exposure + 1999) / 2000; + clockdiv = (90 * sd->ctrls[EXPOSURE].val + 1999) / 2000; /* Note clockdiv = 3 also works, but when running at 30 fps, depending on the scene being recorded, the camera switches to another @@ -664,7 +626,7 @@ static void setexposure(struct gspca_dev *gspca_dev) /* frame exposure time in ms = 1000 * clockdiv / 90 -> exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90) */ - exposure = (sd->exposure * 45 * 448) / (1000 * clockdiv); + exposure = (sd->ctrls[EXPOSURE].val * 45 * 448) / (1000 * clockdiv); /* 0 = use full frametime, 448 = no exposure, reverse it */ exposure = 448 - exposure; @@ -677,15 +639,35 @@ static void setexposure(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x11, 0x01); } +static void setautogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* when switching to autogain set defaults to make sure + we are on a valid point of the autogain gain / + exposure knee graph, and give this change time to + take effect before doing autogain. */ + if (sd->ctrls[AUTOGAIN].val) { + sd->ctrls[EXPOSURE].val = EXPOSURE_DEF; + sd->ctrls[GAIN].val = GAIN_DEF; + sd->autogain_ignore_frames = + PAC_AUTOGAIN_IGNORE_FRAMES; + } else { + sd->autogain_ignore_frames = -1; + } + setexposure(gspca_dev); + setgain(gspca_dev); +} + static void sethvflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 data, hflip, vflip; - hflip = sd->hflip; + hflip = sd->ctrls[HFLIP].val; if (sd->flags & FL_HFLIP) hflip = !hflip; - vflip = sd->vflip; + vflip = sd->ctrls[VFLIP].val; if (sd->flags & FL_VFLIP) vflip = !vflip; @@ -708,8 +690,6 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - sd->sof_read = 0; - reg_w_var(gspca_dev, start_7302, page3_7302, sizeof(page3_7302)); setbrightcont(gspca_dev); @@ -717,15 +697,13 @@ static int sd_start(struct gspca_dev *gspca_dev) setwhitebalance(gspca_dev); setredbalance(gspca_dev); setbluebalance(gspca_dev); - setgain(gspca_dev); - setexposure(gspca_dev); + setautogain(gspca_dev); sethvflip(gspca_dev); /* only resolution 640x480 is supported for pac7302 */ sd->sof_read = 0; - sd->autogain_ignore_frames = 0; - atomic_set(&sd->avg_lum, -1); + atomic_set(&sd->avg_lum, 270 + sd->ctrls[BRIGHTNESS].val); /* start stream */ reg_w(gspca_dev, 0xff, 0x01); @@ -751,8 +729,10 @@ static void sd_stop0(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x78, 0x40); } -/* Include pac common sof detection functions */ -#include "pac_common.h" +/* !! coarse_grained_expo_autogain is not used !! */ +#define exp_too_low_cnt flags +#define exp_too_high_cnt sof_read +#include "autogain_functions.h" static void do_autogain(struct gspca_dev *gspca_dev) { @@ -761,65 +741,44 @@ static void do_autogain(struct gspca_dev *gspca_dev) int desired_lum; const int deadzone = 30; - if (avg_lum == -1) + if (sd->autogain_ignore_frames < 0) return; - desired_lum = 270 + sd->brightness; - - if (sd->autogain_ignore_frames > 0) + if (sd->autogain_ignore_frames > 0) { sd->autogain_ignore_frames--; - else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum, - deadzone, GAIN_KNEE, EXPOSURE_KNEE)) + } else { + desired_lum = 270 + sd->ctrls[BRIGHTNESS].val; + + auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum, + deadzone, GAIN_KNEE, EXPOSURE_KNEE); sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; + } } -/* JPEG header, part 1 */ -static const unsigned char pac_jpeg_header1[] = { - 0xff, 0xd8, /* SOI: Start of Image */ - - 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */ - 0x00, 0x11, /* length = 17 bytes (including this length field) */ - 0x08 /* Precision: 8 */ - /* 2 bytes is placed here: number of image lines */ - /* 2 bytes is placed here: samples per line */ -}; - -/* JPEG header, continued */ -static const unsigned char pac_jpeg_header2[] = { - 0x03, /* Number of image components: 3 */ - 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */ - 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */ - 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */ - - 0xff, 0xda, /* SOS: Start Of Scan */ - 0x00, 0x0c, /* length = 12 bytes (including this length field) */ - 0x03, /* number of components: 3 */ - 0x01, 0x00, /* selector 1, table 0x00 */ - 0x02, 0x11, /* selector 2, table 0x11 */ - 0x03, 0x11, /* selector 3, table 0x11 */ - 0x00, 0x3f, /* Spectral selection: 0 .. 63 */ - 0x00 /* Successive approximation: 0 */ +/* JPEG header */ +static const u8 jpeg_header[] = { + 0xff, 0xd8, /* SOI: Start of Image */ + + 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */ + 0x00, 0x11, /* length = 17 bytes (including this length field) */ + 0x08, /* Precision: 8 */ + 0x02, 0x80, /* height = 640 (image rotated) */ + 0x01, 0xe0, /* width = 480 */ + 0x03, /* Number of image components: 3 */ + 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */ + 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */ + 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */ + + 0xff, 0xda, /* SOS: Start Of Scan */ + 0x00, 0x0c, /* length = 12 bytes (including this length field) */ + 0x03, /* number of components: 3 */ + 0x01, 0x00, /* selector 1, table 0x00 */ + 0x02, 0x11, /* selector 2, table 0x11 */ + 0x03, 0x11, /* selector 3, table 0x11 */ + 0x00, 0x3f, /* Spectral selection: 0 .. 63 */ + 0x00 /* Successive approximation: 0 */ }; -static void pac_start_frame(struct gspca_dev *gspca_dev, - __u16 lines, __u16 samples_per_line) -{ - unsigned char tmpbuf[4]; - - gspca_frame_add(gspca_dev, FIRST_PACKET, - pac_jpeg_header1, sizeof(pac_jpeg_header1)); - - tmpbuf[0] = lines >> 8; - tmpbuf[1] = lines & 0xff; - tmpbuf[2] = samples_per_line >> 8; - tmpbuf[3] = samples_per_line & 0xff; - - gspca_frame_add(gspca_dev, INTER_PACKET, - tmpbuf, sizeof(tmpbuf)); - gspca_frame_add(gspca_dev, INTER_PACKET, - pac_jpeg_header2, sizeof(pac_jpeg_header2)); -} - /* this function is run at interrupt level */ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ @@ -827,7 +786,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; u8 *image; - unsigned char *sof; + u8 *sof; sof = pac_find_sof(&sd->sof_read, data, len); if (sof) { @@ -864,234 +823,21 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, n >= lum_offset) atomic_set(&sd->avg_lum, data[-lum_offset] + data[-lum_offset + 1]); - else - atomic_set(&sd->avg_lum, -1); /* Start the new frame with the jpeg header */ /* The PAC7302 has the image rotated 90 degrees */ - pac_start_frame(gspca_dev, - gspca_dev->width, gspca_dev->height); + gspca_frame_add(gspca_dev, FIRST_PACKET, + jpeg_header, sizeof jpeg_header); } gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->brightness = val; - if (gspca_dev->streaming) - setbrightcont(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->brightness; - return 0; -} - -static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->contrast = val; - if (gspca_dev->streaming) - setbrightcont(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->contrast; - return 0; -} - -static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->colors = val; - if (gspca_dev->streaming) - setcolors(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->colors; - return 0; -} - -static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->white_balance = val; - if (gspca_dev->streaming) - setwhitebalance(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->white_balance; - return 0; -} - -static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->red_balance = val; - if (gspca_dev->streaming) - setredbalance(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->red_balance; - return 0; -} - -static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->blue_balance = val; - if (gspca_dev->streaming) - setbluebalance(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->blue_balance; - return 0; -} - -static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->gain = val; - if (gspca_dev->streaming) - setgain(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->gain; - return 0; -} - -static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->exposure = val; - if (gspca_dev->streaming) - setexposure(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->exposure; - return 0; -} - -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->autogain = val; - /* when switching to autogain set defaults to make sure - we are on a valid point of the autogain gain / - exposure knee graph, and give this change time to - take effect before doing autogain. */ - if (sd->autogain) { - sd->exposure = EXPOSURE_DEF; - sd->gain = GAIN_DEF; - if (gspca_dev->streaming) { - sd->autogain_ignore_frames = - PAC_AUTOGAIN_IGNORE_FRAMES; - setexposure(gspca_dev); - setgain(gspca_dev); - } - } - - return gspca_dev->usb_err; -} - -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->autogain; - return 0; -} - -static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->hflip = val; - if (gspca_dev->streaming) - sethvflip(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->hflip; - return 0; -} - -static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->vflip = val; - if (gspca_dev->streaming) - sethvflip(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->vflip; - return 0; -} - #ifdef CONFIG_VIDEO_ADV_DEBUG static int sd_dbg_s_register(struct gspca_dev *gspca_dev, struct v4l2_dbg_register *reg) { - __u8 index; - __u8 value; + u8 index; + u8 value; /* reg->reg: bit0..15: reserved for register index (wIndex is 16bit long on the USB bus) @@ -1103,8 +849,8 @@ static int sd_dbg_s_register(struct gspca_dev *gspca_dev, ) { /* Currently writing to page 0 is only supported. */ /* reg_w() only supports 8bit index */ - index = reg->reg & 0x000000ff; - value = reg->val & 0x000000ff; + index = reg->reg; + value = reg->val; /* Note that there shall be no access to other page by any other function between the page swith and @@ -1165,7 +911,7 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, /* sub-driver description for pac7302 */ static const struct sd_desc sd_desc = { - .name = MODULE_NAME, + .name = KBUILD_MODNAME, .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, @@ -1187,6 +933,7 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x06f8, 0x3009)}, + {USB_DEVICE(0x06f8, 0x301b)}, {USB_DEVICE(0x093a, 0x2620)}, {USB_DEVICE(0x093a, 0x2621)}, {USB_DEVICE(0x093a, 0x2622), .driver_info = FL_VFLIP}, @@ -1211,7 +958,7 @@ static int sd_probe(struct usb_interface *intf, } static struct usb_driver sd_driver = { - .name = MODULE_NAME, + .name = KBUILD_MODNAME, .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index 9e198b45c3c8..7e71aa2d2522 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -1,5 +1,7 @@ /* * Sonix sn9c201 sn9c202 library + * + * Copyright (C) 2012 Jean-Francois Moine <http://moinejf.free.fr> * Copyright (C) 2008-2009 microdia project <microdia@googlegroups.com> * Copyright (C) 2009 Brian Johnson <brijohn@gmail.com> * @@ -33,8 +35,6 @@ MODULE_AUTHOR("Brian Johnson <brijohn@gmail.com>, " MODULE_DESCRIPTION("GSPCA/SN9C20X USB Camera Driver"); MODULE_LICENSE("GPL"); -#define MODULE_NAME "sn9c20x" - /* * Pixel format private data */ @@ -66,10 +66,37 @@ MODULE_LICENSE("GPL"); #define LED_REVERSE 0x2 /* some cameras unset gpio to turn on leds */ #define FLIP_DETECT 0x4 +enum e_ctrl { + BRIGHTNESS, + CONTRAST, + SATURATION, + HUE, + GAMMA, + BLUE, + RED, + VFLIP, + HFLIP, + EXPOSURE, + GAIN, + AUTOGAIN, + QUALITY, + NCTRLS /* number of controls */ +}; + /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; + struct gspca_ctrl ctrls[NCTRLS]; + + struct work_struct work; + struct workqueue_struct *work_thread; + + u32 pktsz; /* (used by pkt_scan) */ + u16 npkt; + s8 nchg; + u8 fmt; /* (used for JPEG QTAB update */ + #define MIN_AVG_LUM 80 #define MAX_AVG_LUM 130 atomic_t avg_lum; @@ -77,31 +104,18 @@ struct sd { u8 older_step; u8 exposure_step; - u8 brightness; - u8 contrast; - u8 saturation; - s16 hue; - u8 gamma; - u8 red; - u8 blue; - - u8 hflip; - u8 vflip; - u8 gain; - u16 exposure; - u8 auto_exposure; - u8 i2c_addr; u8 sensor; u8 hstart; u8 vstart; u8 jpeg_hdr[JPEG_HDR_SZ]; - u8 quality; u8 flags; }; +static void qual_upd(struct work_struct *work); + struct i2c_reg_u8 { u8 reg; u8 val; @@ -112,31 +126,6 @@ struct i2c_reg_u16 { u16 val; }; -static int sd_setbrightness(struct gspca_dev *gspca_dev, s32 val); -static int sd_getbrightness(struct gspca_dev *gspca_dev, s32 *val); -static int sd_setcontrast(struct gspca_dev *gspca_dev, s32 val); -static int sd_getcontrast(struct gspca_dev *gspca_dev, s32 *val); -static int sd_setsaturation(struct gspca_dev *gspca_dev, s32 val); -static int sd_getsaturation(struct gspca_dev *gspca_dev, s32 *val); -static int sd_sethue(struct gspca_dev *gspca_dev, s32 val); -static int sd_gethue(struct gspca_dev *gspca_dev, s32 *val); -static int sd_setgamma(struct gspca_dev *gspca_dev, s32 val); -static int sd_getgamma(struct gspca_dev *gspca_dev, s32 *val); -static int sd_setredbalance(struct gspca_dev *gspca_dev, s32 val); -static int sd_getredbalance(struct gspca_dev *gspca_dev, s32 *val); -static int sd_setbluebalance(struct gspca_dev *gspca_dev, s32 val); -static int sd_getbluebalance(struct gspca_dev *gspca_dev, s32 *val); -static int sd_setvflip(struct gspca_dev *gspca_dev, s32 val); -static int sd_getvflip(struct gspca_dev *gspca_dev, s32 *val); -static int sd_sethflip(struct gspca_dev *gspca_dev, s32 val); -static int sd_gethflip(struct gspca_dev *gspca_dev, s32 *val); -static int sd_setgain(struct gspca_dev *gspca_dev, s32 val); -static int sd_getgain(struct gspca_dev *gspca_dev, s32 *val); -static int sd_setexposure(struct gspca_dev *gspca_dev, s32 val); -static int sd_getexposure(struct gspca_dev *gspca_dev, s32 *val); -static int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val); -static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val); - static const struct dmi_system_id flip_dmi_table[] = { { .ident = "MSI MS-1034", @@ -177,9 +166,16 @@ static const struct dmi_system_id flip_dmi_table[] = { {} }; -static const struct ctrl sd_ctrls[] = { - { -#define BRIGHTNESS_IDX 0 +static void set_cmatrix(struct gspca_dev *gspca_dev); +static void set_gamma(struct gspca_dev *gspca_dev); +static void set_redblue(struct gspca_dev *gspca_dev); +static void set_hvflip(struct gspca_dev *gspca_dev); +static void set_exposure(struct gspca_dev *gspca_dev); +static void set_gain(struct gspca_dev *gspca_dev); +static void set_quality(struct gspca_dev *gspca_dev); + +static const struct ctrl sd_ctrls[NCTRLS] = { +[BRIGHTNESS] = { { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, @@ -187,14 +183,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0xff, .step = 1, -#define BRIGHTNESS_DEFAULT 0x7f - .default_value = BRIGHTNESS_DEFAULT, + .default_value = 0x7f }, - .set = sd_setbrightness, - .get = sd_getbrightness, + .set_control = set_cmatrix }, - { -#define CONTRAST_IDX 1 +[CONTRAST] = { { .id = V4L2_CID_CONTRAST, .type = V4L2_CTRL_TYPE_INTEGER, @@ -202,14 +195,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0xff, .step = 1, -#define CONTRAST_DEFAULT 0x7f - .default_value = CONTRAST_DEFAULT, + .default_value = 0x7f }, - .set = sd_setcontrast, - .get = sd_getcontrast, + .set_control = set_cmatrix }, - { -#define SATURATION_IDX 2 +[SATURATION] = { { .id = V4L2_CID_SATURATION, .type = V4L2_CTRL_TYPE_INTEGER, @@ -217,14 +207,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0xff, .step = 1, -#define SATURATION_DEFAULT 0x7f - .default_value = SATURATION_DEFAULT, + .default_value = 0x7f }, - .set = sd_setsaturation, - .get = sd_getsaturation, + .set_control = set_cmatrix }, - { -#define HUE_IDX 3 +[HUE] = { { .id = V4L2_CID_HUE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -232,14 +219,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = -180, .maximum = 180, .step = 1, -#define HUE_DEFAULT 0 - .default_value = HUE_DEFAULT, + .default_value = 0 }, - .set = sd_sethue, - .get = sd_gethue, + .set_control = set_cmatrix }, - { -#define GAMMA_IDX 4 +[GAMMA] = { { .id = V4L2_CID_GAMMA, .type = V4L2_CTRL_TYPE_INTEGER, @@ -247,14 +231,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0xff, .step = 1, -#define GAMMA_DEFAULT 0x10 - .default_value = GAMMA_DEFAULT, + .default_value = 0x10 }, - .set = sd_setgamma, - .get = sd_getgamma, + .set_control = set_gamma }, - { -#define BLUE_IDX 5 +[BLUE] = { { .id = V4L2_CID_BLUE_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -262,14 +243,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0x7f, .step = 1, -#define BLUE_DEFAULT 0x28 - .default_value = BLUE_DEFAULT, + .default_value = 0x28 }, - .set = sd_setbluebalance, - .get = sd_getbluebalance, + .set_control = set_redblue }, - { -#define RED_IDX 6 +[RED] = { { .id = V4L2_CID_RED_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -277,14 +255,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0x7f, .step = 1, -#define RED_DEFAULT 0x28 - .default_value = RED_DEFAULT, + .default_value = 0x28 }, - .set = sd_setredbalance, - .get = sd_getredbalance, + .set_control = set_redblue }, - { -#define HFLIP_IDX 7 +[HFLIP] = { { .id = V4L2_CID_HFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -292,14 +267,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define HFLIP_DEFAULT 0 - .default_value = HFLIP_DEFAULT, + .default_value = 0, }, - .set = sd_sethflip, - .get = sd_gethflip, + .set_control = set_hvflip }, - { -#define VFLIP_IDX 8 +[VFLIP] = { { .id = V4L2_CID_VFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -307,14 +279,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define VFLIP_DEFAULT 0 - .default_value = VFLIP_DEFAULT, + .default_value = 0, }, - .set = sd_setvflip, - .get = sd_getvflip, + .set_control = set_hvflip }, - { -#define EXPOSURE_IDX 9 +[EXPOSURE] = { { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -322,14 +291,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0x1780, .step = 1, -#define EXPOSURE_DEFAULT 0x33 - .default_value = EXPOSURE_DEFAULT, + .default_value = 0x33, }, - .set = sd_setexposure, - .get = sd_getexposure, + .set_control = set_exposure }, - { -#define GAIN_IDX 10 +[GAIN] = { { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, @@ -337,14 +303,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 28, .step = 1, -#define GAIN_DEFAULT 0x00 - .default_value = GAIN_DEFAULT, + .default_value = 0, }, - .set = sd_setgain, - .get = sd_getgain, + .set_control = set_gain }, - { -#define AUTOGAIN_IDX 11 +[AUTOGAIN] = { { .id = V4L2_CID_AUTOGAIN, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -352,11 +315,23 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define AUTO_EXPOSURE_DEFAULT 1 - .default_value = AUTO_EXPOSURE_DEFAULT, + .default_value = 1, + }, + }, +[QUALITY] = { + { + .id = V4L2_CID_JPEG_COMPRESSION_QUALITY, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Compression Quality", +#define QUALITY_MIN 50 +#define QUALITY_MAX 90 +#define QUALITY_DEF 80 + .minimum = QUALITY_MIN, + .maximum = QUALITY_MAX, + .step = 1, + .default_value = QUALITY_DEF, }, - .set = sd_setautoexposure, - .get = sd_getautoexposure, + .set_control = set_quality }, }; @@ -876,7 +851,7 @@ static u8 hv7131r_gain[] = { }; static struct i2c_reg_u8 soi968_init[] = { - {0x12, 0x80}, {0x0c, 0x00}, {0x0f, 0x1f}, + {0x0c, 0x00}, {0x0f, 0x1f}, {0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00}, {0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c}, {0x37, 0x04}, {0x45, 0x04}, {0x47, 0xff}, @@ -902,7 +877,7 @@ static struct i2c_reg_u8 ov7660_init[] = { }; static struct i2c_reg_u8 ov7670_init[] = { - {0x12, 0x80}, {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01}, + {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01}, {0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00}, {0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0}, {0xa2, 0x02}, {0x13, 0xe0}, {0x00, 0x00}, {0x10, 0x00}, @@ -959,7 +934,7 @@ static struct i2c_reg_u8 ov7670_init[] = { }; static struct i2c_reg_u8 ov9650_init[] = { - {0x12, 0x80}, {0x00, 0x00}, {0x01, 0x78}, + {0x00, 0x00}, {0x01, 0x78}, {0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03}, {0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00}, {0x09, 0x01}, {0x0c, 0x00}, {0x0d, 0x00}, @@ -989,7 +964,7 @@ static struct i2c_reg_u8 ov9650_init[] = { }; static struct i2c_reg_u8 ov9655_init[] = { - {0x12, 0x80}, {0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba}, + {0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba}, {0x14, 0x2e}, {0x16, 0x24}, {0x1e, 0x04}, {0x27, 0x08}, {0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x34, 0x3d}, {0x35, 0x00}, {0x38, 0x12}, {0x0f, 0x42}, {0x39, 0x57}, @@ -1112,10 +1087,13 @@ static struct i2c_reg_u8 hv7131r_init[] = { {0x23, 0x09}, {0x01, 0x08}, }; -static int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length) +static void reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length) { struct usb_device *dev = gspca_dev->dev; int result; + + if (gspca_dev->usb_err < 0) + return; result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x00, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, @@ -1125,17 +1103,19 @@ static int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length) length, 500); if (unlikely(result < 0 || result != length)) { - pr_err("Read register failed 0x%02X\n", reg); - return -EIO; + pr_err("Read register %02x failed %d\n", reg, result); + gspca_dev->usb_err = result; } - return 0; } -static int reg_w(struct gspca_dev *gspca_dev, u16 reg, +static void reg_w(struct gspca_dev *gspca_dev, u16 reg, const u8 *buffer, int length) { struct usb_device *dev = gspca_dev->dev; int result; + + if (gspca_dev->usb_err < 0) + return; memcpy(gspca_dev->usb_buf, buffer, length); result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x08, @@ -1146,38 +1126,41 @@ static int reg_w(struct gspca_dev *gspca_dev, u16 reg, length, 500); if (unlikely(result < 0 || result != length)) { - pr_err("Write register failed index 0x%02X\n", reg); - return -EIO; + pr_err("Write register %02x failed %d\n", reg, result); + gspca_dev->usb_err = result; } - return 0; } -static int reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value) +static void reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value) { - u8 data[1] = {value}; - return reg_w(gspca_dev, reg, data, 1); + reg_w(gspca_dev, reg, &value, 1); } -static int i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer) +static void i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer) { int i; + reg_w(gspca_dev, 0x10c0, buffer, 8); for (i = 0; i < 5; i++) { reg_r(gspca_dev, 0x10c0, 1); + if (gspca_dev->usb_err < 0) + return; if (gspca_dev->usb_buf[0] & 0x04) { - if (gspca_dev->usb_buf[0] & 0x08) - return -EIO; - return 0; + if (gspca_dev->usb_buf[0] & 0x08) { + pr_err("i2c_w error\n"); + gspca_dev->usb_err = -EIO; + } + return; } - msleep(1); + msleep(10); } - return -EIO; + pr_err("i2c_w reg %02x no response\n", buffer[2]); +/* gspca_dev->usb_err = -EIO; fixme: may occur */ } -static int i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) +static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) { struct sd *sd = (struct sd *) gspca_dev; - u8 row[8]; /* @@ -1193,10 +1176,19 @@ static int i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) row[6] = 0x00; row[7] = 0x10; - return i2c_w(gspca_dev, row); + i2c_w(gspca_dev, row); +} + +static void i2c_w1_buf(struct gspca_dev *gspca_dev, + struct i2c_reg_u8 *buf, int sz) +{ + while (--sz >= 0) { + i2c_w1(gspca_dev, buf->reg, buf->val); + buf++; + } } -static int i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) +static void i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) { struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; @@ -1208,16 +1200,25 @@ static int i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) row[0] = 0x81 | (3 << 4); row[1] = sd->i2c_addr; row[2] = reg; - row[3] = (val >> 8) & 0xff; - row[4] = val & 0xff; + row[3] = val >> 8; + row[4] = val; row[5] = 0x00; row[6] = 0x00; row[7] = 0x10; - return i2c_w(gspca_dev, row); + i2c_w(gspca_dev, row); +} + +static void i2c_w2_buf(struct gspca_dev *gspca_dev, + struct i2c_reg_u16 *buf, int sz) +{ + while (--sz >= 0) { + i2c_w2(gspca_dev, buf->reg, buf->val); + buf++; + } } -static int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) +static void i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) { struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; @@ -1230,19 +1231,15 @@ static int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) row[5] = 0; row[6] = 0; row[7] = 0x10; - if (i2c_w(gspca_dev, row) < 0) - return -EIO; + i2c_w(gspca_dev, row); row[0] = 0x81 | (1 << 4) | 0x02; row[2] = 0; - if (i2c_w(gspca_dev, row) < 0) - return -EIO; - if (reg_r(gspca_dev, 0x10c2, 5) < 0) - return -EIO; + i2c_w(gspca_dev, row); + reg_r(gspca_dev, 0x10c2, 5); *val = gspca_dev->usb_buf[4]; - return 0; } -static int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) +static void i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) { struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; @@ -1255,233 +1252,204 @@ static int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) row[5] = 0; row[6] = 0; row[7] = 0x10; - if (i2c_w(gspca_dev, row) < 0) - return -EIO; + i2c_w(gspca_dev, row); row[0] = 0x81 | (2 << 4) | 0x02; row[2] = 0; - if (i2c_w(gspca_dev, row) < 0) - return -EIO; - if (reg_r(gspca_dev, 0x10c2, 5) < 0) - return -EIO; + i2c_w(gspca_dev, row); + reg_r(gspca_dev, 0x10c2, 5); *val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; - return 0; } -static int ov9650_init_sensor(struct gspca_dev *gspca_dev) +static void ov9650_init_sensor(struct gspca_dev *gspca_dev) { - int i; u16 id; struct sd *sd = (struct sd *) gspca_dev; - if (i2c_r2(gspca_dev, 0x1c, &id) < 0) - return -EINVAL; + i2c_r2(gspca_dev, 0x1c, &id); + if (gspca_dev->usb_err < 0) + return; if (id != 0x7fa2) { pr_err("sensor id for ov9650 doesn't match (0x%04x)\n", id); - return -ENODEV; + gspca_dev->usb_err = -ENODEV; + return; } - for (i = 0; i < ARRAY_SIZE(ov9650_init); i++) { - if (i2c_w1(gspca_dev, ov9650_init[i].reg, - ov9650_init[i].val) < 0) { - pr_err("OV9650 sensor initialization failed\n"); - return -ENODEV; - } - } + i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ + msleep(200); + i2c_w1_buf(gspca_dev, ov9650_init, ARRAY_SIZE(ov9650_init)); + if (gspca_dev->usb_err < 0) + pr_err("OV9650 sensor initialization failed\n"); sd->hstart = 1; sd->vstart = 7; - return 0; } -static int ov9655_init_sensor(struct gspca_dev *gspca_dev) +static void ov9655_init_sensor(struct gspca_dev *gspca_dev) { - int i; struct sd *sd = (struct sd *) gspca_dev; - for (i = 0; i < ARRAY_SIZE(ov9655_init); i++) { - if (i2c_w1(gspca_dev, ov9655_init[i].reg, - ov9655_init[i].val) < 0) { - pr_err("OV9655 sensor initialization failed\n"); - return -ENODEV; - } - } + i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ + msleep(200); + i2c_w1_buf(gspca_dev, ov9655_init, ARRAY_SIZE(ov9655_init)); + if (gspca_dev->usb_err < 0) + pr_err("OV9655 sensor initialization failed\n"); + /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP); sd->hstart = 1; sd->vstart = 2; - return 0; } -static int soi968_init_sensor(struct gspca_dev *gspca_dev) +static void soi968_init_sensor(struct gspca_dev *gspca_dev) { - int i; struct sd *sd = (struct sd *) gspca_dev; - for (i = 0; i < ARRAY_SIZE(soi968_init); i++) { - if (i2c_w1(gspca_dev, soi968_init[i].reg, - soi968_init[i].val) < 0) { - pr_err("SOI968 sensor initialization failed\n"); - return -ENODEV; - } - } + i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ + msleep(200); + i2c_w1_buf(gspca_dev, soi968_init, ARRAY_SIZE(soi968_init)); + if (gspca_dev->usb_err < 0) + pr_err("SOI968 sensor initialization failed\n"); + /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX) - | (1 << EXPOSURE_IDX); + gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP) + | (1 << EXPOSURE); sd->hstart = 60; sd->vstart = 11; - return 0; } -static int ov7660_init_sensor(struct gspca_dev *gspca_dev) +static void ov7660_init_sensor(struct gspca_dev *gspca_dev) { - int i; struct sd *sd = (struct sd *) gspca_dev; - for (i = 0; i < ARRAY_SIZE(ov7660_init); i++) { - if (i2c_w1(gspca_dev, ov7660_init[i].reg, - ov7660_init[i].val) < 0) { - pr_err("OV7660 sensor initialization failed\n"); - return -ENODEV; - } - } + i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ + msleep(200); + i2c_w1_buf(gspca_dev, ov7660_init, ARRAY_SIZE(ov7660_init)); + if (gspca_dev->usb_err < 0) + pr_err("OV7660 sensor initialization failed\n"); sd->hstart = 3; sd->vstart = 3; - return 0; } -static int ov7670_init_sensor(struct gspca_dev *gspca_dev) +static void ov7670_init_sensor(struct gspca_dev *gspca_dev) { - int i; struct sd *sd = (struct sd *) gspca_dev; - for (i = 0; i < ARRAY_SIZE(ov7670_init); i++) { - if (i2c_w1(gspca_dev, ov7670_init[i].reg, - ov7670_init[i].val) < 0) { - pr_err("OV7670 sensor initialization failed\n"); - return -ENODEV; - } - } + i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ + msleep(200); + i2c_w1_buf(gspca_dev, ov7670_init, ARRAY_SIZE(ov7670_init)); + if (gspca_dev->usb_err < 0) + pr_err("OV7670 sensor initialization failed\n"); + /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP); sd->hstart = 0; sd->vstart = 1; - return 0; } -static int mt9v_init_sensor(struct gspca_dev *gspca_dev) +static void mt9v_init_sensor(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int i; u16 value; - int ret; sd->i2c_addr = 0x5d; - ret = i2c_r2(gspca_dev, 0xff, &value); - if ((ret == 0) && (value == 0x8243)) { - for (i = 0; i < ARRAY_SIZE(mt9v011_init); i++) { - if (i2c_w2(gspca_dev, mt9v011_init[i].reg, - mt9v011_init[i].val) < 0) { - pr_err("MT9V011 sensor initialization failed\n"); - return -ENODEV; - } + i2c_r2(gspca_dev, 0xff, &value); + if (gspca_dev->usb_err >= 0 + && value == 0x8243) { + i2c_w2_buf(gspca_dev, mt9v011_init, ARRAY_SIZE(mt9v011_init)); + if (gspca_dev->usb_err < 0) { + pr_err("MT9V011 sensor initialization failed\n"); + return; } sd->hstart = 2; sd->vstart = 2; sd->sensor = SENSOR_MT9V011; pr_info("MT9V011 sensor detected\n"); - return 0; + return; } + gspca_dev->usb_err = 0; sd->i2c_addr = 0x5c; i2c_w2(gspca_dev, 0x01, 0x0004); - ret = i2c_r2(gspca_dev, 0xff, &value); - if ((ret == 0) && (value == 0x823a)) { - for (i = 0; i < ARRAY_SIZE(mt9v111_init); i++) { - if (i2c_w2(gspca_dev, mt9v111_init[i].reg, - mt9v111_init[i].val) < 0) { - pr_err("MT9V111 sensor initialization failed\n"); - return -ENODEV; - } + i2c_r2(gspca_dev, 0xff, &value); + if (gspca_dev->usb_err >= 0 + && value == 0x823a) { + i2c_w2_buf(gspca_dev, mt9v111_init, ARRAY_SIZE(mt9v111_init)); + if (gspca_dev->usb_err < 0) { + pr_err("MT9V111 sensor initialization failed\n"); + return; } - gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) - | (1 << AUTOGAIN_IDX) - | (1 << GAIN_IDX); + gspca_dev->ctrl_dis = (1 << EXPOSURE) + | (1 << AUTOGAIN) + | (1 << GAIN); sd->hstart = 2; sd->vstart = 2; sd->sensor = SENSOR_MT9V111; pr_info("MT9V111 sensor detected\n"); - return 0; + return; } + gspca_dev->usb_err = 0; sd->i2c_addr = 0x5d; - ret = i2c_w2(gspca_dev, 0xf0, 0x0000); - if (ret < 0) { + i2c_w2(gspca_dev, 0xf0, 0x0000); + if (gspca_dev->usb_err < 0) { + gspca_dev->usb_err = 0; sd->i2c_addr = 0x48; i2c_w2(gspca_dev, 0xf0, 0x0000); } - ret = i2c_r2(gspca_dev, 0x00, &value); - if ((ret == 0) && (value == 0x1229)) { - for (i = 0; i < ARRAY_SIZE(mt9v112_init); i++) { - if (i2c_w2(gspca_dev, mt9v112_init[i].reg, - mt9v112_init[i].val) < 0) { - pr_err("MT9V112 sensor initialization failed\n"); - return -ENODEV; - } + i2c_r2(gspca_dev, 0x00, &value); + if (gspca_dev->usb_err >= 0 + && value == 0x1229) { + i2c_w2_buf(gspca_dev, mt9v112_init, ARRAY_SIZE(mt9v112_init)); + if (gspca_dev->usb_err < 0) { + pr_err("MT9V112 sensor initialization failed\n"); + return; } sd->hstart = 6; sd->vstart = 2; sd->sensor = SENSOR_MT9V112; pr_info("MT9V112 sensor detected\n"); - return 0; + return; } - return -ENODEV; + gspca_dev->usb_err = -ENODEV; } -static int mt9m112_init_sensor(struct gspca_dev *gspca_dev) +static void mt9m112_init_sensor(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int i; - for (i = 0; i < ARRAY_SIZE(mt9m112_init); i++) { - if (i2c_w2(gspca_dev, mt9m112_init[i].reg, - mt9m112_init[i].val) < 0) { - pr_err("MT9M112 sensor initialization failed\n"); - return -ENODEV; - } - } - gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX) - | (1 << GAIN_IDX); + + i2c_w2_buf(gspca_dev, mt9m112_init, ARRAY_SIZE(mt9m112_init)); + if (gspca_dev->usb_err < 0) + pr_err("MT9M112 sensor initialization failed\n"); + + gspca_dev->ctrl_dis = (1 << EXPOSURE) | (1 << AUTOGAIN) + | (1 << GAIN); sd->hstart = 0; sd->vstart = 2; - return 0; } -static int mt9m111_init_sensor(struct gspca_dev *gspca_dev) +static void mt9m111_init_sensor(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int i; - for (i = 0; i < ARRAY_SIZE(mt9m111_init); i++) { - if (i2c_w2(gspca_dev, mt9m111_init[i].reg, - mt9m111_init[i].val) < 0) { - pr_err("MT9M111 sensor initialization failed\n"); - return -ENODEV; - } - } - gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX) - | (1 << GAIN_IDX); + + i2c_w2_buf(gspca_dev, mt9m111_init, ARRAY_SIZE(mt9m111_init)); + if (gspca_dev->usb_err < 0) + pr_err("MT9M111 sensor initialization failed\n"); + + gspca_dev->ctrl_dis = (1 << EXPOSURE) | (1 << AUTOGAIN) + | (1 << GAIN); sd->hstart = 0; sd->vstart = 2; - return 0; } -static int mt9m001_init_sensor(struct gspca_dev *gspca_dev) +static void mt9m001_init_sensor(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int i; u16 id; - if (i2c_r2(gspca_dev, 0x00, &id) < 0) - return -EINVAL; + i2c_r2(gspca_dev, 0x00, &id); + if (gspca_dev->usb_err < 0) + return; /* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */ switch (id) { @@ -1494,85 +1462,78 @@ static int mt9m001_init_sensor(struct gspca_dev *gspca_dev) break; default: pr_err("No MT9M001 chip detected, ID = %x\n\n", id); - return -ENODEV; + gspca_dev->usb_err = -ENODEV; + return; } - for (i = 0; i < ARRAY_SIZE(mt9m001_init); i++) { - if (i2c_w2(gspca_dev, mt9m001_init[i].reg, - mt9m001_init[i].val) < 0) { - pr_err("MT9M001 sensor initialization failed\n"); - return -ENODEV; - } - } + i2c_w2_buf(gspca_dev, mt9m001_init, ARRAY_SIZE(mt9m001_init)); + if (gspca_dev->usb_err < 0) + pr_err("MT9M001 sensor initialization failed\n"); + /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP); sd->hstart = 1; sd->vstart = 1; - return 0; } -static int hv7131r_init_sensor(struct gspca_dev *gspca_dev) +static void hv7131r_init_sensor(struct gspca_dev *gspca_dev) { - int i; struct sd *sd = (struct sd *) gspca_dev; - for (i = 0; i < ARRAY_SIZE(hv7131r_init); i++) { - if (i2c_w1(gspca_dev, hv7131r_init[i].reg, - hv7131r_init[i].val) < 0) { - pr_err("HV7131R Sensor initialization failed\n"); - return -ENODEV; - } - } + i2c_w1_buf(gspca_dev, hv7131r_init, ARRAY_SIZE(hv7131r_init)); + if (gspca_dev->usb_err < 0) + pr_err("HV7131R Sensor initialization failed\n"); + sd->hstart = 0; sd->vstart = 1; - return 0; } -static int set_cmatrix(struct gspca_dev *gspca_dev) +static void set_cmatrix(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - s32 hue_coord, hue_index = 180 + sd->hue; + int satur; + s32 hue_coord, hue_index = 180 + sd->ctrls[HUE].val; u8 cmatrix[21]; memset(cmatrix, 0, sizeof cmatrix); - cmatrix[2] = (sd->contrast * 0x25 / 0x100) + 0x26; + cmatrix[2] = (sd->ctrls[CONTRAST].val * 0x25 / 0x100) + 0x26; cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25; cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25; - cmatrix[18] = sd->brightness - 0x80; + cmatrix[18] = sd->ctrls[BRIGHTNESS].val - 0x80; - hue_coord = (hsv_red_x[hue_index] * sd->saturation) >> 8; + satur = sd->ctrls[SATURATION].val; + hue_coord = (hsv_red_x[hue_index] * satur) >> 8; cmatrix[6] = hue_coord; cmatrix[7] = (hue_coord >> 8) & 0x0f; - hue_coord = (hsv_red_y[hue_index] * sd->saturation) >> 8; + hue_coord = (hsv_red_y[hue_index] * satur) >> 8; cmatrix[8] = hue_coord; cmatrix[9] = (hue_coord >> 8) & 0x0f; - hue_coord = (hsv_green_x[hue_index] * sd->saturation) >> 8; + hue_coord = (hsv_green_x[hue_index] * satur) >> 8; cmatrix[10] = hue_coord; cmatrix[11] = (hue_coord >> 8) & 0x0f; - hue_coord = (hsv_green_y[hue_index] * sd->saturation) >> 8; + hue_coord = (hsv_green_y[hue_index] * satur) >> 8; cmatrix[12] = hue_coord; cmatrix[13] = (hue_coord >> 8) & 0x0f; - hue_coord = (hsv_blue_x[hue_index] * sd->saturation) >> 8; + hue_coord = (hsv_blue_x[hue_index] * satur) >> 8; cmatrix[14] = hue_coord; cmatrix[15] = (hue_coord >> 8) & 0x0f; - hue_coord = (hsv_blue_y[hue_index] * sd->saturation) >> 8; + hue_coord = (hsv_blue_y[hue_index] * satur) >> 8; cmatrix[16] = hue_coord; cmatrix[17] = (hue_coord >> 8) & 0x0f; - return reg_w(gspca_dev, 0x10e1, cmatrix, 21); + reg_w(gspca_dev, 0x10e1, cmatrix, 21); } -static int set_gamma(struct gspca_dev *gspca_dev) +static void set_gamma(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 gamma[17]; - u8 gval = sd->gamma * 0xb8 / 0x100; - + u8 gval = sd->ctrls[GAMMA].val * 0xb8 / 0x100; gamma[0] = 0x0a; gamma[1] = 0x13 + (gval * (0xcb - 0x13) / 0xb8); @@ -1592,29 +1553,29 @@ static int set_gamma(struct gspca_dev *gspca_dev) gamma[15] = 0xea + (gval * (0xf9 - 0xea) / 0xb8); gamma[16] = 0xf5; - return reg_w(gspca_dev, 0x1190, gamma, 17); + reg_w(gspca_dev, 0x1190, gamma, 17); } -static int set_redblue(struct gspca_dev *gspca_dev) +static void set_redblue(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - reg_w1(gspca_dev, 0x118c, sd->red); - reg_w1(gspca_dev, 0x118f, sd->blue); - return 0; + + reg_w1(gspca_dev, 0x118c, sd->ctrls[RED].val); + reg_w1(gspca_dev, 0x118f, sd->ctrls[BLUE].val); } -static int set_hvflip(struct gspca_dev *gspca_dev) +static void set_hvflip(struct gspca_dev *gspca_dev) { u8 value, tslb, hflip, vflip; u16 value2; struct sd *sd = (struct sd *) gspca_dev; if ((sd->flags & FLIP_DETECT) && dmi_check_system(flip_dmi_table)) { - hflip = !sd->hflip; - vflip = !sd->vflip; + hflip = !sd->ctrls[HFLIP].val; + vflip = !sd->ctrls[VFLIP].val; } else { - hflip = sd->hflip; - vflip = sd->vflip; + hflip = sd->ctrls[HFLIP].val; + vflip = sd->ctrls[VFLIP].val; } switch (sd->sensor) { @@ -1625,8 +1586,9 @@ static int set_hvflip(struct gspca_dev *gspca_dev) if (vflip) { value |= 0x10; sd->vstart = 2; - } else + } else { sd->vstart = 3; + } reg_w1(gspca_dev, 0x1182, sd->vstart); i2c_w1(gspca_dev, 0x1e, value); break; @@ -1674,13 +1636,15 @@ static int set_hvflip(struct gspca_dev *gspca_dev) i2c_w1(gspca_dev, 0x01, value); break; } - return 0; } -static int set_exposure(struct gspca_dev *gspca_dev) +static void set_exposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 exp[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e}; + int expo; + + expo = sd->ctrls[EXPOSURE].val; switch (sd->sensor) { case SENSOR_OV7660: case SENSOR_OV7670: @@ -1688,35 +1652,37 @@ static int set_exposure(struct gspca_dev *gspca_dev) case SENSOR_OV9650: exp[0] |= (3 << 4); exp[2] = 0x2d; - exp[3] = sd->exposure & 0xff; - exp[4] = sd->exposure >> 8; + exp[3] = expo; + exp[4] = expo >> 8; break; case SENSOR_MT9M001: case SENSOR_MT9V112: case SENSOR_MT9V011: exp[0] |= (3 << 4); exp[2] = 0x09; - exp[3] = sd->exposure >> 8; - exp[4] = sd->exposure & 0xff; + exp[3] = expo >> 8; + exp[4] = expo; break; case SENSOR_HV7131R: exp[0] |= (4 << 4); exp[2] = 0x25; - exp[3] = (sd->exposure >> 5) & 0xff; - exp[4] = (sd->exposure << 3) & 0xff; + exp[3] = expo >> 5; + exp[4] = expo << 3; exp[5] = 0; break; default: - return 0; + return; } i2c_w(gspca_dev, exp); - return 0; } -static int set_gain(struct gspca_dev *gspca_dev) +static void set_gain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 gain[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d}; + int g; + + g = sd->ctrls[GAIN].val; switch (sd->sensor) { case SENSOR_OV7660: case SENSOR_OV7670: @@ -1724,238 +1690,50 @@ static int set_gain(struct gspca_dev *gspca_dev) case SENSOR_OV9655: case SENSOR_OV9650: gain[0] |= (2 << 4); - gain[3] = ov_gain[sd->gain]; + gain[3] = ov_gain[g]; break; case SENSOR_MT9V011: gain[0] |= (3 << 4); gain[2] = 0x35; - gain[3] = micron1_gain[sd->gain] >> 8; - gain[4] = micron1_gain[sd->gain] & 0xff; + gain[3] = micron1_gain[g] >> 8; + gain[4] = micron1_gain[g]; break; case SENSOR_MT9V112: gain[0] |= (3 << 4); gain[2] = 0x2f; - gain[3] = micron1_gain[sd->gain] >> 8; - gain[4] = micron1_gain[sd->gain] & 0xff; + gain[3] = micron1_gain[g] >> 8; + gain[4] = micron1_gain[g]; break; case SENSOR_MT9M001: gain[0] |= (3 << 4); gain[2] = 0x2f; - gain[3] = micron2_gain[sd->gain] >> 8; - gain[4] = micron2_gain[sd->gain] & 0xff; + gain[3] = micron2_gain[g] >> 8; + gain[4] = micron2_gain[g]; break; case SENSOR_HV7131R: gain[0] |= (2 << 4); gain[2] = 0x30; - gain[3] = hv7131r_gain[sd->gain]; + gain[3] = hv7131r_gain[g]; break; default: - return 0; + return; } i2c_w(gspca_dev, gain); - return 0; -} - -static int sd_setbrightness(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->brightness = val; - if (gspca_dev->streaming) - return set_cmatrix(gspca_dev); - return 0; -} - -static int sd_getbrightness(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->brightness; - return 0; -} - - -static int sd_setcontrast(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->contrast = val; - if (gspca_dev->streaming) - return set_cmatrix(gspca_dev); - return 0; -} - -static int sd_getcontrast(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->contrast; - return 0; -} - -static int sd_setsaturation(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->saturation = val; - if (gspca_dev->streaming) - return set_cmatrix(gspca_dev); - return 0; } -static int sd_getsaturation(struct gspca_dev *gspca_dev, s32 *val) +static void set_quality(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - *val = sd->saturation; - return 0; -} - -static int sd_sethue(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->hue = val; - if (gspca_dev->streaming) - return set_cmatrix(gspca_dev); - return 0; -} - -static int sd_gethue(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->hue; - return 0; -} - -static int sd_setgamma(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->gamma = val; - if (gspca_dev->streaming) - return set_gamma(gspca_dev); - return 0; -} - -static int sd_getgamma(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->gamma; - return 0; -} - -static int sd_setredbalance(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->red = val; - if (gspca_dev->streaming) - return set_redblue(gspca_dev); - return 0; -} - -static int sd_getredbalance(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->red; - return 0; -} - -static int sd_setbluebalance(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->blue = val; - if (gspca_dev->streaming) - return set_redblue(gspca_dev); - return 0; -} - -static int sd_getbluebalance(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->blue; - return 0; -} - -static int sd_sethflip(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->hflip = val; - if (gspca_dev->streaming) - return set_hvflip(gspca_dev); - return 0; -} - -static int sd_gethflip(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->hflip; - return 0; -} - -static int sd_setvflip(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->vflip = val; - if (gspca_dev->streaming) - return set_hvflip(gspca_dev); - return 0; -} - -static int sd_getvflip(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->vflip; - return 0; -} - -static int sd_setexposure(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->exposure = val; - if (gspca_dev->streaming) - return set_exposure(gspca_dev); - return 0; -} - -static int sd_getexposure(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->exposure; - return 0; -} - -static int sd_setgain(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->gain = val; - if (gspca_dev->streaming) - return set_gain(gspca_dev); - return 0; -} - -static int sd_getgain(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->gain; - return 0; -} - -static int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - sd->auto_exposure = val; - return 0; -} -static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->auto_exposure; - return 0; + jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val); + reg_w1(gspca_dev, 0x1061, 0x01); /* stop transfer */ + reg_w1(gspca_dev, 0x10e0, sd->fmt | 0x20); /* write QTAB */ + reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64); + reg_w(gspca_dev, 0x1140, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64); + reg_w1(gspca_dev, 0x1061, 0x03); /* restart transfer */ + reg_w1(gspca_dev, 0x10e0, sd->fmt); + sd->fmt ^= 0x0c; /* invert QTAB use + write */ + reg_w1(gspca_dev, 0x10e0, sd->fmt); } #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -1963,28 +1741,26 @@ static int sd_dbg_g_register(struct gspca_dev *gspca_dev, struct v4l2_dbg_register *reg) { struct sd *sd = (struct sd *) gspca_dev; + switch (reg->match.type) { case V4L2_CHIP_MATCH_HOST: if (reg->match.addr != 0) return -EINVAL; if (reg->reg < 0x1000 || reg->reg > 0x11ff) return -EINVAL; - if (reg_r(gspca_dev, reg->reg, 1) < 0) - return -EINVAL; + reg_r(gspca_dev, reg->reg, 1); reg->val = gspca_dev->usb_buf[0]; - return 0; + return gspca_dev->usb_err; case V4L2_CHIP_MATCH_I2C_ADDR: if (reg->match.addr != sd->i2c_addr) return -EINVAL; if (sd->sensor >= SENSOR_MT9V011 && sd->sensor <= SENSOR_MT9M112) { - if (i2c_r2(gspca_dev, reg->reg, (u16 *)®->val) < 0) - return -EINVAL; + i2c_r2(gspca_dev, reg->reg, (u16 *) ®->val); } else { - if (i2c_r1(gspca_dev, reg->reg, (u8 *)®->val) < 0) - return -EINVAL; + i2c_r1(gspca_dev, reg->reg, (u8 *) ®->val); } - return 0; + return gspca_dev->usb_err; } return -EINVAL; } @@ -1993,27 +1769,25 @@ static int sd_dbg_s_register(struct gspca_dev *gspca_dev, struct v4l2_dbg_register *reg) { struct sd *sd = (struct sd *) gspca_dev; + switch (reg->match.type) { case V4L2_CHIP_MATCH_HOST: if (reg->match.addr != 0) return -EINVAL; if (reg->reg < 0x1000 || reg->reg > 0x11ff) return -EINVAL; - if (reg_w1(gspca_dev, reg->reg, reg->val) < 0) - return -EINVAL; - return 0; + reg_w1(gspca_dev, reg->reg, reg->val); + return gspca_dev->usb_err; case V4L2_CHIP_MATCH_I2C_ADDR: if (reg->match.addr != sd->i2c_addr) return -EINVAL; if (sd->sensor >= SENSOR_MT9V011 && sd->sensor <= SENSOR_MT9M112) { - if (i2c_w2(gspca_dev, reg->reg, reg->val) < 0) - return -EINVAL; + i2c_w2(gspca_dev, reg->reg, reg->val); } else { - if (i2c_w1(gspca_dev, reg->reg, reg->val) < 0) - return -EINVAL; + i2c_w1(gspca_dev, reg->reg, reg->val); } - return 0; + return gspca_dev->usb_err; } return -EINVAL; } @@ -2050,9 +1824,9 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; cam->needs_full_bandwidth = 1; - sd->sensor = (id->driver_info >> 8) & 0xff; - sd->i2c_addr = id->driver_info & 0xff; - sd->flags = (id->driver_info >> 16) & 0xff; + sd->sensor = id->driver_info >> 8; + sd->i2c_addr = id->driver_info; + sd->flags = id->driver_info >> 16; switch (sd->sensor) { case SENSOR_MT9M112: @@ -2076,21 +1850,9 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->older_step = 0; sd->exposure_step = 16; - sd->brightness = BRIGHTNESS_DEFAULT; - sd->contrast = CONTRAST_DEFAULT; - sd->saturation = SATURATION_DEFAULT; - sd->hue = HUE_DEFAULT; - sd->gamma = GAMMA_DEFAULT; - sd->red = RED_DEFAULT; - sd->blue = BLUE_DEFAULT; + gspca_dev->cam.ctrls = sd->ctrls; - sd->hflip = HFLIP_DEFAULT; - sd->vflip = VFLIP_DEFAULT; - sd->exposure = EXPOSURE_DEFAULT; - sd->gain = GAIN_DEFAULT; - sd->auto_exposure = AUTO_EXPOSURE_DEFAULT; - - sd->quality = 95; + INIT_WORK(&sd->work, qual_upd); return 0; } @@ -2105,9 +1867,10 @@ static int sd_init(struct gspca_dev *gspca_dev) for (i = 0; i < ARRAY_SIZE(bridge_init); i++) { value = bridge_init[i][1]; - if (reg_w(gspca_dev, bridge_init[i][0], &value, 1) < 0) { + reg_w(gspca_dev, bridge_init[i][0], &value, 1); + if (gspca_dev->usb_err < 0) { pr_err("Device initialization failed\n"); - return -ENODEV; + return gspca_dev->usb_err; } } @@ -2116,72 +1879,85 @@ static int sd_init(struct gspca_dev *gspca_dev) else reg_w1(gspca_dev, 0x1006, 0x20); - if (reg_w(gspca_dev, 0x10c0, i2c_init, 9) < 0) { + reg_w(gspca_dev, 0x10c0, i2c_init, 9); + if (gspca_dev->usb_err < 0) { pr_err("Device initialization failed\n"); - return -ENODEV; + return gspca_dev->usb_err; } switch (sd->sensor) { case SENSOR_OV9650: - if (ov9650_init_sensor(gspca_dev) < 0) - return -ENODEV; + ov9650_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; pr_info("OV9650 sensor detected\n"); break; case SENSOR_OV9655: - if (ov9655_init_sensor(gspca_dev) < 0) - return -ENODEV; + ov9655_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; pr_info("OV9655 sensor detected\n"); break; case SENSOR_SOI968: - if (soi968_init_sensor(gspca_dev) < 0) - return -ENODEV; + soi968_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; pr_info("SOI968 sensor detected\n"); break; case SENSOR_OV7660: - if (ov7660_init_sensor(gspca_dev) < 0) - return -ENODEV; + ov7660_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; pr_info("OV7660 sensor detected\n"); break; case SENSOR_OV7670: - if (ov7670_init_sensor(gspca_dev) < 0) - return -ENODEV; + ov7670_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; pr_info("OV7670 sensor detected\n"); break; case SENSOR_MT9VPRB: - if (mt9v_init_sensor(gspca_dev) < 0) - return -ENODEV; + mt9v_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; + pr_info("MT9VPRB sensor detected\n"); break; case SENSOR_MT9M111: - if (mt9m111_init_sensor(gspca_dev) < 0) - return -ENODEV; + mt9m111_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; pr_info("MT9M111 sensor detected\n"); break; case SENSOR_MT9M112: - if (mt9m112_init_sensor(gspca_dev) < 0) - return -ENODEV; + mt9m112_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; pr_info("MT9M112 sensor detected\n"); break; case SENSOR_MT9M001: - if (mt9m001_init_sensor(gspca_dev) < 0) - return -ENODEV; + mt9m001_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; break; case SENSOR_HV7131R: - if (hv7131r_init_sensor(gspca_dev) < 0) - return -ENODEV; + hv7131r_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; pr_info("HV7131R sensor detected\n"); break; default: - pr_info("Unsupported Sensor\n"); - return -ENODEV; + pr_err("Unsupported sensor\n"); + gspca_dev->usb_err = -ENODEV; } - return 0; + return gspca_dev->usb_err; } static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode) { struct sd *sd = (struct sd *) gspca_dev; u8 value; + switch (sd->sensor) { case SENSOR_SOI968: if (mode & MODE_SXGA) { @@ -2264,6 +2040,7 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev) break; default: /* >= 640x480 */ gspca_dev->alt = 9; + break; } } @@ -2290,14 +2067,15 @@ static int sd_start(struct gspca_dev *gspca_dev) jpeg_define(sd->jpeg_hdr, height, width, 0x21); - jpeg_set_qual(sd->jpeg_hdr, sd->quality); + jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val); if (mode & MODE_RAW) fmt = 0x2d; else if (mode & MODE_JPEG) - fmt = 0x2c; + fmt = 0x24; else fmt = 0x2f; /* YUV 420 */ + sd->fmt = fmt; switch (mode & SCALE_MASK) { case SCALE_1280x1024: @@ -2334,18 +2112,37 @@ static int sd_start(struct gspca_dev *gspca_dev) set_hvflip(gspca_dev); reg_w1(gspca_dev, 0x1007, 0x20); + reg_w1(gspca_dev, 0x1061, 0x03); + + /* if JPEG, prepare the compression quality update */ + if (mode & MODE_JPEG) { + sd->pktsz = sd->npkt = 0; + sd->nchg = 0; + sd->work_thread = + create_singlethread_workqueue(KBUILD_MODNAME); + } - reg_r(gspca_dev, 0x1061, 1); - reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] | 0x02); - return 0; + return gspca_dev->usb_err; } static void sd_stopN(struct gspca_dev *gspca_dev) { reg_w1(gspca_dev, 0x1007, 0x00); + reg_w1(gspca_dev, 0x1061, 0x01); +} + +/* called on streamoff with alt==0 and on disconnect */ +/* the usb_lock is held at entry - restore on exit */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; - reg_r(gspca_dev, 0x1061, 1); - reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] & ~0x02); + if (sd->work_thread != NULL) { + mutex_unlock(&gspca_dev->usb_lock); + destroy_workqueue(sd->work_thread); + mutex_lock(&gspca_dev->usb_lock); + sd->work_thread = NULL; + } } static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) @@ -2359,15 +2156,15 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) * and exposure steps */ if (avg_lum < MIN_AVG_LUM) { - if (sd->exposure > 0x1770) + if (sd->ctrls[EXPOSURE].val > 0x1770) return; - new_exp = sd->exposure + sd->exposure_step; + new_exp = sd->ctrls[EXPOSURE].val + sd->exposure_step; if (new_exp > 0x1770) new_exp = 0x1770; if (new_exp < 0x10) new_exp = 0x10; - sd->exposure = new_exp; + sd->ctrls[EXPOSURE].val = new_exp; set_exposure(gspca_dev); sd->older_step = sd->old_step; @@ -2379,14 +2176,14 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) sd->exposure_step += 2; } if (avg_lum > MAX_AVG_LUM) { - if (sd->exposure < 0x10) + if (sd->ctrls[EXPOSURE].val < 0x10) return; - new_exp = sd->exposure - sd->exposure_step; + new_exp = sd->ctrls[EXPOSURE].val - sd->exposure_step; if (new_exp > 0x1700) new_exp = 0x1770; if (new_exp < 0x10) new_exp = 0x10; - sd->exposure = new_exp; + sd->ctrls[EXPOSURE].val = new_exp; set_exposure(gspca_dev); sd->older_step = sd->old_step; sd->old_step = 0; @@ -2403,14 +2200,14 @@ static void do_autogain(struct gspca_dev *gspca_dev, u16 avg_lum) struct sd *sd = (struct sd *) gspca_dev; if (avg_lum < MIN_AVG_LUM) { - if (sd->gain + 1 <= 28) { - sd->gain++; + if (sd->ctrls[GAIN].val + 1 <= 28) { + sd->ctrls[GAIN].val++; set_gain(gspca_dev); } } if (avg_lum > MAX_AVG_LUM) { - if (sd->gain > 0) { - sd->gain--; + if (sd->ctrls[GAIN].val > 0) { + sd->ctrls[GAIN].val--; set_gain(gspca_dev); } } @@ -2421,7 +2218,7 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int avg_lum; - if (!sd->auto_exposure) + if (!sd->ctrls[AUTOGAIN].val) return; avg_lum = atomic_read(&sd->avg_lum); @@ -2431,33 +2228,92 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev) do_autoexposure(gspca_dev, avg_lum); } +/* JPEG quality update */ +/* This function is executed from a work queue. */ +static void qual_upd(struct work_struct *work) +{ + struct sd *sd = container_of(work, struct sd, work); + struct gspca_dev *gspca_dev = &sd->gspca_dev; + + mutex_lock(&gspca_dev->usb_lock); + PDEBUG(D_STREAM, "qual_upd %d%%", sd->ctrls[QUALITY].val); + set_quality(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); +} + #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* interrupt packet */ int len) /* interrupt packet length */ { struct sd *sd = (struct sd *) gspca_dev; - int ret = -EINVAL; + if (!(sd->flags & HAS_NO_BUTTON) && len == 1) { - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); - input_sync(gspca_dev->input_dev); - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); - input_sync(gspca_dev->input_dev); - ret = 0; + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + return 0; } - return ret; + return -EINVAL; } #endif +/* check the JPEG compression */ +static void transfer_check(struct gspca_dev *gspca_dev, + u8 *data) +{ + struct sd *sd = (struct sd *) gspca_dev; + int new_qual, r; + + new_qual = 0; + + /* if USB error, discard the frame and decrease the quality */ + if (data[6] & 0x08) { /* USB FIFO full */ + gspca_dev->last_packet_type = DISCARD_PACKET; + new_qual = -5; + } else { + + /* else, compute the filling rate and a new JPEG quality */ + r = (sd->pktsz * 100) / + (sd->npkt * + gspca_dev->urb[0]->iso_frame_desc[0].length); + if (r >= 85) + new_qual = -3; + else if (r < 75) + new_qual = 2; + } + if (new_qual != 0) { + sd->nchg += new_qual; + if (sd->nchg < -6 || sd->nchg >= 12) { + sd->nchg = 0; + new_qual += sd->ctrls[QUALITY].val; + if (new_qual < QUALITY_MIN) + new_qual = QUALITY_MIN; + else if (new_qual > QUALITY_MAX) + new_qual = QUALITY_MAX; + if (new_qual != sd->ctrls[QUALITY].val) { + sd->ctrls[QUALITY].val = new_qual; + queue_work(sd->work_thread, &sd->work); + } + } + } else { + sd->nchg = 0; + } + sd->pktsz = sd->npkt = 0; +} + static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; - int avg_lum; + int avg_lum, is_jpeg; static u8 frame_header[] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; - if (len == 64 && memcmp(data, frame_header, 6) == 0) { + + is_jpeg = (sd->fmt & 0x03) == 0; + if (len >= 64 && memcmp(data, frame_header, 6) == 0) { avg_lum = ((data[35] >> 2) & 3) | (data[20] << 2) | (data[19] << 10); @@ -2484,12 +2340,18 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, (data[33] << 10); avg_lum >>= 9; atomic_set(&sd->avg_lum, avg_lum); + + if (is_jpeg) + transfer_check(gspca_dev, data); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - return; + len -= 64; + if (len == 0) + return; + data += 64; } if (gspca_dev->last_packet_type == LAST_PACKET) { - if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv - & MODE_JPEG) { + if (is_jpeg) { gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); gspca_frame_add(gspca_dev, INTER_PACKET, @@ -2499,13 +2361,18 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data, len); } } else { + /* if JPEG, count the packets and their size */ + if (is_jpeg) { + sd->npkt++; + sd->pktsz += len; + } gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } } /* sub-driver description */ static const struct sd_desc sd_desc = { - .name = MODULE_NAME, + .name = KBUILD_MODNAME, .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, @@ -2513,6 +2380,7 @@ static const struct sd_desc sd_desc = { .isoc_init = sd_isoc_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) .int_pkt_scan = sd_int_pkt_scan, @@ -2581,7 +2449,7 @@ static int sd_probe(struct usb_interface *intf, } static struct usb_driver sd_driver = { - .name = MODULE_NAME, + .name = KBUILD_MODNAME, .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 0c9e6ddabd2c..db8e5084df06 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -39,7 +39,9 @@ enum e_ctrl { BLUE, RED, GAMMA, + EXPOSURE, AUTOGAIN, + GAIN, HFLIP, VFLIP, SHARPNESS, @@ -131,7 +133,9 @@ static void setcontrast(struct gspca_dev *gspca_dev); static void setcolors(struct gspca_dev *gspca_dev); static void setredblue(struct gspca_dev *gspca_dev); static void setgamma(struct gspca_dev *gspca_dev); -static void setautogain(struct gspca_dev *gspca_dev); +static void setexposure(struct gspca_dev *gspca_dev); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static void setgain(struct gspca_dev *gspca_dev); static void sethvflip(struct gspca_dev *gspca_dev); static void setsharpness(struct gspca_dev *gspca_dev); static void setillum(struct gspca_dev *gspca_dev); @@ -213,6 +217,18 @@ static const struct ctrl sd_ctrls[NCTRLS] = { }, .set_control = setgamma }, +[EXPOSURE] = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 500, + .maximum = 1500, + .step = 1, + .default_value = 1024 + }, + .set_control = setexposure + }, [AUTOGAIN] = { { .id = V4L2_CID_AUTOGAIN, @@ -223,7 +239,19 @@ static const struct ctrl sd_ctrls[NCTRLS] = { .step = 1, .default_value = 1 }, - .set_control = setautogain + .set = sd_setautogain, + }, +[GAIN] = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 4, + .maximum = 49, + .step = 1, + .default_value = 15 + }, + .set_control = setgain }, [HFLIP] = { { @@ -290,60 +318,87 @@ static const struct ctrl sd_ctrls[NCTRLS] = { /* table of the disabled controls */ static const __u32 ctrl_dis[] = { -[SENSOR_ADCM1700] = (1 << AUTOGAIN) | +[SENSOR_ADCM1700] = (1 << EXPOSURE) | + (1 << AUTOGAIN) | + (1 << GAIN) | (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_GC0307] = (1 << HFLIP) | +[SENSOR_GC0307] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_HV7131R] = (1 << HFLIP) | +[SENSOR_HV7131R] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | (1 << FREQ), -[SENSOR_MI0360] = (1 << HFLIP) | +[SENSOR_MI0360] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_MI0360B] = (1 << HFLIP) | +[SENSOR_MI0360B] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_MO4000] = (1 << HFLIP) | +[SENSOR_MO4000] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_MT9V111] = (1 << HFLIP) | +[SENSOR_MT9V111] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_OM6802] = (1 << HFLIP) | +[SENSOR_OM6802] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_OV7630] = (1 << HFLIP), +[SENSOR_OV7630] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP), -[SENSOR_OV7648] = (1 << HFLIP), +[SENSOR_OV7648] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP), -[SENSOR_OV7660] = (1 << AUTOGAIN) | +[SENSOR_OV7660] = (1 << EXPOSURE) | + (1 << AUTOGAIN) | + (1 << GAIN) | (1 << HFLIP) | (1 << VFLIP), -[SENSOR_PO1030] = (1 << AUTOGAIN) | +[SENSOR_PO1030] = (1 << EXPOSURE) | + (1 << AUTOGAIN) | + (1 << GAIN) | (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_PO2030N] = (1 << AUTOGAIN) | - (1 << FREQ), +[SENSOR_PO2030N] = (1 << FREQ), -[SENSOR_SOI768] = (1 << AUTOGAIN) | +[SENSOR_SOI768] = (1 << EXPOSURE) | + (1 << AUTOGAIN) | + (1 << GAIN) | (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_SP80708] = (1 << AUTOGAIN) | +[SENSOR_SP80708] = (1 << EXPOSURE) | + (1 << AUTOGAIN) | + (1 << GAIN) | (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), @@ -1242,14 +1297,6 @@ static const u8 po2030n_sensor_param1[][8] = { {0xa1, 0x6e, 0x05, 0x6f, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x6e, 0x07, 0x25, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10}, - {0xc1, 0x6e, 0x16, 0x52, 0x40, 0x48, 0x00, 0x10}, -/*after start*/ - {0xa1, 0x6e, 0x15, 0x0f, 0x00, 0x00, 0x00, 0x10}, - {DELAY, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 5ms */ - {0xa1, 0x6e, 0x1a, 0x05, 0x00, 0x00, 0x00, 0x10}, - {DELAY, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 5ms */ - {0xa1, 0x6e, 0x1b, 0x53, 0x00, 0x00, 0x00, 0x10}, {} }; @@ -1858,7 +1905,7 @@ static int sd_init(struct gspca_dev *gspca_dev) return gspca_dev->usb_err; } -static u32 setexposure(struct gspca_dev *gspca_dev, +static u32 expo_adjust(struct gspca_dev *gspca_dev, u32 expo) { struct sd *sd = (struct sd *) gspca_dev; @@ -1982,28 +2029,28 @@ static void setbrightness(struct gspca_dev *gspca_dev) expo = 0x002dc6c0; else if (expo < 0x02a0) expo = 0x02a0; - sd->exposure = setexposure(gspca_dev, expo); + sd->exposure = expo_adjust(gspca_dev, expo); break; case SENSOR_MI0360: case SENSOR_MO4000: expo = brightness << 4; - sd->exposure = setexposure(gspca_dev, expo); + sd->exposure = expo_adjust(gspca_dev, expo); break; case SENSOR_MI0360B: expo = brightness << 2; - sd->exposure = setexposure(gspca_dev, expo); + sd->exposure = expo_adjust(gspca_dev, expo); break; case SENSOR_GC0307: expo = brightness; - sd->exposure = setexposure(gspca_dev, expo); + sd->exposure = expo_adjust(gspca_dev, expo); return; /* don't set the Y offset */ case SENSOR_MT9V111: expo = brightness << 2; - sd->exposure = setexposure(gspca_dev, expo); + sd->exposure = expo_adjust(gspca_dev, expo); return; /* don't set the Y offset */ case SENSOR_OM6802: expo = brightness << 2; - sd->exposure = setexposure(gspca_dev, expo); + sd->exposure = expo_adjust(gspca_dev, expo); return; /* Y offset already set */ } @@ -2112,6 +2159,23 @@ static void setgamma(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x20, gamma, sizeof gamma); } +static void setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PO2030N) { + u8 rexpo[] = /* 1a: expo H, 1b: expo M */ + {0xa1, 0x6e, 0x1a, 0x00, 0x40, 0x00, 0x00, 0x10}; + + rexpo[3] = sd->ctrls[EXPOSURE].val >> 8; + i2c_w8(gspca_dev, rexpo); + msleep(6); + rexpo[2] = 0x1b; + rexpo[3] = sd->ctrls[EXPOSURE].val; + i2c_w8(gspca_dev, rexpo); + } +} + static void setautogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -2139,6 +2203,19 @@ static void setautogain(struct gspca_dev *gspca_dev) sd->ag_cnt = -1; } +static void setgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PO2030N) { + u8 rgain[] = /* 15: gain */ + {0xa1, 0x6e, 0x15, 0x00, 0x40, 0x00, 0x00, 0x15}; + + rgain[3] = sd->ctrls[GAIN].val; + i2c_w8(gspca_dev, rgain); + } +} + static void sethvflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -2623,6 +2700,10 @@ static int sd_start(struct gspca_dev *gspca_dev) setcontrast(gspca_dev); setcolors(gspca_dev); setautogain(gspca_dev); + if (!(gspca_dev->ctrl_inac & ((1 << EXPOSURE) | (1 << GAIN)))) { + setexposure(gspca_dev); + setgain(gspca_dev); + } setfreq(gspca_dev); sd->pktsz = sd->npkt = 0; @@ -2719,6 +2800,12 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } } +/* !! coarse_grained_expo_autogain is not used !! */ +#define exp_too_low_cnt bridge +#define exp_too_high_cnt sensor + +#include "autogain_functions.h" + static void do_autogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -2736,6 +2823,13 @@ static void do_autogain(struct gspca_dev *gspca_dev) delta = atomic_read(&sd->avg_lum); PDEBUG(D_FRAM, "mean lum %d", delta); + + if (sd->sensor == SENSOR_PO2030N) { + auto_gain_n_exposure(gspca_dev, delta, luma_mean, luma_delta, + 15, 1024); + return; + } + if (delta < luma_mean - luma_delta || delta > luma_mean + luma_delta) { switch (sd->sensor) { @@ -2744,7 +2838,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) expotimes += (luma_mean - delta) >> 6; if (expotimes < 0) expotimes = 0; - sd->exposure = setexposure(gspca_dev, + sd->exposure = expo_adjust(gspca_dev, (unsigned int) expotimes); break; case SENSOR_HV7131R: @@ -2752,7 +2846,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) expotimes += (luma_mean - delta) >> 4; if (expotimes < 0) expotimes = 0; - sd->exposure = setexposure(gspca_dev, + sd->exposure = expo_adjust(gspca_dev, (unsigned int) (expotimes << 8)); break; case SENSOR_OM6802: @@ -2761,7 +2855,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) expotimes += (luma_mean - delta) >> 2; if (expotimes < 0) expotimes = 0; - sd->exposure = setexposure(gspca_dev, + sd->exposure = expo_adjust(gspca_dev, (unsigned int) expotimes); setredblue(gspca_dev); break; @@ -2773,7 +2867,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) expotimes += (luma_mean - delta) >> 6; if (expotimes < 0) expotimes = 0; - sd->exposure = setexposure(gspca_dev, + sd->exposure = expo_adjust(gspca_dev, (unsigned int) expotimes); setredblue(gspca_dev); break; @@ -2948,16 +3042,18 @@ marker_found: } } -static int sd_get_jcomp(struct gspca_dev *gspca_dev, - struct v4l2_jpegcompression *jcomp) +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - memset(jcomp, 0, sizeof *jcomp); - jcomp->quality = sd->quality; - jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT - | V4L2_JPEG_MARKER_DQT; - return 0; + sd->ctrls[AUTOGAIN].val = val; + if (val) + gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN); + else + gspca_dev->ctrl_inac &= ~(1 << EXPOSURE) & ~(1 << GAIN); + if (gspca_dev->streaming) + setautogain(gspca_dev); + return gspca_dev->usb_err; } static int sd_querymenu(struct gspca_dev *gspca_dev, @@ -3012,7 +3108,6 @@ static const struct sd_desc sd_desc = { .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, - .get_jcomp = sd_get_jcomp, .querymenu = sd_querymenu, #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) .int_pkt_scan = sd_int_pkt_scan, diff --git a/drivers/media/video/gspca/stv06xx/Makefile b/drivers/media/video/gspca/stv06xx/Makefile index 5b318faf9aa8..38bc41061d83 100644 --- a/drivers/media/video/gspca/stv06xx/Makefile +++ b/drivers/media/video/gspca/stv06xx/Makefile @@ -6,5 +6,5 @@ gspca_stv06xx-objs := stv06xx.o \ stv06xx_pb0100.o \ stv06xx_st6422.o -ccflags-y += -Idrivers/media/video/gspca +ccflags-y += -I$(srctree)/drivers/media/video/gspca diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index b9e15bb0328b..7d9a4f1be9dc 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -1,7 +1,7 @@ /* - * Z-Star/Vimicro zc301/zc302p/vc30x library + * Z-Star/Vimicro zc301/zc302p/vc30x driver * - * Copyright (C) 2009-2011 Jean-Francois Moine <http://moinejf.free.fr> + * Copyright (C) 2009-2012 Jean-Francois Moine <http://moinejf.free.fr> * Copyright (C) 2004 2005 2006 Michel Xhaard mxhaard@magic.fr * * This program is free software; you can redistribute it and/or modify @@ -21,8 +21,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define MODULE_NAME "zc3xx" - #include <linux/input.h> #include "gspca.h" #include "jpeg.h" @@ -34,7 +32,7 @@ MODULE_LICENSE("GPL"); static int force_sensor = -1; -#define QUANT_VAL 1 /* quantization table */ +#define REG08_DEF 3 /* default JPEG compression (70%) */ #include "zc3xx-reg.h" /* controls */ @@ -46,6 +44,7 @@ enum e_ctrl { AUTOGAIN, LIGHTFREQ, SHARPNESS, + QUALITY, NCTRLS /* number of controls */ }; @@ -57,10 +56,10 @@ struct sd { struct gspca_ctrl ctrls[NCTRLS]; - u8 quality; /* image quality */ -#define QUALITY_MIN 50 -#define QUALITY_MAX 80 -#define QUALITY_DEF 70 + struct work_struct work; + struct workqueue_struct *work_thread; + + u8 reg08; /* webcam compression quality */ u8 bridge; u8 sensor; /* Type of image sensor chip */ @@ -101,6 +100,7 @@ static void setexposure(struct gspca_dev *gspca_dev); static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); static void setlightfreq(struct gspca_dev *gspca_dev); static void setsharpness(struct gspca_dev *gspca_dev); +static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val); static const struct ctrl sd_ctrls[NCTRLS] = { [BRIGHTNESS] = { @@ -188,6 +188,18 @@ static const struct ctrl sd_ctrls[NCTRLS] = { }, .set_control = setsharpness }, +[QUALITY] = { + { + .id = V4L2_CID_JPEG_COMPRESSION_QUALITY, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Compression Quality", + .minimum = 40, + .maximum = 70, + .step = 1, + .default_value = 70 /* updated in sd_init() */ + }, + .set = sd_setquality + }, }; static const struct v4l2_pix_format vga_mode[] = { @@ -229,6 +241,9 @@ static const struct v4l2_pix_format sif_mode[] = { .priv = 0}, }; +/* bridge reg08 -> JPEG quality conversion table */ +static u8 jpeg_qual[] = {40, 50, 60, 70, /*80*/}; + /* usb exchanges */ struct usb_action { u8 req; @@ -3894,7 +3909,6 @@ static const struct usb_action pas106b_Initial[] = { /* 352x288 */ /* Gains */ {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF}, {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xa0, ZC3XX_R11D_GLOBALGAIN}, {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* Auto correction */ {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, @@ -5640,7 +5654,7 @@ static const struct usb_action gc0303_NoFlikerScale[] = { {} }; -static u8 reg_r_i(struct gspca_dev *gspca_dev, +static u8 reg_r(struct gspca_dev *gspca_dev, u16 index) { int ret; @@ -5655,24 +5669,14 @@ static u8 reg_r_i(struct gspca_dev *gspca_dev, index, gspca_dev->usb_buf, 1, 500); if (ret < 0) { - pr_err("reg_r_i err %d\n", ret); + pr_err("reg_r err %d\n", ret); gspca_dev->usb_err = ret; return 0; } return gspca_dev->usb_buf[0]; } -static u8 reg_r(struct gspca_dev *gspca_dev, - u16 index) -{ - u8 ret; - - ret = reg_r_i(gspca_dev, index); - PDEBUG(D_USBI, "reg r [%04x] -> %02x", index, ret); - return ret; -} - -static void reg_w_i(struct gspca_dev *gspca_dev, +static void reg_w(struct gspca_dev *gspca_dev, u8 value, u16 index) { @@ -5692,14 +5696,6 @@ static void reg_w_i(struct gspca_dev *gspca_dev, } } -static void reg_w(struct gspca_dev *gspca_dev, - u8 value, - u16 index) -{ - PDEBUG(D_USBO, "reg w [%04x] = %02x", index, value); - reg_w_i(gspca_dev, value, index); -} - static u16 i2c_read(struct gspca_dev *gspca_dev, u8 reg) { @@ -5708,16 +5704,14 @@ static u16 i2c_read(struct gspca_dev *gspca_dev, if (gspca_dev->usb_err < 0) return 0; - reg_w_i(gspca_dev, reg, 0x0092); - reg_w_i(gspca_dev, 0x02, 0x0090); /* <- read command */ + reg_w(gspca_dev, reg, 0x0092); + reg_w(gspca_dev, 0x02, 0x0090); /* <- read command */ msleep(20); - retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ + retbyte = reg_r(gspca_dev, 0x0091); /* read status */ if (retbyte != 0x00) pr_err("i2c_r status error %02x\n", retbyte); - retval = reg_r_i(gspca_dev, 0x0095); /* read Lowbyte */ - retval |= reg_r_i(gspca_dev, 0x0096) << 8; /* read Hightbyte */ - PDEBUG(D_USBI, "i2c r [%02x] -> %04x (%02x)", - reg, retval, retbyte); + retval = reg_r(gspca_dev, 0x0095); /* read Lowbyte */ + retval |= reg_r(gspca_dev, 0x0096) << 8; /* read Hightbyte */ return retval; } @@ -5730,16 +5724,14 @@ static u8 i2c_write(struct gspca_dev *gspca_dev, if (gspca_dev->usb_err < 0) return 0; - reg_w_i(gspca_dev, reg, 0x92); - reg_w_i(gspca_dev, valL, 0x93); - reg_w_i(gspca_dev, valH, 0x94); - reg_w_i(gspca_dev, 0x01, 0x90); /* <- write command */ + reg_w(gspca_dev, reg, 0x92); + reg_w(gspca_dev, valL, 0x93); + reg_w(gspca_dev, valH, 0x94); + reg_w(gspca_dev, 0x01, 0x90); /* <- write command */ msleep(1); - retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ + retbyte = reg_r(gspca_dev, 0x0091); /* read status */ if (retbyte != 0x00) pr_err("i2c_w status error %02x\n", retbyte); - PDEBUG(D_USBO, "i2c w [%02x] = %02x%02x (%02x)", - reg, valH, valL, retbyte); return retbyte; } @@ -5906,6 +5898,8 @@ static void getexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + if (sd->sensor != SENSOR_HV7131R) + return; sd->ctrls[EXPOSURE].val = (i2c_read(gspca_dev, 0x25) << 9) | (i2c_read(gspca_dev, 0x26) << 1) | (i2c_read(gspca_dev, 0x27) >> 7); @@ -5916,6 +5910,8 @@ static void setexposure(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int val; + if (sd->sensor != SENSOR_HV7131R) + return; val = sd->ctrls[EXPOSURE].val; i2c_write(gspca_dev, 0x25, val >> 9, 0x00); i2c_write(gspca_dev, 0x26, val >> 1, 0x00); @@ -5925,32 +5921,20 @@ static void setexposure(struct gspca_dev *gspca_dev) static void setquality(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - u8 frxt; + s8 reg07; + reg07 = 0; switch (sd->sensor) { - case SENSOR_ADCM2700: - case SENSOR_GC0305: - case SENSOR_HV7131B: - case SENSOR_HV7131R: case SENSOR_OV7620: + reg07 = 0x30; + break; + case SENSOR_HV7131R: case SENSOR_PAS202B: - case SENSOR_PO2030: - return; + return; /* done by work queue */ } -/*fixme: is it really 0008 0007 0018 for all other sensors? */ - reg_w(gspca_dev, QUANT_VAL, 0x0008); - frxt = 0x30; - reg_w(gspca_dev, frxt, 0x0007); -#if QUANT_VAL == 0 || QUANT_VAL == 1 || QUANT_VAL == 2 - frxt = 0xff; -#elif QUANT_VAL == 3 - frxt = 0xf0; -#elif QUANT_VAL == 4 - frxt = 0xe0; -#else - frxt = 0x20; -#endif - reg_w(gspca_dev, frxt, 0x0018); + reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING); + if (reg07 != 0) + reg_w(gspca_dev, reg07, 0x0007); } /* Matches the sensor's internal frame rate to the lighting frequency. @@ -6084,6 +6068,115 @@ static void setautogain(struct gspca_dev *gspca_dev) reg_w(gspca_dev, autoval, 0x0180); } +/* update the transfer parameters */ +/* This function is executed from a work queue. */ +/* The exact use of the bridge registers 07 and 08 is not known. + * The following algorithm has been adapted from ms-win traces */ +static void transfer_update(struct work_struct *work) +{ + struct sd *sd = container_of(work, struct sd, work); + struct gspca_dev *gspca_dev = &sd->gspca_dev; + int change, good; + u8 reg07, reg11; + + /* synchronize with the main driver and initialize the registers */ + mutex_lock(&gspca_dev->usb_lock); + reg07 = 0; /* max */ + reg_w(gspca_dev, reg07, 0x0007); + reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING); + mutex_unlock(&gspca_dev->usb_lock); + + good = 0; + for (;;) { + msleep(100); + + /* get the transfer status */ + /* the bit 0 of the bridge register 11 indicates overflow */ + mutex_lock(&gspca_dev->usb_lock); + if (!gspca_dev->present || !gspca_dev->streaming) + goto err; + reg11 = reg_r(gspca_dev, 0x0011); + if (gspca_dev->usb_err < 0 + || !gspca_dev->present || !gspca_dev->streaming) + goto err; + + change = reg11 & 0x01; + if (change) { /* overflow */ + switch (reg07) { + case 0: /* max */ + reg07 = sd->sensor == SENSOR_HV7131R + ? 0x30 : 0x32; + if (sd->reg08 != 0) { + change = 3; + sd->reg08--; + } + break; + case 0x32: + reg07 -= 4; + break; + default: + reg07 -= 2; + break; + case 2: + change = 0; /* already min */ + break; + } + good = 0; + } else { /* no overflow */ + if (reg07 != 0) { /* if not max */ + good++; + if (good >= 10) { + good = 0; + change = 1; + reg07 += 2; + switch (reg07) { + case 0x30: + if (sd->sensor == SENSOR_PAS202B) + reg07 += 2; + break; + case 0x32: + case 0x34: + reg07 = 0; + break; + } + } + } else { /* reg07 max */ + if (sd->reg08 < sizeof jpeg_qual - 1) { + good++; + if (good > 10) { + sd->reg08++; + change = 2; + } + } + } + } + if (change) { + if (change & 1) { + reg_w(gspca_dev, reg07, 0x0007); + if (gspca_dev->usb_err < 0 + || !gspca_dev->present + || !gspca_dev->streaming) + goto err; + } + if (change & 2) { + reg_w(gspca_dev, sd->reg08, + ZC3XX_R008_CLOCKSETTING); + if (gspca_dev->usb_err < 0 + || !gspca_dev->present + || !gspca_dev->streaming) + goto err; + sd->ctrls[QUALITY].val = jpeg_qual[sd->reg08]; + jpeg_set_qual(sd->jpeg_hdr, + jpeg_qual[sd->reg08]); + } + } + mutex_unlock(&gspca_dev->usb_lock); + } + return; +err: + mutex_unlock(&gspca_dev->usb_lock); +} + static void send_unknown(struct gspca_dev *gspca_dev, int sensor) { reg_w(gspca_dev, 0x01, 0x0000); /* bridge reset */ @@ -6411,7 +6504,9 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor = id->driver_info; gspca_dev->cam.ctrls = sd->ctrls; - sd->quality = QUALITY_DEF; + sd->reg08 = REG08_DEF; + + INIT_WORK(&sd->work, transfer_update); return 0; } @@ -6464,6 +6559,27 @@ static int sd_init(struct gspca_dev *gspca_dev) [SENSOR_PO2030] = 1, [SENSOR_TAS5130C] = 1, }; + static const u8 reg08_tb[SENSOR_MAX] = { + [SENSOR_ADCM2700] = 1, + [SENSOR_CS2102] = 3, + [SENSOR_CS2102K] = 3, + [SENSOR_GC0303] = 2, + [SENSOR_GC0305] = 3, + [SENSOR_HDCS2020] = 1, + [SENSOR_HV7131B] = 3, + [SENSOR_HV7131R] = 3, + [SENSOR_ICM105A] = 3, + [SENSOR_MC501CB] = 3, + [SENSOR_MT9V111_1] = 3, + [SENSOR_MT9V111_3] = 3, + [SENSOR_OV7620] = 1, + [SENSOR_OV7630C] = 3, + [SENSOR_PAS106] = 3, + [SENSOR_PAS202B] = 3, + [SENSOR_PB0330] = 3, + [SENSOR_PO2030] = 2, + [SENSOR_TAS5130C] = 3, + }; sensor = zcxx_probeSensor(gspca_dev); if (sensor >= 0) @@ -6528,7 +6644,6 @@ static int sd_init(struct gspca_dev *gspca_dev) case 0x0e: PDEBUG(D_PROBE, "Find Sensor PAS202B"); sd->sensor = SENSOR_PAS202B; -/* sd->sharpness = 1; */ break; case 0x0f: PDEBUG(D_PROBE, "Find Sensor PAS106"); @@ -6616,13 +6731,21 @@ static int sd_init(struct gspca_dev *gspca_dev) } sd->ctrls[GAMMA].def = gamma[sd->sensor]; + sd->reg08 = reg08_tb[sd->sensor]; + sd->ctrls[QUALITY].def = jpeg_qual[sd->reg08]; + sd->ctrls[QUALITY].min = jpeg_qual[0]; + sd->ctrls[QUALITY].max = jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1]; switch (sd->sensor) { case SENSOR_HV7131R: + gspca_dev->ctrl_dis = (1 << QUALITY); break; case SENSOR_OV7630C: gspca_dev->ctrl_dis = (1 << LIGHTFREQ) | (1 << EXPOSURE); break; + case SENSOR_PAS202B: + gspca_dev->ctrl_dis = (1 << QUALITY) | (1 << EXPOSURE); + break; default: gspca_dev->ctrl_dis = (1 << EXPOSURE); break; @@ -6685,7 +6808,6 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ - jpeg_set_qual(sd->jpeg_hdr, sd->quality); mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; switch (sd->sensor) { @@ -6761,10 +6883,9 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_r(gspca_dev, 0x0180); /* from win */ reg_w(gspca_dev, 0x00, 0x0180); break; - default: - setquality(gspca_dev); - break; } + setquality(gspca_dev); + jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08]); setlightfreq(gspca_dev); switch (sd->sensor) { @@ -6776,8 +6897,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x40, 0x0117); break; case SENSOR_HV7131R: - if (!sd->ctrls[AUTOGAIN].val) - setexposure(gspca_dev); + setexposure(gspca_dev); reg_w(gspca_dev, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN); break; case SENSOR_GC0305: @@ -6802,13 +6922,19 @@ static int sd_start(struct gspca_dev *gspca_dev) } setautogain(gspca_dev); - switch (sd->sensor) { - case SENSOR_PO2030: - msleep(50); - reg_w(gspca_dev, 0x00, 0x0007); /* (from win traces) */ - reg_w(gspca_dev, 0x02, ZC3XX_R008_CLOCKSETTING); - break; + + /* start the transfer update thread if needed */ + if (gspca_dev->usb_err >= 0) { + switch (sd->sensor) { + case SENSOR_HV7131R: + case SENSOR_PAS202B: + sd->work_thread = + create_singlethread_workqueue(KBUILD_MODNAME); + queue_work(sd->work_thread, &sd->work); + break; + } } + return gspca_dev->usb_err; } @@ -6817,6 +6943,12 @@ static void sd_stop0(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + if (sd->work_thread != NULL) { + mutex_unlock(&gspca_dev->usb_lock); + destroy_workqueue(sd->work_thread); + mutex_lock(&gspca_dev->usb_lock); + sd->work_thread = NULL; + } if (!gspca_dev->present) return; send_unknown(gspca_dev, sd->sensor); @@ -6893,19 +7025,33 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, return -EINVAL; } +static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + + for (i = 0; i < ARRAY_SIZE(jpeg_qual) - 1; i++) { + if (val <= jpeg_qual[i]) + break; + } + if (i > 0 + && i == sd->reg08 + && val < jpeg_qual[sd->reg08]) + i--; + sd->reg08 = i; + sd->ctrls[QUALITY].val = jpeg_qual[i]; + if (gspca_dev->streaming) + jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val); + return gspca_dev->usb_err; +} + static int sd_set_jcomp(struct gspca_dev *gspca_dev, struct v4l2_jpegcompression *jcomp) { struct sd *sd = (struct sd *) gspca_dev; - if (jcomp->quality < QUALITY_MIN) - sd->quality = QUALITY_MIN; - else if (jcomp->quality > QUALITY_MAX) - sd->quality = QUALITY_MAX; - else - sd->quality = jcomp->quality; - if (gspca_dev->streaming) - jpeg_set_qual(sd->jpeg_hdr, sd->quality); + sd_setquality(gspca_dev, jcomp->quality); + jcomp->quality = sd->ctrls[QUALITY].val; return gspca_dev->usb_err; } @@ -6915,7 +7061,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; memset(jcomp, 0, sizeof *jcomp); - jcomp->quality = sd->quality; + jcomp->quality = sd->ctrls[QUALITY].val; jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT; return 0; @@ -6938,7 +7084,7 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, #endif static const struct sd_desc sd_desc = { - .name = MODULE_NAME, + .name = KBUILD_MODNAME, .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, @@ -7023,7 +7169,7 @@ static int sd_probe(struct usb_interface *intf, /* USB driver */ static struct usb_driver sd_driver = { - .name = MODULE_NAME, + .name = KBUILD_MODNAME, .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, diff --git a/drivers/media/video/imx074.c b/drivers/media/video/imx074.c index eec75bb57203..351e9bafe8fe 100644 --- a/drivers/media/video/imx074.c +++ b/drivers/media/video/imx074.c @@ -468,18 +468,7 @@ static struct i2c_driver imx074_i2c_driver = { .id_table = imx074_id, }; -static int __init imx074_mod_init(void) -{ - return i2c_add_driver(&imx074_i2c_driver); -} - -static void __exit imx074_mod_exit(void) -{ - i2c_del_driver(&imx074_i2c_driver); -} - -module_init(imx074_mod_init); -module_exit(imx074_mod_exit); +module_i2c_driver(imx074_i2c_driver); MODULE_DESCRIPTION("Sony IMX074 Camera driver"); MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); diff --git a/drivers/media/video/indycam.c b/drivers/media/video/indycam.c index e5ed4db32e7b..548236333cce 100644 --- a/drivers/media/video/indycam.c +++ b/drivers/media/video/indycam.c @@ -387,15 +387,4 @@ static struct i2c_driver indycam_driver = { .id_table = indycam_id, }; -static __init int init_indycam(void) -{ - return i2c_add_driver(&indycam_driver); -} - -static __exit void exit_indycam(void) -{ - i2c_del_driver(&indycam_driver); -} - -module_init(init_indycam); -module_exit(exit_indycam); +module_i2c_driver(indycam_driver); diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index a7c41d32f414..04f192a0398a 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -471,7 +471,7 @@ static const struct i2c_device_id ir_kbd_id[] = { { } }; -static struct i2c_driver driver = { +static struct i2c_driver ir_kbd_driver = { .driver = { .name = "ir-kbd-i2c", }, @@ -480,21 +480,10 @@ static struct i2c_driver driver = { .id_table = ir_kbd_id, }; +module_i2c_driver(ir_kbd_driver); + /* ----------------------------------------------------------------------- */ MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, Ulrich Mueller"); MODULE_DESCRIPTION("input driver for i2c IR remote controls"); MODULE_LICENSE("GPL"); - -static int __init ir_init(void) -{ - return i2c_add_driver(&driver); -} - -static void __exit ir_fini(void) -{ - i2c_del_driver(&driver); -} - -module_init(ir_init); -module_exit(ir_fini); diff --git a/drivers/media/video/ivtv/Makefile b/drivers/media/video/ivtv/Makefile index 71ab76a5ab26..77de8a45b46f 100644 --- a/drivers/media/video/ivtv/Makefile +++ b/drivers/media/video/ivtv/Makefile @@ -7,8 +7,8 @@ ivtv-objs := ivtv-routing.o ivtv-cards.o ivtv-controls.o \ obj-$(CONFIG_VIDEO_IVTV) += ivtv.o obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.o -ccflags-y += -Idrivers/media/video -ccflags-y += -Idrivers/media/common/tuners -ccflags-y += -Idrivers/media/dvb/dvb-core -ccflags-y += -Idrivers/media/dvb/frontends +ccflags-y += -I$(srctree)/drivers/media/video +ccflags-y += -I$(srctree)/drivers/media/common/tuners +ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core +ccflags-y += -I$(srctree)/drivers/media/dvb/frontends diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c index b31ee1bceef8..c60424601cb9 100644 --- a/drivers/media/video/ivtv/ivtv-controls.c +++ b/drivers/media/video/ivtv/ivtv-controls.c @@ -21,6 +21,7 @@ #include "ivtv-driver.h" #include "ivtv-ioctl.h" #include "ivtv-controls.h" +#include "ivtv-mailbox.h" static int ivtv_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt) { @@ -99,3 +100,64 @@ struct cx2341x_handler_ops ivtv_cxhdl_ops = { .s_video_encoding = ivtv_s_video_encoding, .s_stream_vbi_fmt = ivtv_s_stream_vbi_fmt, }; + +int ivtv_g_pts_frame(struct ivtv *itv, s64 *pts, s64 *frame) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + + if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) { + *pts = (s64)((u64)itv->last_dec_timing[2] << 32) | + (u64)itv->last_dec_timing[1]; + *frame = itv->last_dec_timing[0]; + return 0; + } + *pts = 0; + *frame = 0; + if (atomic_read(&itv->decoding)) { + if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) { + IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n"); + return -EIO; + } + memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing)); + set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags); + *pts = (s64)((u64) data[2] << 32) | (u64) data[1]; + *frame = data[0]; + /*timing->scr = (u64) (((u64) data[4] << 32) | (u64) (data[3]));*/ + } + return 0; +} + +static int ivtv_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ivtv *itv = container_of(ctrl->handler, struct ivtv, cxhdl.hdl); + + switch (ctrl->id) { + /* V4L2_CID_MPEG_VIDEO_DEC_PTS and V4L2_CID_MPEG_VIDEO_DEC_FRAME + control cluster */ + case V4L2_CID_MPEG_VIDEO_DEC_PTS: + return ivtv_g_pts_frame(itv, &itv->ctrl_pts->val64, + &itv->ctrl_frame->val64); + } + return 0; +} + +static int ivtv_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ivtv *itv = container_of(ctrl->handler, struct ivtv, cxhdl.hdl); + + switch (ctrl->id) { + /* V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK and MULTILINGUAL_PLAYBACK + control cluster */ + case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: + itv->audio_stereo_mode = itv->ctrl_audio_playback->val - 1; + itv->audio_bilingual_mode = itv->ctrl_audio_multilingual_playback->val - 1; + ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); + break; + } + return 0; +} + +const struct v4l2_ctrl_ops ivtv_hdl_out_ops = { + .s_ctrl = ivtv_s_ctrl, + .g_volatile_ctrl = ivtv_g_volatile_ctrl, +}; diff --git a/drivers/media/video/ivtv/ivtv-controls.h b/drivers/media/video/ivtv/ivtv-controls.h index d12893dd0183..3999e6358312 100644 --- a/drivers/media/video/ivtv/ivtv-controls.h +++ b/drivers/media/video/ivtv/ivtv-controls.h @@ -22,5 +22,7 @@ #define IVTV_CONTROLS_H extern struct cx2341x_handler_ops ivtv_cxhdl_ops; +extern const struct v4l2_ctrl_ops ivtv_hdl_out_ops; +int ivtv_g_pts_frame(struct ivtv *itv, s64 *pts, s64 *frame); #endif diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 3949b7dc2368..679262ed13bc 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -55,7 +55,7 @@ #include "ivtv-routing.h" #include "ivtv-controls.h" #include "ivtv-gpio.h" - +#include <linux/dma-mapping.h> #include <media/tveeprom.h> #include <media/saa7115.h> #include <media/v4l2-chip-ident.h> @@ -99,7 +99,7 @@ static int i2c_clock_period[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, static unsigned int cardtype_c = 1; static unsigned int tuner_c = 1; -static bool radio_c = 1; +static int radio_c = 1; static unsigned int i2c_clock_period_c = 1; static char pal[] = "---"; static char secam[] = "--"; @@ -139,7 +139,7 @@ static int tunertype = -1; static int newi2c = -1; module_param_array(tuner, int, &tuner_c, 0644); -module_param_array(radio, bool, &radio_c, 0644); +module_param_array(radio, int, &radio_c, 0644); module_param_array(cardtype, int, &cardtype_c, 0644); module_param_string(pal, pal, sizeof(pal), 0644); module_param_string(secam, secam, sizeof(secam), 0644); @@ -744,8 +744,6 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv) itv->cur_dma_stream = -1; itv->cur_pio_stream = -1; - itv->audio_stereo_mode = AUDIO_STEREO; - itv->audio_bilingual_mode = AUDIO_MONO_LEFT; /* Ctrls */ itv->speed = 1000; @@ -815,7 +813,7 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev, IVTV_ERR("Can't enable device!\n"); return -EIO; } - if (pci_set_dma_mask(pdev, 0xffffffff)) { + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { IVTV_ERR("No suitable DMA available.\n"); return -EIO; } @@ -1200,6 +1198,32 @@ static int __devinit ivtv_probe(struct pci_dev *pdev, itv->tuner_std = itv->std; if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { + struct v4l2_ctrl_handler *hdl = itv->v4l2_dev.ctrl_handler; + + itv->ctrl_pts = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops, + V4L2_CID_MPEG_VIDEO_DEC_PTS, 0, 0, 1, 0); + itv->ctrl_frame = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops, + V4L2_CID_MPEG_VIDEO_DEC_FRAME, 0, 0x7fffffff, 1, 0); + /* Note: V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO is not supported, + mask that menu item. */ + itv->ctrl_audio_playback = + v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops, + V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK, + V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO, + 1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO, + V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO); + itv->ctrl_audio_multilingual_playback = + v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops, + V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK, + V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO, + 1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO, + V4L2_MPEG_AUDIO_DEC_PLAYBACK_LEFT); + if (hdl->error) { + retval = hdl->error; + goto free_i2c; + } + v4l2_ctrl_cluster(2, &itv->ctrl_pts); + v4l2_ctrl_cluster(2, &itv->ctrl_audio_playback); ivtv_call_all(itv, video, s_std_output, itv->std); /* Turn off the output signal. The mpeg decoder is not yet active so without this you would get a green image until the @@ -1236,6 +1260,7 @@ free_streams: free_irq: free_irq(itv->pdev->irq, (void *)itv); free_i2c: + v4l2_ctrl_handler_free(&itv->cxhdl.hdl); exit_ivtv_i2c(itv); free_io: ivtv_iounmap(itv); @@ -1375,7 +1400,7 @@ static void ivtv_remove(struct pci_dev *pdev) else type = IVTV_DEC_STREAM_TYPE_MPG; ivtv_stop_v4l2_decode_stream(&itv->streams[type], - VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0); + V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY, 0); } ivtv_halt_firmware(itv); } @@ -1391,6 +1416,8 @@ static void ivtv_remove(struct pci_dev *pdev) ivtv_streams_cleanup(itv, 1); ivtv_udma_free(itv); + v4l2_ctrl_handler_free(&itv->cxhdl.hdl); + exit_ivtv_i2c(itv); free_irq(itv->pdev->irq, (void *)itv); diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h index 06f3d78389bf..f767df943954 100644 --- a/drivers/media/video/ivtv/ivtv-driver.h +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -331,6 +331,7 @@ struct ivtv_stream { struct ivtv *itv; /* for ease of use */ const char *name; /* name of the stream */ int type; /* stream type */ + u32 caps; /* V4L2 capabilities */ struct v4l2_fh *fh; /* pointer to the streaming filehandle */ spinlock_t qlock; /* locks access to the queues */ @@ -630,6 +631,16 @@ struct ivtv { struct v4l2_device v4l2_dev; struct cx2341x_handler cxhdl; + struct { + /* PTS/Frame count control cluster */ + struct v4l2_ctrl *ctrl_pts; + struct v4l2_ctrl *ctrl_frame; + }; + struct { + /* Audio Playback control cluster */ + struct v4l2_ctrl *ctrl_audio_playback; + struct v4l2_ctrl *ctrl_audio_multilingual_playback; + }; struct v4l2_ctrl_handler hdl_gpio; struct v4l2_subdev sd_gpio; /* GPIO sub-device */ u16 instance; @@ -649,7 +660,6 @@ struct ivtv { u8 audio_stereo_mode; /* decoder setting how to handle stereo MPEG audio */ u8 audio_bilingual_mode; /* decoder setting how to handle bilingual MPEG audio */ - /* Locking */ spinlock_t lock; /* lock access to this struct */ struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */ diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index 2cd6c89b7d91..c9663e885b9f 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -900,7 +900,7 @@ int ivtv_v4l2_close(struct file *filp) if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) { struct ivtv_stream *s_vout = &itv->streams[IVTV_DEC_STREAM_TYPE_VOUT]; - ivtv_stop_decoding(id, VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0); + ivtv_stop_decoding(id, V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY, 0); /* If all output streams are closed, and if the user doesn't have IVTV_DEC_STREAM_TYPE_VOUT open, then disable CC on TV-out. */ diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index c4bc48143098..5452beef8e11 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -246,34 +246,40 @@ static int ivtv_validate_speed(int cur_speed, int new_speed) } static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id, - struct video_command *vc, int try) + struct v4l2_decoder_cmd *dc, int try) { struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) return -EINVAL; - switch (vc->cmd) { - case VIDEO_CMD_PLAY: { - vc->flags = 0; - vc->play.speed = ivtv_validate_speed(itv->speed, vc->play.speed); - if (vc->play.speed < 0) - vc->play.format = VIDEO_PLAY_FMT_GOP; + switch (dc->cmd) { + case V4L2_DEC_CMD_START: { + dc->flags &= V4L2_DEC_CMD_START_MUTE_AUDIO; + dc->start.speed = ivtv_validate_speed(itv->speed, dc->start.speed); + if (dc->start.speed < 0) + dc->start.format = V4L2_DEC_START_FMT_GOP; + else + dc->start.format = V4L2_DEC_START_FMT_NONE; + if (dc->start.speed != 500 && dc->start.speed != 1500) + dc->flags = dc->start.speed == 1000 ? 0 : + V4L2_DEC_CMD_START_MUTE_AUDIO; if (try) break; + itv->speed_mute_audio = dc->flags & V4L2_DEC_CMD_START_MUTE_AUDIO; if (ivtv_set_output_mode(itv, OUT_MPG) != OUT_MPG) return -EBUSY; if (test_and_clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags)) { /* forces ivtv_set_speed to be called */ itv->speed = 0; } - return ivtv_start_decoding(id, vc->play.speed); + return ivtv_start_decoding(id, dc->start.speed); } - case VIDEO_CMD_STOP: - vc->flags &= VIDEO_CMD_STOP_IMMEDIATELY|VIDEO_CMD_STOP_TO_BLACK; - if (vc->flags & VIDEO_CMD_STOP_IMMEDIATELY) - vc->stop.pts = 0; + case V4L2_DEC_CMD_STOP: + dc->flags &= V4L2_DEC_CMD_STOP_IMMEDIATELY | V4L2_DEC_CMD_STOP_TO_BLACK; + if (dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) + dc->stop.pts = 0; if (try) break; if (atomic_read(&itv->decoding) == 0) return 0; @@ -281,22 +287,22 @@ static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id, return -EBUSY; itv->output_mode = OUT_NONE; - return ivtv_stop_v4l2_decode_stream(s, vc->flags, vc->stop.pts); + return ivtv_stop_v4l2_decode_stream(s, dc->flags, dc->stop.pts); - case VIDEO_CMD_FREEZE: - vc->flags &= VIDEO_CMD_FREEZE_TO_BLACK; + case V4L2_DEC_CMD_PAUSE: + dc->flags &= V4L2_DEC_CMD_PAUSE_TO_BLACK; if (try) break; if (itv->output_mode != OUT_MPG) return -EBUSY; if (atomic_read(&itv->decoding) > 0) { ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1, - (vc->flags & VIDEO_CMD_FREEZE_TO_BLACK) ? 1 : 0); + (dc->flags & V4L2_DEC_CMD_PAUSE_TO_BLACK) ? 1 : 0); set_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags); } break; - case VIDEO_CMD_CONTINUE: - vc->flags = 0; + case V4L2_DEC_CMD_RESUME: + dc->flags = 0; if (try) break; if (itv->output_mode != OUT_MPG) return -EBUSY; @@ -754,12 +760,15 @@ static int ivtv_s_register(struct file *file, void *fh, struct v4l2_dbg_register static int ivtv_querycap(struct file *file, void *fh, struct v4l2_capability *vcap) { - struct ivtv *itv = fh2id(fh)->itv; + struct ivtv_open_id *id = fh2id(file->private_data); + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver)); strlcpy(vcap->card, itv->card_name, sizeof(vcap->card)); snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(itv->pdev)); - vcap->capabilities = itv->v4l2_cap; /* capabilities */ + vcap->capabilities = itv->v4l2_cap | V4L2_CAP_DEVICE_CAPS; + vcap->device_caps = s->caps; return 0; } @@ -1476,8 +1485,6 @@ static int ivtv_log_status(struct file *file, void *fh) struct v4l2_audio audin; int i; - IVTV_INFO("================= START STATUS CARD #%d =================\n", - itv->instance); IVTV_INFO("Version: %s Card: %s\n", IVTV_VERSION, itv->card_name); if (itv->hw_flags & IVTV_HW_TVEEPROM) { struct tveeprom tv; @@ -1501,13 +1508,6 @@ static int ivtv_log_status(struct file *file, void *fh) "YUV Frames", "Passthrough", }; - static const char * const audio_modes[5] = { - "Stereo", - "Left", - "Right", - "Mono", - "Swapped" - }; static const char * const alpha_mode[4] = { "None", "Global", @@ -1536,9 +1536,6 @@ static int ivtv_log_status(struct file *file, void *fh) ivtv_get_output(itv, itv->active_output, &vidout); ivtv_get_audio_output(itv, 0, &audout); IVTV_INFO("Video Output: %s\n", vidout.name); - IVTV_INFO("Audio Output: %s (Stereo/Bilingual: %s/%s)\n", audout.name, - audio_modes[itv->audio_stereo_mode], - audio_modes[itv->audio_bilingual_mode]); if (mode < 0 || mode > OUT_PASSTHROUGH) mode = OUT_NONE; IVTV_INFO("Output Mode: %s\n", output_modes[mode]); @@ -1566,12 +1563,27 @@ static int ivtv_log_status(struct file *file, void *fh) IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n", (long long)itv->mpg_data_received, (long long)itv->vbi_data_inserted); - IVTV_INFO("================== END STATUS CARD #%d ==================\n", - itv->instance); - return 0; } +static int ivtv_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dec) +{ + struct ivtv_open_id *id = fh2id(file->private_data); + struct ivtv *itv = id->itv; + + IVTV_DEBUG_IOCTL("VIDIOC_DECODER_CMD %d\n", dec->cmd); + return ivtv_video_command(itv, id, dec, false); +} + +static int ivtv_try_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dec) +{ + struct ivtv_open_id *id = fh2id(file->private_data); + struct ivtv *itv = id->itv; + + IVTV_DEBUG_IOCTL("VIDIOC_TRY_DECODER_CMD %d\n", dec->cmd); + return ivtv_video_command(itv, id, dec, true); +} + static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) { struct ivtv_open_id *id = fh2id(filp->private_data); @@ -1605,9 +1617,15 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) return ivtv_yuv_prep_frame(itv, args); } + case IVTV_IOC_PASSTHROUGH_MODE: + IVTV_DEBUG_IOCTL("IVTV_IOC_PASSTHROUGH_MODE\n"); + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + return ivtv_passthrough_mode(itv, *(int *)arg != 0); + case VIDEO_GET_PTS: { - u32 data[CX2341X_MBOX_MAX_DATA]; - u64 *pts = arg; + s64 *pts = arg; + s64 frame; IVTV_DEBUG_IOCTL("VIDEO_GET_PTS\n"); if (s->type < IVTV_DEC_STREAM_TYPE_MPG) { @@ -1616,29 +1634,12 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) } if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) return -EINVAL; - - if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) { - *pts = (u64) ((u64)itv->last_dec_timing[2] << 32) | - (u64)itv->last_dec_timing[1]; - break; - } - *pts = 0; - if (atomic_read(&itv->decoding)) { - if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) { - IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n"); - return -EIO; - } - memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing)); - set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags); - *pts = (u64) ((u64) data[2] << 32) | (u64) data[1]; - /*timing->scr = (u64) (((u64) data[4] << 32) | (u64) (data[3]));*/ - } - break; + return ivtv_g_pts_frame(itv, pts, &frame); } case VIDEO_GET_FRAME_COUNT: { - u32 data[CX2341X_MBOX_MAX_DATA]; - u64 *frame = arg; + s64 *frame = arg; + s64 pts; IVTV_DEBUG_IOCTL("VIDEO_GET_FRAME_COUNT\n"); if (s->type < IVTV_DEC_STREAM_TYPE_MPG) { @@ -1647,71 +1648,58 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) } if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) return -EINVAL; - - if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) { - *frame = itv->last_dec_timing[0]; - break; - } - *frame = 0; - if (atomic_read(&itv->decoding)) { - if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) { - IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n"); - return -EIO; - } - memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing)); - set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags); - *frame = data[0]; - } - break; + return ivtv_g_pts_frame(itv, &pts, frame); } case VIDEO_PLAY: { - struct video_command vc; + struct v4l2_decoder_cmd dc; IVTV_DEBUG_IOCTL("VIDEO_PLAY\n"); - memset(&vc, 0, sizeof(vc)); - vc.cmd = VIDEO_CMD_PLAY; - return ivtv_video_command(itv, id, &vc, 0); + memset(&dc, 0, sizeof(dc)); + dc.cmd = V4L2_DEC_CMD_START; + return ivtv_video_command(itv, id, &dc, 0); } case VIDEO_STOP: { - struct video_command vc; + struct v4l2_decoder_cmd dc; IVTV_DEBUG_IOCTL("VIDEO_STOP\n"); - memset(&vc, 0, sizeof(vc)); - vc.cmd = VIDEO_CMD_STOP; - vc.flags = VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY; - return ivtv_video_command(itv, id, &vc, 0); + memset(&dc, 0, sizeof(dc)); + dc.cmd = V4L2_DEC_CMD_STOP; + dc.flags = V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY; + return ivtv_video_command(itv, id, &dc, 0); } case VIDEO_FREEZE: { - struct video_command vc; + struct v4l2_decoder_cmd dc; IVTV_DEBUG_IOCTL("VIDEO_FREEZE\n"); - memset(&vc, 0, sizeof(vc)); - vc.cmd = VIDEO_CMD_FREEZE; - return ivtv_video_command(itv, id, &vc, 0); + memset(&dc, 0, sizeof(dc)); + dc.cmd = V4L2_DEC_CMD_PAUSE; + return ivtv_video_command(itv, id, &dc, 0); } case VIDEO_CONTINUE: { - struct video_command vc; + struct v4l2_decoder_cmd dc; IVTV_DEBUG_IOCTL("VIDEO_CONTINUE\n"); - memset(&vc, 0, sizeof(vc)); - vc.cmd = VIDEO_CMD_CONTINUE; - return ivtv_video_command(itv, id, &vc, 0); + memset(&dc, 0, sizeof(dc)); + dc.cmd = V4L2_DEC_CMD_RESUME; + return ivtv_video_command(itv, id, &dc, 0); } case VIDEO_COMMAND: case VIDEO_TRY_COMMAND: { - struct video_command *vc = arg; + /* Note: struct v4l2_decoder_cmd has the same layout as + struct video_command */ + struct v4l2_decoder_cmd *dc = arg; int try = (cmd == VIDEO_TRY_COMMAND); if (try) - IVTV_DEBUG_IOCTL("VIDEO_TRY_COMMAND %d\n", vc->cmd); + IVTV_DEBUG_IOCTL("VIDEO_TRY_COMMAND %d\n", dc->cmd); else - IVTV_DEBUG_IOCTL("VIDEO_COMMAND %d\n", vc->cmd); - return ivtv_video_command(itv, id, vc, try); + IVTV_DEBUG_IOCTL("VIDEO_COMMAND %d\n", dc->cmd); + return ivtv_video_command(itv, id, dc, try); } case VIDEO_GET_EVENT: { @@ -1775,17 +1763,13 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) IVTV_DEBUG_IOCTL("AUDIO_CHANNEL_SELECT\n"); if (iarg > AUDIO_STEREO_SWAPPED) return -EINVAL; - itv->audio_stereo_mode = iarg; - ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); - return 0; + return v4l2_ctrl_s_ctrl(itv->ctrl_audio_playback, iarg); case AUDIO_BILINGUAL_CHANNEL_SELECT: IVTV_DEBUG_IOCTL("AUDIO_BILINGUAL_CHANNEL_SELECT\n"); if (iarg > AUDIO_STEREO_SWAPPED) return -EINVAL; - itv->audio_bilingual_mode = iarg; - ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); - return 0; + return v4l2_ctrl_s_ctrl(itv->ctrl_audio_multilingual_playback, iarg); default: return -EINVAL; @@ -1800,6 +1784,7 @@ static long ivtv_default(struct file *file, void *fh, bool valid_prio, if (!valid_prio) { switch (cmd) { + case IVTV_IOC_PASSTHROUGH_MODE: case VIDEO_PLAY: case VIDEO_STOP: case VIDEO_FREEZE: @@ -1825,6 +1810,7 @@ static long ivtv_default(struct file *file, void *fh, bool valid_prio, } case IVTV_IOC_DMA_FRAME: + case IVTV_IOC_PASSTHROUGH_MODE: case VIDEO_GET_PTS: case VIDEO_GET_FRAME_COUNT: case VIDEO_GET_EVENT: @@ -1889,6 +1875,8 @@ static const struct v4l2_ioctl_ops ivtv_ioctl_ops = { .vidioc_enum_fmt_vid_cap = ivtv_enum_fmt_vid_cap, .vidioc_encoder_cmd = ivtv_encoder_cmd, .vidioc_try_encoder_cmd = ivtv_try_encoder_cmd, + .vidioc_decoder_cmd = ivtv_decoder_cmd, + .vidioc_try_decoder_cmd = ivtv_try_decoder_cmd, .vidioc_enum_fmt_vid_out = ivtv_enum_fmt_vid_out, .vidioc_g_fmt_vid_cap = ivtv_g_fmt_vid_cap, .vidioc_g_fmt_vbi_cap = ivtv_g_fmt_vbi_cap, diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index c6e28b4ebbed..7ea5ca7f012b 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -78,60 +78,73 @@ static struct { int num_offset; int dma, pio; enum v4l2_buf_type buf_type; + u32 v4l2_caps; const struct v4l2_file_operations *fops; } ivtv_stream_info[] = { { /* IVTV_ENC_STREAM_TYPE_MPG */ "encoder MPG", VFL_TYPE_GRABBER, 0, PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | + V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_enc_fops }, { /* IVTV_ENC_STREAM_TYPE_YUV */ "encoder YUV", VFL_TYPE_GRABBER, IVTV_V4L2_ENC_YUV_OFFSET, PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | + V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_enc_fops }, { /* IVTV_ENC_STREAM_TYPE_VBI */ "encoder VBI", VFL_TYPE_VBI, 0, PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_TUNER | + V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_enc_fops }, { /* IVTV_ENC_STREAM_TYPE_PCM */ "encoder PCM", VFL_TYPE_GRABBER, IVTV_V4L2_ENC_PCM_OFFSET, PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_PRIVATE, + V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_enc_fops }, { /* IVTV_ENC_STREAM_TYPE_RAD */ "encoder radio", VFL_TYPE_RADIO, 0, PCI_DMA_NONE, 1, V4L2_BUF_TYPE_PRIVATE, + V4L2_CAP_RADIO | V4L2_CAP_TUNER, &ivtv_v4l2_enc_fops }, { /* IVTV_DEC_STREAM_TYPE_MPG */ "decoder MPG", VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET, PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT, + V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_dec_fops }, { /* IVTV_DEC_STREAM_TYPE_VBI */ "decoder VBI", VFL_TYPE_VBI, IVTV_V4L2_DEC_VBI_OFFSET, PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_READWRITE, &ivtv_v4l2_enc_fops }, { /* IVTV_DEC_STREAM_TYPE_VOUT */ "decoder VOUT", VFL_TYPE_VBI, IVTV_V4L2_DEC_VOUT_OFFSET, PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_OUTPUT, + V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_dec_fops }, { /* IVTV_DEC_STREAM_TYPE_YUV */ "decoder YUV", VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET, PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT, + V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_dec_fops } }; @@ -149,6 +162,7 @@ static void ivtv_stream_init(struct ivtv *itv, int type) s->itv = itv; s->type = type; s->name = ivtv_stream_info[type].name; + s->caps = ivtv_stream_info[type].v4l2_caps; if (ivtv_stream_info[type].pio) s->dma = PCI_DMA_NONE; @@ -209,8 +223,8 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) s->vdev->num = num; s->vdev->v4l2_dev = &itv->v4l2_dev; - s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler; s->vdev->fops = ivtv_stream_info[type].fops; + s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler; s->vdev->release = video_device_release; s->vdev->tvnorms = V4L2_STD_ALL; s->vdev->lock = &itv->serialize_lock; @@ -891,7 +905,7 @@ int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts) IVTV_DEBUG_INFO("Stop Decode at %llu, flags: %x\n", (unsigned long long)pts, flags); /* Stop Decoder */ - if (!(flags & VIDEO_CMD_STOP_IMMEDIATELY) || pts) { + if (!(flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) || pts) { u32 tmp = 0; /* Wait until the decoder is no longer running */ @@ -911,7 +925,7 @@ int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts) break; } } - ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & VIDEO_CMD_STOP_TO_BLACK, 0, 0); + ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & V4L2_DEC_CMD_STOP_TO_BLACK, 0, 0); /* turn off notification of dual/stereo mode change */ ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_DEC_AUD_MODE_CHG, -1); diff --git a/drivers/media/video/ks0127.c b/drivers/media/video/ks0127.c index afa91182b448..ee7ca2dcca2f 100644 --- a/drivers/media/video/ks0127.c +++ b/drivers/media/video/ks0127.c @@ -721,15 +721,4 @@ static struct i2c_driver ks0127_driver = { .id_table = ks0127_id, }; -static __init int init_ks0127(void) -{ - return i2c_add_driver(&ks0127_driver); -} - -static __exit void exit_ks0127(void) -{ - i2c_del_driver(&ks0127_driver); -} - -module_init(init_ks0127); -module_exit(exit_ks0127); +module_i2c_driver(ks0127_driver); diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c index 303ffa7df4ac..0991576f4c82 100644 --- a/drivers/media/video/m52790.c +++ b/drivers/media/video/m52790.c @@ -213,15 +213,4 @@ static struct i2c_driver m52790_driver = { .id_table = m52790_id, }; -static __init int init_m52790(void) -{ - return i2c_add_driver(&m52790_driver); -} - -static __exit void exit_m52790(void) -{ - i2c_del_driver(&m52790_driver); -} - -module_init(init_m52790); -module_exit(exit_m52790); +module_i2c_driver(m52790_driver); diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c index 93d768db9f33..d718aee01c77 100644 --- a/drivers/media/video/m5mols/m5mols_core.c +++ b/drivers/media/video/m5mols/m5mols_core.c @@ -982,8 +982,8 @@ static int __devinit m5mols_probe(struct i2c_client *client, } sd = &info->sd; - strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); v4l2_i2c_subdev_init(sd, client, &m5mols_ops); + strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sd->internal_ops = &m5mols_subdev_internal_ops; @@ -1057,18 +1057,7 @@ static struct i2c_driver m5mols_i2c_driver = { .id_table = m5mols_id, }; -static int __init m5mols_mod_init(void) -{ - return i2c_add_driver(&m5mols_i2c_driver); -} - -static void __exit m5mols_mod_exit(void) -{ - i2c_del_driver(&m5mols_i2c_driver); -} - -module_init(m5mols_mod_init); -module_exit(m5mols_mod_exit); +module_i2c_driver(m5mols_i2c_driver); MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>"); MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>"); diff --git a/drivers/media/video/marvell-ccic/mcam-core.c b/drivers/media/video/marvell-ccic/mcam-core.c index 37d20e73908a..996ac34d9a89 100644 --- a/drivers/media/video/marvell-ccic/mcam-core.c +++ b/drivers/media/video/marvell-ccic/mcam-core.c @@ -509,11 +509,17 @@ static void mcam_sg_next_buffer(struct mcam_camera *cam) buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue); list_del_init(&buf->queue); + /* + * Very Bad Not Good Things happen if you don't clear + * C1_DESC_ENA before making any descriptor changes. + */ + mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_ENA); mcam_reg_write(cam, REG_DMA_DESC_Y, buf->dma_desc_pa); mcam_reg_write(cam, REG_DESC_LEN_Y, buf->dma_desc_nent*sizeof(struct mcam_dma_desc)); mcam_reg_write(cam, REG_DESC_LEN_U, 0); mcam_reg_write(cam, REG_DESC_LEN_V, 0); + mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA); cam->vb_bufs[0] = buf; } @@ -533,7 +539,6 @@ static void mcam_ctlr_dma_sg(struct mcam_camera *cam) mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_3WORD); mcam_sg_next_buffer(cam); - mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA); cam->nbufs = 3; } @@ -556,17 +561,16 @@ static void mcam_dma_sg_done(struct mcam_camera *cam, int frame) struct mcam_vb_buffer *buf = cam->vb_bufs[0]; /* - * Very Bad Not Good Things happen if you don't clear - * C1_DESC_ENA before making any descriptor changes. + * If we're no longer supposed to be streaming, don't do anything. */ - mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_ENA); + if (cam->state != S_STREAMING) + return; /* * If we have another buffer available, put it in and * restart the engine. */ if (!list_empty(&cam->buffers)) { mcam_sg_next_buffer(cam); - mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA); mcam_ctlr_start(cam); /* * Otherwise set CF_SG_RESTART and the controller will @@ -737,7 +741,14 @@ static void mcam_ctlr_stop_dma(struct mcam_camera *cam) mcam_ctlr_stop(cam); cam->state = S_IDLE; spin_unlock_irqrestore(&cam->dev_lock, flags); - msleep(40); + /* + * This is a brutally long sleep, but experience shows that + * it can take the controller a while to get the message that + * it needs to stop grabbing frames. In particular, we can + * sometimes (on mmp) get a frame at the end WITHOUT the + * start-of-frame indication. + */ + msleep(150); if (test_bit(CF_DMA_ACTIVE, &cam->flags)) cam_err(cam, "Timeout waiting for DMA to end\n"); /* This would be bad news - what now? */ @@ -880,6 +891,7 @@ static int mcam_read_setup(struct mcam_camera *cam) * Turn it loose. */ spin_lock_irqsave(&cam->dev_lock, flags); + clear_bit(CF_DMA_ACTIVE, &cam->flags); mcam_reset_buffers(cam); mcam_ctlr_irq_enable(cam); cam->state = S_STREAMING; @@ -922,7 +934,7 @@ static void mcam_vb_buf_queue(struct vb2_buffer *vb) spin_lock_irqsave(&cam->dev_lock, flags); start = (cam->state == S_BUFWAIT) && !list_empty(&cam->buffers); list_add(&mvb->queue, &cam->buffers); - if (test_bit(CF_SG_RESTART, &cam->flags)) + if (cam->state == S_STREAMING && test_bit(CF_SG_RESTART, &cam->flags)) mcam_sg_restart(cam); spin_unlock_irqrestore(&cam->dev_lock, flags); if (start) @@ -1555,15 +1567,12 @@ static int mcam_v4l_release(struct file *filp) { struct mcam_camera *cam = filp->private_data; - cam_err(cam, "Release, %d frames, %d singles, %d delivered\n", frames, + cam_dbg(cam, "Release, %d frames, %d singles, %d delivered\n", frames, singles, delivered); mutex_lock(&cam->s_mutex); (cam->users)--; - if (filp == cam->owner) { - mcam_ctlr_stop_dma(cam); - cam->owner = NULL; - } if (cam->users == 0) { + mcam_ctlr_stop_dma(cam); mcam_cleanup_vb2(cam); mcam_ctlr_power_down(cam); if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read) @@ -1688,6 +1697,8 @@ int mccic_irq(struct mcam_camera *cam, unsigned int irqs) if (irqs & (IRQ_EOF0 << frame)) { mcam_frame_complete(cam, frame); handled = 1; + if (cam->buffer_mode == B_DMA_sg) + break; } /* * If a frame starts, note that we have DMA active. This diff --git a/drivers/media/video/marvell-ccic/mcam-core.h b/drivers/media/video/marvell-ccic/mcam-core.h index 917200e63255..bd6acba9fb37 100644 --- a/drivers/media/video/marvell-ccic/mcam-core.h +++ b/drivers/media/video/marvell-ccic/mcam-core.h @@ -107,7 +107,6 @@ struct mcam_camera { enum mcam_state state; unsigned long flags; /* Buffer status, mainly (dev_lock) */ int users; /* How many open FDs */ - struct file *owner; /* Who has data access (v4l2) */ /* * Subsystem structures. diff --git a/drivers/media/video/marvell-ccic/mmp-driver.c b/drivers/media/video/marvell-ccic/mmp-driver.c index 0d64e2d7474a..d23552323f45 100644 --- a/drivers/media/video/marvell-ccic/mmp-driver.c +++ b/drivers/media/video/marvell-ccic/mmp-driver.c @@ -106,6 +106,13 @@ static struct mmp_camera *mmpcam_find_device(struct platform_device *pdev) /* * Power control. */ +static void mmpcam_power_up_ctlr(struct mmp_camera *cam) +{ + iowrite32(0x3f, cam->power_regs + REG_CCIC_DCGCR); + iowrite32(0x3805b, cam->power_regs + REG_CCIC_CRCR); + mdelay(1); +} + static void mmpcam_power_up(struct mcam_camera *mcam) { struct mmp_camera *cam = mcam_to_cam(mcam); @@ -113,9 +120,7 @@ static void mmpcam_power_up(struct mcam_camera *mcam) /* * Turn on power and clocks to the controller. */ - iowrite32(0x3f, cam->power_regs + REG_CCIC_DCGCR); - iowrite32(0x3805b, cam->power_regs + REG_CCIC_CRCR); - mdelay(1); + mmpcam_power_up_ctlr(cam); /* * Provide power to the sensor. */ @@ -335,7 +340,7 @@ static int mmpcam_resume(struct platform_device *pdev) * touch a register even if nothing was active before; trust * me, it's better this way. */ - mmpcam_power_up(&cam->mcam); + mmpcam_power_up_ctlr(cam); return mccic_resume(&cam->mcam); } diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index d7cd0f633f63..82ce50721de3 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -881,18 +881,7 @@ static struct i2c_driver msp_driver = { .id_table = msp_id, }; -static __init int init_msp(void) -{ - return i2c_add_driver(&msp_driver); -} - -static __exit void exit_msp(void) -{ - i2c_del_driver(&msp_driver); -} - -module_init(init_msp); -module_exit(exit_msp); +module_i2c_driver(msp_driver); /* * Overrides for Emacs so that we follow Linus's tabbing style. diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index 097c9d3d04a8..7e648183f157 100644 --- a/drivers/media/video/mt9m001.c +++ b/drivers/media/video/mt9m001.c @@ -730,18 +730,7 @@ static struct i2c_driver mt9m001_i2c_driver = { .id_table = mt9m001_id, }; -static int __init mt9m001_mod_init(void) -{ - return i2c_add_driver(&mt9m001_i2c_driver); -} - -static void __exit mt9m001_mod_exit(void) -{ - i2c_del_driver(&mt9m001_i2c_driver); -} - -module_init(mt9m001_mod_init); -module_exit(mt9m001_mod_exit); +module_i2c_driver(mt9m001_i2c_driver); MODULE_DESCRIPTION("Micron MT9M001 Camera driver"); MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); diff --git a/drivers/media/video/mt9m032.c b/drivers/media/video/mt9m032.c new file mode 100644 index 000000000000..7636672c3548 --- /dev/null +++ b/drivers/media/video/mt9m032.c @@ -0,0 +1,868 @@ +/* + * Driver for MT9M032 CMOS Image Sensor from Micron + * + * Copyright (C) 2010-2011 Lund Engineering + * Contact: Gil Lund <gwlund@lundeng.com> + * Author: Martin Hostettler <martin@neutronstar.dyndns.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/v4l2-mediabus.h> + +#include <media/media-entity.h> +#include <media/mt9m032.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> + +#include "aptina-pll.h" + +/* + * width and height include active boundary and black parts + * + * column 0- 15 active boundary + * column 16-1455 image + * column 1456-1471 active boundary + * column 1472-1599 black + * + * row 0- 51 black + * row 53- 59 active boundary + * row 60-1139 image + * row 1140-1147 active boundary + * row 1148-1151 black + */ + +#define MT9M032_PIXEL_ARRAY_WIDTH 1600 +#define MT9M032_PIXEL_ARRAY_HEIGHT 1152 + +#define MT9M032_CHIP_VERSION 0x00 +#define MT9M032_CHIP_VERSION_VALUE 0x1402 +#define MT9M032_ROW_START 0x01 +#define MT9M032_ROW_START_MIN 0 +#define MT9M032_ROW_START_MAX 1152 +#define MT9M032_ROW_START_DEF 60 +#define MT9M032_COLUMN_START 0x02 +#define MT9M032_COLUMN_START_MIN 0 +#define MT9M032_COLUMN_START_MAX 1600 +#define MT9M032_COLUMN_START_DEF 16 +#define MT9M032_ROW_SIZE 0x03 +#define MT9M032_ROW_SIZE_MIN 32 +#define MT9M032_ROW_SIZE_MAX 1152 +#define MT9M032_ROW_SIZE_DEF 1080 +#define MT9M032_COLUMN_SIZE 0x04 +#define MT9M032_COLUMN_SIZE_MIN 32 +#define MT9M032_COLUMN_SIZE_MAX 1600 +#define MT9M032_COLUMN_SIZE_DEF 1440 +#define MT9M032_HBLANK 0x05 +#define MT9M032_VBLANK 0x06 +#define MT9M032_VBLANK_MAX 0x7ff +#define MT9M032_SHUTTER_WIDTH_HIGH 0x08 +#define MT9M032_SHUTTER_WIDTH_LOW 0x09 +#define MT9M032_SHUTTER_WIDTH_MIN 1 +#define MT9M032_SHUTTER_WIDTH_MAX 1048575 +#define MT9M032_SHUTTER_WIDTH_DEF 1943 +#define MT9M032_PIX_CLK_CTRL 0x0a +#define MT9M032_PIX_CLK_CTRL_INV_PIXCLK 0x8000 +#define MT9M032_RESTART 0x0b +#define MT9M032_RESET 0x0d +#define MT9M032_PLL_CONFIG1 0x11 +#define MT9M032_PLL_CONFIG1_OUTDIV_MASK 0x3f +#define MT9M032_PLL_CONFIG1_MUL_SHIFT 8 +#define MT9M032_READ_MODE1 0x1e +#define MT9M032_READ_MODE2 0x20 +#define MT9M032_READ_MODE2_VFLIP_SHIFT 15 +#define MT9M032_READ_MODE2_HFLIP_SHIFT 14 +#define MT9M032_READ_MODE2_ROW_BLC 0x40 +#define MT9M032_GAIN_GREEN1 0x2b +#define MT9M032_GAIN_BLUE 0x2c +#define MT9M032_GAIN_RED 0x2d +#define MT9M032_GAIN_GREEN2 0x2e + +/* write only */ +#define MT9M032_GAIN_ALL 0x35 +#define MT9M032_GAIN_DIGITAL_MASK 0x7f +#define MT9M032_GAIN_DIGITAL_SHIFT 8 +#define MT9M032_GAIN_AMUL_SHIFT 6 +#define MT9M032_GAIN_ANALOG_MASK 0x3f +#define MT9M032_FORMATTER1 0x9e +#define MT9M032_FORMATTER2 0x9f +#define MT9M032_FORMATTER2_DOUT_EN 0x1000 +#define MT9M032_FORMATTER2_PIXCLK_EN 0x2000 + +/* + * The available MT9M032 datasheet is missing documentation for register 0x10 + * MT9P031 seems to be close enough, so use constants from that datasheet for + * now. + * But keep the name MT9P031 to remind us, that this isn't really confirmed + * for this sensor. + */ +#define MT9P031_PLL_CONTROL 0x10 +#define MT9P031_PLL_CONTROL_PWROFF 0x0050 +#define MT9P031_PLL_CONTROL_PWRON 0x0051 +#define MT9P031_PLL_CONTROL_USEPLL 0x0052 +#define MT9P031_PLL_CONFIG2 0x11 +#define MT9P031_PLL_CONFIG2_P1_DIV_MASK 0x1f + +struct mt9m032 { + struct v4l2_subdev subdev; + struct media_pad pad; + struct mt9m032_platform_data *pdata; + + unsigned int pix_clock; + + struct v4l2_ctrl_handler ctrls; + struct { + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; + + struct mutex lock; /* Protects streaming, format, interval and crop */ + + bool streaming; + + struct v4l2_mbus_framefmt format; + struct v4l2_rect crop; + struct v4l2_fract frame_interval; +}; + +#define to_mt9m032(sd) container_of(sd, struct mt9m032, subdev) +#define to_dev(sensor) \ + (&((struct i2c_client *)v4l2_get_subdevdata(&(sensor)->subdev))->dev) + +static int mt9m032_read(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_word_swapped(client, reg); +} + +static int mt9m032_write(struct i2c_client *client, u8 reg, const u16 data) +{ + return i2c_smbus_write_word_swapped(client, reg, data); +} + +static u32 mt9m032_row_time(struct mt9m032 *sensor, unsigned int width) +{ + unsigned int effective_width; + u32 ns; + + effective_width = width + 716; /* empirical value */ + ns = div_u64(1000000000ULL * effective_width, sensor->pix_clock); + dev_dbg(to_dev(sensor), "MT9M032 line time: %u ns\n", ns); + return ns; +} + +static int mt9m032_update_timing(struct mt9m032 *sensor, + struct v4l2_fract *interval) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + struct v4l2_rect *crop = &sensor->crop; + unsigned int min_vblank; + unsigned int vblank; + u32 row_time; + + if (!interval) + interval = &sensor->frame_interval; + + row_time = mt9m032_row_time(sensor, crop->width); + + vblank = div_u64(1000000000ULL * interval->numerator, + (u64)row_time * interval->denominator) + - crop->height; + + if (vblank > MT9M032_VBLANK_MAX) { + /* hardware limits to 11 bit values */ + interval->denominator = 1000; + interval->numerator = + div_u64((crop->height + MT9M032_VBLANK_MAX) * + (u64)row_time * interval->denominator, + 1000000000ULL); + vblank = div_u64(1000000000ULL * interval->numerator, + (u64)row_time * interval->denominator) + - crop->height; + } + /* enforce minimal 1.6ms blanking time. */ + min_vblank = 1600000 / row_time; + vblank = clamp_t(unsigned int, vblank, min_vblank, MT9M032_VBLANK_MAX); + + return mt9m032_write(client, MT9M032_VBLANK, vblank); +} + +static int mt9m032_update_geom_timing(struct mt9m032 *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int ret; + + ret = mt9m032_write(client, MT9M032_COLUMN_SIZE, + sensor->crop.width - 1); + if (!ret) + ret = mt9m032_write(client, MT9M032_ROW_SIZE, + sensor->crop.height - 1); + if (!ret) + ret = mt9m032_write(client, MT9M032_COLUMN_START, + sensor->crop.left); + if (!ret) + ret = mt9m032_write(client, MT9M032_ROW_START, + sensor->crop.top); + if (!ret) + ret = mt9m032_update_timing(sensor, NULL); + return ret; +} + +static int update_formatter2(struct mt9m032 *sensor, bool streaming) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + u16 reg_val = MT9M032_FORMATTER2_DOUT_EN + | 0x0070; /* parts reserved! */ + /* possibly for changing to 14-bit mode */ + + if (streaming) + reg_val |= MT9M032_FORMATTER2_PIXCLK_EN; /* pixclock enable */ + + return mt9m032_write(client, MT9M032_FORMATTER2, reg_val); +} + +static int mt9m032_setup_pll(struct mt9m032 *sensor) +{ + static const struct aptina_pll_limits limits = { + .ext_clock_min = 8000000, + .ext_clock_max = 16500000, + .int_clock_min = 2000000, + .int_clock_max = 24000000, + .out_clock_min = 322000000, + .out_clock_max = 693000000, + .pix_clock_max = 99000000, + .n_min = 1, + .n_max = 64, + .m_min = 16, + .m_max = 255, + .p1_min = 1, + .p1_max = 128, + }; + + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + struct mt9m032_platform_data *pdata = sensor->pdata; + struct aptina_pll pll; + int ret; + + pll.ext_clock = pdata->ext_clock; + pll.pix_clock = pdata->pix_clock; + + ret = aptina_pll_calculate(&client->dev, &limits, &pll); + if (ret < 0) + return ret; + + sensor->pix_clock = pdata->pix_clock; + + ret = mt9m032_write(client, MT9M032_PLL_CONFIG1, + (pll.m << MT9M032_PLL_CONFIG1_MUL_SHIFT) + | (pll.p1 - 1)); + if (!ret) + ret = mt9m032_write(client, MT9P031_PLL_CONFIG2, pll.n - 1); + if (!ret) + ret = mt9m032_write(client, MT9P031_PLL_CONTROL, + MT9P031_PLL_CONTROL_PWRON | + MT9P031_PLL_CONTROL_USEPLL); + if (!ret) /* more reserved, Continuous, Master Mode */ + ret = mt9m032_write(client, MT9M032_READ_MODE1, 0x8006); + if (!ret) /* Set 14-bit mode, select 7 divider */ + ret = mt9m032_write(client, MT9M032_FORMATTER1, 0x111e); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Subdev pad operations + */ + +static int mt9m032_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + + code->code = V4L2_MBUS_FMT_Y8_1X8; + return 0; +} + +static int mt9m032_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index != 0 || fse->code != V4L2_MBUS_FMT_Y8_1X8) + return -EINVAL; + + fse->min_width = MT9M032_COLUMN_SIZE_DEF; + fse->max_width = MT9M032_COLUMN_SIZE_DEF; + fse->min_height = MT9M032_ROW_SIZE_DEF; + fse->max_height = MT9M032_ROW_SIZE_DEF; + + return 0; +} + +/** + * __mt9m032_get_pad_crop() - get crop rect + * @sensor: pointer to the sensor struct + * @fh: file handle for getting the try crop rect from + * @which: select try or active crop rect + * + * Returns a pointer the current active or fh relative try crop rect + */ +static struct v4l2_rect * +__mt9m032_get_pad_crop(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(fh, 0); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &sensor->crop; + default: + return NULL; + } +} + +/** + * __mt9m032_get_pad_format() - get format + * @sensor: pointer to the sensor struct + * @fh: file handle for getting the try format from + * @which: select try or active format + * + * Returns a pointer the current active or fh relative try format + */ +static struct v4l2_mbus_framefmt * +__mt9m032_get_pad_format(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(fh, 0); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &sensor->format; + default: + return NULL; + } +} + +static int mt9m032_get_pad_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + + mutex_lock(&sensor->lock); + fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which); + mutex_unlock(&sensor->lock); + + return 0; +} + +static int mt9m032_set_pad_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + int ret; + + mutex_lock(&sensor->lock); + + if (sensor->streaming && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ret = -EBUSY; + goto done; + } + + /* Scaling is not supported, the format is thus fixed. */ + ret = mt9m032_get_pad_format(subdev, fh, fmt); + +done: + mutex_lock(&sensor->lock); + return ret; +} + +static int mt9m032_get_pad_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + + mutex_lock(&sensor->lock); + crop->rect = *__mt9m032_get_pad_crop(sensor, fh, crop->which); + mutex_unlock(&sensor->lock); + + return 0; +} + +static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *__crop; + struct v4l2_rect rect; + int ret = 0; + + mutex_lock(&sensor->lock); + + if (sensor->streaming && crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ret = -EBUSY; + goto done; + } + + /* Clamp the crop rectangle boundaries and align them to a multiple of 2 + * pixels to ensure a GRBG Bayer pattern. + */ + rect.left = clamp(ALIGN(crop->rect.left, 2), MT9M032_COLUMN_START_MIN, + MT9M032_COLUMN_START_MAX); + rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN, + MT9M032_ROW_START_MAX); + rect.width = clamp(ALIGN(crop->rect.width, 2), MT9M032_COLUMN_SIZE_MIN, + MT9M032_COLUMN_SIZE_MAX); + rect.height = clamp(ALIGN(crop->rect.height, 2), MT9M032_ROW_SIZE_MIN, + MT9M032_ROW_SIZE_MAX); + + rect.width = min(rect.width, MT9M032_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min(rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top); + + __crop = __mt9m032_get_pad_crop(sensor, fh, crop->which); + + if (rect.width != __crop->width || rect.height != __crop->height) { + /* Reset the output image size if the crop rectangle size has + * been modified. + */ + format = __mt9m032_get_pad_format(sensor, fh, crop->which); + format->width = rect.width; + format->height = rect.height; + } + + *__crop = rect; + crop->rect = rect; + + if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) + ret = mt9m032_update_geom_timing(sensor); + +done: + mutex_unlock(&sensor->lock); + return ret; +} + +static int mt9m032_get_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_frame_interval *fi) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + + mutex_lock(&sensor->lock); + memset(fi, 0, sizeof(*fi)); + fi->interval = sensor->frame_interval; + mutex_unlock(&sensor->lock); + + return 0; +} + +static int mt9m032_set_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_frame_interval *fi) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + int ret; + + mutex_lock(&sensor->lock); + + if (sensor->streaming) { + ret = -EBUSY; + goto done; + } + + /* Avoid divisions by 0. */ + if (fi->interval.denominator == 0) + fi->interval.denominator = 1; + + ret = mt9m032_update_timing(sensor, &fi->interval); + if (!ret) + sensor->frame_interval = fi->interval; + +done: + mutex_unlock(&sensor->lock); + return ret; +} + +static int mt9m032_s_stream(struct v4l2_subdev *subdev, int streaming) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + int ret; + + mutex_lock(&sensor->lock); + ret = update_formatter2(sensor, streaming); + if (!ret) + sensor->streaming = streaming; + mutex_unlock(&sensor->lock); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev core operations + */ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9m032_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct mt9m032 *sensor = to_mt9m032(sd); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int val; + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + if (reg->match.addr != client->addr) + return -ENODEV; + + val = mt9m032_read(client, reg->reg); + if (val < 0) + return -EIO; + + reg->size = 2; + reg->val = val; + + return 0; +} + +static int mt9m032_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct mt9m032 *sensor = to_mt9m032(sd); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + + if (reg->match.addr != client->addr) + return -ENODEV; + + return mt9m032_write(client, reg->reg, reg->val); +} +#endif + +/* ----------------------------------------------------------------------------- + * V4L2 subdev control operations + */ + +static int update_read_mode2(struct mt9m032 *sensor, bool vflip, bool hflip) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int reg_val = (vflip << MT9M032_READ_MODE2_VFLIP_SHIFT) + | (hflip << MT9M032_READ_MODE2_HFLIP_SHIFT) + | MT9M032_READ_MODE2_ROW_BLC + | 0x0007; + + return mt9m032_write(client, MT9M032_READ_MODE2, reg_val); +} + +static int mt9m032_set_gain(struct mt9m032 *sensor, s32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int digital_gain_val; /* in 1/8th (0..127) */ + int analog_mul; /* 0 or 1 */ + int analog_gain_val; /* in 1/16th. (0..63) */ + u16 reg_val; + + digital_gain_val = 51; /* from setup example */ + + if (val < 63) { + analog_mul = 0; + analog_gain_val = val; + } else { + analog_mul = 1; + analog_gain_val = val / 2; + } + + /* a_gain = (1 + analog_mul) + (analog_gain_val + 1) / 16 */ + /* overall_gain = a_gain * (1 + digital_gain_val / 8) */ + + reg_val = ((digital_gain_val & MT9M032_GAIN_DIGITAL_MASK) + << MT9M032_GAIN_DIGITAL_SHIFT) + | ((analog_mul & 1) << MT9M032_GAIN_AMUL_SHIFT) + | (analog_gain_val & MT9M032_GAIN_ANALOG_MASK); + + return mt9m032_write(client, MT9M032_GAIN_ALL, reg_val); +} + +static int mt9m032_try_ctrl(struct v4l2_ctrl *ctrl) +{ + if (ctrl->id == V4L2_CID_GAIN && ctrl->val >= 63) { + /* round because of multiplier used for values >= 63 */ + ctrl->val &= ~1; + } + + return 0; +} + +static int mt9m032_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9m032 *sensor = + container_of(ctrl->handler, struct mt9m032, ctrls); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int ret; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + return mt9m032_set_gain(sensor, ctrl->val); + + case V4L2_CID_HFLIP: + /* case V4L2_CID_VFLIP: -- In the same cluster */ + return update_read_mode2(sensor, sensor->vflip->val, + sensor->hflip->val); + + case V4L2_CID_EXPOSURE: + ret = mt9m032_write(client, MT9M032_SHUTTER_WIDTH_HIGH, + (ctrl->val >> 16) & 0xffff); + if (ret < 0) + return ret; + + return mt9m032_write(client, MT9M032_SHUTTER_WIDTH_LOW, + ctrl->val & 0xffff); + } + + return 0; +} + +static struct v4l2_ctrl_ops mt9m032_ctrl_ops = { + .s_ctrl = mt9m032_set_ctrl, + .try_ctrl = mt9m032_try_ctrl, +}; + +/* -------------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops mt9m032_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9m032_g_register, + .s_register = mt9m032_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops mt9m032_video_ops = { + .s_stream = mt9m032_s_stream, + .g_frame_interval = mt9m032_get_frame_interval, + .s_frame_interval = mt9m032_set_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops mt9m032_pad_ops = { + .enum_mbus_code = mt9m032_enum_mbus_code, + .enum_frame_size = mt9m032_enum_frame_size, + .get_fmt = mt9m032_get_pad_format, + .set_fmt = mt9m032_set_pad_format, + .set_crop = mt9m032_set_pad_crop, + .get_crop = mt9m032_get_pad_crop, +}; + +static const struct v4l2_subdev_ops mt9m032_ops = { + .core = &mt9m032_core_ops, + .video = &mt9m032_video_ops, + .pad = &mt9m032_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Driver initialization and probing + */ + +static int mt9m032_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct i2c_adapter *adapter = client->adapter; + struct mt9m032 *sensor; + int chip_version; + int ret; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&client->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + if (!client->dev.platform_data) + return -ENODEV; + + sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); + if (sensor == NULL) + return -ENOMEM; + + mutex_init(&sensor->lock); + + sensor->pdata = client->dev.platform_data; + + v4l2_i2c_subdev_init(&sensor->subdev, client, &mt9m032_ops); + sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + chip_version = mt9m032_read(client, MT9M032_CHIP_VERSION); + if (chip_version != MT9M032_CHIP_VERSION_VALUE) { + dev_err(&client->dev, "MT9M032 not detected, wrong version " + "0x%04x\n", chip_version); + ret = -ENODEV; + goto error_sensor; + } + + dev_info(&client->dev, "MT9M032 detected at address 0x%02x\n", + client->addr); + + sensor->frame_interval.numerator = 1; + sensor->frame_interval.denominator = 30; + + sensor->crop.left = MT9M032_COLUMN_START_DEF; + sensor->crop.top = MT9M032_ROW_START_DEF; + sensor->crop.width = MT9M032_COLUMN_SIZE_DEF; + sensor->crop.height = MT9M032_ROW_SIZE_DEF; + + sensor->format.width = sensor->crop.width; + sensor->format.height = sensor->crop.height; + sensor->format.code = V4L2_MBUS_FMT_Y8_1X8; + sensor->format.field = V4L2_FIELD_NONE; + sensor->format.colorspace = V4L2_COLORSPACE_SRGB; + + v4l2_ctrl_handler_init(&sensor->ctrls, 4); + + v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, + V4L2_CID_GAIN, 0, 127, 1, 64); + + sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls, + &mt9m032_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls, + &mt9m032_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, + V4L2_CID_EXPOSURE, MT9M032_SHUTTER_WIDTH_MIN, + MT9M032_SHUTTER_WIDTH_MAX, 1, + MT9M032_SHUTTER_WIDTH_DEF); + + if (sensor->ctrls.error) { + ret = sensor->ctrls.error; + dev_err(&client->dev, "control initialization error %d\n", ret); + goto error_ctrl; + } + + v4l2_ctrl_cluster(2, &sensor->hflip); + + sensor->subdev.ctrl_handler = &sensor->ctrls; + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sensor->subdev.entity, 1, &sensor->pad, 0); + if (ret < 0) + goto error_ctrl; + + ret = mt9m032_write(client, MT9M032_RESET, 1); /* reset on */ + if (ret < 0) + goto error_entity; + mt9m032_write(client, MT9M032_RESET, 0); /* reset off */ + if (ret < 0) + goto error_entity; + + ret = mt9m032_setup_pll(sensor); + if (ret < 0) + goto error_entity; + usleep_range(10000, 11000); + + ret = v4l2_ctrl_handler_setup(&sensor->ctrls); + if (ret < 0) + goto error_entity; + + /* SIZE */ + ret = mt9m032_update_geom_timing(sensor); + if (ret < 0) + goto error_entity; + + ret = mt9m032_write(client, 0x41, 0x0000); /* reserved !!! */ + if (ret < 0) + goto error_entity; + ret = mt9m032_write(client, 0x42, 0x0003); /* reserved !!! */ + if (ret < 0) + goto error_entity; + ret = mt9m032_write(client, 0x43, 0x0003); /* reserved !!! */ + if (ret < 0) + goto error_entity; + ret = mt9m032_write(client, 0x7f, 0x0000); /* reserved !!! */ + if (ret < 0) + goto error_entity; + if (sensor->pdata->invert_pixclock) { + ret = mt9m032_write(client, MT9M032_PIX_CLK_CTRL, + MT9M032_PIX_CLK_CTRL_INV_PIXCLK); + if (ret < 0) + goto error_entity; + } + + ret = mt9m032_write(client, MT9M032_RESTART, 1); /* Restart on */ + if (ret < 0) + goto error_entity; + msleep(100); + ret = mt9m032_write(client, MT9M032_RESTART, 0); /* Restart off */ + if (ret < 0) + goto error_entity; + msleep(100); + ret = update_formatter2(sensor, false); + if (ret < 0) + goto error_entity; + + return ret; + +error_entity: + media_entity_cleanup(&sensor->subdev.entity); +error_ctrl: + v4l2_ctrl_handler_free(&sensor->ctrls); +error_sensor: + mutex_destroy(&sensor->lock); + kfree(sensor); + return ret; +} + +static int mt9m032_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct mt9m032 *sensor = to_mt9m032(subdev); + + v4l2_device_unregister_subdev(&sensor->subdev); + v4l2_ctrl_handler_free(&sensor->ctrls); + media_entity_cleanup(&sensor->subdev.entity); + mutex_destroy(&sensor->lock); + kfree(sensor); + return 0; +} + +static const struct i2c_device_id mt9m032_id_table[] = { + { MT9M032_NAME, 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, mt9m032_id_table); + +static struct i2c_driver mt9m032_i2c_driver = { + .driver = { + .name = MT9M032_NAME, + }, + .probe = mt9m032_probe, + .remove = mt9m032_remove, + .id_table = mt9m032_id_table, +}; + +module_i2c_driver(mt9m032_i2c_driver); + +MODULE_AUTHOR("Martin Hostettler <martin@neutronstar.dyndns.org>"); +MODULE_DESCRIPTION("MT9M032 camera sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c index bee65bff46e8..b0c529964329 100644 --- a/drivers/media/video/mt9m111.c +++ b/drivers/media/video/mt9m111.c @@ -1008,18 +1008,7 @@ static struct i2c_driver mt9m111_i2c_driver = { .id_table = mt9m111_id, }; -static int __init mt9m111_mod_init(void) -{ - return i2c_add_driver(&mt9m111_i2c_driver); -} - -static void __exit mt9m111_mod_exit(void) -{ - i2c_del_driver(&mt9m111_i2c_driver); -} - -module_init(mt9m111_mod_init); -module_exit(mt9m111_mod_exit); +module_i2c_driver(mt9m111_i2c_driver); MODULE_DESCRIPTION("Micron/Aptina MT9M111/MT9M112/MT9M131 Camera driver"); MODULE_AUTHOR("Robert Jarzmik"); diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c index 93c3ec7426e8..c81eaf4fbe01 100644 --- a/drivers/media/video/mt9p031.c +++ b/drivers/media/video/mt9p031.c @@ -19,7 +19,6 @@ #include <linux/log2.h> #include <linux/pm.h> #include <linux/slab.h> -#include <media/v4l2-subdev.h> #include <linux/videodev2.h> #include <media/mt9p031.h> @@ -28,6 +27,8 @@ #include <media/v4l2-device.h> #include <media/v4l2-subdev.h> +#include "aptina-pll.h" + #define MT9P031_PIXEL_ARRAY_WIDTH 2752 #define MT9P031_PIXEL_ARRAY_HEIGHT 2004 @@ -98,14 +99,6 @@ #define MT9P031_TEST_PATTERN_RED 0xa2 #define MT9P031_TEST_PATTERN_BLUE 0xa3 -struct mt9p031_pll_divs { - u32 ext_freq; - u32 target_freq; - u8 m; - u8 n; - u8 p1; -}; - struct mt9p031 { struct v4l2_subdev subdev; struct media_pad pad; @@ -115,10 +108,8 @@ struct mt9p031 { struct mt9p031_platform_data *pdata; struct mutex power_lock; /* lock to protect power_count */ int power_count; - u16 xskip; - u16 yskip; - const struct mt9p031_pll_divs *pll; + struct aptina_pll pll; /* Registers cache */ u16 output_control; @@ -186,33 +177,31 @@ static int mt9p031_reset(struct mt9p031 *mt9p031) 0); } -/* - * This static table uses ext_freq and vdd_io values to select suitable - * PLL dividers m, n and p1 which have been calculated as specifiec in p36 - * of Aptina's mt9p031 datasheet. New values should be added here. - */ -static const struct mt9p031_pll_divs mt9p031_divs[] = { - /* ext_freq target_freq m n p1 */ - {21000000, 48000000, 26, 2, 6} -}; - -static int mt9p031_pll_get_divs(struct mt9p031 *mt9p031) +static int mt9p031_pll_setup(struct mt9p031 *mt9p031) { + static const struct aptina_pll_limits limits = { + .ext_clock_min = 6000000, + .ext_clock_max = 27000000, + .int_clock_min = 2000000, + .int_clock_max = 13500000, + .out_clock_min = 180000000, + .out_clock_max = 360000000, + .pix_clock_max = 96000000, + .n_min = 1, + .n_max = 64, + .m_min = 16, + .m_max = 255, + .p1_min = 1, + .p1_max = 128, + }; + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - int i; + struct mt9p031_platform_data *pdata = mt9p031->pdata; - for (i = 0; i < ARRAY_SIZE(mt9p031_divs); i++) { - if (mt9p031_divs[i].ext_freq == mt9p031->pdata->ext_freq && - mt9p031_divs[i].target_freq == mt9p031->pdata->target_freq) { - mt9p031->pll = &mt9p031_divs[i]; - return 0; - } - } + mt9p031->pll.ext_clock = pdata->ext_freq; + mt9p031->pll.pix_clock = pdata->target_freq; - dev_err(&client->dev, "Couldn't find PLL dividers for ext_freq = %d, " - "target_freq = %d\n", mt9p031->pdata->ext_freq, - mt9p031->pdata->target_freq); - return -EINVAL; + return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll); } static int mt9p031_pll_enable(struct mt9p031 *mt9p031) @@ -226,11 +215,11 @@ static int mt9p031_pll_enable(struct mt9p031 *mt9p031) return ret; ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1, - (mt9p031->pll->m << 8) | (mt9p031->pll->n - 1)); + (mt9p031->pll.m << 8) | (mt9p031->pll.n - 1)); if (ret < 0) return ret; - ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll->p1 - 1); + ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll.p1 - 1); if (ret < 0) return ret; @@ -785,8 +774,6 @@ static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) format->field = V4L2_FIELD_NONE; format->colorspace = V4L2_COLORSPACE_SRGB; - mt9p031->xskip = 1; - mt9p031->yskip = 1; return mt9p031_set_power(subdev, 1); } @@ -905,7 +892,7 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->format.field = V4L2_FIELD_NONE; mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB; - ret = mt9p031_pll_get_divs(mt9p031); + ret = mt9p031_pll_setup(mt9p031); done: if (ret < 0) { @@ -945,18 +932,7 @@ static struct i2c_driver mt9p031_i2c_driver = { .id_table = mt9p031_id, }; -static int __init mt9p031_mod_init(void) -{ - return i2c_add_driver(&mt9p031_i2c_driver); -} - -static void __exit mt9p031_mod_exit(void) -{ - i2c_del_driver(&mt9p031_i2c_driver); -} - -module_init(mt9p031_mod_init); -module_exit(mt9p031_mod_exit); +module_i2c_driver(mt9p031_i2c_driver); MODULE_DESCRIPTION("Aptina MT9P031 Camera driver"); MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>"); diff --git a/drivers/media/video/mt9t001.c b/drivers/media/video/mt9t001.c index cd81d04a529e..49ca3cbfc6f1 100644 --- a/drivers/media/video/mt9t001.c +++ b/drivers/media/video/mt9t001.c @@ -817,18 +817,7 @@ static struct i2c_driver mt9t001_driver = { .id_table = mt9t001_id, }; -static int __init mt9t001_init(void) -{ - return i2c_add_driver(&mt9t001_driver); -} - -static void __exit mt9t001_exit(void) -{ - i2c_del_driver(&mt9t001_driver); -} - -module_init(mt9t001_init); -module_exit(mt9t001_exit); +module_i2c_driver(mt9t001_driver); MODULE_DESCRIPTION("Aptina (Micron) MT9T001 Camera driver"); MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c index 84add1aef139..1415074138a5 100644 --- a/drivers/media/video/mt9t031.c +++ b/drivers/media/video/mt9t031.c @@ -850,18 +850,7 @@ static struct i2c_driver mt9t031_i2c_driver = { .id_table = mt9t031_id, }; -static int __init mt9t031_mod_init(void) -{ - return i2c_add_driver(&mt9t031_i2c_driver); -} - -static void __exit mt9t031_mod_exit(void) -{ - i2c_del_driver(&mt9t031_i2c_driver); -} - -module_init(mt9t031_mod_init); -module_exit(mt9t031_mod_exit); +module_i2c_driver(mt9t031_i2c_driver); MODULE_DESCRIPTION("Micron MT9T031 Camera driver"); MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>"); diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c index 7b34b11daf24..8d1445f12708 100644 --- a/drivers/media/video/mt9t112.c +++ b/drivers/media/video/mt9t112.c @@ -1117,21 +1117,7 @@ static struct i2c_driver mt9t112_i2c_driver = { .id_table = mt9t112_id, }; -/************************************************************************ - module function -************************************************************************/ -static int __init mt9t112_module_init(void) -{ - return i2c_add_driver(&mt9t112_i2c_driver); -} - -static void __exit mt9t112_module_exit(void) -{ - i2c_del_driver(&mt9t112_i2c_driver); -} - -module_init(mt9t112_module_init); -module_exit(mt9t112_module_exit); +module_i2c_driver(mt9t112_i2c_driver); MODULE_DESCRIPTION("SoC Camera driver for mt9t112"); MODULE_AUTHOR("Kuninori Morimoto"); diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c index db74dd27c722..6bf01ad62765 100644 --- a/drivers/media/video/mt9v011.c +++ b/drivers/media/video/mt9v011.c @@ -709,15 +709,4 @@ static struct i2c_driver mt9v011_driver = { .id_table = mt9v011_id, }; -static __init int init_mt9v011(void) -{ - return i2c_add_driver(&mt9v011_driver); -} - -static __exit void exit_mt9v011(void) -{ - i2c_del_driver(&mt9v011_driver); -} - -module_init(init_mt9v011); -module_exit(exit_mt9v011); +module_i2c_driver(mt9v011_driver); diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index 944940758fa3..bf63417adb8f 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -872,18 +872,7 @@ static struct i2c_driver mt9v022_i2c_driver = { .id_table = mt9v022_id, }; -static int __init mt9v022_mod_init(void) -{ - return i2c_add_driver(&mt9v022_i2c_driver); -} - -static void __exit mt9v022_mod_exit(void) -{ - i2c_del_driver(&mt9v022_i2c_driver); -} - -module_init(mt9v022_mod_init); -module_exit(mt9v022_mod_exit); +module_i2c_driver(mt9v022_i2c_driver); MODULE_DESCRIPTION("Micron MT9V022 Camera driver"); MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); diff --git a/drivers/media/video/mt9v032.c b/drivers/media/video/mt9v032.c index d90b982cc218..75e253a343c5 100644 --- a/drivers/media/video/mt9v032.c +++ b/drivers/media/video/mt9v032.c @@ -756,18 +756,7 @@ static struct i2c_driver mt9v032_driver = { .id_table = mt9v032_id, }; -static int __init mt9v032_init(void) -{ - return i2c_add_driver(&mt9v032_driver); -} - -static void __exit mt9v032_exit(void) -{ - i2c_del_driver(&mt9v032_driver); -} - -module_init(mt9v032_init); -module_exit(mt9v032_exit); +module_i2c_driver(mt9v032_driver); MODULE_DESCRIPTION("Aptina MT9V032 Camera driver"); MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c index 04aab0c538aa..18afaeeadb7b 100644 --- a/drivers/media/video/mx2_camera.c +++ b/drivers/media/video/mx2_camera.c @@ -3,6 +3,7 @@ * * Copyright (C) 2008, Sascha Hauer, Pengutronix * Copyright (C) 2010, Baruch Siach, Orex Computed Radiography + * Copyright (C) 2012, Javier Martin, Vista Silicon S.L. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +19,7 @@ #include <linux/dma-mapping.h> #include <linux/errno.h> #include <linux/fs.h> +#include <linux/gcd.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/mm.h> @@ -30,17 +32,14 @@ #include <media/v4l2-common.h> #include <media/v4l2-dev.h> -#include <media/videobuf-core.h> -#include <media/videobuf-dma-contig.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> #include <media/soc_camera.h> #include <media/soc_mediabus.h> #include <linux/videodev2.h> #include <mach/mx2_cam.h> -#ifdef CONFIG_MACH_MX27 -#include <mach/dma-mx1-mx2.h> -#endif #include <mach/hardware.h> #include <asm/dma.h> @@ -206,10 +205,23 @@ #define PRP_INTR_LBOVF (1 << 7) #define PRP_INTR_CH2OVF (1 << 8) -#define mx27_camera_emma(pcdev) (cpu_is_mx27() && pcdev->use_emma) +/* Resizing registers */ +#define PRP_RZ_VALID_TBL_LEN(x) ((x) << 24) +#define PRP_RZ_VALID_BILINEAR (1 << 31) #define MAX_VIDEO_MEM 16 +#define RESIZE_NUM_MIN 1 +#define RESIZE_NUM_MAX 20 +#define BC_COEF 3 +#define SZ_COEF (1 << BC_COEF) + +#define RESIZE_DIR_H 0 +#define RESIZE_DIR_V 1 + +#define RESIZE_ALGO_BILINEAR 0 +#define RESIZE_ALGO_AVERAGING 1 + struct mx2_prp_cfg { int channel; u32 in_fmt; @@ -219,6 +231,13 @@ struct mx2_prp_cfg { u32 irq_flags; }; +/* prp resizing parameters */ +struct emma_prp_resize { + int algo; /* type of algorithm used */ + int len; /* number of coefficients */ + unsigned char s[RESIZE_NUM_MAX]; /* table of coefficients */ +}; + /* prp configuration for a client-host fmt pair */ struct mx2_fmt_cfg { enum v4l2_mbus_pixelcode in_fmt; @@ -226,6 +245,26 @@ struct mx2_fmt_cfg { struct mx2_prp_cfg cfg; }; +enum mx2_buffer_state { + MX2_STATE_QUEUED, + MX2_STATE_ACTIVE, + MX2_STATE_DONE, +}; + +struct mx2_buf_internal { + struct list_head queue; + int bufnum; + bool discard; +}; + +/* buffer for one video frame */ +struct mx2_buffer { + /* common v4l buffer stuff -- must be first */ + struct vb2_buffer vb; + enum mx2_buffer_state state; + struct mx2_buf_internal internal; +}; + struct mx2_camera_dev { struct device *dev; struct soc_camera_host soc_host; @@ -242,6 +281,7 @@ struct mx2_camera_dev { struct list_head capture; struct list_head active_bufs; + struct list_head discard; spinlock_t lock; @@ -250,26 +290,23 @@ struct mx2_camera_dev { struct mx2_buffer *fb1_active; struct mx2_buffer *fb2_active; - int use_emma; - u32 csicr1; + struct mx2_buf_internal buf_discard[2]; void *discard_buffer; dma_addr_t discard_buffer_dma; size_t discard_size; struct mx2_fmt_cfg *emma_prp; + struct emma_prp_resize resizing[2]; + unsigned int s_width, s_height; u32 frame_count; + struct vb2_alloc_ctx *alloc_ctx; }; -/* buffer for one video frame */ -struct mx2_buffer { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - - enum v4l2_mbus_pixelcode code; - - int bufnum; -}; +static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf) +{ + return container_of(int_buf, struct mx2_buffer, internal); +} static struct mx2_fmt_cfg mx27_emma_prp_table[] = { /* @@ -324,13 +361,36 @@ static struct mx2_fmt_cfg *mx27_emma_prp_get_format( return &mx27_emma_prp_table[0]; }; +static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev, + unsigned long phys, int bufnum) +{ + struct mx2_fmt_cfg *prp = pcdev->emma_prp; + + if (prp->cfg.channel == 1) { + writel(phys, pcdev->base_emma + + PRP_DEST_RGB1_PTR + 4 * bufnum); + } else { + writel(phys, pcdev->base_emma + + PRP_DEST_Y_PTR - 0x14 * bufnum); + if (prp->out_fmt == V4L2_PIX_FMT_YUV420) { + u32 imgsize = pcdev->icd->user_height * + pcdev->icd->user_width; + + writel(phys + imgsize, pcdev->base_emma + + PRP_DEST_CB_PTR - 0x14 * bufnum); + writel(phys + ((5 * imgsize) / 4), pcdev->base_emma + + PRP_DEST_CR_PTR - 0x14 * bufnum); + } + } +} + static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev) { unsigned long flags; clk_disable(pcdev->clk_csi); writel(0, pcdev->base_csi + CSICR1); - if (mx27_camera_emma(pcdev)) { + if (cpu_is_mx27()) { writel(0, pcdev->base_emma + PRP_CNTL); } else if (cpu_is_mx25()) { spin_lock_irqsave(&pcdev->lock, flags); @@ -362,7 +422,7 @@ static int mx2_camera_add_device(struct soc_camera_device *icd) csicr1 = CSICR1_MCLKEN; - if (mx27_camera_emma(pcdev)) { + if (cpu_is_mx27()) { csicr1 |= CSICR1_PRP_IF_EN | CSICR1_FCC | CSICR1_RXFF_LEVEL(0); } else if (cpu_is_mx27()) @@ -392,56 +452,13 @@ static void mx2_camera_remove_device(struct soc_camera_device *icd) mx2_camera_deactivate(pcdev); - if (pcdev->discard_buffer) { - dma_free_coherent(ici->v4l2_dev.dev, pcdev->discard_size, - pcdev->discard_buffer, - pcdev->discard_buffer_dma); - pcdev->discard_buffer = NULL; - } - pcdev->icd = NULL; } -#ifdef CONFIG_MACH_MX27 -static void mx27_camera_dma_enable(struct mx2_camera_dev *pcdev) -{ - u32 tmp; - - imx_dma_enable(pcdev->dma); - - tmp = readl(pcdev->base_csi + CSICR1); - tmp |= CSICR1_RF_OR_INTEN; - writel(tmp, pcdev->base_csi + CSICR1); -} - -static irqreturn_t mx27_camera_irq(int irq_csi, void *data) -{ - struct mx2_camera_dev *pcdev = data; - u32 status = readl(pcdev->base_csi + CSISR); - - if (status & CSISR_SOF_INT && pcdev->active) { - u32 tmp; - - tmp = readl(pcdev->base_csi + CSICR1); - writel(tmp | CSICR1_CLR_RXFIFO, pcdev->base_csi + CSICR1); - mx27_camera_dma_enable(pcdev); - } - - writel(CSISR_SOF_INT | CSISR_RFF_OR_INT, pcdev->base_csi + CSISR); - - return IRQ_HANDLED; -} -#else -static irqreturn_t mx27_camera_irq(int irq_csi, void *data) -{ - return IRQ_NONE; -} -#endif /* CONFIG_MACH_MX27 */ - static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb, int state) { - struct videobuf_buffer *vb; + struct vb2_buffer *vb; struct mx2_buffer *buf; struct mx2_buffer **fb_active = fb == 1 ? &pcdev->fb1_active : &pcdev->fb2_active; @@ -454,25 +471,24 @@ static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb, goto out; vb = &(*fb_active)->vb; - dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); + dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, + vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - vb->state = state; - do_gettimeofday(&vb->ts); - vb->field_count++; - - wake_up(&vb->done); + do_gettimeofday(&vb->v4l2_buf.timestamp); + vb->v4l2_buf.sequence++; + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); if (list_empty(&pcdev->capture)) { buf = NULL; writel(0, pcdev->base_csi + fb_reg); } else { - buf = list_entry(pcdev->capture.next, struct mx2_buffer, - vb.queue); + buf = list_first_entry(&pcdev->capture, struct mx2_buffer, + internal.queue); vb = &buf->vb; - list_del(&vb->queue); - vb->state = VIDEOBUF_ACTIVE; - writel(videobuf_to_dma_contig(vb), pcdev->base_csi + fb_reg); + list_del(&buf->internal.queue); + buf->state = MX2_STATE_ACTIVE; + writel(vb2_dma_contig_plane_dma_addr(vb, 0), + pcdev->base_csi + fb_reg); } *fb_active = buf; @@ -487,9 +503,9 @@ static irqreturn_t mx25_camera_irq(int irq_csi, void *data) u32 status = readl(pcdev->base_csi + CSISR); if (status & CSISR_DMA_TSF_FB1_INT) - mx25_camera_frame_done(pcdev, 1, VIDEOBUF_DONE); + mx25_camera_frame_done(pcdev, 1, MX2_STATE_DONE); else if (status & CSISR_DMA_TSF_FB2_INT) - mx25_camera_frame_done(pcdev, 2, VIDEOBUF_DONE); + mx25_camera_frame_done(pcdev, 2, MX2_STATE_DONE); /* FIXME: handle CSISR_RFF_OR_INT */ @@ -501,59 +517,50 @@ static irqreturn_t mx25_camera_irq(int irq_csi, void *data) /* * Videobuf operations */ -static int mx2_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) +static int mx2_videobuf_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, + unsigned int *count, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_device *icd = soc_camera_from_vb2q(vq); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct mx2_camera_dev *pcdev = ici->priv; int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); - dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size); + dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]); + + /* TODO: support for VIDIOC_CREATE_BUFS not ready */ + if (fmt != NULL) + return -ENOTTY; if (bytes_per_line < 0) return bytes_per_line; - *size = bytes_per_line * icd->user_height; + alloc_ctxs[0] = pcdev->alloc_ctx; + + sizes[0] = bytes_per_line * icd->user_height; if (0 == *count) *count = 32; - if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) - *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size; + if (!*num_planes && + sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024) + *count = (MAX_VIDEO_MEM * 1024 * 1024) / sizes[0]; - return 0; -} + *num_planes = 1; -static void free_buffer(struct videobuf_queue *vq, struct mx2_buffer *buf) -{ - struct soc_camera_device *icd = vq->priv_data; - struct videobuf_buffer *vb = &buf->vb; - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); - - /* - * This waits until this buffer is out of danger, i.e., until it is no - * longer in state VIDEOBUF_QUEUED or VIDEOBUF_ACTIVE - */ - videobuf_waiton(vq, vb, 0, 0); - - videobuf_dma_contig_free(vq, vb); - dev_dbg(icd->parent, "%s freed\n", __func__); - - vb->state = VIDEOBUF_NEEDS_INIT; + return 0; } -static int mx2_videobuf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, enum v4l2_field field) +static int mx2_videobuf_prepare(struct vb2_buffer *vb) { - struct soc_camera_device *icd = vq->priv_data; - struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); + struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); int ret = 0; - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, + vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); if (bytes_per_line < 0) return bytes_per_line; @@ -563,99 +570,58 @@ static int mx2_videobuf_prepare(struct videobuf_queue *vq, * This can be useful if you want to see if we actually fill * the buffer with something */ - memset((void *)vb->baddr, 0xaa, vb->bsize); + memset((void *)vb2_plane_vaddr(vb, 0), + 0xaa, vb2_get_plane_payload(vb, 0)); #endif - if (buf->code != icd->current_fmt->code || - vb->width != icd->user_width || - vb->height != icd->user_height || - vb->field != field) { - buf->code = icd->current_fmt->code; - vb->width = icd->user_width; - vb->height = icd->user_height; - vb->field = field; - vb->state = VIDEOBUF_NEEDS_INIT; - } - - vb->size = bytes_per_line * vb->height; - if (vb->baddr && vb->bsize < vb->size) { + vb2_set_plane_payload(vb, 0, bytes_per_line * icd->user_height); + if (vb2_plane_vaddr(vb, 0) && + vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) { ret = -EINVAL; goto out; } - if (vb->state == VIDEOBUF_NEEDS_INIT) { - ret = videobuf_iolock(vq, vb, NULL); - if (ret) - goto fail; - - vb->state = VIDEOBUF_PREPARED; - } - return 0; -fail: - free_buffer(vq, buf); out: return ret; } -static void mx2_videobuf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void mx2_videobuf_queue(struct vb2_buffer *vb) { - struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); unsigned long flags; - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, + vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); spin_lock_irqsave(&pcdev->lock, flags); - vb->state = VIDEOBUF_QUEUED; - list_add_tail(&vb->queue, &pcdev->capture); + buf->state = MX2_STATE_QUEUED; + list_add_tail(&buf->internal.queue, &pcdev->capture); - if (mx27_camera_emma(pcdev)) { - goto out; -#ifdef CONFIG_MACH_MX27 - } else if (cpu_is_mx27()) { - int ret; - - if (pcdev->active == NULL) { - ret = imx_dma_setup_single(pcdev->dma, - videobuf_to_dma_contig(vb), vb->size, - (u32)pcdev->base_dma + 0x10, - DMA_MODE_READ); - if (ret) { - vb->state = VIDEOBUF_ERROR; - wake_up(&vb->done); - goto out; - } - - vb->state = VIDEOBUF_ACTIVE; - pcdev->active = buf; - } -#endif - } else { /* cpu_is_mx25() */ + if (cpu_is_mx25()) { u32 csicr3, dma_inten = 0; if (pcdev->fb1_active == NULL) { - writel(videobuf_to_dma_contig(vb), + writel(vb2_dma_contig_plane_dma_addr(vb, 0), pcdev->base_csi + CSIDMASA_FB1); pcdev->fb1_active = buf; dma_inten = CSICR1_FB1_DMA_INTEN; } else if (pcdev->fb2_active == NULL) { - writel(videobuf_to_dma_contig(vb), + writel(vb2_dma_contig_plane_dma_addr(vb, 0), pcdev->base_csi + CSIDMASA_FB2); pcdev->fb2_active = buf; dma_inten = CSICR1_FB2_DMA_INTEN; } if (dma_inten) { - list_del(&vb->queue); - vb->state = VIDEOBUF_ACTIVE; + list_del(&buf->internal.queue); + buf->state = MX2_STATE_ACTIVE; csicr3 = readl(pcdev->base_csi + CSICR3); @@ -674,36 +640,31 @@ static void mx2_videobuf_queue(struct videobuf_queue *vq, } } -out: spin_unlock_irqrestore(&pcdev->lock, flags); } -static void mx2_videobuf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void mx2_videobuf_release(struct vb2_buffer *vb) { - struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); unsigned long flags; #ifdef DEBUG - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, + vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - switch (vb->state) { - case VIDEOBUF_ACTIVE: + switch (buf->state) { + case MX2_STATE_ACTIVE: dev_info(icd->parent, "%s (active)\n", __func__); break; - case VIDEOBUF_QUEUED: + case MX2_STATE_QUEUED: dev_info(icd->parent, "%s (queued)\n", __func__); break; - case VIDEOBUF_PREPARED: - dev_info(icd->parent, "%s (prepared)\n", __func__); - break; default: dev_info(icd->parent, "%s (unknown) %d\n", __func__, - vb->state); + buf->state); break; } #endif @@ -717,11 +678,9 @@ static void mx2_videobuf_release(struct videobuf_queue *vq, * state. This requires a specific handling for each of the these DMA * types. */ + spin_lock_irqsave(&pcdev->lock, flags); - if (vb->state == VIDEOBUF_QUEUED) { - list_del(&vb->queue); - vb->state = VIDEOBUF_ERROR; - } else if (cpu_is_mx25() && vb->state == VIDEOBUF_ACTIVE) { + if (cpu_is_mx25() && buf->state == MX2_STATE_ACTIVE) { if (pcdev->fb1_active == buf) { pcdev->csicr1 &= ~CSICR1_FB1_DMA_INTEN; writel(0, pcdev->base_csi + CSIDMASA_FB1); @@ -732,75 +691,178 @@ static void mx2_videobuf_release(struct videobuf_queue *vq, pcdev->fb2_active = NULL; } writel(pcdev->csicr1, pcdev->base_csi + CSICR1); - vb->state = VIDEOBUF_ERROR; } spin_unlock_irqrestore(&pcdev->lock, flags); - - free_buffer(vq, buf); } -static struct videobuf_queue_ops mx2_videobuf_ops = { - .buf_setup = mx2_videobuf_setup, - .buf_prepare = mx2_videobuf_prepare, - .buf_queue = mx2_videobuf_queue, - .buf_release = mx2_videobuf_release, -}; - -static void mx2_camera_init_videobuf(struct videobuf_queue *q, - struct soc_camera_device *icd) +static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, + int bytesperline) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct soc_camera_host *ici = + to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; + struct mx2_fmt_cfg *prp = pcdev->emma_prp; - videobuf_queue_dma_contig_init(q, &mx2_videobuf_ops, pcdev->dev, - &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_NONE, sizeof(struct mx2_buffer), - icd, &icd->video_lock); -} + writel((pcdev->s_width << 16) | pcdev->s_height, + pcdev->base_emma + PRP_SRC_FRAME_SIZE); + writel(prp->cfg.src_pixel, + pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL); + if (prp->cfg.channel == 1) { + writel((icd->user_width << 16) | icd->user_height, + pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE); + writel(bytesperline, + pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE); + writel(prp->cfg.ch1_pixel, + pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL); + } else { /* channel 2 */ + writel((icd->user_width << 16) | icd->user_height, + pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE); + } -#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \ - V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_VSYNC_ACTIVE_LOW | \ - V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_HSYNC_ACTIVE_LOW | \ - V4L2_MBUS_PCLK_SAMPLE_RISING | \ - V4L2_MBUS_PCLK_SAMPLE_FALLING | \ - V4L2_MBUS_DATA_ACTIVE_HIGH | \ - V4L2_MBUS_DATA_ACTIVE_LOW) + /* Enable interrupts */ + writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL); +} -static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev) +static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev) { - u32 cntl; - int count = 0; + int dir; - cntl = readl(pcdev->base_emma + PRP_CNTL); - writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL); - while (count++ < 100) { - if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST)) - return 0; - barrier(); - udelay(1); - } + for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) { + unsigned char *s = pcdev->resizing[dir].s; + int len = pcdev->resizing[dir].len; + unsigned int coeff[2] = {0, 0}; + unsigned int valid = 0; + int i; - return -ETIMEDOUT; + if (len == 0) + continue; + + for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) { + int j; + + j = i > 9 ? 1 : 0; + coeff[j] = (coeff[j] << BC_COEF) | + (s[i] & (SZ_COEF - 1)); + + if (i == 5 || i == 15) + coeff[j] <<= 1; + + valid = (valid << 1) | (s[i] >> BC_COEF); + } + + valid |= PRP_RZ_VALID_TBL_LEN(len); + + if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR) + valid |= PRP_RZ_VALID_BILINEAR; + + if (pcdev->emma_prp->cfg.channel == 1) { + if (dir == RESIZE_DIR_H) { + writel(coeff[0], pcdev->base_emma + + PRP_CH1_RZ_HORI_COEF1); + writel(coeff[1], pcdev->base_emma + + PRP_CH1_RZ_HORI_COEF2); + writel(valid, pcdev->base_emma + + PRP_CH1_RZ_HORI_VALID); + } else { + writel(coeff[0], pcdev->base_emma + + PRP_CH1_RZ_VERT_COEF1); + writel(coeff[1], pcdev->base_emma + + PRP_CH1_RZ_VERT_COEF2); + writel(valid, pcdev->base_emma + + PRP_CH1_RZ_VERT_VALID); + } + } else { + if (dir == RESIZE_DIR_H) { + writel(coeff[0], pcdev->base_emma + + PRP_CH2_RZ_HORI_COEF1); + writel(coeff[1], pcdev->base_emma + + PRP_CH2_RZ_HORI_COEF2); + writel(valid, pcdev->base_emma + + PRP_CH2_RZ_HORI_VALID); + } else { + writel(coeff[0], pcdev->base_emma + + PRP_CH2_RZ_VERT_COEF1); + writel(coeff[1], pcdev->base_emma + + PRP_CH2_RZ_VERT_COEF2); + writel(valid, pcdev->base_emma + + PRP_CH2_RZ_VERT_VALID); + } + } + } } -static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, - int bytesperline) +static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) { + struct soc_camera_device *icd = soc_camera_from_vb2q(q); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; struct mx2_fmt_cfg *prp = pcdev->emma_prp; - u32 imgsize = pcdev->icd->user_height * pcdev->icd->user_width; + struct vb2_buffer *vb; + struct mx2_buffer *buf; + unsigned long phys; + int bytesperline; - if (prp->cfg.channel == 1) { - writel(pcdev->discard_buffer_dma, - pcdev->base_emma + PRP_DEST_RGB1_PTR); - writel(pcdev->discard_buffer_dma, - pcdev->base_emma + PRP_DEST_RGB2_PTR); + if (cpu_is_mx27()) { + unsigned long flags; + if (count < 2) + return -EINVAL; + + spin_lock_irqsave(&pcdev->lock, flags); + + buf = list_first_entry(&pcdev->capture, struct mx2_buffer, + internal.queue); + buf->internal.bufnum = 0; + vb = &buf->vb; + buf->state = MX2_STATE_ACTIVE; + + phys = vb2_dma_contig_plane_dma_addr(vb, 0); + mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); + list_move_tail(pcdev->capture.next, &pcdev->active_bufs); + + buf = list_first_entry(&pcdev->capture, struct mx2_buffer, + internal.queue); + buf->internal.bufnum = 1; + vb = &buf->vb; + buf->state = MX2_STATE_ACTIVE; - writel(PRP_CNTL_CH1EN | + phys = vb2_dma_contig_plane_dma_addr(vb, 0); + mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); + list_move_tail(pcdev->capture.next, &pcdev->active_bufs); + + bytesperline = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + if (bytesperline < 0) + return bytesperline; + + /* + * I didn't manage to properly enable/disable the prp + * on a per frame basis during running transfers, + * thus we allocate a buffer here and use it to + * discard frames when no buffer is available. + * Feel free to work on this ;) + */ + pcdev->discard_size = icd->user_height * bytesperline; + pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, + pcdev->discard_size, &pcdev->discard_buffer_dma, + GFP_KERNEL); + if (!pcdev->discard_buffer) + return -ENOMEM; + + pcdev->buf_discard[0].discard = true; + list_add_tail(&pcdev->buf_discard[0].queue, + &pcdev->discard); + + pcdev->buf_discard[1].discard = true; + list_add_tail(&pcdev->buf_discard[1].queue, + &pcdev->discard); + + mx2_prp_resize_commit(pcdev); + + mx27_camera_emma_buf_init(icd, bytesperline); + + if (prp->cfg.channel == 1) { + writel(PRP_CNTL_CH1EN | PRP_CNTL_CSIEN | prp->cfg.in_fmt | prp->cfg.out_fmt | @@ -809,56 +871,107 @@ static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, PRP_CNTL_CH1_TSKIP(0) | PRP_CNTL_IN_TSKIP(0), pcdev->base_emma + PRP_CNTL); + } else { + writel(PRP_CNTL_CH2EN | + PRP_CNTL_CSIEN | + prp->cfg.in_fmt | + prp->cfg.out_fmt | + PRP_CNTL_CH2_LEN | + PRP_CNTL_CH2_TSKIP(0) | + PRP_CNTL_IN_TSKIP(0), + pcdev->base_emma + PRP_CNTL); + } + spin_unlock_irqrestore(&pcdev->lock, flags); + } - writel((icd->user_width << 16) | icd->user_height, - pcdev->base_emma + PRP_SRC_FRAME_SIZE); - writel((icd->user_width << 16) | icd->user_height, - pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE); - writel(bytesperline, - pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE); - writel(prp->cfg.src_pixel, - pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL); - writel(prp->cfg.ch1_pixel, - pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL); - } else { /* channel 2 */ - writel(pcdev->discard_buffer_dma, - pcdev->base_emma + PRP_DEST_Y_PTR); - writel(pcdev->discard_buffer_dma, - pcdev->base_emma + PRP_SOURCE_Y_PTR); - - if (prp->cfg.out_fmt == PRP_CNTL_CH2_OUT_YUV420) { - writel(pcdev->discard_buffer_dma + imgsize, - pcdev->base_emma + PRP_DEST_CB_PTR); - writel(pcdev->discard_buffer_dma + ((5 * imgsize) / 4), - pcdev->base_emma + PRP_DEST_CR_PTR); - writel(pcdev->discard_buffer_dma + imgsize, - pcdev->base_emma + PRP_SOURCE_CB_PTR); - writel(pcdev->discard_buffer_dma + ((5 * imgsize) / 4), - pcdev->base_emma + PRP_SOURCE_CR_PTR); + return 0; +} + +static int mx2_stop_streaming(struct vb2_queue *q) +{ + struct soc_camera_device *icd = soc_camera_from_vb2q(q); + struct soc_camera_host *ici = + to_soc_camera_host(icd->parent); + struct mx2_camera_dev *pcdev = ici->priv; + struct mx2_fmt_cfg *prp = pcdev->emma_prp; + unsigned long flags; + void *b; + u32 cntl; + + if (cpu_is_mx27()) { + spin_lock_irqsave(&pcdev->lock, flags); + + cntl = readl(pcdev->base_emma + PRP_CNTL); + if (prp->cfg.channel == 1) { + writel(cntl & ~PRP_CNTL_CH1EN, + pcdev->base_emma + PRP_CNTL); + } else { + writel(cntl & ~PRP_CNTL_CH2EN, + pcdev->base_emma + PRP_CNTL); } + INIT_LIST_HEAD(&pcdev->capture); + INIT_LIST_HEAD(&pcdev->active_bufs); + INIT_LIST_HEAD(&pcdev->discard); - writel(PRP_CNTL_CH2EN | - PRP_CNTL_CSIEN | - prp->cfg.in_fmt | - prp->cfg.out_fmt | - PRP_CNTL_CH2_LEN | - PRP_CNTL_CH2_TSKIP(0) | - PRP_CNTL_IN_TSKIP(0), - pcdev->base_emma + PRP_CNTL); + b = pcdev->discard_buffer; + pcdev->discard_buffer = NULL; - writel((icd->user_width << 16) | icd->user_height, - pcdev->base_emma + PRP_SRC_FRAME_SIZE); + spin_unlock_irqrestore(&pcdev->lock, flags); - writel((icd->user_width << 16) | icd->user_height, - pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE); + dma_free_coherent(ici->v4l2_dev.dev, + pcdev->discard_size, b, pcdev->discard_buffer_dma); + } - writel(prp->cfg.src_pixel, - pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL); + return 0; +} + +static struct vb2_ops mx2_videobuf_ops = { + .queue_setup = mx2_videobuf_setup, + .buf_prepare = mx2_videobuf_prepare, + .buf_queue = mx2_videobuf_queue, + .buf_cleanup = mx2_videobuf_release, + .start_streaming = mx2_start_streaming, + .stop_streaming = mx2_stop_streaming, +}; + +static int mx2_camera_init_videobuf(struct vb2_queue *q, + struct soc_camera_device *icd) +{ + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR; + q->drv_priv = icd; + q->ops = &mx2_videobuf_ops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct mx2_buffer); + + return vb2_queue_init(q); +} +#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \ + V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_VSYNC_ACTIVE_LOW | \ + V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_HSYNC_ACTIVE_LOW | \ + V4L2_MBUS_PCLK_SAMPLE_RISING | \ + V4L2_MBUS_PCLK_SAMPLE_FALLING | \ + V4L2_MBUS_DATA_ACTIVE_HIGH | \ + V4L2_MBUS_DATA_ACTIVE_LOW) + +static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev) +{ + u32 cntl; + int count = 0; + + cntl = readl(pcdev->base_emma + PRP_CNTL); + writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL); + while (count++ < 100) { + if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST)) + return 0; + barrier(); + udelay(1); } - /* Enable interrupts */ - writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL); + return -ETIMEDOUT; } static int mx2_camera_set_bus_param(struct soc_camera_device *icd) @@ -939,31 +1052,10 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd) if (bytesperline < 0) return bytesperline; - if (mx27_camera_emma(pcdev)) { + if (cpu_is_mx27()) { ret = mx27_camera_emma_prp_reset(pcdev); if (ret) return ret; - - if (pcdev->discard_buffer) - dma_free_coherent(ici->v4l2_dev.dev, - pcdev->discard_size, pcdev->discard_buffer, - pcdev->discard_buffer_dma); - - /* - * I didn't manage to properly enable/disable the prp - * on a per frame basis during running transfers, - * thus we allocate a buffer here and use it to - * discard frames when no buffer is available. - * Feel free to work on this ;) - */ - pcdev->discard_size = icd->user_height * bytesperline; - pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, - pcdev->discard_size, &pcdev->discard_buffer_dma, - GFP_KERNEL); - if (!pcdev->discard_buffer) - return -ENOMEM; - - mx27_camera_emma_buf_init(icd, bytesperline); } else if (cpu_is_mx25()) { writel((bytesperline * icd->user_height) >> 2, pcdev->base_csi + CSIRXCNT); @@ -1052,6 +1144,123 @@ static int mx2_camera_get_formats(struct soc_camera_device *icd, return formats; } +static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev, + struct v4l2_mbus_framefmt *mf_in, + struct v4l2_pix_format *pix_out, bool apply) +{ + int num, den; + unsigned long m; + int i, dir; + + for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) { + struct emma_prp_resize tmprsz; + unsigned char *s = tmprsz.s; + int len = 0; + int in, out; + + if (dir == RESIZE_DIR_H) { + in = mf_in->width; + out = pix_out->width; + } else { + in = mf_in->height; + out = pix_out->height; + } + + if (in < out) + return -EINVAL; + else if (in == out) + continue; + + /* Calculate ratio */ + m = gcd(in, out); + num = in / m; + den = out / m; + if (num > RESIZE_NUM_MAX) + return -EINVAL; + + if ((num >= 2 * den) && (den == 1) && + (num < 9) && (!(num & 0x01))) { + int sum = 0; + int j; + + /* Average scaling for >= 2:1 ratios */ + /* Support can be added for num >=9 and odd values */ + + tmprsz.algo = RESIZE_ALGO_AVERAGING; + len = num; + + for (i = 0; i < (len / 2); i++) + s[i] = 8; + + do { + for (i = 0; i < (len / 2); i++) { + s[i] = s[i] >> 1; + sum = 0; + for (j = 0; j < (len / 2); j++) + sum += s[j]; + if (sum == 4) + break; + } + } while (sum != 4); + + for (i = (len / 2); i < len; i++) + s[i] = s[len - i - 1]; + + s[len - 1] |= SZ_COEF; + } else { + /* bilinear scaling for < 2:1 ratios */ + int v; /* overflow counter */ + int coeff, nxt; /* table output */ + int in_pos_inc = 2 * den; + int out_pos = num; + int out_pos_inc = 2 * num; + int init_carry = num - den; + int carry = init_carry; + + tmprsz.algo = RESIZE_ALGO_BILINEAR; + v = den + in_pos_inc; + do { + coeff = v - out_pos; + out_pos += out_pos_inc; + carry += out_pos_inc; + for (nxt = 0; v < out_pos; nxt++) { + v += in_pos_inc; + carry -= in_pos_inc; + } + + if (len > RESIZE_NUM_MAX) + return -EINVAL; + + coeff = ((coeff << BC_COEF) + + (in_pos_inc >> 1)) / in_pos_inc; + + if (coeff >= (SZ_COEF - 1)) + coeff--; + + coeff |= SZ_COEF; + s[len] = (unsigned char)coeff; + len++; + + for (i = 1; i < nxt; i++) { + if (len >= RESIZE_NUM_MAX) + return -EINVAL; + s[len] = 0; + len++; + } + } while (carry != init_carry); + } + tmprsz.len = len; + if (dir == RESIZE_DIR_H) + mf_in->width = pix_out->width; + else + mf_in->height = pix_out->height; + + if (apply) + memcpy(&pcdev->resizing[dir], &tmprsz, sizeof(tmprsz)); + } + return 0; +} + static int mx2_camera_set_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { @@ -1063,6 +1272,9 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd, struct v4l2_mbus_framefmt mf; int ret; + dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", + __func__, pix->width, pix->height); + xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { dev_warn(icd->parent, "Format %x not found\n", @@ -1080,6 +1292,22 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd, if (ret < 0 && ret != -ENOIOCTLCMD) return ret; + /* Store width and height returned by the sensor for resizing */ + pcdev->s_width = mf.width; + pcdev->s_height = mf.height; + dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n", + __func__, pcdev->s_width, pcdev->s_height); + + pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, + xlate->host_fmt->fourcc); + + memset(pcdev->resizing, 0, sizeof(pcdev->resizing)); + if ((mf.width != pix->width || mf.height != pix->height) && + pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { + if (mx2_emmaprp_resize(pcdev, &mf, pix, true) < 0) + dev_dbg(icd->parent, "%s: can't resize\n", __func__); + } + if (mf.code != xlate->code) return -EINVAL; @@ -1089,9 +1317,8 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd, pix->colorspace = mf.colorspace; icd->current_fmt = xlate; - if (mx27_camera_emma(pcdev)) - pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, - xlate->host_fmt->fourcc); + dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", + __func__, pix->width, pix->height); return 0; } @@ -1104,9 +1331,14 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_mbus_framefmt mf; __u32 pixfmt = pix->pixelformat; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct mx2_camera_dev *pcdev = ici->priv; unsigned int width_limit; int ret; + dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", + __func__, pix->width, pix->height); + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (pixfmt && !xlate) { dev_warn(icd->parent, "Format %x not found\n", pixfmt); @@ -1156,6 +1388,20 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, if (ret < 0) return ret; + dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n", + __func__, pcdev->s_width, pcdev->s_height); + + /* If the sensor does not support image size try PrP resizing */ + pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, + xlate->host_fmt->fourcc); + + memset(pcdev->resizing, 0, sizeof(pcdev->resizing)); + if ((mf.width != pix->width || mf.height != pix->height) && + pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { + if (mx2_emmaprp_resize(pcdev, &mf, pix, false) < 0) + dev_dbg(icd->parent, "%s: can't resize\n", __func__); + } + if (mf.field == V4L2_FIELD_ANY) mf.field = V4L2_FIELD_NONE; /* @@ -1174,6 +1420,9 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, pix->field = mf.field; pix->colorspace = mf.colorspace; + dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", + __func__, pix->width, pix->height); + return 0; } @@ -1187,136 +1436,11 @@ static int mx2_camera_querycap(struct soc_camera_host *ici, return 0; } -static int mx2_camera_reqbufs(struct soc_camera_device *icd, - struct v4l2_requestbuffers *p) -{ - int i; - - for (i = 0; i < p->count; i++) { - struct mx2_buffer *buf = container_of(icd->vb_vidq.bufs[i], - struct mx2_buffer, vb); - INIT_LIST_HEAD(&buf->vb.queue); - } - - return 0; -} - -#ifdef CONFIG_MACH_MX27 -static void mx27_camera_frame_done(struct mx2_camera_dev *pcdev, int state) -{ - struct videobuf_buffer *vb; - struct mx2_buffer *buf; - unsigned long flags; - int ret; - - spin_lock_irqsave(&pcdev->lock, flags); - - if (!pcdev->active) { - dev_err(pcdev->dev, "%s called with no active buffer!\n", - __func__); - goto out; - } - - vb = &pcdev->active->vb; - buf = container_of(vb, struct mx2_buffer, vb); - WARN_ON(list_empty(&vb->queue)); - dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); - - /* _init is used to debug races, see comment in pxa_camera_reqbufs() */ - list_del_init(&vb->queue); - vb->state = state; - do_gettimeofday(&vb->ts); - vb->field_count++; - - wake_up(&vb->done); - - if (list_empty(&pcdev->capture)) { - pcdev->active = NULL; - goto out; - } - - pcdev->active = list_entry(pcdev->capture.next, - struct mx2_buffer, vb.queue); - - vb = &pcdev->active->vb; - vb->state = VIDEOBUF_ACTIVE; - - ret = imx_dma_setup_single(pcdev->dma, videobuf_to_dma_contig(vb), - vb->size, (u32)pcdev->base_dma + 0x10, DMA_MODE_READ); - - if (ret) { - vb->state = VIDEOBUF_ERROR; - pcdev->active = NULL; - wake_up(&vb->done); - } - -out: - spin_unlock_irqrestore(&pcdev->lock, flags); -} - -static void mx27_camera_dma_err_callback(int channel, void *data, int err) -{ - struct mx2_camera_dev *pcdev = data; - - mx27_camera_frame_done(pcdev, VIDEOBUF_ERROR); -} - -static void mx27_camera_dma_callback(int channel, void *data) -{ - struct mx2_camera_dev *pcdev = data; - - mx27_camera_frame_done(pcdev, VIDEOBUF_DONE); -} - -#define DMA_REQ_CSI_RX 31 /* FIXME: Add this to a resource */ - -static int __devinit mx27_camera_dma_init(struct platform_device *pdev, - struct mx2_camera_dev *pcdev) -{ - int err; - - pcdev->dma = imx_dma_request_by_prio("CSI RX DMA", DMA_PRIO_HIGH); - if (pcdev->dma < 0) { - dev_err(&pdev->dev, "%s failed to request DMA channel\n", - __func__); - return pcdev->dma; - } - - err = imx_dma_setup_handlers(pcdev->dma, mx27_camera_dma_callback, - mx27_camera_dma_err_callback, pcdev); - if (err) { - dev_err(&pdev->dev, "%s failed to set DMA callback\n", - __func__); - goto err_out; - } - - err = imx_dma_config_channel(pcdev->dma, - IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_FIFO, - IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, - DMA_REQ_CSI_RX, 1); - if (err) { - dev_err(&pdev->dev, "%s failed to config DMA channel\n", - __func__); - goto err_out; - } - - imx_dma_config_burstlen(pcdev->dma, 64); - - return 0; - -err_out: - imx_dma_free(pcdev->dma); - - return err; -} -#endif /* CONFIG_MACH_MX27 */ - static unsigned int mx2_camera_poll(struct file *file, poll_table *pt) { struct soc_camera_device *icd = file->private_data; - return videobuf_poll_stream(file, &icd->vb_vidq, pt); + return vb2_poll(&icd->vb2_vidq, file, pt); } static struct soc_camera_host_ops mx2_soc_camera_host_ops = { @@ -1327,144 +1451,148 @@ static struct soc_camera_host_ops mx2_soc_camera_host_ops = { .set_crop = mx2_camera_set_crop, .get_formats = mx2_camera_get_formats, .try_fmt = mx2_camera_try_fmt, - .init_videobuf = mx2_camera_init_videobuf, - .reqbufs = mx2_camera_reqbufs, + .init_videobuf2 = mx2_camera_init_videobuf, .poll = mx2_camera_poll, .querycap = mx2_camera_querycap, .set_bus_param = mx2_camera_set_bus_param, }; static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev, - int bufnum, int state) + int bufnum, bool err) { - u32 imgsize = pcdev->icd->user_height * pcdev->icd->user_width; +#ifdef DEBUG struct mx2_fmt_cfg *prp = pcdev->emma_prp; +#endif + struct mx2_buf_internal *ibuf; struct mx2_buffer *buf; - struct videobuf_buffer *vb; + struct vb2_buffer *vb; unsigned long phys; - if (!list_empty(&pcdev->active_bufs)) { - buf = list_entry(pcdev->active_bufs.next, - struct mx2_buffer, vb.queue); + ibuf = list_first_entry(&pcdev->active_bufs, struct mx2_buf_internal, + queue); + + BUG_ON(ibuf->bufnum != bufnum); - BUG_ON(buf->bufnum != bufnum); + if (ibuf->discard) { + /* + * Discard buffer must not be returned to user space. + * Just return it to the discard queue. + */ + list_move_tail(pcdev->active_bufs.next, &pcdev->discard); + } else { + buf = mx2_ibuf_to_buf(ibuf); vb = &buf->vb; #ifdef DEBUG - phys = videobuf_to_dma_contig(vb); + phys = vb2_dma_contig_plane_dma_addr(vb, 0); if (prp->cfg.channel == 1) { if (readl(pcdev->base_emma + PRP_DEST_RGB1_PTR + 4 * bufnum) != phys) { - dev_err(pcdev->dev, "%p != %p\n", phys, - readl(pcdev->base_emma + - PRP_DEST_RGB1_PTR + - 4 * bufnum)); + dev_err(pcdev->dev, "%lx != %x\n", phys, + readl(pcdev->base_emma + + PRP_DEST_RGB1_PTR + 4 * bufnum)); } } else { if (readl(pcdev->base_emma + PRP_DEST_Y_PTR - 0x14 * bufnum) != phys) { - dev_err(pcdev->dev, "%p != %p\n", phys, - readl(pcdev->base_emma + - PRP_DEST_Y_PTR - - 0x14 * bufnum)); + dev_err(pcdev->dev, "%lx != %x\n", phys, + readl(pcdev->base_emma + + PRP_DEST_Y_PTR - 0x14 * bufnum)); } } #endif - dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, - vb->baddr, vb->bsize); + dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb, + vb2_plane_vaddr(vb, 0), + vb2_get_plane_payload(vb, 0)); - list_del(&vb->queue); - vb->state = state; - do_gettimeofday(&vb->ts); - vb->field_count = pcdev->frame_count * 2; - pcdev->frame_count++; - - wake_up(&vb->done); + list_del_init(&buf->internal.queue); + do_gettimeofday(&vb->v4l2_buf.timestamp); + vb->v4l2_buf.sequence = pcdev->frame_count; + if (err) + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); + else + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); } + pcdev->frame_count++; + if (list_empty(&pcdev->capture)) { - if (prp->cfg.channel == 1) { - writel(pcdev->discard_buffer_dma, pcdev->base_emma + - PRP_DEST_RGB1_PTR + 4 * bufnum); - } else { - writel(pcdev->discard_buffer_dma, pcdev->base_emma + - PRP_DEST_Y_PTR - - 0x14 * bufnum); - if (prp->out_fmt == V4L2_PIX_FMT_YUV420) { - writel(pcdev->discard_buffer_dma + imgsize, - pcdev->base_emma + PRP_DEST_CB_PTR - - 0x14 * bufnum); - writel(pcdev->discard_buffer_dma + - ((5 * imgsize) / 4), pcdev->base_emma + - PRP_DEST_CR_PTR - 0x14 * bufnum); - } + if (list_empty(&pcdev->discard)) { + dev_warn(pcdev->dev, "%s: trying to access empty discard list\n", + __func__); + return; } + + ibuf = list_first_entry(&pcdev->discard, + struct mx2_buf_internal, queue); + ibuf->bufnum = bufnum; + + list_move_tail(pcdev->discard.next, &pcdev->active_bufs); + mx27_update_emma_buf(pcdev, pcdev->discard_buffer_dma, bufnum); return; } - buf = list_entry(pcdev->capture.next, - struct mx2_buffer, vb.queue); + buf = list_first_entry(&pcdev->capture, struct mx2_buffer, + internal.queue); - buf->bufnum = !bufnum; + buf->internal.bufnum = bufnum; list_move_tail(pcdev->capture.next, &pcdev->active_bufs); vb = &buf->vb; - vb->state = VIDEOBUF_ACTIVE; + buf->state = MX2_STATE_ACTIVE; - phys = videobuf_to_dma_contig(vb); - if (prp->cfg.channel == 1) { - writel(phys, pcdev->base_emma + PRP_DEST_RGB1_PTR + 4 * bufnum); - } else { - writel(phys, pcdev->base_emma + - PRP_DEST_Y_PTR - 0x14 * bufnum); - if (prp->cfg.out_fmt == PRP_CNTL_CH2_OUT_YUV420) { - writel(phys + imgsize, pcdev->base_emma + - PRP_DEST_CB_PTR - 0x14 * bufnum); - writel(phys + ((5 * imgsize) / 4), pcdev->base_emma + - PRP_DEST_CR_PTR - 0x14 * bufnum); - } - } + phys = vb2_dma_contig_plane_dma_addr(vb, 0); + mx27_update_emma_buf(pcdev, phys, bufnum); } static irqreturn_t mx27_camera_emma_irq(int irq_emma, void *data) { struct mx2_camera_dev *pcdev = data; unsigned int status = readl(pcdev->base_emma + PRP_INTRSTATUS); - struct mx2_buffer *buf; + struct mx2_buf_internal *ibuf; + + spin_lock(&pcdev->lock); + + if (list_empty(&pcdev->active_bufs)) { + dev_warn(pcdev->dev, "%s: called while active list is empty\n", + __func__); + + if (!status) { + spin_unlock(&pcdev->lock); + return IRQ_NONE; + } + } if (status & (1 << 7)) { /* overflow */ - u32 cntl; - /* - * We only disable channel 1 here since this is the only - * enabled channel - * - * FIXME: the correct DMA overflow handling should be resetting - * the buffer, returning an error frame, and continuing with - * the next one. - */ - cntl = readl(pcdev->base_emma + PRP_CNTL); + u32 cntl = readl(pcdev->base_emma + PRP_CNTL); writel(cntl & ~(PRP_CNTL_CH1EN | PRP_CNTL_CH2EN), pcdev->base_emma + PRP_CNTL); writel(cntl, pcdev->base_emma + PRP_CNTL); - } - if ((((status & (3 << 5)) == (3 << 5)) || - ((status & (3 << 3)) == (3 << 3))) - && !list_empty(&pcdev->active_bufs)) { + + ibuf = list_first_entry(&pcdev->active_bufs, + struct mx2_buf_internal, queue); + mx27_camera_frame_done_emma(pcdev, + ibuf->bufnum, true); + + status &= ~(1 << 7); + } else if (((status & (3 << 5)) == (3 << 5)) || + ((status & (3 << 3)) == (3 << 3))) { /* * Both buffers have triggered, process the one we're expecting * to first */ - buf = list_entry(pcdev->active_bufs.next, - struct mx2_buffer, vb.queue); - mx27_camera_frame_done_emma(pcdev, buf->bufnum, VIDEOBUF_DONE); - status &= ~(1 << (6 - buf->bufnum)); /* mark processed */ + ibuf = list_first_entry(&pcdev->active_bufs, + struct mx2_buf_internal, queue); + mx27_camera_frame_done_emma(pcdev, ibuf->bufnum, false); + status &= ~(1 << (6 - ibuf->bufnum)); /* mark processed */ + } else if ((status & (1 << 6)) || (status & (1 << 4))) { + mx27_camera_frame_done_emma(pcdev, 0, false); + } else if ((status & (1 << 5)) || (status & (1 << 3))) { + mx27_camera_frame_done_emma(pcdev, 1, false); } - if ((status & (1 << 6)) || (status & (1 << 4))) - mx27_camera_frame_done_emma(pcdev, 0, VIDEOBUF_DONE); - if ((status & (1 << 5)) || (status & (1 << 3))) - mx27_camera_frame_done_emma(pcdev, 1, VIDEOBUF_DONE); + spin_unlock(&pcdev->lock); writel(status, pcdev->base_emma + PRP_INTRSTATUS); return IRQ_HANDLED; @@ -1527,8 +1655,6 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) struct resource *res_csi, *res_emma; void __iomem *base_csi; int irq_csi, irq_emma; - irq_handler_t mx2_cam_irq_handler = cpu_is_mx25() ? mx25_camera_irq - : mx27_camera_irq; int err = 0; dev_dbg(&pdev->dev, "initialising\n"); @@ -1550,22 +1676,11 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) pcdev->clk_csi = clk_get(&pdev->dev, NULL); if (IS_ERR(pcdev->clk_csi)) { + dev_err(&pdev->dev, "Could not get csi clock\n"); err = PTR_ERR(pcdev->clk_csi); goto exit_kfree; } - dev_dbg(&pdev->dev, "Camera clock frequency: %ld\n", - clk_get_rate(pcdev->clk_csi)); - - /* Initialize DMA */ -#ifdef CONFIG_MACH_MX27 - if (cpu_is_mx27()) { - err = mx27_camera_dma_init(pdev, pcdev); - if (err) - goto exit_clk_put; - } -#endif /* CONFIG_MACH_MX27 */ - pcdev->res_csi = res_csi; pcdev->pdata = pdev->dev.platform_data; if (pcdev->pdata) { @@ -1585,6 +1700,7 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) INIT_LIST_HEAD(&pcdev->capture); INIT_LIST_HEAD(&pcdev->active_bufs); + INIT_LIST_HEAD(&pcdev->discard); spin_lock_init(&pcdev->lock); /* @@ -1606,11 +1722,13 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) pcdev->base_dma = res_csi->start; pcdev->dev = &pdev->dev; - err = request_irq(pcdev->irq_csi, mx2_cam_irq_handler, 0, - MX2_CAM_DRV_NAME, pcdev); - if (err) { - dev_err(pcdev->dev, "Camera interrupt register failed \n"); - goto exit_iounmap; + if (cpu_is_mx25()) { + err = request_irq(pcdev->irq_csi, mx25_camera_irq, 0, + MX2_CAM_DRV_NAME, pcdev); + if (err) { + dev_err(pcdev->dev, "Camera interrupt register failed \n"); + goto exit_iounmap; + } } if (cpu_is_mx27()) { @@ -1618,14 +1736,15 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 1); irq_emma = platform_get_irq(pdev, 1); - if (res_emma && irq_emma >= 0) { - dev_info(&pdev->dev, "Using EMMA\n"); - pcdev->use_emma = 1; - pcdev->res_emma = res_emma; - pcdev->irq_emma = irq_emma; - if (mx27_camera_emma_init(pcdev)) - goto exit_free_irq; + if (!res_emma || !irq_emma) { + dev_err(&pdev->dev, "no EMMA resources\n"); + goto exit_free_irq; } + + pcdev->res_emma = res_emma; + pcdev->irq_emma = irq_emma; + if (mx27_camera_emma_init(pcdev)) + goto exit_free_irq; } pcdev->soc_host.drv_name = MX2_CAM_DRV_NAME, @@ -1633,6 +1752,12 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) pcdev->soc_host.priv = pcdev; pcdev->soc_host.v4l2_dev.dev = &pdev->dev; pcdev->soc_host.nr = pdev->id; + + pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(pcdev->alloc_ctx)) { + err = PTR_ERR(pcdev->alloc_ctx); + goto eallocctx; + } err = soc_camera_host_register(&pcdev->soc_host); if (err) goto exit_free_emma; @@ -1643,26 +1768,24 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) return 0; exit_free_emma: - if (mx27_camera_emma(pcdev)) { + vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); +eallocctx: + if (cpu_is_mx27()) { free_irq(pcdev->irq_emma, pcdev); clk_disable(pcdev->clk_emma); clk_put(pcdev->clk_emma); iounmap(pcdev->base_emma); - release_mem_region(res_emma->start, resource_size(res_emma)); + release_mem_region(pcdev->res_emma->start, resource_size(pcdev->res_emma)); } exit_free_irq: - free_irq(pcdev->irq_csi, pcdev); + if (cpu_is_mx25()) + free_irq(pcdev->irq_csi, pcdev); exit_iounmap: iounmap(base_csi); exit_release: release_mem_region(res_csi->start, resource_size(res_csi)); exit_dma_free: -#ifdef CONFIG_MACH_MX27 - if (cpu_is_mx27()) - imx_dma_free(pcdev->dma); -exit_clk_put: clk_put(pcdev->clk_csi); -#endif /* CONFIG_MACH_MX27 */ exit_kfree: kfree(pcdev); exit: @@ -1677,19 +1800,18 @@ static int __devexit mx2_camera_remove(struct platform_device *pdev) struct resource *res; clk_put(pcdev->clk_csi); -#ifdef CONFIG_MACH_MX27 + if (cpu_is_mx25()) + free_irq(pcdev->irq_csi, pcdev); if (cpu_is_mx27()) - imx_dma_free(pcdev->dma); -#endif /* CONFIG_MACH_MX27 */ - free_irq(pcdev->irq_csi, pcdev); - if (mx27_camera_emma(pcdev)) free_irq(pcdev->irq_emma, pcdev); soc_camera_host_unregister(&pcdev->soc_host); + vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); + iounmap(pcdev->base_csi); - if (mx27_camera_emma(pcdev)) { + if (cpu_is_mx27()) { clk_disable(pcdev->clk_emma); clk_put(pcdev->clk_emma); iounmap(pcdev->base_emma); diff --git a/drivers/media/video/mx2_emmaprp.c b/drivers/media/video/mx2_emmaprp.c new file mode 100644 index 000000000000..ba89a7401c8c --- /dev/null +++ b/drivers/media/video/mx2_emmaprp.c @@ -0,0 +1,1008 @@ +/* + * Support eMMa-PrP through mem2mem framework. + * + * eMMa-PrP is a piece of HW that allows fetching buffers + * from one memory location and do several operations on + * them such as scaling or format conversion giving, as a result + * a new processed buffer in another memory location. + * + * Based on mem2mem_testdev.c by Pawel Osciak. + * + * Copyright (c) 2011 Vista Silicon S.L. + * Javier Martin <javier.martin@vista-silicon.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the + * License, or (at your option) any later version + */ +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/io.h> + +#include <linux/platform_device.h> +#include <media/v4l2-mem2mem.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-dma-contig.h> +#include <asm/sizes.h> + +#define EMMAPRP_MODULE_NAME "mem2mem-emmaprp" + +MODULE_DESCRIPTION("Mem-to-mem device which supports eMMa-PrP present in mx2 SoCs"); +MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.0.1"); + +static bool debug; +module_param(debug, bool, 0644); + +#define MIN_W 32 +#define MIN_H 32 +#define MAX_W 2040 +#define MAX_H 2046 + +#define S_ALIGN 1 /* multiple of 2 */ +#define W_ALIGN_YUV420 3 /* multiple of 8 */ +#define W_ALIGN_OTHERS 2 /* multiple of 4 */ +#define H_ALIGN 1 /* multiple of 2 */ + +/* Flags that indicate a format can be used for capture/output */ +#define MEM2MEM_CAPTURE (1 << 0) +#define MEM2MEM_OUTPUT (1 << 1) + +#define MEM2MEM_NAME "m2m-emmaprp" + +/* In bytes, per queue */ +#define MEM2MEM_VID_MEM_LIMIT SZ_16M + +#define dprintk(dev, fmt, arg...) \ + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg) + +/* EMMA PrP */ +#define PRP_CNTL 0x00 +#define PRP_INTR_CNTL 0x04 +#define PRP_INTRSTATUS 0x08 +#define PRP_SOURCE_Y_PTR 0x0c +#define PRP_SOURCE_CB_PTR 0x10 +#define PRP_SOURCE_CR_PTR 0x14 +#define PRP_DEST_RGB1_PTR 0x18 +#define PRP_DEST_RGB2_PTR 0x1c +#define PRP_DEST_Y_PTR 0x20 +#define PRP_DEST_CB_PTR 0x24 +#define PRP_DEST_CR_PTR 0x28 +#define PRP_SRC_FRAME_SIZE 0x2c +#define PRP_DEST_CH1_LINE_STRIDE 0x30 +#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34 +#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38 +#define PRP_CH1_OUT_IMAGE_SIZE 0x3c +#define PRP_CH2_OUT_IMAGE_SIZE 0x40 +#define PRP_SRC_LINE_STRIDE 0x44 +#define PRP_CSC_COEF_012 0x48 +#define PRP_CSC_COEF_345 0x4c +#define PRP_CSC_COEF_678 0x50 +#define PRP_CH1_RZ_HORI_COEF1 0x54 +#define PRP_CH1_RZ_HORI_COEF2 0x58 +#define PRP_CH1_RZ_HORI_VALID 0x5c +#define PRP_CH1_RZ_VERT_COEF1 0x60 +#define PRP_CH1_RZ_VERT_COEF2 0x64 +#define PRP_CH1_RZ_VERT_VALID 0x68 +#define PRP_CH2_RZ_HORI_COEF1 0x6c +#define PRP_CH2_RZ_HORI_COEF2 0x70 +#define PRP_CH2_RZ_HORI_VALID 0x74 +#define PRP_CH2_RZ_VERT_COEF1 0x78 +#define PRP_CH2_RZ_VERT_COEF2 0x7c +#define PRP_CH2_RZ_VERT_VALID 0x80 + +#define PRP_CNTL_CH1EN (1 << 0) +#define PRP_CNTL_CH2EN (1 << 1) +#define PRP_CNTL_CSIEN (1 << 2) +#define PRP_CNTL_DATA_IN_YUV420 (0 << 3) +#define PRP_CNTL_DATA_IN_YUV422 (1 << 3) +#define PRP_CNTL_DATA_IN_RGB16 (2 << 3) +#define PRP_CNTL_DATA_IN_RGB32 (3 << 3) +#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5) +#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5) +#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5) +#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5) +#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7) +#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7) +#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7) +#define PRP_CNTL_CH1_LEN (1 << 9) +#define PRP_CNTL_CH2_LEN (1 << 10) +#define PRP_CNTL_SKIP_FRAME (1 << 11) +#define PRP_CNTL_SWRST (1 << 12) +#define PRP_CNTL_CLKEN (1 << 13) +#define PRP_CNTL_WEN (1 << 14) +#define PRP_CNTL_CH1BYP (1 << 15) +#define PRP_CNTL_IN_TSKIP(x) ((x) << 16) +#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19) +#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22) +#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25) +#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27) +#define PRP_CNTL_CH2B1EN (1 << 29) +#define PRP_CNTL_CH2B2EN (1 << 30) +#define PRP_CNTL_CH2FEN (1 << 31) + +#define PRP_SIZE_HEIGHT(x) (x) +#define PRP_SIZE_WIDTH(x) ((x) << 16) + +/* IRQ Enable and status register */ +#define PRP_INTR_RDERR (1 << 0) +#define PRP_INTR_CH1WERR (1 << 1) +#define PRP_INTR_CH2WERR (1 << 2) +#define PRP_INTR_CH1FC (1 << 3) +#define PRP_INTR_CH2FC (1 << 5) +#define PRP_INTR_LBOVF (1 << 7) +#define PRP_INTR_CH2OVF (1 << 8) + +#define PRP_INTR_ST_RDERR (1 << 0) +#define PRP_INTR_ST_CH1WERR (1 << 1) +#define PRP_INTR_ST_CH2WERR (1 << 2) +#define PRP_INTR_ST_CH2B2CI (1 << 3) +#define PRP_INTR_ST_CH2B1CI (1 << 4) +#define PRP_INTR_ST_CH1B2CI (1 << 5) +#define PRP_INTR_ST_CH1B1CI (1 << 6) +#define PRP_INTR_ST_LBOVF (1 << 7) +#define PRP_INTR_ST_CH2OVF (1 << 8) + +struct emmaprp_fmt { + char *name; + u32 fourcc; + /* Types the format can be used for */ + u32 types; +}; + +static struct emmaprp_fmt formats[] = { + { + .name = "YUV 4:2:0 Planar", + .fourcc = V4L2_PIX_FMT_YUV420, + .types = MEM2MEM_CAPTURE, + }, + { + .name = "4:2:2, packed, YUYV", + .fourcc = V4L2_PIX_FMT_YUYV, + .types = MEM2MEM_OUTPUT, + }, +}; + +/* Per-queue, driver-specific private data */ +struct emmaprp_q_data { + unsigned int width; + unsigned int height; + unsigned int sizeimage; + struct emmaprp_fmt *fmt; +}; + +enum { + V4L2_M2M_SRC = 0, + V4L2_M2M_DST = 1, +}; + +#define NUM_FORMATS ARRAY_SIZE(formats) + +static struct emmaprp_fmt *find_format(struct v4l2_format *f) +{ + struct emmaprp_fmt *fmt; + unsigned int k; + + for (k = 0; k < NUM_FORMATS; k++) { + fmt = &formats[k]; + if (fmt->fourcc == f->fmt.pix.pixelformat) + break; + } + + if (k == NUM_FORMATS) + return NULL; + + return &formats[k]; +} + +struct emmaprp_dev { + struct v4l2_device v4l2_dev; + struct video_device *vfd; + + struct mutex dev_mutex; + spinlock_t irqlock; + + int irq_emma; + void __iomem *base_emma; + struct clk *clk_emma; + struct resource *res_emma; + + struct v4l2_m2m_dev *m2m_dev; + struct vb2_alloc_ctx *alloc_ctx; +}; + +struct emmaprp_ctx { + struct emmaprp_dev *dev; + /* Abort requested by m2m */ + int aborting; + struct emmaprp_q_data q_data[2]; + struct v4l2_m2m_ctx *m2m_ctx; +}; + +static struct emmaprp_q_data *get_q_data(struct emmaprp_ctx *ctx, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return &(ctx->q_data[V4L2_M2M_SRC]); + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &(ctx->q_data[V4L2_M2M_DST]); + default: + BUG(); + } + return NULL; +} + +/* + * mem2mem callbacks + */ +static void emmaprp_job_abort(void *priv) +{ + struct emmaprp_ctx *ctx = priv; + struct emmaprp_dev *pcdev = ctx->dev; + + ctx->aborting = 1; + + dprintk(pcdev, "Aborting task\n"); + + v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->m2m_ctx); +} + +static void emmaprp_lock(void *priv) +{ + struct emmaprp_ctx *ctx = priv; + struct emmaprp_dev *pcdev = ctx->dev; + mutex_lock(&pcdev->dev_mutex); +} + +static void emmaprp_unlock(void *priv) +{ + struct emmaprp_ctx *ctx = priv; + struct emmaprp_dev *pcdev = ctx->dev; + mutex_unlock(&pcdev->dev_mutex); +} + +static inline void emmaprp_dump_regs(struct emmaprp_dev *pcdev) +{ + dprintk(pcdev, + "eMMa-PrP Registers:\n" + " SOURCE_Y_PTR = 0x%08X\n" + " SRC_FRAME_SIZE = 0x%08X\n" + " DEST_Y_PTR = 0x%08X\n" + " DEST_CR_PTR = 0x%08X\n" + " DEST_CB_PTR = 0x%08X\n" + " CH2_OUT_IMAGE_SIZE = 0x%08X\n" + " CNTL = 0x%08X\n", + readl(pcdev->base_emma + PRP_SOURCE_Y_PTR), + readl(pcdev->base_emma + PRP_SRC_FRAME_SIZE), + readl(pcdev->base_emma + PRP_DEST_Y_PTR), + readl(pcdev->base_emma + PRP_DEST_CR_PTR), + readl(pcdev->base_emma + PRP_DEST_CB_PTR), + readl(pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE), + readl(pcdev->base_emma + PRP_CNTL)); +} + +static void emmaprp_device_run(void *priv) +{ + struct emmaprp_ctx *ctx = priv; + struct emmaprp_q_data *s_q_data, *d_q_data; + struct vb2_buffer *src_buf, *dst_buf; + struct emmaprp_dev *pcdev = ctx->dev; + unsigned int s_width, s_height; + unsigned int d_width, d_height; + unsigned int d_size; + dma_addr_t p_in, p_out; + u32 tmp; + + src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + + s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + s_width = s_q_data->width; + s_height = s_q_data->height; + + d_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + d_width = d_q_data->width; + d_height = d_q_data->height; + d_size = d_width * d_height; + + p_in = vb2_dma_contig_plane_dma_addr(src_buf, 0); + p_out = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + if (!p_in || !p_out) { + v4l2_err(&pcdev->v4l2_dev, + "Acquiring kernel pointers to buffers failed\n"); + return; + } + + /* Input frame parameters */ + writel(p_in, pcdev->base_emma + PRP_SOURCE_Y_PTR); + writel(PRP_SIZE_WIDTH(s_width) | PRP_SIZE_HEIGHT(s_height), + pcdev->base_emma + PRP_SRC_FRAME_SIZE); + + /* Output frame parameters */ + writel(p_out, pcdev->base_emma + PRP_DEST_Y_PTR); + writel(p_out + d_size, pcdev->base_emma + PRP_DEST_CB_PTR); + writel(p_out + d_size + (d_size >> 2), + pcdev->base_emma + PRP_DEST_CR_PTR); + writel(PRP_SIZE_WIDTH(d_width) | PRP_SIZE_HEIGHT(d_height), + pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE); + + /* IRQ configuration */ + tmp = readl(pcdev->base_emma + PRP_INTR_CNTL); + writel(tmp | PRP_INTR_RDERR | + PRP_INTR_CH2WERR | + PRP_INTR_CH2FC, + pcdev->base_emma + PRP_INTR_CNTL); + + emmaprp_dump_regs(pcdev); + + /* Enable transfer */ + tmp = readl(pcdev->base_emma + PRP_CNTL); + writel(tmp | PRP_CNTL_CH2_OUT_YUV420 | + PRP_CNTL_DATA_IN_YUV422 | + PRP_CNTL_CH2EN, + pcdev->base_emma + PRP_CNTL); +} + +static irqreturn_t emmaprp_irq(int irq_emma, void *data) +{ + struct emmaprp_dev *pcdev = data; + struct emmaprp_ctx *curr_ctx; + struct vb2_buffer *src_vb, *dst_vb; + unsigned long flags; + u32 irqst; + + /* Check irq flags and clear irq */ + irqst = readl(pcdev->base_emma + PRP_INTRSTATUS); + writel(irqst, pcdev->base_emma + PRP_INTRSTATUS); + dprintk(pcdev, "irqst = 0x%08x\n", irqst); + + curr_ctx = v4l2_m2m_get_curr_priv(pcdev->m2m_dev); + if (curr_ctx == NULL) { + pr_err("Instance released before the end of transaction\n"); + return IRQ_HANDLED; + } + + if (!curr_ctx->aborting) { + if ((irqst & PRP_INTR_ST_RDERR) || + (irqst & PRP_INTR_ST_CH2WERR)) { + pr_err("PrP bus error ocurred, this transfer is probably corrupted\n"); + writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL); + } else if (irqst & PRP_INTR_ST_CH2B1CI) { /* buffer ready */ + src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); + dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); + + spin_lock_irqsave(&pcdev->irqlock, flags); + v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); + spin_unlock_irqrestore(&pcdev->irqlock, flags); + } + } + + v4l2_m2m_job_finish(pcdev->m2m_dev, curr_ctx->m2m_ctx); + return IRQ_HANDLED; +} + +/* + * video ioctls + */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1); + strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT + | V4L2_CAP_STREAMING; + + return 0; +} + +static int enum_fmt(struct v4l2_fmtdesc *f, u32 type) +{ + int i, num; + struct emmaprp_fmt *fmt; + + num = 0; + + for (i = 0; i < NUM_FORMATS; ++i) { + if (formats[i].types & type) { + /* index-th format of type type found ? */ + if (num == f->index) + break; + /* Correct type but haven't reached our index yet, + * just increment per-type index */ + ++num; + } + } + + if (i < NUM_FORMATS) { + /* Format found */ + fmt = &formats[i]; + strlcpy(f->description, fmt->name, sizeof(f->description) - 1); + f->pixelformat = fmt->fourcc; + return 0; + } + + /* Format not found */ + return -EINVAL; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return enum_fmt(f, MEM2MEM_CAPTURE); +} + +static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return enum_fmt(f, MEM2MEM_OUTPUT); +} + +static int vidioc_g_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f) +{ + struct vb2_queue *vq; + struct emmaprp_q_data *q_data; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(ctx, f->type); + + f->fmt.pix.width = q_data->width; + f->fmt.pix.height = q_data->height; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.pixelformat = q_data->fmt->fourcc; + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) + f->fmt.pix.bytesperline = q_data->width * 3 / 2; + else /* YUYV */ + f->fmt.pix.bytesperline = q_data->width * 2; + f->fmt.pix.sizeimage = q_data->sizeimage; + + return 0; +} + +static int vidioc_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + return vidioc_g_fmt(priv, f); +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + return vidioc_g_fmt(priv, f); +} + +static int vidioc_try_fmt(struct v4l2_format *f) +{ + enum v4l2_field field; + + + if (!find_format(f)) + return -EINVAL; + + field = f->fmt.pix.field; + if (field == V4L2_FIELD_ANY) + field = V4L2_FIELD_NONE; + else if (V4L2_FIELD_NONE != field) + return -EINVAL; + + /* V4L2 specification suggests the driver corrects the format struct + * if any of the dimensions is unsupported */ + f->fmt.pix.field = field; + + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) { + v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W, + W_ALIGN_YUV420, &f->fmt.pix.height, + MIN_H, MAX_H, H_ALIGN, S_ALIGN); + f->fmt.pix.bytesperline = f->fmt.pix.width * 3 / 2; + } else { + v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W, + W_ALIGN_OTHERS, &f->fmt.pix.height, + MIN_H, MAX_H, H_ALIGN, S_ALIGN); + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + } + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct emmaprp_fmt *fmt; + struct emmaprp_ctx *ctx = priv; + + fmt = find_format(f); + if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) { + v4l2_err(&ctx->dev->v4l2_dev, + "Fourcc format (0x%08x) invalid.\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + return vidioc_try_fmt(f); +} + +static int vidioc_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct emmaprp_fmt *fmt; + struct emmaprp_ctx *ctx = priv; + + fmt = find_format(f); + if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) { + v4l2_err(&ctx->dev->v4l2_dev, + "Fourcc format (0x%08x) invalid.\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + return vidioc_try_fmt(f); +} + +static int vidioc_s_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f) +{ + struct emmaprp_q_data *q_data; + struct vb2_queue *vq; + int ret; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + if (vb2_is_busy(vq)) { + v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + ret = vidioc_try_fmt(f); + if (ret) + return ret; + + q_data->fmt = find_format(f); + q_data->width = f->fmt.pix.width; + q_data->height = f->fmt.pix.height; + if (q_data->fmt->fourcc == V4L2_PIX_FMT_YUV420) + q_data->sizeimage = q_data->width * q_data->height * 3 / 2; + else /* YUYV */ + q_data->sizeimage = q_data->width * q_data->height * 2; + + dprintk(ctx->dev, + "Setting format for type %d, wxh: %dx%d, fmt: %d\n", + f->type, q_data->width, q_data->height, q_data->fmt->fourcc); + + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret; + + ret = vidioc_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + return vidioc_s_fmt(priv, f); +} + +static int vidioc_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret; + + ret = vidioc_try_fmt_vid_out(file, priv, f); + if (ret) + return ret; + + return vidioc_s_fmt(priv, f); +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbufs) +{ + struct emmaprp_ctx *ctx = priv; + + return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct emmaprp_ctx *ctx = priv; + + return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct emmaprp_ctx *ctx = priv; + + return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct emmaprp_ctx *ctx = priv; + + return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); +} + +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct emmaprp_ctx *ctx = priv; + + return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); +} + +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct emmaprp_ctx *ctx = priv; + + return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); +} + +static const struct v4l2_ioctl_ops emmaprp_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, + + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, +}; + + +/* + * Queue operations + */ +static int emmaprp_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct emmaprp_ctx *ctx = vb2_get_drv_priv(vq); + struct emmaprp_q_data *q_data; + unsigned int size, count = *nbuffers; + + q_data = get_q_data(ctx, vq->type); + + if (q_data->fmt->fourcc == V4L2_PIX_FMT_YUV420) + size = q_data->width * q_data->height * 3 / 2; + else + size = q_data->width * q_data->height * 2; + + while (size * count > MEM2MEM_VID_MEM_LIMIT) + (count)--; + + *nplanes = 1; + *nbuffers = count; + sizes[0] = size; + + alloc_ctxs[0] = ctx->dev->alloc_ctx; + + dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size); + + return 0; +} + +static int emmaprp_buf_prepare(struct vb2_buffer *vb) +{ + struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct emmaprp_q_data *q_data; + + dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type); + + q_data = get_q_data(ctx, vb->vb2_queue->type); + + if (vb2_plane_size(vb, 0) < q_data->sizeimage) { + dprintk(ctx->dev, "%s data will not fit into plane" + "(%lu < %lu)\n", __func__, + vb2_plane_size(vb, 0), + (long)q_data->sizeimage); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, q_data->sizeimage); + + return 0; +} + +static void emmaprp_buf_queue(struct vb2_buffer *vb) +{ + struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); +} + +static struct vb2_ops emmaprp_qops = { + .queue_setup = emmaprp_queue_setup, + .buf_prepare = emmaprp_buf_prepare, + .buf_queue = emmaprp_buf_queue, +}; + +static int queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct emmaprp_ctx *ctx = priv; + int ret; + + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_MMAP; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->ops = &emmaprp_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_MMAP; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->ops = &emmaprp_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + + return vb2_queue_init(dst_vq); +} + +/* + * File operations + */ +static int emmaprp_open(struct file *file) +{ + struct emmaprp_dev *pcdev = video_drvdata(file); + struct emmaprp_ctx *ctx; + + ctx = kzalloc(sizeof *ctx, GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + file->private_data = ctx; + ctx->dev = pcdev; + + ctx->m2m_ctx = v4l2_m2m_ctx_init(pcdev->m2m_dev, ctx, &queue_init); + + if (IS_ERR(ctx->m2m_ctx)) { + int ret = PTR_ERR(ctx->m2m_ctx); + + kfree(ctx); + return ret; + } + + clk_enable(pcdev->clk_emma); + ctx->q_data[V4L2_M2M_SRC].fmt = &formats[1]; + ctx->q_data[V4L2_M2M_DST].fmt = &formats[0]; + + dprintk(pcdev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx); + + return 0; +} + +static int emmaprp_release(struct file *file) +{ + struct emmaprp_dev *pcdev = video_drvdata(file); + struct emmaprp_ctx *ctx = file->private_data; + + dprintk(pcdev, "Releasing instance %p\n", ctx); + + clk_disable(pcdev->clk_emma); + v4l2_m2m_ctx_release(ctx->m2m_ctx); + kfree(ctx); + + return 0; +} + +static unsigned int emmaprp_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct emmaprp_ctx *ctx = file->private_data; + + return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); +} + +static int emmaprp_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct emmaprp_ctx *ctx = file->private_data; + + return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); +} + +static const struct v4l2_file_operations emmaprp_fops = { + .owner = THIS_MODULE, + .open = emmaprp_open, + .release = emmaprp_release, + .poll = emmaprp_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = emmaprp_mmap, +}; + +static struct video_device emmaprp_videodev = { + .name = MEM2MEM_NAME, + .fops = &emmaprp_fops, + .ioctl_ops = &emmaprp_ioctl_ops, + .minor = -1, + .release = video_device_release, +}; + +static struct v4l2_m2m_ops m2m_ops = { + .device_run = emmaprp_device_run, + .job_abort = emmaprp_job_abort, + .lock = emmaprp_lock, + .unlock = emmaprp_unlock, +}; + +static int emmaprp_probe(struct platform_device *pdev) +{ + struct emmaprp_dev *pcdev; + struct video_device *vfd; + struct resource *res_emma; + int irq_emma; + int ret; + + pcdev = kzalloc(sizeof *pcdev, GFP_KERNEL); + if (!pcdev) + return -ENOMEM; + + spin_lock_init(&pcdev->irqlock); + + pcdev->clk_emma = clk_get(&pdev->dev, NULL); + if (IS_ERR(pcdev->clk_emma)) { + ret = PTR_ERR(pcdev->clk_emma); + goto free_dev; + } + + irq_emma = platform_get_irq(pdev, 0); + res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (irq_emma < 0 || res_emma == NULL) { + dev_err(&pdev->dev, "Missing platform resources data\n"); + ret = -ENODEV; + goto free_clk; + } + + ret = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev); + if (ret) + goto free_clk; + + mutex_init(&pcdev->dev_mutex); + + vfd = video_device_alloc(); + if (!vfd) { + v4l2_err(&pcdev->v4l2_dev, "Failed to allocate video device\n"); + ret = -ENOMEM; + goto unreg_dev; + } + + *vfd = emmaprp_videodev; + vfd->lock = &pcdev->dev_mutex; + + video_set_drvdata(vfd, pcdev); + snprintf(vfd->name, sizeof(vfd->name), "%s", emmaprp_videodev.name); + pcdev->vfd = vfd; + v4l2_info(&pcdev->v4l2_dev, EMMAPRP_MODULE_NAME + " Device registered as /dev/video%d\n", vfd->num); + + platform_set_drvdata(pdev, pcdev); + + if (devm_request_mem_region(&pdev->dev, res_emma->start, + resource_size(res_emma), MEM2MEM_NAME) == NULL) + goto rel_vdev; + + pcdev->base_emma = devm_ioremap(&pdev->dev, res_emma->start, + resource_size(res_emma)); + if (!pcdev->base_emma) + goto rel_vdev; + + pcdev->irq_emma = irq_emma; + pcdev->res_emma = res_emma; + + if (devm_request_irq(&pdev->dev, pcdev->irq_emma, emmaprp_irq, + 0, MEM2MEM_NAME, pcdev) < 0) + goto rel_vdev; + + pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(pcdev->alloc_ctx)) { + v4l2_err(&pcdev->v4l2_dev, "Failed to alloc vb2 context\n"); + ret = PTR_ERR(pcdev->alloc_ctx); + goto rel_vdev; + } + + pcdev->m2m_dev = v4l2_m2m_init(&m2m_ops); + if (IS_ERR(pcdev->m2m_dev)) { + v4l2_err(&pcdev->v4l2_dev, "Failed to init mem2mem device\n"); + ret = PTR_ERR(pcdev->m2m_dev); + goto rel_ctx; + } + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n"); + goto rel_m2m; + } + + return 0; + + +rel_m2m: + v4l2_m2m_release(pcdev->m2m_dev); +rel_ctx: + vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); +rel_vdev: + video_device_release(vfd); +unreg_dev: + v4l2_device_unregister(&pcdev->v4l2_dev); +free_clk: + clk_put(pcdev->clk_emma); +free_dev: + kfree(pcdev); + + return ret; +} + +static int emmaprp_remove(struct platform_device *pdev) +{ + struct emmaprp_dev *pcdev = platform_get_drvdata(pdev); + + v4l2_info(&pcdev->v4l2_dev, "Removing " EMMAPRP_MODULE_NAME); + + video_unregister_device(pcdev->vfd); + v4l2_m2m_release(pcdev->m2m_dev); + vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); + v4l2_device_unregister(&pcdev->v4l2_dev); + clk_put(pcdev->clk_emma); + kfree(pcdev); + + return 0; +} + +static struct platform_driver emmaprp_pdrv = { + .probe = emmaprp_probe, + .remove = emmaprp_remove, + .driver = { + .name = MEM2MEM_NAME, + .owner = THIS_MODULE, + }, +}; + +static void __exit emmaprp_exit(void) +{ + platform_driver_unregister(&emmaprp_pdrv); +} + +static int __init emmaprp_init(void) +{ + return platform_driver_register(&emmaprp_pdrv); +} + +module_init(emmaprp_init); +module_exit(emmaprp_exit); diff --git a/drivers/media/video/noon010pc30.c b/drivers/media/video/noon010pc30.c index 50838bf84204..440c12962bae 100644 --- a/drivers/media/video/noon010pc30.c +++ b/drivers/media/video/noon010pc30.c @@ -725,8 +725,8 @@ static int noon010_probe(struct i2c_client *client, mutex_init(&info->lock); sd = &info->sd; - strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); v4l2_i2c_subdev_init(sd, client, &noon010_ops); + strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); sd->internal_ops = &noon010_subdev_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -844,18 +844,7 @@ static struct i2c_driver noon010_i2c_driver = { .id_table = noon010_id, }; -static int __init noon010_init(void) -{ - return i2c_add_driver(&noon010_i2c_driver); -} - -static void __exit noon010_exit(void) -{ - i2c_del_driver(&noon010_i2c_driver); -} - -module_init(noon010_init); -module_exit(noon010_exit); +module_i2c_driver(noon010_i2c_driver); MODULE_DESCRIPTION("Siliconfile NOON010PC30 camera driver"); MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); diff --git a/drivers/media/video/omap/omap_vout.c b/drivers/media/video/omap/omap_vout.c index 1fb7d5bd5ec2..88cf9d952631 100644 --- a/drivers/media/video/omap/omap_vout.c +++ b/drivers/media/video/omap/omap_vout.c @@ -2268,13 +2268,12 @@ static struct platform_driver omap_vout_driver = { .driver = { .name = VOUT_NAME, }, - .probe = omap_vout_probe, .remove = omap_vout_remove, }; static int __init omap_vout_init(void) { - if (platform_driver_register(&omap_vout_driver) != 0) { + if (platform_driver_probe(&omap_vout_driver, omap_vout_probe) != 0) { printk(KERN_ERR VOUT_NAME ":Could not register Video driver\n"); return -EINVAL; } diff --git a/drivers/media/video/ov2640.c b/drivers/media/video/ov2640.c index b5247cb64fde..3c2c5d3bcc6b 100644 --- a/drivers/media/video/ov2640.c +++ b/drivers/media/video/ov2640.c @@ -1103,21 +1103,7 @@ static struct i2c_driver ov2640_i2c_driver = { .id_table = ov2640_id, }; -/* - * Module functions - */ -static int __init ov2640_module_init(void) -{ - return i2c_add_driver(&ov2640_i2c_driver); -} - -static void __exit ov2640_module_exit(void) -{ - i2c_del_driver(&ov2640_i2c_driver); -} - -module_init(ov2640_module_init); -module_exit(ov2640_module_exit); +module_i2c_driver(ov2640_i2c_driver); MODULE_DESCRIPTION("SoC Camera driver for Omni Vision 2640 sensor"); MODULE_AUTHOR("Alberto Panizzo"); diff --git a/drivers/media/video/ov5642.c b/drivers/media/video/ov5642.c index bb37ec80f274..80e07794ac8e 100644 --- a/drivers/media/video/ov5642.c +++ b/drivers/media/video/ov5642.c @@ -1068,18 +1068,7 @@ static struct i2c_driver ov5642_i2c_driver = { .id_table = ov5642_id, }; -static int __init ov5642_mod_init(void) -{ - return i2c_add_driver(&ov5642_i2c_driver); -} - -static void __exit ov5642_mod_exit(void) -{ - i2c_del_driver(&ov5642_i2c_driver); -} - -module_init(ov5642_mod_init); -module_exit(ov5642_mod_exit); +module_i2c_driver(ov5642_i2c_driver); MODULE_DESCRIPTION("Omnivision OV5642 Camera driver"); MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>"); diff --git a/drivers/media/video/ov6650.c b/drivers/media/video/ov6650.c index 3627f3225bbb..3e028b1970dd 100644 --- a/drivers/media/video/ov6650.c +++ b/drivers/media/video/ov6650.c @@ -1046,18 +1046,7 @@ static struct i2c_driver ov6650_i2c_driver = { .id_table = ov6650_id, }; -static int __init ov6650_module_init(void) -{ - return i2c_add_driver(&ov6650_i2c_driver); -} - -static void __exit ov6650_module_exit(void) -{ - i2c_del_driver(&ov6650_i2c_driver); -} - -module_init(ov6650_module_init); -module_exit(ov6650_module_exit); +module_i2c_driver(ov6650_i2c_driver); MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV6650"); MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>"); diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c index 6a564964853a..e7c82b297514 100644 --- a/drivers/media/video/ov7670.c +++ b/drivers/media/video/ov7670.c @@ -1583,15 +1583,4 @@ static struct i2c_driver ov7670_driver = { .id_table = ov7670_id, }; -static __init int init_ov7670(void) -{ - return i2c_add_driver(&ov7670_driver); -} - -static __exit void exit_ov7670(void) -{ - i2c_del_driver(&ov7670_driver); -} - -module_init(init_ov7670); -module_exit(exit_ov7670); +module_i2c_driver(ov7670_driver); diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index 9f6ce3d8a29e..74e77d327ed8 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -1123,22 +1123,7 @@ static struct i2c_driver ov772x_i2c_driver = { .id_table = ov772x_id, }; -/* - * module function - */ - -static int __init ov772x_module_init(void) -{ - return i2c_add_driver(&ov772x_i2c_driver); -} - -static void __exit ov772x_module_exit(void) -{ - i2c_del_driver(&ov772x_i2c_driver); -} - -module_init(ov772x_module_init); -module_exit(ov772x_module_exit); +module_i2c_driver(ov772x_i2c_driver); MODULE_DESCRIPTION("SoC Camera driver for ov772x"); MODULE_AUTHOR("Kuninori Morimoto"); diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c index a4f99797eb56..23412debb36b 100644 --- a/drivers/media/video/ov9640.c +++ b/drivers/media/video/ov9640.c @@ -738,18 +738,7 @@ static struct i2c_driver ov9640_i2c_driver = { .id_table = ov9640_id, }; -static int __init ov9640_module_init(void) -{ - return i2c_add_driver(&ov9640_i2c_driver); -} - -static void __exit ov9640_module_exit(void) -{ - i2c_del_driver(&ov9640_i2c_driver); -} - -module_init(ov9640_module_init); -module_exit(ov9640_module_exit); +module_i2c_driver(ov9640_i2c_driver); MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx"); MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); diff --git a/drivers/media/video/ov9740.c b/drivers/media/video/ov9740.c index d9a9f7174f7a..3eb07c22516e 100644 --- a/drivers/media/video/ov9740.c +++ b/drivers/media/video/ov9740.c @@ -998,18 +998,7 @@ static struct i2c_driver ov9740_i2c_driver = { .id_table = ov9740_id, }; -static int __init ov9740_module_init(void) -{ - return i2c_add_driver(&ov9740_i2c_driver); -} - -static void __exit ov9740_module_exit(void) -{ - i2c_del_driver(&ov9740_i2c_driver); -} - -module_init(ov9740_module_init); -module_exit(ov9740_module_exit); +module_i2c_driver(ov9740_i2c_driver); MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV9740"); MODULE_AUTHOR("Andrew Chew <achew@nvidia.com>"); diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c index c6da8f77e1a2..d8c898278e8c 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -320,7 +320,17 @@ static struct tda829x_config tda829x_no_probe = { .probe_tuner = TDA829X_DONT_PROBE, }; +static struct tda18271_std_map hauppauge_tda18271_dvbt_std_map = { + .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6, + .if_lvl = 1, .rfagc_top = 0x37, }, +}; + static struct tda18271_config hauppauge_tda18271_dvb_config = { + .std_map = &hauppauge_tda18271_dvbt_std_map, .gate = TDA18271_GATE_ANALOG, .output_opt = TDA18271_OUTPUT_LT_OFF, }; diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 6d666174dbb4..e1111d968a3d 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -96,7 +96,6 @@ static struct v4l2_capability pvr_capability ={ .capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO | V4L2_CAP_READWRITE), - .reserved = {0,0,0,0} }; static struct v4l2_fmtdesc pvr_fmtdesc [] = { diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index f495eeb5403a..2834e3e65b39 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -1146,14 +1146,6 @@ leave: return ret; } -static int pwc_log_status(struct file *file, void *priv) -{ - struct pwc_device *pdev = video_drvdata(file); - - v4l2_ctrl_handler_log_status(&pdev->ctrl_handler, PWC_NAME); - return 0; -} - const struct v4l2_ioctl_ops pwc_ioctl_ops = { .vidioc_querycap = pwc_querycap, .vidioc_enum_input = pwc_enum_input, @@ -1169,7 +1161,7 @@ const struct v4l2_ioctl_ops pwc_ioctl_ops = { .vidioc_dqbuf = pwc_dqbuf, .vidioc_streamon = pwc_streamon, .vidioc_streamoff = pwc_streamoff, - .vidioc_log_status = pwc_log_status, + .vidioc_log_status = v4l2_ctrl_log_status, .vidioc_enum_framesizes = pwc_enum_framesizes, .vidioc_enum_frameintervals = pwc_enum_frameintervals, .vidioc_g_parm = pwc_g_parm, diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 0bd7da26d018..5a413f4427e0 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -921,12 +921,12 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev) /* "Safe default" - 13MHz */ recalculate_fifo_timeout(pcdev, 13000000); - clk_enable(pcdev->clk); + clk_prepare_enable(pcdev->clk); } static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev) { - clk_disable(pcdev->clk); + clk_disable_unprepare(pcdev->clk); } static irqreturn_t pxa_camera_irq(int irq, void *data) diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c index 9937386a3bae..f6419b22c258 100644 --- a/drivers/media/video/rj54n1cb0c.c +++ b/drivers/media/video/rj54n1cb0c.c @@ -1407,18 +1407,7 @@ static struct i2c_driver rj54n1_i2c_driver = { .id_table = rj54n1_id, }; -static int __init rj54n1_mod_init(void) -{ - return i2c_add_driver(&rj54n1_i2c_driver); -} - -static void __exit rj54n1_mod_exit(void) -{ - i2c_del_driver(&rj54n1_i2c_driver); -} - -module_init(rj54n1_mod_init); -module_exit(rj54n1_mod_exit); +module_i2c_driver(rj54n1_i2c_driver); MODULE_DESCRIPTION("Sharp RJ54N1CB0C Camera driver"); MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index c1bef6187661..4894cbb1c547 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -134,7 +134,7 @@ /* usb config commands */ #define IN_DATA_TOKEN cpu_to_le32(0x2255c0de) -#define CMD_2255 cpu_to_le32(0xc2255000) +#define CMD_2255 0xc2255000 #define CMD_SET_MODE cpu_to_le32((CMD_2255 | 0x10)) #define CMD_START cpu_to_le32((CMD_2255 | 0x20)) #define CMD_STOP cpu_to_le32((CMD_2255 | 0x30)) @@ -852,15 +852,13 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - int index = 0; - if (f) - index = f->index; + int index = f->index; if (index >= ARRAY_SIZE(formats)) return -EINVAL; - if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) || - (formats[index].fourcc == V4L2_PIX_FMT_MJPEG))) - return -EINVAL; + if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) || + (formats[index].fourcc == V4L2_PIX_FMT_MJPEG))) + return -EINVAL; dprintk(4, "name %s\n", formats[index].name); strlcpy(f->description, formats[index].name, sizeof(f->description)); f->pixelformat = formats[index].fourcc; @@ -2027,7 +2025,7 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) pdata[1]); offset = jj + PREFIX_SIZE; bframe = 1; - cc = pdword[1]; + cc = le32_to_cpu(pdword[1]); if (cc >= MAX_CHANNELS) { printk(KERN_ERR "bad channel\n"); @@ -2036,22 +2034,22 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) /* reverse it */ dev->cc = G_chnmap[cc]; channel = &dev->channel[dev->cc]; - payload = pdword[3]; + payload = le32_to_cpu(pdword[3]); if (payload > channel->req_image_size) { channel->bad_payload++; /* discard the bad frame */ return -EINVAL; } channel->pkt_size = payload; - channel->jpg_size = pdword[4]; + channel->jpg_size = le32_to_cpu(pdword[4]); break; case S2255_MARKER_RESPONSE: pdata += DEF_USB_BLOCK; jj += DEF_USB_BLOCK; - if (pdword[1] >= MAX_CHANNELS) + if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS) break; - cc = G_chnmap[pdword[1]]; + cc = G_chnmap[le32_to_cpu(pdword[1])]; if (cc >= MAX_CHANNELS) break; channel = &dev->channel[cc]; @@ -2074,11 +2072,11 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) wake_up(&dev->fw_data->wait_fw); break; case S2255_RESPONSE_STATUS: - channel->vidstatus = pdword[3]; + channel->vidstatus = le32_to_cpu(pdword[3]); channel->vidstatus_ready = 1; wake_up(&channel->wait_vidstatus); dprintk(5, "got vidstatus %x chan %d\n", - pdword[3], cc); + le32_to_cpu(pdword[3]), cc); break; default: printk(KERN_INFO "s2255 unknown resp\n"); @@ -2605,10 +2603,11 @@ static int s2255_probe(struct usb_interface *interface, __le32 *pRel; pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4]; printk(KERN_INFO "s2255 dsp fw version %x\n", *pRel); - dev->dsp_fw_ver = *pRel; - if (*pRel < S2255_CUR_DSP_FWVER) + dev->dsp_fw_ver = le32_to_cpu(*pRel); + if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER) printk(KERN_INFO "s2255: f2255usb.bin out of date.\n"); - if (dev->pid == 0x2257 && *pRel < S2255_MIN_DSP_COLORFILTER) + if (dev->pid == 0x2257 && + dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) printk(KERN_WARNING "s2255: 2257 requires firmware %d" " or above.\n", S2255_MIN_DSP_COLORFILTER); } diff --git a/drivers/media/video/s5k6aa.c b/drivers/media/video/s5k6aa.c index 0df7f2a41814..6625e46a4638 100644 --- a/drivers/media/video/s5k6aa.c +++ b/drivers/media/video/s5k6aa.c @@ -1582,8 +1582,8 @@ static int s5k6aa_probe(struct i2c_client *client, s5k6aa->inv_vflip = pdata->vert_flip; sd = &s5k6aa->sd; - strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name)); v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops); + strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name)); sd->internal_ops = &s5k6aa_subdev_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -1663,18 +1663,7 @@ static struct i2c_driver s5k6aa_i2c_driver = { .id_table = s5k6aa_id, }; -static int __init s5k6aa_init(void) -{ - return i2c_add_driver(&s5k6aa_i2c_driver); -} - -static void __exit s5k6aa_exit(void) -{ - i2c_del_driver(&s5k6aa_i2c_driver); -} - -module_init(s5k6aa_init); -module_exit(s5k6aa_exit); +module_i2c_driver(s5k6aa_i2c_driver); MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver"); MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index a9e9653beeb4..b06efd208328 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -1019,52 +1019,117 @@ static int fimc_cap_dqbuf(struct file *file, void *priv, return vb2_dqbuf(&fimc->vid_cap.vbq, buf, file->f_flags & O_NONBLOCK); } -static int fimc_cap_cropcap(struct file *file, void *fh, - struct v4l2_cropcap *cr) +static int fimc_cap_create_bufs(struct file *file, void *priv, + struct v4l2_create_buffers *create) { struct fimc_dev *fimc = video_drvdata(file); - struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame; - if (cr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - return -EINVAL; + return vb2_create_bufs(&fimc->vid_cap.vbq, create); +} - cr->bounds.left = 0; - cr->bounds.top = 0; - cr->bounds.width = f->o_width; - cr->bounds.height = f->o_height; - cr->defrect = cr->bounds; +static int fimc_cap_prepare_buf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct fimc_dev *fimc = video_drvdata(file); - return 0; + return vb2_prepare_buf(&fimc->vid_cap.vbq, b); } -static int fimc_cap_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) +static int fimc_cap_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) { struct fimc_dev *fimc = video_drvdata(file); - struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame; + struct fimc_ctx *ctx = fimc->vid_cap.ctx; + struct fimc_frame *f = &ctx->s_frame; - cr->c.left = f->offs_h; - cr->c.top = f->offs_v; - cr->c.width = f->width; - cr->c.height = f->height; + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; - return 0; + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + f = &ctx->d_frame; + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + s->r.left = 0; + s->r.top = 0; + s->r.width = f->o_width; + s->r.height = f->o_height; + return 0; + + case V4L2_SEL_TGT_COMPOSE_ACTIVE: + f = &ctx->d_frame; + case V4L2_SEL_TGT_CROP_ACTIVE: + s->r.left = f->offs_h; + s->r.top = f->offs_v; + s->r.width = f->width; + s->r.height = f->height; + return 0; + } + + return -EINVAL; } -static int fimc_cap_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) +/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ +int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) +{ + if (a->left < b->left || a->top < b->top) + return 0; + if (a->left + a->width > b->left + b->width) + return 0; + if (a->top + a->height > b->top + b->height) + return 0; + + return 1; +} + +static int fimc_cap_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) { struct fimc_dev *fimc = video_drvdata(file); struct fimc_ctx *ctx = fimc->vid_cap.ctx; - struct fimc_frame *ff; + struct v4l2_rect rect = s->r; + struct fimc_frame *f; unsigned long flags; + unsigned int pad; - fimc_capture_try_crop(ctx, &cr->c, FIMC_SD_PAD_SINK); - ff = &ctx->s_frame; + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_ACTIVE: + f = &ctx->d_frame; + pad = FIMC_SD_PAD_SOURCE; + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_ACTIVE: + f = &ctx->s_frame; + pad = FIMC_SD_PAD_SINK; + break; + default: + return -EINVAL; + } + + fimc_capture_try_crop(ctx, &rect, pad); + + if (s->flags & V4L2_SEL_FLAG_LE && + !enclosed_rectangle(&rect, &s->r)) + return -ERANGE; + + if (s->flags & V4L2_SEL_FLAG_GE && + !enclosed_rectangle(&s->r, &rect)) + return -ERANGE; + + s->r = rect; spin_lock_irqsave(&fimc->slock, flags); - set_frame_crop(ff, cr->c.left, cr->c.top, cr->c.width, cr->c.height); - set_bit(ST_CAPT_APPLY_CFG, &fimc->state); + set_frame_crop(f, s->r.left, s->r.top, s->r.width, + s->r.height); spin_unlock_irqrestore(&fimc->slock, flags); + set_bit(ST_CAPT_APPLY_CFG, &fimc->state); return 0; } @@ -1082,12 +1147,14 @@ static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = { .vidioc_qbuf = fimc_cap_qbuf, .vidioc_dqbuf = fimc_cap_dqbuf, + .vidioc_prepare_buf = fimc_cap_prepare_buf, + .vidioc_create_bufs = fimc_cap_create_bufs, + .vidioc_streamon = fimc_cap_streamon, .vidioc_streamoff = fimc_cap_streamoff, - .vidioc_g_crop = fimc_cap_g_crop, - .vidioc_s_crop = fimc_cap_s_crop, - .vidioc_cropcap = fimc_cap_cropcap, + .vidioc_g_selection = fimc_cap_g_selection, + .vidioc_s_selection = fimc_cap_s_selection, .vidioc_enum_input = fimc_cap_enum_input, .vidioc_s_input = fimc_cap_s_input, diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 81bcbb9492ea..e184e650022a 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -1602,24 +1602,35 @@ static void fimc_clk_put(struct fimc_dev *fimc) { int i; for (i = 0; i < fimc->num_clocks; i++) { - if (fimc->clock[i]) - clk_put(fimc->clock[i]); + if (IS_ERR_OR_NULL(fimc->clock[i])) + continue; + clk_unprepare(fimc->clock[i]); + clk_put(fimc->clock[i]); + fimc->clock[i] = NULL; } } static int fimc_clk_get(struct fimc_dev *fimc) { - int i; + int i, ret; + for (i = 0; i < fimc->num_clocks; i++) { fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]); - if (!IS_ERR_OR_NULL(fimc->clock[i])) - continue; - dev_err(&fimc->pdev->dev, "failed to get fimc clock: %s\n", - fimc_clocks[i]); - return -ENXIO; + if (IS_ERR(fimc->clock[i])) + goto err; + ret = clk_prepare(fimc->clock[i]); + if (ret < 0) { + clk_put(fimc->clock[i]); + fimc->clock[i] = NULL; + goto err; + } } - return 0; +err: + fimc_clk_put(fimc); + dev_err(&fimc->pdev->dev, "failed to get clock: %s\n", + fimc_clocks[i]); + return -ENXIO; } static int fimc_m2m_suspend(struct fimc_dev *fimc) @@ -1667,8 +1678,6 @@ static int fimc_probe(struct platform_device *pdev) struct s5p_platform_fimc *pdata; int ret = 0; - dev_dbg(&pdev->dev, "%s():\n", __func__); - drv_data = (struct samsung_fimc_driverdata *) platform_get_device_id(pdev)->driver_data; @@ -1678,7 +1687,7 @@ static int fimc_probe(struct platform_device *pdev) return -EINVAL; } - fimc = kzalloc(sizeof(struct fimc_dev), GFP_KERNEL); + fimc = devm_kzalloc(&pdev->dev, sizeof(*fimc), GFP_KERNEL); if (!fimc) return -ENOMEM; @@ -1689,51 +1698,35 @@ static int fimc_probe(struct platform_device *pdev) pdata = pdev->dev.platform_data; fimc->pdata = pdata; - init_waitqueue_head(&fimc->irq_queue); spin_lock_init(&fimc->slock); mutex_init(&fimc->lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "failed to find the registers\n"); - ret = -ENOENT; - goto err_info; - } - - fimc->regs_res = request_mem_region(res->start, resource_size(res), - dev_name(&pdev->dev)); - if (!fimc->regs_res) { - dev_err(&pdev->dev, "failed to obtain register region\n"); - ret = -ENOENT; - goto err_info; - } - - fimc->regs = ioremap(res->start, resource_size(res)); - if (!fimc->regs) { - dev_err(&pdev->dev, "failed to map registers\n"); - ret = -ENXIO; - goto err_req_region; + fimc->regs = devm_request_and_ioremap(&pdev->dev, res); + if (fimc->regs == NULL) { + dev_err(&pdev->dev, "Failed to obtain io memory\n"); + return -ENOENT; } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(&pdev->dev, "failed to get IRQ resource\n"); - ret = -ENXIO; - goto err_regs_unmap; + if (res == NULL) { + dev_err(&pdev->dev, "Failed to get IRQ resource\n"); + return -ENXIO; } fimc->irq = res->start; fimc->num_clocks = MAX_FIMC_CLOCKS; ret = fimc_clk_get(fimc); if (ret) - goto err_regs_unmap; + return ret; clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency); clk_enable(fimc->clock[CLK_BUS]); platform_set_drvdata(pdev, fimc); - ret = request_irq(fimc->irq, fimc_irq_handler, 0, pdev->name, fimc); + ret = devm_request_irq(&pdev->dev, fimc->irq, fimc_irq_handler, + 0, pdev->name, fimc); if (ret) { dev_err(&pdev->dev, "failed to install irq (%d)\n", ret); goto err_clk; @@ -1742,7 +1735,7 @@ static int fimc_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); ret = pm_runtime_get_sync(&pdev->dev); if (ret < 0) - goto err_irq; + goto err_clk; /* Initialize contiguous memory allocator */ fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); if (IS_ERR(fimc->alloc_ctx)) { @@ -1757,17 +1750,8 @@ static int fimc_probe(struct platform_device *pdev) err_pm: pm_runtime_put(&pdev->dev); -err_irq: - free_irq(fimc->irq, fimc); err_clk: fimc_clk_put(fimc); -err_regs_unmap: - iounmap(fimc->regs); -err_req_region: - release_resource(fimc->regs_res); - kfree(fimc->regs_res); -err_info: - kfree(fimc); return ret; } @@ -1854,11 +1838,6 @@ static int __devexit fimc_remove(struct platform_device *pdev) clk_disable(fimc->clock[CLK_BUS]); fimc_clk_put(fimc); - free_irq(fimc->irq, fimc); - iounmap(fimc->regs); - release_resource(fimc->regs_res); - kfree(fimc->regs_res); - kfree(fimc); dev_info(&pdev->dev, "driver unloaded\n"); return 0; diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 4e20560c73d4..a18291e648e2 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -434,7 +434,6 @@ struct fimc_ctx; * @num_clocks: the number of clocks managed by this device instance * @clock: clocks required for FIMC operation * @regs: the mapped hardware registers - * @regs_res: the resource claimed for IO registers * @irq: FIMC interrupt number * @irq_queue: interrupt handler waitqueue * @v4l2_dev: root v4l2_device @@ -454,7 +453,6 @@ struct fimc_dev { u16 num_clocks; struct clk *clock[MAX_FIMC_CLOCKS]; void __iomem *regs; - struct resource *regs_res; int irq; wait_queue_head_t irq_queue; struct v4l2_device *v4l2_dev; diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c index 63eccb55728f..62ed37e40149 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c @@ -750,7 +750,7 @@ static int __devinit fimc_md_probe(struct platform_device *pdev) struct fimc_md *fmd; int ret; - fmd = kzalloc(sizeof(struct fimc_md), GFP_KERNEL); + fmd = devm_kzalloc(&pdev->dev, sizeof(*fmd), GFP_KERNEL); if (!fmd) return -ENOMEM; @@ -771,7 +771,7 @@ static int __devinit fimc_md_probe(struct platform_device *pdev) ret = v4l2_device_register(&pdev->dev, &fmd->v4l2_dev); if (ret < 0) { v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret); - goto err1; + return ret; } ret = media_device_register(&fmd->media_dev); if (ret < 0) { @@ -813,8 +813,6 @@ err3: fimc_md_unregister_entities(fmd); err2: v4l2_device_unregister(&fmd->v4l2_dev); -err1: - kfree(fmd); return ret; } @@ -828,7 +826,6 @@ static int __devexit fimc_md_remove(struct platform_device *pdev) fimc_md_unregister_entities(fmd); media_device_unregister(&fmd->media_dev); fimc_md_put_clocks(fmd); - kfree(fmd); return 0; } diff --git a/drivers/media/video/s5p-fimc/mipi-csis.c b/drivers/media/video/s5p-fimc/mipi-csis.c index 130335cf62fd..f44f690397f7 100644 --- a/drivers/media/video/s5p-fimc/mipi-csis.c +++ b/drivers/media/video/s5p-fimc/mipi-csis.c @@ -1,8 +1,8 @@ /* * Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com> + * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki, <s.nawrocki@samsung.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -100,7 +100,6 @@ enum { * @pads: CSIS pads array * @sd: v4l2_subdev associated with CSIS device instance * @pdev: CSIS platform device - * @regs_res: requested I/O register memory resource * @regs: mmaped I/O registers memory * @clock: CSIS clocks * @irq: requested s5p-mipi-csis irq number @@ -113,7 +112,6 @@ struct csis_state { struct media_pad pads[CSIS_PADS_NUM]; struct v4l2_subdev sd; struct platform_device *pdev; - struct resource *regs_res; void __iomem *regs; struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES]; struct clk *clock[NUM_CSIS_CLOCKS]; @@ -258,26 +256,36 @@ static void s5pcsis_clk_put(struct csis_state *state) { int i; - for (i = 0; i < NUM_CSIS_CLOCKS; i++) - if (!IS_ERR_OR_NULL(state->clock[i])) - clk_put(state->clock[i]); + for (i = 0; i < NUM_CSIS_CLOCKS; i++) { + if (IS_ERR_OR_NULL(state->clock[i])) + continue; + clk_unprepare(state->clock[i]); + clk_put(state->clock[i]); + state->clock[i] = NULL; + } } static int s5pcsis_clk_get(struct csis_state *state) { struct device *dev = &state->pdev->dev; - int i; + int i, ret; for (i = 0; i < NUM_CSIS_CLOCKS; i++) { state->clock[i] = clk_get(dev, csi_clock_name[i]); - if (IS_ERR(state->clock[i])) { - s5pcsis_clk_put(state); - dev_err(dev, "failed to get clock: %s\n", - csi_clock_name[i]); - return -ENXIO; + if (IS_ERR(state->clock[i])) + goto err; + ret = clk_prepare(state->clock[i]); + if (ret < 0) { + clk_put(state->clock[i]); + state->clock[i] = NULL; + goto err; } } return 0; +err: + s5pcsis_clk_put(state); + dev_err(dev, "failed to get clock: %s\n", csi_clock_name[i]); + return -ENXIO; } static int s5pcsis_s_power(struct v4l2_subdev *sd, int on) @@ -480,12 +488,11 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev) { struct s5p_platform_mipi_csis *pdata; struct resource *mem_res; - struct resource *regs_res; struct csis_state *state; int ret = -ENOMEM; int i; - state = kzalloc(sizeof(*state), GFP_KERNEL); + state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL); if (!state) return -ENOMEM; @@ -495,52 +502,27 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev) pdata = pdev->dev.platform_data; if (pdata == NULL || pdata->phy_enable == NULL) { dev_err(&pdev->dev, "Platform data not fully specified\n"); - goto e_free; + return -EINVAL; } if ((pdev->id == 1 && pdata->lanes > CSIS1_MAX_LANES) || pdata->lanes > CSIS0_MAX_LANES) { - ret = -EINVAL; dev_err(&pdev->dev, "Unsupported number of data lanes: %d\n", pdata->lanes); - goto e_free; + return -EINVAL; } mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem_res) { - dev_err(&pdev->dev, "Failed to get IO memory region\n"); - goto e_free; + state->regs = devm_request_and_ioremap(&pdev->dev, mem_res); + if (state->regs == NULL) { + dev_err(&pdev->dev, "Failed to request and remap io memory\n"); + return -ENXIO; } - regs_res = request_mem_region(mem_res->start, resource_size(mem_res), - pdev->name); - if (!regs_res) { - dev_err(&pdev->dev, "Failed to request IO memory region\n"); - goto e_free; - } - state->regs_res = regs_res; - - state->regs = ioremap(mem_res->start, resource_size(mem_res)); - if (!state->regs) { - dev_err(&pdev->dev, "Failed to remap IO region\n"); - goto e_reqmem; - } - - ret = s5pcsis_clk_get(state); - if (ret) - goto e_unmap; - - clk_enable(state->clock[CSIS_CLK_MUX]); - if (pdata->clk_rate) - clk_set_rate(state->clock[CSIS_CLK_MUX], pdata->clk_rate); - else - dev_WARN(&pdev->dev, "No clock frequency specified!\n"); - state->irq = platform_get_irq(pdev, 0); if (state->irq < 0) { - ret = state->irq; dev_err(&pdev->dev, "Failed to get irq\n"); - goto e_clkput; + return state->irq; } for (i = 0; i < CSIS_NUM_SUPPLIES; i++) @@ -549,12 +531,22 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev) ret = regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES, state->supplies); if (ret) + return ret; + + ret = s5pcsis_clk_get(state); + if (ret) goto e_clkput; - ret = request_irq(state->irq, s5pcsis_irq_handler, 0, - dev_name(&pdev->dev), state); + clk_enable(state->clock[CSIS_CLK_MUX]); + if (pdata->clk_rate) + clk_set_rate(state->clock[CSIS_CLK_MUX], pdata->clk_rate); + else + dev_WARN(&pdev->dev, "No clock frequency specified!\n"); + + ret = devm_request_irq(&pdev->dev, state->irq, s5pcsis_irq_handler, + 0, dev_name(&pdev->dev), state); if (ret) { - dev_err(&pdev->dev, "request_irq failed\n"); + dev_err(&pdev->dev, "Interrupt request failed\n"); goto e_regput; } @@ -573,7 +565,7 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev) ret = media_entity_init(&state->sd.entity, CSIS_PADS_NUM, state->pads, 0); if (ret < 0) - goto e_irqfree; + goto e_clkput; /* This allows to retrieve the platform device id by the host driver */ v4l2_set_subdevdata(&state->sd, pdev); @@ -582,22 +574,13 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev) platform_set_drvdata(pdev, &state->sd); pm_runtime_enable(&pdev->dev); - return 0; -e_irqfree: - free_irq(state->irq, state); e_regput: regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies); e_clkput: clk_disable(state->clock[CSIS_CLK_MUX]); s5pcsis_clk_put(state); -e_unmap: - iounmap(state->regs); -e_reqmem: - release_mem_region(regs_res->start, resource_size(regs_res)); -e_free: - kfree(state); return ret; } @@ -699,21 +682,15 @@ static int __devexit s5pcsis_remove(struct platform_device *pdev) { struct v4l2_subdev *sd = platform_get_drvdata(pdev); struct csis_state *state = sd_to_csis_state(sd); - struct resource *res = state->regs_res; pm_runtime_disable(&pdev->dev); - s5pcsis_suspend(&pdev->dev); + s5pcsis_pm_suspend(&pdev->dev, false); clk_disable(state->clock[CSIS_CLK_MUX]); pm_runtime_set_suspended(&pdev->dev); - s5pcsis_clk_put(state); regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies); media_entity_cleanup(&state->sd.entity); - free_irq(state->irq, state); - iounmap(state->regs); - release_mem_region(res->start, resource_size(res)); - kfree(state); return 0; } diff --git a/drivers/media/video/s5p-g2d/g2d-hw.c b/drivers/media/video/s5p-g2d/g2d-hw.c index 39937cf03c88..5b86cbe408e2 100644 --- a/drivers/media/video/s5p-g2d/g2d-hw.c +++ b/drivers/media/video/s5p-g2d/g2d-hw.c @@ -77,6 +77,11 @@ void g2d_set_rop4(struct g2d_dev *d, u32 r) w(r, ROP4_REG); } +void g2d_set_flip(struct g2d_dev *d, u32 r) +{ + w(r, SRC_MSK_DIRECT_REG); +} + u32 g2d_cmd_stretch(u32 e) { e &= 1; diff --git a/drivers/media/video/s5p-g2d/g2d.c b/drivers/media/video/s5p-g2d/g2d.c index febaa673d363..789de74014e5 100644 --- a/drivers/media/video/s5p-g2d/g2d.c +++ b/drivers/media/video/s5p-g2d/g2d.c @@ -178,6 +178,9 @@ static int g2d_s_ctrl(struct v4l2_ctrl *ctrl) { struct g2d_ctx *ctx = container_of(ctrl->handler, struct g2d_ctx, ctrl_handler); + unsigned long flags; + + spin_lock_irqsave(&ctx->dev->ctrl_lock, flags); switch (ctrl->id) { case V4L2_CID_COLORFX: if (ctrl->val == V4L2_COLORFX_NEGATIVE) @@ -185,10 +188,13 @@ static int g2d_s_ctrl(struct v4l2_ctrl *ctrl) else ctx->rop = ROP4_COPY; break; - default: - v4l2_err(&ctx->dev->v4l2_dev, "unknown control\n"); - return -EINVAL; + + case V4L2_CID_HFLIP: + ctx->flip = ctx->ctrl_hflip->val | (ctx->ctrl_vflip->val << 1); + break; + } + spin_unlock_irqrestore(&ctx->dev->ctrl_lock, flags); return 0; } @@ -200,11 +206,13 @@ int g2d_setup_ctrls(struct g2d_ctx *ctx) { struct g2d_dev *dev = ctx->dev; - v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1); - if (ctx->ctrl_handler.error) { - v4l2_err(&dev->v4l2_dev, "v4l2_ctrl_handler_init failed\n"); - return ctx->ctrl_handler.error; - } + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3); + + ctx->ctrl_hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); v4l2_ctrl_new_std_menu( &ctx->ctrl_handler, @@ -215,10 +223,14 @@ int g2d_setup_ctrls(struct g2d_ctx *ctx) V4L2_COLORFX_NONE); if (ctx->ctrl_handler.error) { - v4l2_err(&dev->v4l2_dev, "v4l2_ctrl_handler_init failed\n"); - return ctx->ctrl_handler.error; + int err = ctx->ctrl_handler.error; + v4l2_err(&dev->v4l2_dev, "g2d_setup_ctrls failed\n"); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + return err; } + v4l2_ctrl_cluster(2, &ctx->ctrl_hflip); + return 0; } @@ -547,6 +559,7 @@ static void device_run(void *prv) struct g2d_ctx *ctx = prv; struct g2d_dev *dev = ctx->dev; struct vb2_buffer *src, *dst; + unsigned long flags; u32 cmd = 0; dev->curr = ctx; @@ -557,6 +570,8 @@ static void device_run(void *prv) clk_enable(dev->gate); g2d_reset(dev); + spin_lock_irqsave(&dev->ctrl_lock, flags); + g2d_set_src_size(dev, &ctx->in); g2d_set_src_addr(dev, vb2_dma_contig_plane_dma_addr(src, 0)); @@ -564,11 +579,15 @@ static void device_run(void *prv) g2d_set_dst_addr(dev, vb2_dma_contig_plane_dma_addr(dst, 0)); g2d_set_rop4(dev, ctx->rop); + g2d_set_flip(dev, ctx->flip); + if (ctx->in.c_width != ctx->out.c_width || ctx->in.c_height != ctx->out.c_height) cmd |= g2d_cmd_stretch(1); g2d_set_cmd(dev, cmd); g2d_start(dev); + + spin_unlock_irqrestore(&dev->ctrl_lock, flags); } static irqreturn_t g2d_isr(int irq, void *prv) @@ -658,7 +677,7 @@ static int g2d_probe(struct platform_device *pdev) dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; - spin_lock_init(&dev->irqlock); + spin_lock_init(&dev->ctrl_lock); mutex_init(&dev->mutex); atomic_set(&dev->num_inst, 0); init_waitqueue_head(&dev->irq_queue); @@ -693,18 +712,30 @@ static int g2d_probe(struct platform_device *pdev) goto unmap_regs; } + ret = clk_prepare(dev->clk); + if (ret) { + dev_err(&pdev->dev, "failed to prepare g2d clock\n"); + goto put_clk; + } + dev->gate = clk_get(&pdev->dev, "fimg2d"); if (IS_ERR_OR_NULL(dev->gate)) { dev_err(&pdev->dev, "failed to get g2d clock gate\n"); ret = -ENXIO; - goto put_clk; + goto unprep_clk; + } + + ret = clk_prepare(dev->gate); + if (ret) { + dev_err(&pdev->dev, "failed to prepare g2d clock gate\n"); + goto put_clk_gate; } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { dev_err(&pdev->dev, "failed to find IRQ\n"); ret = -ENXIO; - goto put_clk_gate; + goto unprep_clk_gate; } dev->irq = res->start; @@ -764,8 +795,12 @@ alloc_ctx_cleanup: vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); rel_irq: free_irq(dev->irq, dev); +unprep_clk_gate: + clk_unprepare(dev->gate); put_clk_gate: clk_put(dev->gate); +unprep_clk: + clk_unprepare(dev->clk); put_clk: clk_put(dev->clk); unmap_regs: @@ -787,7 +822,9 @@ static int g2d_remove(struct platform_device *pdev) v4l2_device_unregister(&dev->v4l2_dev); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); free_irq(dev->irq, dev); + clk_unprepare(dev->gate); clk_put(dev->gate); + clk_unprepare(dev->clk); clk_put(dev->clk); iounmap(dev->regs); release_resource(dev->res_regs); diff --git a/drivers/media/video/s5p-g2d/g2d.h b/drivers/media/video/s5p-g2d/g2d.h index 5eae90107bf8..1b82065aeaef 100644 --- a/drivers/media/video/s5p-g2d/g2d.h +++ b/drivers/media/video/s5p-g2d/g2d.h @@ -20,7 +20,7 @@ struct g2d_dev { struct v4l2_m2m_dev *m2m_dev; struct video_device *vfd; struct mutex mutex; - spinlock_t irqlock; + spinlock_t ctrl_lock; atomic_t num_inst; struct vb2_alloc_ctx *alloc_ctx; struct resource *res_regs; @@ -57,8 +57,11 @@ struct g2d_ctx { struct v4l2_m2m_ctx *m2m_ctx; struct g2d_frame in; struct g2d_frame out; + struct v4l2_ctrl *ctrl_hflip; + struct v4l2_ctrl *ctrl_vflip; struct v4l2_ctrl_handler ctrl_handler; u32 rop; + u32 flip; }; struct g2d_fmt { @@ -77,6 +80,7 @@ void g2d_set_dst_addr(struct g2d_dev *d, dma_addr_t a); void g2d_start(struct g2d_dev *d); void g2d_clear_int(struct g2d_dev *d); void g2d_set_rop4(struct g2d_dev *d, u32 r); +void g2d_set_flip(struct g2d_dev *d, u32 r); u32 g2d_cmd_stretch(u32 e); void g2d_set_cmd(struct g2d_dev *d, u32 c); diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.c b/drivers/media/video/s5p-jpeg/jpeg-core.c index 1105a8749c8b..5a49c307f9c1 100644 --- a/drivers/media/video/s5p-jpeg/jpeg-core.c +++ b/drivers/media/video/s5p-jpeg/jpeg-core.c @@ -32,10 +32,9 @@ static struct s5p_jpeg_fmt formats_enc[] = { { - .name = "YUV 4:2:0 planar, YCbCr", - .fourcc = V4L2_PIX_FMT_YUV420, - .depth = 12, - .colplanes = 3, + .name = "JPEG JFIF", + .fourcc = V4L2_PIX_FMT_JPEG, + .colplanes = 1, .types = MEM2MEM_CAPTURE, }, { @@ -43,7 +42,7 @@ static struct s5p_jpeg_fmt formats_enc[] = { .fourcc = V4L2_PIX_FMT_YUYV, .depth = 16, .colplanes = 1, - .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, + .types = MEM2MEM_OUTPUT, }, { .name = "RGB565", @@ -203,6 +202,16 @@ static const unsigned char hactblg0[162] = { 0xf9, 0xfa }; +static inline struct s5p_jpeg_ctx *ctrl_to_ctx(struct v4l2_ctrl *c) +{ + return container_of(c->handler, struct s5p_jpeg_ctx, ctrl_handler); +} + +static inline struct s5p_jpeg_ctx *fh_to_ctx(struct v4l2_fh *fh) +{ + return container_of(fh, struct s5p_jpeg_ctx, fh); +} + static inline void jpeg_set_qtbl(void __iomem *regs, const unsigned char *qtbl, unsigned long tab, int len) { @@ -269,6 +278,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq); static struct s5p_jpeg_fmt *s5p_jpeg_find_format(unsigned int mode, __u32 pixelformat); +static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx); static int s5p_jpeg_open(struct file *file) { @@ -276,12 +286,18 @@ static int s5p_jpeg_open(struct file *file) struct video_device *vfd = video_devdata(file); struct s5p_jpeg_ctx *ctx; struct s5p_jpeg_fmt *out_fmt; + int ret = 0; ctx = kzalloc(sizeof *ctx, GFP_KERNEL); if (!ctx) return -ENOMEM; - file->private_data = ctx; + v4l2_fh_init(&ctx->fh, vfd); + /* Use separate control handler per file handle */ + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + ctx->jpeg = jpeg; if (vfd == jpeg->vfd_encoder) { ctx->mode = S5P_JPEG_ENCODE; @@ -291,24 +307,35 @@ static int s5p_jpeg_open(struct file *file) out_fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_JPEG); } + ret = s5p_jpeg_controls_create(ctx); + if (ret < 0) + goto error; + ctx->m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx, queue_init); if (IS_ERR(ctx->m2m_ctx)) { - int err = PTR_ERR(ctx->m2m_ctx); - kfree(ctx); - return err; + ret = PTR_ERR(ctx->m2m_ctx); + goto error; } ctx->out_q.fmt = out_fmt; ctx->cap_q.fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_YUYV); - return 0; + +error: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + return ret; } static int s5p_jpeg_release(struct file *file) { - struct s5p_jpeg_ctx *ctx = file->private_data; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data); v4l2_m2m_ctx_release(ctx->m2m_ctx); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); kfree(ctx); return 0; @@ -317,14 +344,14 @@ static int s5p_jpeg_release(struct file *file) static unsigned int s5p_jpeg_poll(struct file *file, struct poll_table_struct *wait) { - struct s5p_jpeg_ctx *ctx = file->private_data; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data); return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); } static int s5p_jpeg_mmap(struct file *file, struct vm_area_struct *vma) { - struct s5p_jpeg_ctx *ctx = file->private_data; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data); return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); } @@ -448,7 +475,7 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, static int s5p_jpeg_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (ctx->mode == S5P_JPEG_ENCODE) { strlcpy(cap->driver, S5P_JPEG_M2M_NAME " encoder", @@ -497,9 +524,7 @@ static int enum_fmt(struct s5p_jpeg_fmt *formats, int n, static int s5p_jpeg_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - struct s5p_jpeg_ctx *ctx; - - ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (ctx->mode == S5P_JPEG_ENCODE) return enum_fmt(formats_enc, NUM_FORMATS_ENC, f, @@ -511,9 +536,7 @@ static int s5p_jpeg_enum_fmt_vid_cap(struct file *file, void *priv, static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - struct s5p_jpeg_ctx *ctx; - - ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (ctx->mode == S5P_JPEG_ENCODE) return enum_fmt(formats_enc, NUM_FORMATS_ENC, f, @@ -538,7 +561,7 @@ static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f) struct vb2_queue *vq; struct s5p_jpeg_q_data *q_data = NULL; struct v4l2_pix_format *pix = &f->fmt.pix; - struct s5p_jpeg_ctx *ct = priv; + struct s5p_jpeg_ctx *ct = fh_to_ctx(priv); vq = v4l2_m2m_get_vq(ct->m2m_ctx, f->type); if (!vq) @@ -659,8 +682,8 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct s5p_jpeg_fmt *fmt, static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); struct s5p_jpeg_fmt *fmt; - struct s5p_jpeg_ctx *ctx = priv; fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat); if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) { @@ -676,8 +699,8 @@ static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv, static int s5p_jpeg_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); struct s5p_jpeg_fmt *fmt; - struct s5p_jpeg_ctx *ctx = priv; fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat); if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) { @@ -728,7 +751,7 @@ static int s5p_jpeg_s_fmt_vid_cap(struct file *file, void *priv, if (ret) return ret; - return s5p_jpeg_s_fmt(priv, f); + return s5p_jpeg_s_fmt(fh_to_ctx(priv), f); } static int s5p_jpeg_s_fmt_vid_out(struct file *file, void *priv, @@ -740,13 +763,13 @@ static int s5p_jpeg_s_fmt_vid_out(struct file *file, void *priv, if (ret) return ret; - return s5p_jpeg_s_fmt(priv, f); + return s5p_jpeg_s_fmt(fh_to_ctx(priv), f); } static int s5p_jpeg_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *reqbufs) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); } @@ -754,14 +777,14 @@ static int s5p_jpeg_reqbufs(struct file *file, void *priv, static int s5p_jpeg_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); } static int s5p_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); } @@ -769,7 +792,7 @@ static int s5p_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) static int s5p_jpeg_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); } @@ -777,7 +800,7 @@ static int s5p_jpeg_dqbuf(struct file *file, void *priv, static int s5p_jpeg_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); } @@ -785,7 +808,7 @@ static int s5p_jpeg_streamon(struct file *file, void *priv, static int s5p_jpeg_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); } @@ -793,7 +816,7 @@ static int s5p_jpeg_streamoff(struct file *file, void *priv, int s5p_jpeg_g_selection(struct file *file, void *priv, struct v4l2_selection *s) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -822,33 +845,89 @@ int s5p_jpeg_g_selection(struct file *file, void *priv, return 0; } -static int s5p_jpeg_g_jpegcomp(struct file *file, void *priv, - struct v4l2_jpegcompression *compr) +/* + * V4L2 controls + */ + +static int s5p_jpeg_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl); + struct s5p_jpeg *jpeg = ctx->jpeg; + unsigned long flags; - if (ctx->mode == S5P_JPEG_DECODE) - return -ENOTTY; + switch (ctrl->id) { + case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: + spin_lock_irqsave(&jpeg->slock, flags); - memset(compr, 0, sizeof(*compr)); - compr->quality = ctx->compr_quality; + WARN_ON(ctx->subsampling > S5P_SUBSAMPLING_MODE_GRAY); + if (ctx->subsampling > 2) + ctrl->val = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; + else + ctrl->val = ctx->subsampling; + spin_unlock_irqrestore(&jpeg->slock, flags); + break; + } return 0; } -static int s5p_jpeg_s_jpegcomp(struct file *file, void *priv, - struct v4l2_jpegcompression *compr) +static int s5p_jpeg_s_ctrl(struct v4l2_ctrl *ctrl) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl); + unsigned long flags; - if (ctx->mode == S5P_JPEG_DECODE) - return -ENOTTY; + spin_lock_irqsave(&ctx->jpeg->slock, flags); - compr->quality = clamp(compr->quality, S5P_JPEG_COMPR_QUAL_BEST, - S5P_JPEG_COMPR_QUAL_WORST); + switch (ctrl->id) { + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + ctx->compr_quality = S5P_JPEG_COMPR_QUAL_WORST - ctrl->val; + break; + case V4L2_CID_JPEG_RESTART_INTERVAL: + ctx->restart_interval = ctrl->val; + break; + case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: + ctx->subsampling = ctrl->val; + break; + } - ctx->compr_quality = S5P_JPEG_COMPR_QUAL_WORST - compr->quality; + spin_unlock_irqrestore(&ctx->jpeg->slock, flags); + return 0; +} + +static const struct v4l2_ctrl_ops s5p_jpeg_ctrl_ops = { + .g_volatile_ctrl = s5p_jpeg_g_volatile_ctrl, + .s_ctrl = s5p_jpeg_s_ctrl, +}; +static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx) +{ + unsigned int mask = ~0x27; /* 444, 422, 420, GRAY */ + struct v4l2_ctrl *ctrl; + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3); + + if (ctx->mode == S5P_JPEG_ENCODE) { + v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, + 0, 3, 1, 3); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, + V4L2_CID_JPEG_RESTART_INTERVAL, + 0, 3, 0xffff, 0); + mask = ~0x06; /* 422, 420 */ + } + + ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, + V4L2_CID_JPEG_CHROMA_SUBSAMPLING, + V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, mask, + V4L2_JPEG_CHROMA_SUBSAMPLING_422); + + if (ctx->ctrl_handler.error) + return ctx->ctrl_handler.error; + + if (ctx->mode == S5P_JPEG_DECODE) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_READ_ONLY; return 0; } @@ -877,9 +956,6 @@ static const struct v4l2_ioctl_ops s5p_jpeg_ioctl_ops = { .vidioc_streamoff = s5p_jpeg_streamoff, .vidioc_g_selection = s5p_jpeg_g_selection, - - .vidioc_g_jpegcomp = s5p_jpeg_g_jpegcomp, - .vidioc_s_jpegcomp = s5p_jpeg_s_jpegcomp, }; /* @@ -908,13 +984,8 @@ static void s5p_jpeg_device_run(void *priv) jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_565); else jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_422); - if (ctx->cap_q.fmt->fourcc == V4L2_PIX_FMT_YUYV) - jpeg_subsampling_mode(jpeg->regs, - S5P_JPEG_SUBSAMPLING_422); - else - jpeg_subsampling_mode(jpeg->regs, - S5P_JPEG_SUBSAMPLING_420); - jpeg_dri(jpeg->regs, 0); + jpeg_subsampling_mode(jpeg->regs, ctx->subsampling); + jpeg_dri(jpeg->regs, ctx->restart_interval); jpeg_x(jpeg->regs, ctx->out_q.w); jpeg_y(jpeg->regs, ctx->out_q.h); jpeg_imgadr(jpeg->regs, src_addr); @@ -953,14 +1024,18 @@ static void s5p_jpeg_device_run(void *priv) jpeg_htbl_dc(jpeg->regs, 2); jpeg_htbl_ac(jpeg->regs, 3); jpeg_htbl_dc(jpeg->regs, 3); - } else { + } else { /* S5P_JPEG_DECODE */ jpeg_rst_int_enable(jpeg->regs, true); jpeg_data_num_int_enable(jpeg->regs, true); jpeg_final_mcu_num_int_enable(jpeg->regs, true); - jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422); + if (ctx->cap_q.fmt->fourcc == V4L2_PIX_FMT_YUYV) + jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422); + else + jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_420); jpeg_jpgadr(jpeg->regs, src_addr); jpeg_imgadr(jpeg->regs, dst_addr); } + jpeg_start(jpeg->regs); } @@ -1162,6 +1237,8 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id) bool timer_elapsed = false; bool op_completed = false; + spin_lock(&jpeg->slock); + curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev); src_buf = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); @@ -1192,6 +1269,9 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id) v4l2_m2m_buf_done(dst_buf, state); v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->m2m_ctx); + curr_ctx->subsampling = jpeg_get_subsampling_mode(jpeg->regs); + spin_unlock(&jpeg->slock); + jpeg_clear_int(jpeg->regs); return IRQ_HANDLED; @@ -1215,6 +1295,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev) return -ENOMEM; mutex_init(&jpeg->lock); + spin_lock_init(&jpeg->slock); jpeg->dev = &pdev->dev; /* memory-mapped registers */ diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.h b/drivers/media/video/s5p-jpeg/jpeg-core.h index facad6114f5e..38d7367f7a6d 100644 --- a/drivers/media/video/s5p-jpeg/jpeg-core.h +++ b/drivers/media/video/s5p-jpeg/jpeg-core.h @@ -14,6 +14,8 @@ #define JPEG_CORE_H_ #include <media/v4l2-device.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-ctrls.h> #define S5P_JPEG_M2M_NAME "s5p-jpeg" @@ -47,6 +49,7 @@ /** * struct s5p_jpeg - JPEG IP abstraction * @lock: the mutex protecting this structure + * @slock: spinlock protecting the device contexts * @v4l2_dev: v4l2 device for mem2mem mode * @vfd_encoder: video device node for encoder mem2mem mode * @vfd_decoder: video device node for decoder mem2mem mode @@ -60,6 +63,7 @@ */ struct s5p_jpeg { struct mutex lock; + struct spinlock slock; struct v4l2_device v4l2_dev; struct video_device *vfd_encoder; @@ -117,15 +121,20 @@ struct s5p_jpeg_q_data { * @out_q: source (output) queue information * @cap_fmt: destination (capture) queue queue information * @hdr_parsed: set if header has been parsed during decompression + * @ctrl_handler: controls handler */ struct s5p_jpeg_ctx { struct s5p_jpeg *jpeg; unsigned int mode; - unsigned int compr_quality; + unsigned short compr_quality; + unsigned short restart_interval; + unsigned short subsampling; struct v4l2_m2m_ctx *m2m_ctx; struct s5p_jpeg_q_data out_q; struct s5p_jpeg_q_data cap_q; + struct v4l2_fh fh; bool hdr_parsed; + struct v4l2_ctrl_handler ctrl_handler; }; /** diff --git a/drivers/media/video/s5p-jpeg/jpeg-hw.h b/drivers/media/video/s5p-jpeg/jpeg-hw.h index e10c744e9f23..f12f0fdbde7c 100644 --- a/drivers/media/video/s5p-jpeg/jpeg-hw.h +++ b/drivers/media/video/s5p-jpeg/jpeg-hw.h @@ -13,6 +13,7 @@ #define JPEG_HW_H_ #include <linux/io.h> +#include <linux/videodev2.h> #include "jpeg-hw.h" #include "jpeg-regs.h" @@ -25,8 +26,6 @@ #define S5P_JPEG_DECODE 1 #define S5P_JPEG_RAW_IN_565 0 #define S5P_JPEG_RAW_IN_422 1 -#define S5P_JPEG_SUBSAMPLING_422 0 -#define S5P_JPEG_SUBSAMPLING_420 1 #define S5P_JPEG_RAW_OUT_422 0 #define S5P_JPEG_RAW_OUT_420 1 @@ -91,21 +90,26 @@ static inline void jpeg_proc_mode(void __iomem *regs, unsigned long mode) writel(reg, regs + S5P_JPGMOD); } -static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned long mode) +static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned int mode) { unsigned long reg, m; - m = S5P_SUBSAMPLING_MODE_422; - if (mode == S5P_JPEG_SUBSAMPLING_422) - m = S5P_SUBSAMPLING_MODE_422; - else if (mode == S5P_JPEG_SUBSAMPLING_420) + if (mode == V4L2_JPEG_CHROMA_SUBSAMPLING_420) m = S5P_SUBSAMPLING_MODE_420; + else + m = S5P_SUBSAMPLING_MODE_422; + reg = readl(regs + S5P_JPGMOD); reg &= ~S5P_SUBSAMPLING_MODE_MASK; reg |= m; writel(reg, regs + S5P_JPGMOD); } +static inline unsigned int jpeg_get_subsampling_mode(void __iomem *regs) +{ + return readl(regs + S5P_JPGMOD) & S5P_SUBSAMPLING_MODE_MASK; +} + static inline void jpeg_dri(void __iomem *regs, unsigned int dri) { unsigned long reg; diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_pm.c b/drivers/media/video/s5p-mfc/s5p_mfc_pm.c index f6a3035c4fb7..738a607be43c 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_pm.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_pm.c @@ -41,15 +41,29 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev) pm->clock_gate = clk_get(&dev->plat_dev->dev, MFC_GATE_CLK_NAME); if (IS_ERR(pm->clock_gate)) { mfc_err("Failed to get clock-gating control\n"); - ret = -ENOENT; + ret = PTR_ERR(pm->clock_gate); goto err_g_ip_clk; } + + ret = clk_prepare(pm->clock_gate); + if (ret) { + mfc_err("Failed to preapre clock-gating control\n"); + goto err_p_ip_clk; + } + pm->clock = clk_get(&dev->plat_dev->dev, MFC_CLKNAME); if (IS_ERR(pm->clock)) { mfc_err("Failed to get MFC clock\n"); - ret = -ENOENT; + ret = PTR_ERR(pm->clock); goto err_g_ip_clk_2; } + + ret = clk_prepare(pm->clock); + if (ret) { + mfc_err("Failed to prepare MFC clock\n"); + goto err_p_ip_clk_2; + } + atomic_set(&pm->power, 0); #ifdef CONFIG_PM_RUNTIME pm->device = &dev->plat_dev->dev; @@ -59,7 +73,11 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev) atomic_set(&clk_ref, 0); #endif return 0; +err_p_ip_clk_2: + clk_put(pm->clock); err_g_ip_clk_2: + clk_unprepare(pm->clock_gate); +err_p_ip_clk: clk_put(pm->clock_gate); err_g_ip_clk: return ret; @@ -67,7 +85,9 @@ err_g_ip_clk: void s5p_mfc_final_pm(struct s5p_mfc_dev *dev) { + clk_unprepare(pm->clock_gate); clk_put(pm->clock_gate); + clk_unprepare(pm->clock); clk_put(pm->clock); #ifdef CONFIG_PM_RUNTIME pm_runtime_disable(pm->device); diff --git a/drivers/media/video/s5p-tv/Kconfig b/drivers/media/video/s5p-tv/Kconfig index f2a09779ec8f..f248b2856720 100644 --- a/drivers/media/video/s5p-tv/Kconfig +++ b/drivers/media/video/s5p-tv/Kconfig @@ -46,6 +46,16 @@ config VIDEO_SAMSUNG_S5P_HDMIPHY as module. It is an I2C driver, that exposes a V4L2 subdev for use by other drivers. +config VIDEO_SAMSUNG_S5P_SII9234 + tristate "Samsung SII9234 Driver" + depends on VIDEO_DEV && VIDEO_V4L2 && I2C + depends on VIDEO_SAMSUNG_S5P_TV + help + Say Y here if you want support for the MHL interface + in S5P Samsung SoC. The driver can be compiled + as module. It is an I2C driver, that exposes a V4L2 + subdev for use by other drivers. + config VIDEO_SAMSUNG_S5P_SDO tristate "Samsung Analog TV Driver" depends on VIDEO_DEV && VIDEO_V4L2 diff --git a/drivers/media/video/s5p-tv/Makefile b/drivers/media/video/s5p-tv/Makefile index 37e4c17663b4..f49e756a2fde 100644 --- a/drivers/media/video/s5p-tv/Makefile +++ b/drivers/media/video/s5p-tv/Makefile @@ -8,6 +8,8 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMIPHY) += s5p-hdmiphy.o s5p-hdmiphy-y += hdmiphy_drv.o +obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SII9234) += s5p-sii9234.o +s5p-sii9234-y += sii9234_drv.o obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMI) += s5p-hdmi.o s5p-hdmi-y += hdmi_drv.o obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SDO) += s5p-sdo.o diff --git a/drivers/media/video/s5p-tv/hdmi_drv.c b/drivers/media/video/s5p-tv/hdmi_drv.c index 8b41a0410ab1..4865d25a0e57 100644 --- a/drivers/media/video/s5p-tv/hdmi_drv.c +++ b/drivers/media/video/s5p-tv/hdmi_drv.c @@ -30,6 +30,7 @@ #include <linux/clk.h> #include <linux/regulator/consumer.h> +#include <media/s5p_hdmi.h> #include <media/v4l2-common.h> #include <media/v4l2-dev.h> #include <media/v4l2-device.h> @@ -66,6 +67,8 @@ struct hdmi_device { struct v4l2_device v4l2_dev; /** subdev of HDMIPHY interface */ struct v4l2_subdev *phy_sd; + /** subdev of MHL interface */ + struct v4l2_subdev *mhl_sd; /** configuration of current graphic mode */ const struct hdmi_preset_conf *cur_conf; /** current preset */ @@ -74,10 +77,6 @@ struct hdmi_device { struct hdmi_resources res; }; -struct hdmi_driver_data { - int hdmiphy_bus; -}; - struct hdmi_tg_regs { u8 cmd; u8 h_fsz_l; @@ -129,23 +128,11 @@ struct hdmi_preset_conf { struct v4l2_mbus_framefmt mbus_fmt; }; -/* I2C module and id for HDMIPHY */ -static struct i2c_board_info hdmiphy_info = { - I2C_BOARD_INFO("hdmiphy", 0x38), -}; - -static struct hdmi_driver_data hdmi_driver_data[] = { - { .hdmiphy_bus = 3 }, - { .hdmiphy_bus = 8 }, -}; - static struct platform_device_id hdmi_driver_types[] = { { .name = "s5pv210-hdmi", - .driver_data = (unsigned long)&hdmi_driver_data[0], }, { .name = "exynos4-hdmi", - .driver_data = (unsigned long)&hdmi_driver_data[1], }, { /* end node */ } @@ -587,7 +574,15 @@ static int hdmi_streamon(struct hdmi_device *hdev) if (tries == 0) { dev_err(dev, "hdmiphy's pll could not reach steady state.\n"); v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0); - hdmi_dumpregs(hdev, "s_stream"); + hdmi_dumpregs(hdev, "hdmiphy - s_stream"); + return -EIO; + } + + /* starting MHL */ + ret = v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 1); + if (hdev->mhl_sd && ret) { + v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0); + hdmi_dumpregs(hdev, "mhl - s_stream"); return -EIO; } @@ -618,6 +613,7 @@ static int hdmi_streamoff(struct hdmi_device *hdev) clk_set_parent(res->sclk_hdmi, res->sclk_pixel); clk_enable(res->sclk_hdmi); + v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 0); v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0); hdmi_dumpregs(hdev, "streamoff"); @@ -739,6 +735,7 @@ static int hdmi_runtime_suspend(struct device *dev) struct hdmi_device *hdev = sd_to_hdmi_dev(sd); dev_dbg(dev, "%s\n", __func__); + v4l2_subdev_call(hdev->mhl_sd, core, s_power, 0); hdmi_resource_poweroff(&hdev->res); return 0; } @@ -757,6 +754,11 @@ static int hdmi_runtime_resume(struct device *dev) if (ret) goto fail; + /* starting MHL */ + ret = v4l2_subdev_call(hdev->mhl_sd, core, s_power, 1); + if (hdev->mhl_sd && ret) + goto fail; + dev_dbg(dev, "poweron succeed\n"); return 0; @@ -867,15 +869,21 @@ static int __devinit hdmi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct resource *res; - struct i2c_adapter *phy_adapter; + struct i2c_adapter *adapter; struct v4l2_subdev *sd; struct hdmi_device *hdmi_dev = NULL; - struct hdmi_driver_data *drv_data; + struct s5p_hdmi_platform_data *pdata = dev->platform_data; int ret; dev_dbg(dev, "probe start\n"); - hdmi_dev = kzalloc(sizeof(*hdmi_dev), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "platform data is missing\n"); + ret = -ENODEV; + goto fail; + } + + hdmi_dev = devm_kzalloc(&pdev->dev, sizeof(*hdmi_dev), GFP_KERNEL); if (!hdmi_dev) { dev_err(dev, "out of memory\n"); ret = -ENOMEM; @@ -886,7 +894,7 @@ static int __devinit hdmi_probe(struct platform_device *pdev) ret = hdmi_resources_init(hdmi_dev); if (ret) - goto fail_hdev; + goto fail; /* mapping HDMI registers */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -896,24 +904,26 @@ static int __devinit hdmi_probe(struct platform_device *pdev) goto fail_init; } - hdmi_dev->regs = ioremap(res->start, resource_size(res)); + hdmi_dev->regs = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); if (hdmi_dev->regs == NULL) { dev_err(dev, "register mapping failed.\n"); ret = -ENXIO; - goto fail_hdev; + goto fail_init; } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { dev_err(dev, "get interrupt resource failed.\n"); ret = -ENXIO; - goto fail_regs; + goto fail_init; } - ret = request_irq(res->start, hdmi_irq_handler, 0, "hdmi", hdmi_dev); + ret = devm_request_irq(&pdev->dev, res->start, hdmi_irq_handler, 0, + "hdmi", hdmi_dev); if (ret) { dev_err(dev, "request interrupt failed.\n"); - goto fail_regs; + goto fail_init; } hdmi_dev->irq = res->start; @@ -924,28 +934,54 @@ static int __devinit hdmi_probe(struct platform_device *pdev) ret = v4l2_device_register(NULL, &hdmi_dev->v4l2_dev); if (ret) { dev_err(dev, "could not register v4l2 device.\n"); - goto fail_irq; + goto fail_init; } - drv_data = (struct hdmi_driver_data *) - platform_get_device_id(pdev)->driver_data; - phy_adapter = i2c_get_adapter(drv_data->hdmiphy_bus); - if (phy_adapter == NULL) { - dev_err(dev, "adapter request failed\n"); + /* testing if hdmiphy info is present */ + if (!pdata->hdmiphy_info) { + dev_err(dev, "hdmiphy info is missing in platform data\n"); + ret = -ENXIO; + goto fail_vdev; + } + + adapter = i2c_get_adapter(pdata->hdmiphy_bus); + if (adapter == NULL) { + dev_err(dev, "hdmiphy adapter request failed\n"); ret = -ENXIO; goto fail_vdev; } hdmi_dev->phy_sd = v4l2_i2c_new_subdev_board(&hdmi_dev->v4l2_dev, - phy_adapter, &hdmiphy_info, NULL); + adapter, pdata->hdmiphy_info, NULL); /* on failure or not adapter is no longer useful */ - i2c_put_adapter(phy_adapter); + i2c_put_adapter(adapter); if (hdmi_dev->phy_sd == NULL) { dev_err(dev, "missing subdev for hdmiphy\n"); ret = -ENODEV; goto fail_vdev; } + /* initialization of MHL interface if present */ + if (pdata->mhl_info) { + adapter = i2c_get_adapter(pdata->mhl_bus); + if (adapter == NULL) { + dev_err(dev, "MHL adapter request failed\n"); + ret = -ENXIO; + goto fail_vdev; + } + + hdmi_dev->mhl_sd = v4l2_i2c_new_subdev_board( + &hdmi_dev->v4l2_dev, adapter, + pdata->mhl_info, NULL); + /* on failure or not adapter is no longer useful */ + i2c_put_adapter(adapter); + if (hdmi_dev->mhl_sd == NULL) { + dev_err(dev, "missing subdev for MHL\n"); + ret = -ENODEV; + goto fail_vdev; + } + } + clk_enable(hdmi_dev->res.hdmi); pm_runtime_enable(dev); @@ -962,25 +998,16 @@ static int __devinit hdmi_probe(struct platform_device *pdev) /* storing subdev for call that have only access to struct device */ dev_set_drvdata(dev, sd); - dev_info(dev, "probe sucessful\n"); + dev_info(dev, "probe successful\n"); return 0; fail_vdev: v4l2_device_unregister(&hdmi_dev->v4l2_dev); -fail_irq: - free_irq(hdmi_dev->irq, hdmi_dev); - -fail_regs: - iounmap(hdmi_dev->regs); - fail_init: hdmi_resources_cleanup(hdmi_dev); -fail_hdev: - kfree(hdmi_dev); - fail: dev_err(dev, "probe failed\n"); return ret; @@ -996,11 +1023,8 @@ static int __devexit hdmi_remove(struct platform_device *pdev) clk_disable(hdmi_dev->res.hdmi); v4l2_device_unregister(&hdmi_dev->v4l2_dev); disable_irq(hdmi_dev->irq); - free_irq(hdmi_dev->irq, hdmi_dev); - iounmap(hdmi_dev->regs); hdmi_resources_cleanup(hdmi_dev); - kfree(hdmi_dev); - dev_info(dev, "remove sucessful\n"); + dev_info(dev, "remove successful\n"); return 0; } diff --git a/drivers/media/video/s5p-tv/hdmiphy_drv.c b/drivers/media/video/s5p-tv/hdmiphy_drv.c index 6693f4aff108..0afef77747e5 100644 --- a/drivers/media/video/s5p-tv/hdmiphy_drv.c +++ b/drivers/media/video/s5p-tv/hdmiphy_drv.c @@ -175,14 +175,4 @@ static struct i2c_driver hdmiphy_driver = { .id_table = hdmiphy_id, }; -static int __init hdmiphy_init(void) -{ - return i2c_add_driver(&hdmiphy_driver); -} -module_init(hdmiphy_init); - -static void __exit hdmiphy_exit(void) -{ - i2c_del_driver(&hdmiphy_driver); -} -module_exit(hdmiphy_exit); +module_i2c_driver(hdmiphy_driver); diff --git a/drivers/media/video/s5p-tv/mixer_drv.c b/drivers/media/video/s5p-tv/mixer_drv.c index 00643094b221..a2c0c25ad130 100644 --- a/drivers/media/video/s5p-tv/mixer_drv.c +++ b/drivers/media/video/s5p-tv/mixer_drv.c @@ -444,7 +444,7 @@ static int __devexit mxr_remove(struct platform_device *pdev) kfree(mdev); - dev_info(dev, "remove sucessful\n"); + dev_info(dev, "remove successful\n"); return 0; } diff --git a/drivers/media/video/s5p-tv/sdo_drv.c b/drivers/media/video/s5p-tv/sdo_drv.c index 059e7749ce95..f6bca2c20e89 100644 --- a/drivers/media/video/s5p-tv/sdo_drv.c +++ b/drivers/media/video/s5p-tv/sdo_drv.c @@ -301,7 +301,7 @@ static int __devinit sdo_probe(struct platform_device *pdev) struct clk *sclk_vpll; dev_info(dev, "probe start\n"); - sdev = kzalloc(sizeof *sdev, GFP_KERNEL); + sdev = devm_kzalloc(&pdev->dev, sizeof *sdev, GFP_KERNEL); if (!sdev) { dev_err(dev, "not enough memory.\n"); ret = -ENOMEM; @@ -314,14 +314,14 @@ static int __devinit sdo_probe(struct platform_device *pdev) if (res == NULL) { dev_err(dev, "get memory resource failed.\n"); ret = -ENXIO; - goto fail_sdev; + goto fail; } - sdev->regs = ioremap(res->start, resource_size(res)); + sdev->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (sdev->regs == NULL) { dev_err(dev, "register mapping failed.\n"); ret = -ENXIO; - goto fail_sdev; + goto fail; } /* acquiring interrupt */ @@ -329,12 +329,13 @@ static int __devinit sdo_probe(struct platform_device *pdev) if (res == NULL) { dev_err(dev, "get interrupt resource failed.\n"); ret = -ENXIO; - goto fail_regs; + goto fail; } - ret = request_irq(res->start, sdo_irq_handler, 0, "s5p-sdo", sdev); + ret = devm_request_irq(&pdev->dev, res->start, sdo_irq_handler, 0, + "s5p-sdo", sdev); if (ret) { dev_err(dev, "request interrupt failed.\n"); - goto fail_regs; + goto fail; } sdev->irq = res->start; @@ -343,7 +344,7 @@ static int __devinit sdo_probe(struct platform_device *pdev) if (IS_ERR_OR_NULL(sdev->sclk_dac)) { dev_err(dev, "failed to get clock 'sclk_dac'\n"); ret = -ENXIO; - goto fail_irq; + goto fail; } sdev->dac = clk_get(dev, "dac"); if (IS_ERR_OR_NULL(sdev->dac)) { @@ -415,12 +416,6 @@ fail_dac: clk_put(sdev->dac); fail_sclk_dac: clk_put(sdev->sclk_dac); -fail_irq: - free_irq(sdev->irq, sdev); -fail_regs: - iounmap(sdev->regs); -fail_sdev: - kfree(sdev); fail: dev_info(dev, "probe failed\n"); return ret; @@ -439,9 +434,6 @@ static int __devexit sdo_remove(struct platform_device *pdev) clk_put(sdev->dacphy); clk_put(sdev->dac); clk_put(sdev->sclk_dac); - free_irq(sdev->irq, sdev); - iounmap(sdev->regs); - kfree(sdev); dev_info(&pdev->dev, "remove successful\n"); return 0; diff --git a/drivers/media/video/s5p-tv/sii9234_drv.c b/drivers/media/video/s5p-tv/sii9234_drv.c new file mode 100644 index 000000000000..0f31eccd7b80 --- /dev/null +++ b/drivers/media/video/s5p-tv/sii9234_drv.c @@ -0,0 +1,432 @@ +/* + * Samsung MHL interface driver + * + * Copyright (C) 2011 Samsung Electronics Co.Ltd + * Author: Tomasz Stanislawski <t.stanislaws@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/freezer.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/kthread.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/machine.h> +#include <linux/slab.h> + +#include <mach/gpio.h> +#include <plat/gpio-cfg.h> + +#include <media/sii9234.h> +#include <media/v4l2-subdev.h> + +MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>"); +MODULE_DESCRIPTION("Samsung MHL interface driver"); +MODULE_LICENSE("GPL"); + +struct sii9234_context { + struct i2c_client *client; + struct regulator *power; + int gpio_n_reset; + struct v4l2_subdev sd; +}; + +static inline struct sii9234_context *sd_to_context(struct v4l2_subdev *sd) +{ + return container_of(sd, struct sii9234_context, sd); +} + +static inline int sii9234_readb(struct i2c_client *client, int addr) +{ + return i2c_smbus_read_byte_data(client, addr); +} + +static inline int sii9234_writeb(struct i2c_client *client, int addr, int value) +{ + return i2c_smbus_write_byte_data(client, addr, value); +} + +static inline int sii9234_writeb_mask(struct i2c_client *client, int addr, + int value, int mask) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, addr); + if (ret < 0) + return ret; + ret = (ret & ~mask) | (value & mask); + return i2c_smbus_write_byte_data(client, addr, ret); +} + +static inline int sii9234_readb_idx(struct i2c_client *client, int addr) +{ + int ret; + ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff); + if (ret < 0) + return ret; + return i2c_smbus_read_byte_data(client, 0xbe); +} + +static inline int sii9234_writeb_idx(struct i2c_client *client, int addr, + int value) +{ + int ret; + ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(client, 0xbe, value); + return ret; +} + +static inline int sii9234_writeb_idx_mask(struct i2c_client *client, int addr, + int value, int mask) +{ + int ret; + + ret = sii9234_readb_idx(client, addr); + if (ret < 0) + return ret; + ret = (ret & ~mask) | (value & mask); + return sii9234_writeb_idx(client, addr, ret); +} + +static int sii9234_reset(struct sii9234_context *ctx) +{ + struct i2c_client *client = ctx->client; + struct device *dev = &client->dev; + int ret, tries; + + gpio_direction_output(ctx->gpio_n_reset, 1); + mdelay(1); + gpio_direction_output(ctx->gpio_n_reset, 0); + mdelay(1); + gpio_direction_output(ctx->gpio_n_reset, 1); + mdelay(1); + + /* going to TTPI mode */ + ret = sii9234_writeb(client, 0xc7, 0); + if (ret < 0) { + dev_err(dev, "failed to set TTPI mode\n"); + return ret; + } + for (tries = 0; tries < 100 ; ++tries) { + ret = sii9234_readb(client, 0x1b); + if (ret > 0) + break; + if (ret < 0) { + dev_err(dev, "failed to reset device\n"); + return -EIO; + } + mdelay(1); + } + if (tries == 100) { + dev_err(dev, "maximal number of tries reached\n"); + return -EIO; + } + + return 0; +} + +static int sii9234_verify_version(struct i2c_client *client) +{ + struct device *dev = &client->dev; + int family, rev, tpi_rev, dev_id, sub_id, hdcp, id; + + family = sii9234_readb(client, 0x1b); + rev = sii9234_readb(client, 0x1c) & 0x0f; + tpi_rev = sii9234_readb(client, 0x1d) & 0x7f; + dev_id = sii9234_readb_idx(client, 0x0103); + sub_id = sii9234_readb_idx(client, 0x0102); + hdcp = sii9234_readb(client, 0x30); + + if (family < 0 || rev < 0 || tpi_rev < 0 || dev_id < 0 || + sub_id < 0 || hdcp < 0) { + dev_err(dev, "failed to read chip's version\n"); + return -EIO; + } + + id = (dev_id << 8) | sub_id; + + dev_info(dev, "chip: SiL%02x family: %02x, rev: %02x\n", + id, family, rev); + dev_info(dev, "tpi_rev:%02x, hdcp: %02x\n", tpi_rev, hdcp); + if (id != 0x9234) { + dev_err(dev, "not supported chip\n"); + return -ENODEV; + } + + return 0; +} + +static u8 data[][3] = { +/* setup from driver created by doonsoo45.kim */ + { 0x01, 0x05, 0x04 }, /* Enable Auto soft reset on SCDT = 0 */ + { 0x01, 0x08, 0x35 }, /* Power Up TMDS Tx Core */ + { 0x01, 0x0d, 0x1c }, /* HDMI Transcode mode enable */ + { 0x01, 0x2b, 0x01 }, /* Enable HDCP Compliance workaround */ + { 0x01, 0x79, 0x40 }, /* daniel test...MHL_INT */ + { 0x01, 0x80, 0x34 }, /* Enable Rx PLL Clock Value */ + { 0x01, 0x90, 0x27 }, /* Enable CBUS discovery */ + { 0x01, 0x91, 0xe5 }, /* Skip RGND detection */ + { 0x01, 0x92, 0x46 }, /* Force MHD mode */ + { 0x01, 0x93, 0xdc }, /* Disable CBUS pull-up during RGND measurement */ + { 0x01, 0x94, 0x66 }, /* 1.8V CBUS VTH & GND threshold */ + { 0x01, 0x95, 0x31 }, /* RGND block & single discovery attempt */ + { 0x01, 0x96, 0x22 }, /* use 1K and 2K setting */ + { 0x01, 0xa0, 0x10 }, /* SIMG: Term mode */ + { 0x01, 0xa1, 0xfc }, /* Disable internal Mobile HD driver */ + { 0x01, 0xa3, 0xfa }, /* SIMG: Output Swing default EB, 3x Clk Mult */ + { 0x01, 0xa5, 0x80 }, /* SIMG: RGND Hysterisis, 3x mode for Beast */ + { 0x01, 0xa6, 0x0c }, /* SIMG: Swing Offset */ + { 0x02, 0x3d, 0x3f }, /* Power up CVCC 1.2V core */ + { 0x03, 0x00, 0x00 }, /* SIMG: correcting HW default */ + { 0x03, 0x11, 0x01 }, /* Enable TxPLL Clock */ + { 0x03, 0x12, 0x15 }, /* Enable Tx Clock Path & Equalizer */ + { 0x03, 0x13, 0x60 }, /* SIMG: Set termination value */ + { 0x03, 0x14, 0xf0 }, /* SIMG: Change CKDT level */ + { 0x03, 0x17, 0x07 }, /* SIMG: PLL Calrefsel */ + { 0x03, 0x1a, 0x20 }, /* VCO Cal */ + { 0x03, 0x22, 0xe0 }, /* SIMG: Auto EQ */ + { 0x03, 0x23, 0xc0 }, /* SIMG: Auto EQ */ + { 0x03, 0x24, 0xa0 }, /* SIMG: Auto EQ */ + { 0x03, 0x25, 0x80 }, /* SIMG: Auto EQ */ + { 0x03, 0x26, 0x60 }, /* SIMG: Auto EQ */ + { 0x03, 0x27, 0x40 }, /* SIMG: Auto EQ */ + { 0x03, 0x28, 0x20 }, /* SIMG: Auto EQ */ + { 0x03, 0x29, 0x00 }, /* SIMG: Auto EQ */ + { 0x03, 0x31, 0x0b }, /* SIMG: Rx PLL BW value from I2C BW ~ 4MHz */ + { 0x03, 0x45, 0x06 }, /* SIMG: DPLL Mode */ + { 0x03, 0x4b, 0x06 }, /* SIMG: Correcting HW default */ + { 0x03, 0x4c, 0xa0 }, /* Manual zone control */ + { 0x03, 0x4d, 0x02 }, /* SIMG: PLL Mode Value (order is important) */ +}; + +static int sii9234_set_internal(struct sii9234_context *ctx) +{ + struct i2c_client *client = ctx->client; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(data); ++i) { + int addr = (data[i][0] << 8) | data[i][1]; + ret = sii9234_writeb_idx(client, addr, data[i][2]); + if (ret < 0) + return ret; + } + return 0; +} + +static int sii9234_runtime_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct sii9234_context *ctx = sd_to_context(sd); + struct i2c_client *client = ctx->client; + + dev_info(dev, "suspend start\n"); + + sii9234_writeb_mask(client, 0x1e, 3, 3); + regulator_disable(ctx->power); + + return 0; +} + +static int sii9234_runtime_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct sii9234_context *ctx = sd_to_context(sd); + struct i2c_client *client = ctx->client; + int ret; + + dev_info(dev, "resume start\n"); + regulator_enable(ctx->power); + + ret = sii9234_reset(ctx); + if (ret) + goto fail; + + /* enable tpi */ + ret = sii9234_writeb_mask(client, 0x1e, 1, 0); + if (ret < 0) + goto fail; + ret = sii9234_set_internal(ctx); + if (ret < 0) + goto fail; + + return 0; + +fail: + dev_err(dev, "failed to resume\n"); + regulator_disable(ctx->power); + + return ret; +} + +static const struct dev_pm_ops sii9234_pm_ops = { + .runtime_suspend = sii9234_runtime_suspend, + .runtime_resume = sii9234_runtime_resume, +}; + +static int sii9234_s_power(struct v4l2_subdev *sd, int on) +{ + struct sii9234_context *ctx = sd_to_context(sd); + int ret; + + if (on) + ret = pm_runtime_get_sync(&ctx->client->dev); + else + ret = pm_runtime_put(&ctx->client->dev); + /* only values < 0 indicate errors */ + return IS_ERR_VALUE(ret) ? ret : 0; +} + +static int sii9234_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct sii9234_context *ctx = sd_to_context(sd); + + /* (dis/en)able TDMS output */ + sii9234_writeb_mask(ctx->client, 0x1a, enable ? 0 : ~0 , 1 << 4); + return 0; +} + +static const struct v4l2_subdev_core_ops sii9234_core_ops = { + .s_power = sii9234_s_power, +}; + +static const struct v4l2_subdev_video_ops sii9234_video_ops = { + .s_stream = sii9234_s_stream, +}; + +static const struct v4l2_subdev_ops sii9234_ops = { + .core = &sii9234_core_ops, + .video = &sii9234_video_ops, +}; + +static int __devinit sii9234_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct sii9234_platform_data *pdata = dev->platform_data; + struct sii9234_context *ctx; + int ret; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + dev_err(dev, "out of memory\n"); + ret = -ENOMEM; + goto fail; + } + ctx->client = client; + + ctx->power = regulator_get(dev, "hdmi-en"); + if (IS_ERR(ctx->power)) { + dev_err(dev, "failed to acquire regulator hdmi-en\n"); + ret = PTR_ERR(ctx->power); + goto fail_ctx; + } + + ctx->gpio_n_reset = pdata->gpio_n_reset; + ret = gpio_request(ctx->gpio_n_reset, "MHL_RST"); + if (ret) { + dev_err(dev, "failed to acquire MHL_RST gpio\n"); + goto fail_power; + } + + v4l2_i2c_subdev_init(&ctx->sd, client, &sii9234_ops); + + pm_runtime_enable(dev); + + /* enable device */ + ret = pm_runtime_get_sync(dev); + if (ret) + goto fail_pm; + + /* verify chip version */ + ret = sii9234_verify_version(client); + if (ret) + goto fail_pm_get; + + /* stop processing */ + pm_runtime_put(dev); + + dev_info(dev, "probe successful\n"); + + return 0; + +fail_pm_get: + pm_runtime_put_sync(dev); + +fail_pm: + pm_runtime_disable(dev); + gpio_free(ctx->gpio_n_reset); + +fail_power: + regulator_put(ctx->power); + +fail_ctx: + kfree(ctx); + +fail: + dev_err(dev, "probe failed\n"); + + return ret; +} + +static int __devexit sii9234_remove(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct sii9234_context *ctx = sd_to_context(sd); + + pm_runtime_disable(dev); + gpio_free(ctx->gpio_n_reset); + regulator_put(ctx->power); + kfree(ctx); + + dev_info(dev, "remove successful\n"); + + return 0; +} + + +static const struct i2c_device_id sii9234_id[] = { + { "SII9234", 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, sii9234_id); +static struct i2c_driver sii9234_driver = { + .driver = { + .name = "sii9234", + .owner = THIS_MODULE, + .pm = &sii9234_pm_ops, + }, + .probe = sii9234_probe, + .remove = __devexit_p(sii9234_remove), + .id_table = sii9234_id, +}; + +static int __init sii9234_init(void) +{ + return i2c_add_driver(&sii9234_driver); +} +module_init(sii9234_init); + +static void __exit sii9234_exit(void) +{ + i2c_del_driver(&sii9234_driver); +} +module_exit(sii9234_exit); diff --git a/drivers/media/video/saa6588.c b/drivers/media/video/saa6588.c index 99a2ac16f9e5..0caac50d7cf4 100644 --- a/drivers/media/video/saa6588.c +++ b/drivers/media/video/saa6588.c @@ -539,15 +539,4 @@ static struct i2c_driver saa6588_driver = { .id_table = saa6588_id, }; -static __init int init_saa6588(void) -{ - return i2c_add_driver(&saa6588_driver); -} - -static __exit void exit_saa6588(void) -{ - i2c_del_driver(&saa6588_driver); -} - -module_init(init_saa6588); -module_exit(exit_saa6588); +module_i2c_driver(saa6588_driver); diff --git a/drivers/media/video/saa7110.c b/drivers/media/video/saa7110.c index 99664205ef4e..51cd4c8f0520 100644 --- a/drivers/media/video/saa7110.c +++ b/drivers/media/video/saa7110.c @@ -491,15 +491,4 @@ static struct i2c_driver saa7110_driver = { .id_table = saa7110_id, }; -static __init int init_saa7110(void) -{ - return i2c_add_driver(&saa7110_driver); -} - -static __exit void exit_saa7110(void) -{ - i2c_del_driver(&saa7110_driver); -} - -module_init(init_saa7110); -module_exit(exit_saa7110); +module_i2c_driver(saa7110_driver); diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index 0ef5484696b6..2107336cd836 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -1724,15 +1724,4 @@ static struct i2c_driver saa711x_driver = { .id_table = saa711x_id, }; -static __init int init_saa711x(void) -{ - return i2c_add_driver(&saa711x_driver); -} - -static __exit void exit_saa711x(void) -{ - i2c_del_driver(&saa711x_driver); -} - -module_init(init_saa711x); -module_exit(exit_saa711x); +module_i2c_driver(saa711x_driver); diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c index ad964616c9d2..39c90b08eea8 100644 --- a/drivers/media/video/saa7127.c +++ b/drivers/media/video/saa7127.c @@ -852,15 +852,4 @@ static struct i2c_driver saa7127_driver = { .id_table = saa7127_id, }; -static __init int init_saa7127(void) -{ - return i2c_add_driver(&saa7127_driver); -} - -static __exit void exit_saa7127(void) -{ - i2c_del_driver(&saa7127_driver); -} - -module_init(init_saa7127); -module_exit(exit_saa7127); +module_i2c_driver(saa7127_driver); diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile index a646ccf51696..da3899329f52 100644 --- a/drivers/media/video/saa7134/Makefile +++ b/drivers/media/video/saa7134/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o -ccflags-y += -Idrivers/media/video -ccflags-y += -Idrivers/media/common/tuners -ccflags-y += -Idrivers/media/dvb/dvb-core -ccflags-y += -Idrivers/media/dvb/frontends +ccflags-y += -I$(srctree)/drivers/media/video +ccflags-y += -I$(srctree)/drivers/media/common/tuners +ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core +ccflags-y += -I$(srctree)/drivers/media/dvb/frontends diff --git a/drivers/media/video/saa7134/saa6752hs.c b/drivers/media/video/saa7134/saa6752hs.c index f9f29cc93a8a..f147b05bd860 100644 --- a/drivers/media/video/saa7134/saa6752hs.c +++ b/drivers/media/video/saa7134/saa6752hs.c @@ -1001,18 +1001,7 @@ static struct i2c_driver saa6752hs_driver = { .id_table = saa6752hs_id, }; -static __init int init_saa6752hs(void) -{ - return i2c_add_driver(&saa6752hs_driver); -} - -static __exit void exit_saa6752hs(void) -{ - i2c_del_driver(&saa6752hs_driver); -} - -module_init(init_saa6752hs); -module_exit(exit_saa6752hs); +module_i2c_driver(saa6752hs_driver); /* * Overrides for Emacs so that we follow Linus's tabbing style. diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 065d0f6be4a0..53aae5968ffb 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -33,6 +33,7 @@ #include "tea5767.h" #include "tda18271.h" #include "xc5000.h" +#include "s5h1411.h" /* commly used strings */ static char name_mute[] = "mute"; @@ -5712,6 +5713,36 @@ struct saa7134_board saa7134_boards[] = { .amux = LINE1, } }, }, + [SAA7134_BOARD_KWORLD_PC150U] = { + .name = "Kworld PC150-U", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 1 << 21, + .ts_type = SAA7134_MPEG_TS_PARALLEL, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0000000, + }, + }, }; @@ -6306,6 +6337,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .driver_data = SAA7134_BOARD_KWORLD_ATSC110, /* ATSC 115 */ },{ .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */ + .subvendor = 0x17de, + .subdevice = 0xa134, + .driver_data = SAA7134_BOARD_KWORLD_PC150U, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, .subvendor = 0x1461, .subdevice = 0x7360, @@ -7134,6 +7171,23 @@ static inline int saa7134_kworld_sbtvd_toggle_agc(struct saa7134_dev *dev, return 0; } +static int saa7134_kworld_pc150u_toggle_agc(struct saa7134_dev *dev, + enum tda18271_mode mode) +{ + switch (mode) { + case TDA18271_ANALOG: + saa7134_set_gpio(dev, 18, 0); + break; + case TDA18271_DIGITAL: + saa7134_set_gpio(dev, 18, 1); + msleep(30); + break; + default: + return -EINVAL; + } + return 0; +} + static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev, int command, int arg) { @@ -7150,6 +7204,9 @@ static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev, case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG: ret = saa7134_kworld_sbtvd_toggle_agc(dev, arg); break; + case SAA7134_BOARD_KWORLD_PC150U: + ret = saa7134_kworld_pc150u_toggle_agc(dev, arg); + break; default: break; } @@ -7171,6 +7228,7 @@ static int saa7134_tda8290_callback(struct saa7134_dev *dev, case SAA7134_BOARD_HAUPPAUGE_HVR1120: case SAA7134_BOARD_AVERMEDIA_M733A: case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG: + case SAA7134_BOARD_KWORLD_PC150U: case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2: /* tda8290 + tda18271 */ ret = saa7134_tda8290_18271_callback(dev, command, arg); @@ -7452,6 +7510,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_X7: case SAA7134_BOARD_BEHOLD_H7: case SAA7134_BOARD_BEHOLD_A7: + case SAA7134_BOARD_KWORLD_PC150U: dev->has_remote = SAA7134_REMOTE_I2C; break; case SAA7134_BOARD_AVERMEDIA_A169_B: diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 089fa0fb5c94..aaa5c97a7216 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -61,6 +61,7 @@ #include "zl10036.h" #include "zl10039.h" #include "mt312.h" +#include "s5h1411.h" MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); MODULE_LICENSE("GPL"); @@ -1158,6 +1159,33 @@ static struct tda18271_config prohdtv_pro2_tda18271_config = { .output_opt = TDA18271_OUTPUT_LT_OFF, }; +static struct tda18271_std_map kworld_tda18271_std_map = { + .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 3, + .if_lvl = 6, .rfagc_top = 0x37 }, + .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0, + .if_lvl = 6, .rfagc_top = 0x37 }, +}; + +static struct tda18271_config kworld_pc150u_tda18271_config = { + .std_map = &kworld_tda18271_std_map, + .gate = TDA18271_GATE_ANALOG, + .output_opt = TDA18271_OUTPUT_LT_OFF, + .config = 3, /* Use tuner callback for AGC */ + .rf_cal_on_startup = 1 +}; + +static struct s5h1411_config kworld_s5h1411_config = { + .output_mode = S5H1411_PARALLEL_OUTPUT, + .gpio = S5H1411_GPIO_OFF, + .qam_if = S5H1411_IF_4000, + .vsb_if = S5H1411_IF_3250, + .inversion = S5H1411_INVERSION_ON, + .status_mode = S5H1411_DEMODLOCKING, + .mpeg_timing = + S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, +}; + + /* ================================================================== * Core code */ @@ -1438,6 +1466,22 @@ static int dvb_init(struct saa7134_dev *dev) &dev->i2c_adap, 0x61, TUNER_PHILIPS_TUV1236D); break; + case SAA7134_BOARD_KWORLD_PC150U: + saa7134_set_gpio(dev, 18, 1); /* Switch to digital mode */ + saa7134_tuner_callback(dev, 0, + TDA18271_CALLBACK_CMD_AGC_ENABLE, 1); + fe0->dvb.frontend = dvb_attach(s5h1411_attach, + &kworld_s5h1411_config, + &dev->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x4b, + &tda829x_no_probe); + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap, + &kworld_pc150u_tda18271_config); + } + break; case SAA7134_BOARD_FLYDVBS_LR300: fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap); diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c index 2d3f6d265bbf..a176ec3285e0 100644 --- a/drivers/media/video/saa7134/saa7134-i2c.c +++ b/drivers/media/video/saa7134/saa7134-i2c.c @@ -254,7 +254,9 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap, addr = msgs[i].addr << 1; if (msgs[i].flags & I2C_M_RD) addr |= 1; - if (i > 0 && msgs[i].flags & I2C_M_RD && msgs[i].addr != 0x40) { + if (i > 0 && msgs[i].flags & + I2C_M_RD && msgs[i].addr != 0x40 && + msgs[i].addr != 0x19) { /* workaround for a saa7134 i2c bug * needed to talk to the mt352 demux * thanks to pinnacle for the hint */ @@ -279,6 +281,16 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap, d1printk("%02x", rc); msgs[i].buf[byte] = rc; } + /* discard mysterious extra byte when reading + from Samsung S5H1411. i2c bus gets error + if we do not. */ + if (0x19 == msgs[i].addr) { + d1printk(" ?"); + rc = i2c_recv_byte(dev); + if (rc < 0) + goto err; + d1printk("%02x", rc); + } } else { /* write bytes */ d2printk("write bytes\n"); diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 22ecd7297d2d..48d2878699b7 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -210,6 +210,54 @@ static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *ir_key, return 1; } +/* copied and modified from get_key_msi_tvanywhere_plus() */ +static int get_key_kworld_pc150u(struct IR_i2c *ir, u32 *ir_key, + u32 *ir_raw) +{ + unsigned char b; + unsigned int gpio; + + /* <dev> is needed to access GPIO. Used by the saa_readl macro. */ + struct saa7134_dev *dev = ir->c->adapter->algo_data; + if (dev == NULL) { + i2cdprintk("get_key_kworld_pc150u: " + "ir->c->adapter->algo_data is NULL!\n"); + return -EIO; + } + + /* rising SAA7134_GPIO_GPRESCAN reads the status */ + + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + + gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2); + + /* GPIO&0x100 is pulsed low when a button is pressed. Don't do + I2C receive if gpio&0x100 is not low. */ + + if (gpio & 0x100) + return 0; /* No button press */ + + /* GPIO says there is a button press. Get it. */ + + if (1 != i2c_master_recv(ir->c, &b, 1)) { + i2cdprintk("read error\n"); + return -EIO; + } + + /* No button press */ + + if (b == 0xff) + return 0; + + /* Button pressed */ + + dprintk("get_key_kworld_pc150u: Key = 0x%02X\n", b); + *ir_key = b; + *ir_raw = b; + return 1; +} + static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { unsigned char b; @@ -901,6 +949,21 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) msg_msi.addr, dev->i2c_adap.name, (1 == rc) ? "yes" : "no"); break; + case SAA7134_BOARD_KWORLD_PC150U: + /* copied and modified from MSI TV@nywhere Plus */ + dev->init_data.name = "Kworld PC150-U"; + dev->init_data.get_key = get_key_kworld_pc150u; + dev->init_data.ir_codes = RC_MAP_KWORLD_PC150U; + info.addr = 0x30; + /* MSI TV@nywhere Plus controller doesn't seem to + respond to probes unless we read something from + an existing device. Weird... + REVISIT: might no longer be needed */ + rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1); + dprintk("probe 0x%02x @ %s: %s\n", + msg_msi.addr, dev->i2c_adap.name, + (1 == rc) ? "yes" : "no"); + break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: dev->init_data.name = "HVR 1110"; dev->init_data.get_key = get_key_hvr1110; diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 42fba4f93c72..f625060e6a0f 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -126,8 +126,8 @@ struct saa7134_card_ir { unsigned users; u32 polling; - u32 last_gpio; - u32 mask_keycode, mask_keydown, mask_keyup; + u32 last_gpio; + u32 mask_keycode, mask_keydown, mask_keyup; bool running; bool active; @@ -331,6 +331,7 @@ struct saa7134_card_ir { #define SAA7134_BOARD_BEHOLD_501 186 #define SAA7134_BOARD_BEHOLD_503FM 187 #define SAA7134_BOARD_SENSORAY811_911 188 +#define SAA7134_BOARD_KWORLD_PC150U 189 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 diff --git a/drivers/media/video/saa7164/Makefile b/drivers/media/video/saa7164/Makefile index ecd5811dc486..068443af30c8 100644 --- a/drivers/media/video/saa7164/Makefile +++ b/drivers/media/video/saa7164/Makefile @@ -4,9 +4,9 @@ saa7164-objs := saa7164-cards.o saa7164-core.o saa7164-i2c.o saa7164-dvb.o \ obj-$(CONFIG_VIDEO_SAA7164) += saa7164.o -ccflags-y += -Idrivers/media/video -ccflags-y += -Idrivers/media/common/tuners -ccflags-y += -Idrivers/media/dvb/dvb-core -ccflags-y += -Idrivers/media/dvb/frontends +ccflags-y += -I$(srctree)/drivers/media/video +ccflags-y += -I$(srctree)/drivers/media/common/tuners +ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core +ccflags-y += -I$(srctree)/drivers/media/dvb/frontends ccflags-y += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/video/saa7164/saa7164-encoder.c b/drivers/media/video/saa7164/saa7164-encoder.c index 2fd38a01887f..a9ed686ad08a 100644 --- a/drivers/media/video/saa7164/saa7164-encoder.c +++ b/drivers/media/video/saa7164/saa7164-encoder.c @@ -791,11 +791,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int vidioc_log_status(struct file *file, void *priv) -{ - return 0; -} - static int fill_queryctrl(struct saa7164_encoder_params *params, struct v4l2_queryctrl *c) { @@ -1347,7 +1342,6 @@ static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, - .vidioc_log_status = vidioc_log_status, .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_chip_ident = saa7164_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG diff --git a/drivers/media/video/saa7164/saa7164-vbi.c b/drivers/media/video/saa7164/saa7164-vbi.c index e2e034158718..273cf807401c 100644 --- a/drivers/media/video/saa7164/saa7164-vbi.c +++ b/drivers/media/video/saa7164/saa7164-vbi.c @@ -730,11 +730,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int vidioc_log_status(struct file *file, void *priv) -{ - return 0; -} - static int fill_queryctrl(struct saa7164_vbi_params *params, struct v4l2_queryctrl *c) { @@ -1256,7 +1251,6 @@ static const struct v4l2_ioctl_ops vbi_ioctl_ops = { .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, - .vidioc_log_status = vidioc_log_status, .vidioc_queryctrl = vidioc_queryctrl, #if 0 .vidioc_g_chip_ident = saa7164_g_chip_ident, diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c index b6172c2c517e..1e84466515aa 100644 --- a/drivers/media/video/saa717x.c +++ b/drivers/media/video/saa717x.c @@ -1375,15 +1375,4 @@ static struct i2c_driver saa717x_driver = { .id_table = saa717x_id, }; -static __init int init_saa717x(void) -{ - return i2c_add_driver(&saa717x_driver); -} - -static __exit void exit_saa717x(void) -{ - i2c_del_driver(&saa717x_driver); -} - -module_init(init_saa717x); -module_exit(exit_saa717x); +module_i2c_driver(saa717x_driver); diff --git a/drivers/media/video/saa7185.c b/drivers/media/video/saa7185.c index 96f56c2f11f3..2c6b65c76e2b 100644 --- a/drivers/media/video/saa7185.c +++ b/drivers/media/video/saa7185.c @@ -374,15 +374,4 @@ static struct i2c_driver saa7185_driver = { .id_table = saa7185_id, }; -static __init int init_saa7185(void) -{ - return i2c_add_driver(&saa7185_driver); -} - -static __exit void exit_saa7185(void) -{ - i2c_del_driver(&saa7185_driver); -} - -module_init(init_saa7185); -module_exit(exit_saa7185); +module_i2c_driver(saa7185_driver); diff --git a/drivers/media/video/saa7191.c b/drivers/media/video/saa7191.c index 211fa25a1239..d7d1670e0ca3 100644 --- a/drivers/media/video/saa7191.c +++ b/drivers/media/video/saa7191.c @@ -656,15 +656,4 @@ static struct i2c_driver saa7191_driver = { .id_table = saa7191_id, }; -static __init int init_saa7191(void) -{ - return i2c_add_driver(&saa7191_driver); -} - -static __exit void exit_saa7191(void) -{ - i2c_del_driver(&saa7191_driver); -} - -module_init(init_saa7191); -module_exit(exit_saa7191); +module_i2c_driver(saa7191_driver); diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index f854d85a387c..424dfacd263a 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -112,6 +112,10 @@ struct sh_mobile_ceu_dev { u32 cflcr; + /* static max sizes either from platform data or default */ + int max_width; + int max_height; + enum v4l2_field field; int sequence; @@ -1081,7 +1085,15 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int if (ret < 0) return ret; - while ((mf.width > 2560 || mf.height > 1920) && shift < 4) { + /* + * All currently existing CEU implementations support 2560x1920 + * or larger frames. If the sensor is proposing too big a frame, + * don't bother with possibly supportred by the CEU larger + * sizes, just try VGA multiples. If needed, this can be + * adjusted in the future. + */ + while ((mf.width > pcdev->max_width || + mf.height > pcdev->max_height) && shift < 4) { /* Try 2560x1920, 1280x960, 640x480, 320x240 */ mf.width = 2560 >> shift; mf.height = 1920 >> shift; @@ -1377,6 +1389,8 @@ static int client_s_crop(struct soc_camera_device *icd, struct v4l2_crop *crop, static int client_s_fmt(struct soc_camera_device *icd, struct v4l2_mbus_framefmt *mf, bool ceu_can_scale) { + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; struct sh_mobile_ceu_cam *cam = icd->host_priv; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct device *dev = icd->parent; @@ -1410,8 +1424,8 @@ static int client_s_fmt(struct soc_camera_device *icd, if (ret < 0) return ret; - max_width = min(cap.bounds.width, 2560); - max_height = min(cap.bounds.height, 1920); + max_width = min(cap.bounds.width, pcdev->max_width); + max_height = min(cap.bounds.height, pcdev->max_height); /* Camera set a format, but geometry is not precise, try to improve */ tmp_w = mf->width; @@ -1551,7 +1565,7 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, if (ret < 0) return ret; - if (mf.width > 2560 || mf.height > 1920) + if (mf.width > pcdev->max_width || mf.height > pcdev->max_height) return -EINVAL; /* 4. Calculate camera scales */ @@ -1834,6 +1848,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); @@ -1854,8 +1870,8 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, /* FIXME: calculate using depth and bus width */ /* CFSZR requires height and width to be 4-pixel aligned */ - v4l_bound_align_image(&pix->width, 2, 2560, 2, - &pix->height, 4, 1920, 2, 0); + v4l_bound_align_image(&pix->width, 2, pcdev->max_width, 2, + &pix->height, 4, pcdev->max_height, 2, 0); width = pix->width; height = pix->height; @@ -1890,8 +1906,8 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, * requested a bigger rectangle, it will not return a * smaller one. */ - mf.width = 2560; - mf.height = 1920; + mf.width = pcdev->max_width; + mf.height = pcdev->max_height; ret = v4l2_device_call_until_err(sd->v4l2_dev, soc_camera_grp_id(icd), video, try_mbus_fmt, &mf); @@ -2082,6 +2098,9 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) goto exit_kfree; } + pcdev->max_width = pcdev->pdata->max_width ? : 2560; + pcdev->max_height = pcdev->pdata->max_height ? : 1920; + base = ioremap_nocache(res->start, resource_size(res)); if (!base) { err = -ENXIO; diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index b82710745ba8..eb25756a07af 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -526,10 +526,6 @@ static int soc_camera_open(struct file *file) }, }; - ret = soc_camera_power_on(icd, icl); - if (ret < 0) - goto epower; - /* The camera could have been already on, try to reset */ if (icl->reset) icl->reset(icd->pdev); @@ -540,6 +536,10 @@ static int soc_camera_open(struct file *file) goto eiciadd; } + ret = soc_camera_power_on(icd, icl); + if (ret < 0) + goto epower; + pm_runtime_enable(&icd->vdev->dev); ret = pm_runtime_resume(&icd->vdev->dev); if (ret < 0 && ret != -ENOSYS) @@ -578,10 +578,10 @@ einitvb: esfmt: pm_runtime_disable(&icd->vdev->dev); eresume: - ici->ops->remove(icd); -eiciadd: soc_camera_power_off(icd, icl); epower: + ici->ops->remove(icd); +eiciadd: icd->use_count--; module_put(ici->ops->owner); @@ -1050,6 +1050,14 @@ static int soc_camera_probe(struct soc_camera_device *icd) if (ret < 0) goto ereg; + /* The camera could have been already on, try to reset */ + if (icl->reset) + icl->reset(icd->pdev); + + ret = ici->ops->add(icd); + if (ret < 0) + goto eadd; + /* * This will not yet call v4l2_subdev_core_ops::s_power(1), because the * subdevice has not been initialised yet. We'll have to call it once @@ -1060,14 +1068,6 @@ static int soc_camera_probe(struct soc_camera_device *icd) if (ret < 0) goto epower; - /* The camera could have been already on, try to reset */ - if (icl->reset) - icl->reset(icd->pdev); - - ret = ici->ops->add(icd); - if (ret < 0) - goto eadd; - /* Must have icd->vdev before registering the device */ ret = video_dev_create(icd); if (ret < 0) @@ -1165,10 +1165,10 @@ eadddev: video_device_release(icd->vdev); icd->vdev = NULL; evdc: - ici->ops->remove(icd); -eadd: soc_camera_power_off(icd, icl); epower: + ici->ops->remove(icd); +eadd: regulator_bulk_free(icl->num_regulators, icl->regulators); ereg: v4l2_ctrl_handler_free(&icd->ctrl_handler); diff --git a/drivers/media/video/sr030pc30.c b/drivers/media/video/sr030pc30.c index d1b07aceaf94..e9d95bda2ab1 100644 --- a/drivers/media/video/sr030pc30.c +++ b/drivers/media/video/sr030pc30.c @@ -864,18 +864,7 @@ static struct i2c_driver sr030pc30_i2c_driver = { .id_table = sr030pc30_id, }; -static int __init sr030pc30_init(void) -{ - return i2c_add_driver(&sr030pc30_i2c_driver); -} - -static void __exit sr030pc30_exit(void) -{ - i2c_del_driver(&sr030pc30_i2c_driver); -} - -module_init(sr030pc30_init); -module_exit(sr030pc30_exit); +module_i2c_driver(sr030pc30_i2c_driver); MODULE_DESCRIPTION("Siliconfile SR030PC30 camera driver"); MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c index bd218545da9c..f7707e65761e 100644 --- a/drivers/media/video/tda7432.c +++ b/drivers/media/video/tda7432.c @@ -482,15 +482,4 @@ static struct i2c_driver tda7432_driver = { .id_table = tda7432_id, }; -static __init int init_tda7432(void) -{ - return i2c_add_driver(&tda7432_driver); -} - -static __exit void exit_tda7432(void) -{ - i2c_del_driver(&tda7432_driver); -} - -module_init(init_tda7432); -module_exit(exit_tda7432); +module_i2c_driver(tda7432_driver); diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c index 22fa8202d5ca..465d7086babf 100644 --- a/drivers/media/video/tda9840.c +++ b/drivers/media/video/tda9840.c @@ -208,15 +208,4 @@ static struct i2c_driver tda9840_driver = { .id_table = tda9840_id, }; -static __init int init_tda9840(void) -{ - return i2c_add_driver(&tda9840_driver); -} - -static __exit void exit_tda9840(void) -{ - i2c_del_driver(&tda9840_driver); -} - -module_init(init_tda9840); -module_exit(exit_tda9840); +module_i2c_driver(tda9840_driver); diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c index 827425c5b866..d1d6ea1dd273 100644 --- a/drivers/media/video/tea6415c.c +++ b/drivers/media/video/tea6415c.c @@ -184,15 +184,4 @@ static struct i2c_driver tea6415c_driver = { .id_table = tea6415c_id, }; -static __init int init_tea6415c(void) -{ - return i2c_add_driver(&tea6415c_driver); -} - -static __exit void exit_tea6415c(void) -{ - i2c_del_driver(&tea6415c_driver); -} - -module_init(init_tea6415c); -module_exit(exit_tea6415c); +module_i2c_driver(tea6415c_driver); diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c index f350b6c24500..38757217a074 100644 --- a/drivers/media/video/tea6420.c +++ b/drivers/media/video/tea6420.c @@ -166,15 +166,4 @@ static struct i2c_driver tea6420_driver = { .id_table = tea6420_id, }; -static __init int init_tea6420(void) -{ - return i2c_add_driver(&tea6420_driver); -} - -static __exit void exit_tea6420(void) -{ - i2c_del_driver(&tea6420_driver); -} - -module_init(init_tea6420); -module_exit(exit_tea6420); +module_i2c_driver(tea6420_driver); diff --git a/drivers/media/video/ths7303.c b/drivers/media/video/ths7303.c index 61b1dd118364..e5c0eedebc58 100644 --- a/drivers/media/video/ths7303.c +++ b/drivers/media/video/ths7303.c @@ -137,16 +137,4 @@ static struct i2c_driver ths7303_driver = { .id_table = ths7303_id, }; -static int __init ths7303_init(void) -{ - return i2c_add_driver(&ths7303_driver); -} - -static void __exit ths7303_exit(void) -{ - i2c_del_driver(&ths7303_driver); -} - -module_init(ths7303_init); -module_exit(ths7303_exit); - +module_i2c_driver(ths7303_driver); diff --git a/drivers/media/video/tlv320aic23b.c b/drivers/media/video/tlv320aic23b.c index 286ec7e7062a..809a75a558ee 100644 --- a/drivers/media/video/tlv320aic23b.c +++ b/drivers/media/video/tlv320aic23b.c @@ -227,15 +227,4 @@ static struct i2c_driver tlv320aic23b_driver = { .id_table = tlv320aic23b_id, }; -static __init int init_tlv320aic23b(void) -{ - return i2c_add_driver(&tlv320aic23b_driver); -} - -static __exit void exit_tlv320aic23b(void) -{ - i2c_del_driver(&tlv320aic23b_driver); -} - -module_init(init_tlv320aic23b); -module_exit(exit_tlv320aic23b); +module_i2c_driver(tlv320aic23b_driver); diff --git a/drivers/media/video/tm6000/tm6000-input.c b/drivers/media/video/tm6000/tm6000-input.c index 7844607dd45a..859eb90e4d56 100644 --- a/drivers/media/video/tm6000/tm6000-input.c +++ b/drivers/media/video/tm6000/tm6000-input.c @@ -481,8 +481,6 @@ int tm6000_ir_fini(struct tm6000_core *dev) dprintk(2, "%s\n",__func__); - rc_unregister_device(ir->rc); - if (!ir->polling) __tm6000_ir_int_stop(ir->rc); @@ -492,6 +490,7 @@ int tm6000_ir_fini(struct tm6000_core *dev) tm6000_flash_led(dev, 0); ir->pwled = 0; + rc_unregister_device(ir->rc); kfree(ir); dev->ir = NULL; diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 4059ea178c2d..a5c6397ad591 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -380,6 +380,21 @@ static void set_type(struct i2c_client *c, unsigned int type, tune_now = 0; break; } + case TUNER_XC5000C: + { + struct xc5000_config xc5000c_cfg = { + .i2c_address = t->i2c->addr, + /* if_khz will be set at dvb_attach() */ + .if_khz = 0, + .chip_id = XC5000C, + }; + + if (!dvb_attach(xc5000_attach, + &t->fe, t->i2c->adapter, &xc5000c_cfg)) + goto attach_failed; + tune_now = 0; + break; + } case TUNER_NXP_TDA18271: { struct tda18271_config cfg = { @@ -1314,18 +1329,7 @@ static struct i2c_driver tuner_driver = { .id_table = tuner_id, }; -static __init int init_tuner(void) -{ - return i2c_add_driver(&tuner_driver); -} - -static __exit void exit_tuner(void) -{ - i2c_del_driver(&tuner_driver); -} - -module_init(init_tuner); -module_exit(exit_tuner); +module_i2c_driver(tuner_driver); MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners"); MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index f22dbef9b95b..c5b1a7365e4f 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -2078,15 +2078,4 @@ static struct i2c_driver tvaudio_driver = { .id_table = tvaudio_id, }; -static __init int init_tvaudio(void) -{ - return i2c_add_driver(&tvaudio_driver); -} - -static __exit void exit_tvaudio(void) -{ - i2c_del_driver(&tvaudio_driver); -} - -module_init(init_tvaudio); -module_exit(exit_tvaudio); +module_i2c_driver(tvaudio_driver); diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index 6103d1b1081e..3b6cf034976a 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -286,8 +286,16 @@ hauppauge_tuner[] = { TUNER_ABSENT, "MaxLinear 301"}, { TUNER_ABSENT, "Mirics MSi001"}, { TUNER_ABSENT, "MaxLinear MxL241SF"}, - { TUNER_ABSENT, "Xceive XC5000C"}, + { TUNER_XC5000C, "Xceive XC5000C"}, { TUNER_ABSENT, "Montage M68TS2020"}, + { TUNER_ABSENT, "Siano SMS1530"}, + { TUNER_ABSENT, "Dibcom 7090"}, + { TUNER_ABSENT, "Xceive XC5200C"}, + { TUNER_ABSENT, "NXP 18273"}, + { TUNER_ABSENT, "Montage M88TS2022"}, + /* 180-189 */ + { TUNER_ABSENT, "NXP 18272M"}, + { TUNER_ABSENT, "NXP 18272S"}, }; /* Use V4L2_IDENT_AMBIGUOUS for those audio 'chips' that are diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c index dd26cacd0556..cd615c1d6011 100644 --- a/drivers/media/video/tvp514x.c +++ b/drivers/media/video/tvp514x.c @@ -1163,15 +1163,4 @@ static struct i2c_driver tvp514x_driver = { .id_table = tvp514x_id, }; -static int __init tvp514x_init(void) -{ - return i2c_add_driver(&tvp514x_driver); -} - -static void __exit tvp514x_exit(void) -{ - i2c_del_driver(&tvp514x_driver); -} - -module_init(tvp514x_init); -module_exit(tvp514x_exit); +module_i2c_driver(tvp514x_driver); diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index 6be9910a6e24..1326e11cf4a9 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -17,6 +17,13 @@ #include "tvp5150_reg.h" +#define TVP5150_H_MAX 720 +#define TVP5150_V_MAX_525_60 480 +#define TVP5150_V_MAX_OTHERS 576 +#define TVP5150_MAX_CROP_LEFT 511 +#define TVP5150_MAX_CROP_TOP 127 +#define TVP5150_CROP_SHIFT 2 + MODULE_DESCRIPTION("Texas Instruments TVP5150A video decoder driver"); MODULE_AUTHOR("Mauro Carvalho Chehab"); MODULE_LICENSE("GPL"); @@ -29,6 +36,7 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)"); struct tvp5150 { struct v4l2_subdev sd; struct v4l2_ctrl_handler hdl; + struct v4l2_rect rect; v4l2_std_id norm; /* Current set standard */ u32 input; @@ -732,6 +740,13 @@ static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std) if (decoder->norm == std) return 0; + /* Change cropping height limits */ + if (std & V4L2_STD_525_60) + decoder->rect.height = TVP5150_V_MAX_525_60; + else + decoder->rect.height = TVP5150_V_MAX_OTHERS; + + return tvp5150_set_std(sd, std); } @@ -828,11 +843,8 @@ static int tvp5150_mbus_fmt(struct v4l2_subdev *sd, else std = decoder->norm; - f->width = 720; - if (std & V4L2_STD_525_60) - f->height = 480; - else - f->height = 576; + f->width = decoder->rect.width; + f->height = decoder->rect.height; f->code = V4L2_MBUS_FMT_YUYV8_2X8; f->field = V4L2_FIELD_SEQ_TB; @@ -843,6 +855,99 @@ static int tvp5150_mbus_fmt(struct v4l2_subdev *sd, return 0; } +static int tvp5150_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct v4l2_rect rect = a->c; + struct tvp5150 *decoder = to_tvp5150(sd); + v4l2_std_id std; + int hmax; + + v4l2_dbg(1, debug, sd, "%s left=%d, top=%d, width=%d, height=%d\n", + __func__, rect.left, rect.top, rect.width, rect.height); + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* tvp5150 has some special limits */ + rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT); + rect.width = clamp(rect.width, + TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left, + TVP5150_H_MAX - rect.left); + rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP); + + /* Calculate height based on current standard */ + if (decoder->norm == V4L2_STD_ALL) + std = tvp5150_read_std(sd); + else + std = decoder->norm; + + if (std & V4L2_STD_525_60) + hmax = TVP5150_V_MAX_525_60; + else + hmax = TVP5150_V_MAX_OTHERS; + + rect.height = clamp(rect.height, + hmax - TVP5150_MAX_CROP_TOP - rect.top, + hmax - rect.top); + + tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top); + tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, + rect.top + rect.height - hmax); + tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_MSB, + rect.left >> TVP5150_CROP_SHIFT); + tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_LSB, + rect.left | (1 << TVP5150_CROP_SHIFT)); + tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_MSB, + (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >> + TVP5150_CROP_SHIFT); + tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_LSB, + rect.left + rect.width - TVP5150_MAX_CROP_LEFT); + + decoder->rect = rect; + + return 0; +} + +static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); + + a->c = decoder->rect; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); + v4l2_std_id std; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = TVP5150_H_MAX; + + /* Calculate height based on current standard */ + if (decoder->norm == V4L2_STD_ALL) + std = tvp5150_read_std(sd); + else + std = decoder->norm; + + if (std & V4L2_STD_525_60) + a->bounds.height = TVP5150_V_MAX_525_60; + else + a->bounds.height = TVP5150_V_MAX_OTHERS; + + a->defrect = a->bounds; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + /**************************************************************************** I2C Command ****************************************************************************/ @@ -998,6 +1103,10 @@ static const struct v4l2_subdev_video_ops tvp5150_video_ops = { .enum_mbus_fmt = tvp5150_enum_mbus_fmt, .s_mbus_fmt = tvp5150_mbus_fmt, .try_mbus_fmt = tvp5150_mbus_fmt, + .g_mbus_fmt = tvp5150_mbus_fmt, + .s_crop = tvp5150_s_crop, + .g_crop = tvp5150_g_crop, + .cropcap = tvp5150_cropcap, }; static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = { @@ -1083,6 +1192,15 @@ static int tvp5150_probe(struct i2c_client *c, } v4l2_ctrl_handler_setup(&core->hdl); + /* Default is no cropping */ + core->rect.top = 0; + if (tvp5150_read_std(sd) & V4L2_STD_525_60) + core->rect.height = TVP5150_V_MAX_525_60; + else + core->rect.height = TVP5150_V_MAX_OTHERS; + core->rect.left = 0; + core->rect.width = TVP5150_H_MAX; + if (debug > 1) tvp5150_log_status(sd); return 0; @@ -1121,15 +1239,4 @@ static struct i2c_driver tvp5150_driver = { .id_table = tvp5150_id, }; -static __init int init_tvp5150(void) -{ - return i2c_add_driver(&tvp5150_driver); -} - -static __exit void exit_tvp5150(void) -{ - i2c_del_driver(&tvp5150_driver); -} - -module_init(init_tvp5150); -module_exit(exit_tvp5150); +module_i2c_driver(tvp5150_driver); diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c index 236c559d5f51..d7676d85c4df 100644 --- a/drivers/media/video/tvp7002.c +++ b/drivers/media/video/tvp7002.c @@ -1069,27 +1069,4 @@ static struct i2c_driver tvp7002_driver = { .id_table = tvp7002_id, }; -/* - * tvp7002_init - Initialize driver via I2C interface - * - * Register the TVP7002 driver. - * Return 0 on success or error code on failure. - */ -static int __init tvp7002_init(void) -{ - return i2c_add_driver(&tvp7002_driver); -} - -/* - * tvp7002_exit - Remove driver via I2C interface - * - * Unregister the TVP7002 driver. - * Returns nothing. - */ -static void __exit tvp7002_exit(void) -{ - i2c_del_driver(&tvp7002_driver); -} - -module_init(tvp7002_init); -module_exit(tvp7002_exit); +module_i2c_driver(tvp7002_driver); diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c index a514fa61116c..8768efb8508a 100644 --- a/drivers/media/video/tw9910.c +++ b/drivers/media/video/tw9910.c @@ -951,21 +951,7 @@ static struct i2c_driver tw9910_i2c_driver = { .id_table = tw9910_id, }; -/* - * module function - */ -static int __init tw9910_module_init(void) -{ - return i2c_add_driver(&tw9910_i2c_driver); -} - -static void __exit tw9910_module_exit(void) -{ - i2c_del_driver(&tw9910_i2c_driver); -} - -module_init(tw9910_module_init); -module_exit(tw9910_module_exit); +module_i2c_driver(tw9910_i2c_driver); MODULE_DESCRIPTION("SoC Camera driver for tw9910"); MODULE_AUTHOR("Kuninori Morimoto"); diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c index 1aab96a88203..1e7446542091 100644 --- a/drivers/media/video/upd64031a.c +++ b/drivers/media/video/upd64031a.c @@ -271,15 +271,4 @@ static struct i2c_driver upd64031a_driver = { .id_table = upd64031a_id, }; -static __init int init_upd64031a(void) -{ - return i2c_add_driver(&upd64031a_driver); -} - -static __exit void exit_upd64031a(void) -{ - i2c_del_driver(&upd64031a_driver); -} - -module_init(init_upd64031a); -module_exit(exit_upd64031a); +module_i2c_driver(upd64031a_driver); diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c index 65d065aa6091..75d6acc62018 100644 --- a/drivers/media/video/upd64083.c +++ b/drivers/media/video/upd64083.c @@ -243,15 +243,4 @@ static struct i2c_driver upd64083_driver = { .id_table = upd64083_id, }; -static __init int init_upd64083(void) -{ - return i2c_add_driver(&upd64083_driver); -} - -static __exit void exit_upd64083(void) -{ - i2c_del_driver(&upd64083_driver); -} - -module_init(init_upd64083); -module_exit(exit_upd64083); +module_i2c_driver(upd64083_driver); diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index a240d43d15d1..1d131720b6d7 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -23,6 +23,7 @@ * codec can't handle MJPEG data. */ +#include <linux/atomic.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> @@ -32,7 +33,6 @@ #include <linux/vmalloc.h> #include <linux/wait.h> #include <linux/version.h> -#include <asm/atomic.h> #include <asm/unaligned.h> #include <media/v4l2-common.h> @@ -2139,6 +2139,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Dell XPS m1530 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x05a9, + .idProduct = 0x2640, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_DEF }, /* Apple Built-In iSight */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c index 518f77d3a4d8..8f54e24e3f35 100644 --- a/drivers/media/video/uvc/uvc_queue.c +++ b/drivers/media/video/uvc/uvc_queue.c @@ -126,7 +126,7 @@ void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, int drop_corrupted) { queue->queue.type = type; - queue->queue.io_modes = VB2_MMAP; + queue->queue.io_modes = VB2_MMAP | VB2_USERPTR; queue->queue.drv_priv = queue; queue->queue.buf_struct_size = sizeof(struct uvc_buffer); queue->queue.ops = &uvc_queue_qops; diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 2ae4f880ea05..ff2cdddf9bc6 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -11,6 +11,7 @@ * */ +#include <linux/compat.h> #include <linux/kernel.h> #include <linux/version.h> #include <linux/list.h> @@ -1012,7 +1013,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) default: uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd); - return -EINVAL; + return -ENOTTY; } return ret; @@ -1030,6 +1031,207 @@ static long uvc_v4l2_ioctl(struct file *file, return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl); } +#ifdef CONFIG_COMPAT +struct uvc_xu_control_mapping32 { + __u32 id; + __u8 name[32]; + __u8 entity[16]; + __u8 selector; + + __u8 size; + __u8 offset; + __u32 v4l2_type; + __u32 data_type; + + compat_caddr_t menu_info; + __u32 menu_count; + + __u32 reserved[4]; +}; + +static int uvc_v4l2_get_xu_mapping(struct uvc_xu_control_mapping *kp, + const struct uvc_xu_control_mapping32 __user *up) +{ + struct uvc_menu_info __user *umenus; + struct uvc_menu_info __user *kmenus; + compat_caddr_t p; + + if (!access_ok(VERIFY_READ, up, sizeof(*up)) || + __copy_from_user(kp, up, offsetof(typeof(*up), menu_info)) || + __get_user(kp->menu_count, &up->menu_count)) + return -EFAULT; + + memset(kp->reserved, 0, sizeof(kp->reserved)); + + if (kp->menu_count == 0) { + kp->menu_info = NULL; + return 0; + } + + if (__get_user(p, &up->menu_info)) + return -EFAULT; + umenus = compat_ptr(p); + if (!access_ok(VERIFY_READ, umenus, kp->menu_count * sizeof(*umenus))) + return -EFAULT; + + kmenus = compat_alloc_user_space(kp->menu_count * sizeof(*kmenus)); + if (kmenus == NULL) + return -EFAULT; + kp->menu_info = kmenus; + + if (copy_in_user(kmenus, umenus, kp->menu_count * sizeof(*umenus))) + return -EFAULT; + + return 0; +} + +static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp, + struct uvc_xu_control_mapping32 __user *up) +{ + struct uvc_menu_info __user *umenus; + struct uvc_menu_info __user *kmenus = kp->menu_info; + compat_caddr_t p; + + if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || + __copy_to_user(up, kp, offsetof(typeof(*up), menu_info)) || + __put_user(kp->menu_count, &up->menu_count)) + return -EFAULT; + + __clear_user(up->reserved, sizeof(up->reserved)); + + if (kp->menu_count == 0) + return 0; + + if (get_user(p, &up->menu_info)) + return -EFAULT; + umenus = compat_ptr(p); + if (!access_ok(VERIFY_WRITE, umenus, kp->menu_count * sizeof(*umenus))) + return -EFAULT; + + if (copy_in_user(umenus, kmenus, kp->menu_count * sizeof(*umenus))) + return -EFAULT; + + return 0; +} + +struct uvc_xu_control_query32 { + __u8 unit; + __u8 selector; + __u8 query; + __u16 size; + compat_caddr_t data; +}; + +static int uvc_v4l2_get_xu_query(struct uvc_xu_control_query *kp, + const struct uvc_xu_control_query32 __user *up) +{ + u8 __user *udata; + u8 __user *kdata; + compat_caddr_t p; + + if (!access_ok(VERIFY_READ, up, sizeof(*up)) || + __copy_from_user(kp, up, offsetof(typeof(*up), data))) + return -EFAULT; + + if (kp->size == 0) { + kp->data = NULL; + return 0; + } + + if (__get_user(p, &up->data)) + return -EFAULT; + udata = compat_ptr(p); + if (!access_ok(VERIFY_READ, udata, kp->size)) + return -EFAULT; + + kdata = compat_alloc_user_space(kp->size); + if (kdata == NULL) + return -EFAULT; + kp->data = kdata; + + if (copy_in_user(kdata, udata, kp->size)) + return -EFAULT; + + return 0; +} + +static int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp, + struct uvc_xu_control_query32 __user *up) +{ + u8 __user *udata; + u8 __user *kdata = kp->data; + compat_caddr_t p; + + if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || + __copy_to_user(up, kp, offsetof(typeof(*up), data))) + return -EFAULT; + + if (kp->size == 0) + return 0; + + if (get_user(p, &up->data)) + return -EFAULT; + udata = compat_ptr(p); + if (!access_ok(VERIFY_READ, udata, kp->size)) + return -EFAULT; + + if (copy_in_user(udata, kdata, kp->size)) + return -EFAULT; + + return 0; +} + +#define UVCIOC_CTRL_MAP32 _IOWR('u', 0x20, struct uvc_xu_control_mapping32) +#define UVCIOC_CTRL_QUERY32 _IOWR('u', 0x21, struct uvc_xu_control_query32) + +static long uvc_v4l2_compat_ioctl32(struct file *file, + unsigned int cmd, unsigned long arg) +{ + union { + struct uvc_xu_control_mapping xmap; + struct uvc_xu_control_query xqry; + } karg; + void __user *up = compat_ptr(arg); + mm_segment_t old_fs; + long ret; + + switch (cmd) { + case UVCIOC_CTRL_MAP32: + cmd = UVCIOC_CTRL_MAP; + ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up); + break; + + case UVCIOC_CTRL_QUERY32: + cmd = UVCIOC_CTRL_QUERY; + ret = uvc_v4l2_get_xu_query(&karg.xqry, up); + break; + + default: + return -ENOIOCTLCMD; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = uvc_v4l2_ioctl(file, cmd, (unsigned long)&karg); + set_fs(old_fs); + + if (ret < 0) + return ret; + + switch (cmd) { + case UVCIOC_CTRL_MAP: + ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up); + break; + + case UVCIOC_CTRL_QUERY: + ret = uvc_v4l2_put_xu_query(&karg.xqry, up); + break; + } + + return ret; +} +#endif + static ssize_t uvc_v4l2_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { @@ -1076,6 +1278,9 @@ const struct v4l2_file_operations uvc_fops = { .open = uvc_v4l2_open, .release = uvc_v4l2_release, .unlocked_ioctl = uvc_v4l2_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = uvc_v4l2_compat_ioctl32, +#endif .read = uvc_v4l2_read, .mmap = uvc_v4l2_mmap, .poll = uvc_v4l2_poll, diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index af4419e6c658..2829d256e4b7 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -14,12 +14,11 @@ */ #include <linux/compat.h> -#include <linux/videodev2.h> #include <linux/module.h> +#include <linux/videodev2.h> +#include <media/v4l2-dev.h> #include <media/v4l2-ioctl.h> -#ifdef CONFIG_COMPAT - static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret = -ENOIOCTLCMD; @@ -937,6 +936,7 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) { + struct video_device *vdev = video_devdata(file); long ret = -ENOIOCTLCMD; if (!file->f_op->unlocked_ioctl) @@ -1005,6 +1005,8 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_G_ENC_INDEX: case VIDIOC_ENCODER_CMD: case VIDIOC_TRY_ENCODER_CMD: + case VIDIOC_DECODER_CMD: + case VIDIOC_TRY_DECODER_CMD: case VIDIOC_DBG_S_REGISTER: case VIDIOC_DBG_G_REGISTER: case VIDIOC_DBG_G_CHIP_IDENT: @@ -1025,14 +1027,16 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) break; default: - printk(KERN_WARNING "compat_ioctl32: " - "unknown ioctl '%c', dir=%d, #%d (0x%08x)\n", - _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd), cmd); + if (vdev->fops->compat_ioctl32) + ret = vdev->fops->compat_ioctl32(file, cmd, arg); + + if (ret == -ENOIOCTLCMD) + printk(KERN_WARNING "compat_ioctl32: " + "unknown ioctl '%c', dir=%d, #%d (0x%08x)\n", + _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd), + cmd); break; } return ret; } EXPORT_SYMBOL_GPL(v4l2_compat_ioctl32); -#endif - -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index cccd42be718a..18015c0a8d31 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -175,6 +175,15 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "16-bit CRC", NULL }; + static const char * const mpeg_audio_dec_playback[] = { + "Auto", + "Stereo", + "Left", + "Right", + "Mono", + "Swapped Stereo", + NULL + }; static const char * const mpeg_video_encoding[] = { "MPEG-1", "MPEG-2", @@ -236,8 +245,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id) }; static const char * const tune_preemphasis[] = { "No Preemphasis", - "50 useconds", - "75 useconds", + "50 Microseconds", + "75 Microseconds", NULL, }; static const char * const header_mode[] = { @@ -334,7 +343,7 @@ const char * const *v4l2_ctrl_get_menu(u32 id) }; static const char * const mpeg4_profile[] = { "Simple", - "Adcanved Simple", + "Advanced Simple", "Core", "Simple Scalable", "Advanced Coding Efficency", @@ -353,6 +362,16 @@ const char * const *v4l2_ctrl_get_menu(u32 id) NULL, }; + static const char * const jpeg_chroma_subsampling[] = { + "4:4:4", + "4:2:2", + "4:2:0", + "4:1:1", + "4:1:0", + "Gray", + NULL, + }; + switch (id) { case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return mpeg_audio_sampling_freq; @@ -374,6 +393,9 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return mpeg_audio_emphasis; case V4L2_CID_MPEG_AUDIO_CRC: return mpeg_audio_crc; + case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: + case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK: + return mpeg_audio_dec_playback; case V4L2_CID_MPEG_VIDEO_ENCODING: return mpeg_video_encoding; case V4L2_CID_MPEG_VIDEO_ASPECT: @@ -414,6 +436,9 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return mpeg_mpeg4_level; case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: return mpeg4_profile; + case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: + return jpeg_chroma_subsampling; + default: return NULL; } @@ -492,6 +517,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_AUDIO_MUTE: return "Audio Mute"; case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate"; case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate"; + case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: return "Audio Playback"; + case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK: return "Audio Multilingual Playback"; case V4L2_CID_MPEG_VIDEO_ENCODING: return "Video Encoding"; case V4L2_CID_MPEG_VIDEO_ASPECT: return "Video Aspect"; case V4L2_CID_MPEG_VIDEO_B_FRAMES: return "Video B Frames"; @@ -546,6 +573,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: return "Number of MBs in a Slice"; case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: return "Slice Partitioning Method"; case V4L2_CID_MPEG_VIDEO_VBV_SIZE: return "VBV Buffer Size"; + case V4L2_CID_MPEG_VIDEO_DEC_PTS: return "Video Decoder PTS"; + case V4L2_CID_MPEG_VIDEO_DEC_FRAME: return "Video Decoder Frame Count"; /* CAMERA controls */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -607,6 +636,14 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_FLASH_CHARGE: return "Charge"; case V4L2_CID_FLASH_READY: return "Ready to Strobe"; + /* JPEG encoder controls */ + /* Keep the order of the 'case's the same as in videodev2.h! */ + case V4L2_CID_JPEG_CLASS: return "JPEG Compression Controls"; + case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: return "Chroma Subsampling"; + case V4L2_CID_JPEG_RESTART_INTERVAL: return "Restart Interval"; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: return "Compression Quality"; + case V4L2_CID_JPEG_ACTIVE_MARKER: return "Active Markers"; + default: return NULL; } @@ -674,6 +711,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: case V4L2_CID_MPEG_AUDIO_EMPHASIS: case V4L2_CID_MPEG_AUDIO_CRC: + case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: + case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK: case V4L2_CID_MPEG_VIDEO_ENCODING: case V4L2_CID_MPEG_VIDEO_ASPECT: case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: @@ -693,6 +732,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: + case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: *type = V4L2_CTRL_TYPE_MENU; break; case V4L2_CID_RDS_TX_PS_NAME: @@ -704,6 +744,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_CLASS: case V4L2_CID_FM_TX_CLASS: case V4L2_CID_FLASH_CLASS: + case V4L2_CID_JPEG_CLASS: *type = V4L2_CTRL_TYPE_CTRL_CLASS; /* You can neither read not write these */ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY; @@ -717,6 +758,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *max = 0xFFFFFF; break; case V4L2_CID_FLASH_FAULT: + case V4L2_CID_JPEG_ACTIVE_MARKER: *type = V4L2_CTRL_TYPE_BITMASK; break; case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: @@ -724,6 +766,11 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *type = V4L2_CTRL_TYPE_INTEGER; *flags |= V4L2_CTRL_FLAG_READ_ONLY; break; + case V4L2_CID_MPEG_VIDEO_DEC_FRAME: + case V4L2_CID_MPEG_VIDEO_DEC_PTS: + *type = V4L2_CTRL_TYPE_INTEGER64; + *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE; + break; default: *type = V4L2_CTRL_TYPE_INTEGER; break; @@ -1470,7 +1517,7 @@ EXPORT_SYMBOL(v4l2_ctrl_add_ctrl); int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl_handler *add) { - struct v4l2_ctrl *ctrl; + struct v4l2_ctrl_ref *ref; int ret = 0; /* Do nothing if either handler is NULL or if they are the same */ @@ -1479,7 +1526,9 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, if (hdl->error) return hdl->error; mutex_lock(&add->lock); - list_for_each_entry(ctrl, &add->ctrls, node) { + list_for_each_entry(ref, &add->ctrl_refs, node) { + struct v4l2_ctrl *ctrl = ref->ctrl; + /* Skip handler-private controls. */ if (ctrl->is_private) continue; @@ -2359,3 +2408,35 @@ void v4l2_ctrl_del_event(struct v4l2_ctrl *ctrl, v4l2_ctrl_unlock(ctrl); } EXPORT_SYMBOL(v4l2_ctrl_del_event); + +int v4l2_ctrl_log_status(struct file *file, void *fh) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_fh *vfh = file->private_data; + + if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) && vfd->v4l2_dev) + v4l2_ctrl_handler_log_status(vfh->ctrl_handler, + vfd->v4l2_dev->name); + return 0; +} +EXPORT_SYMBOL(v4l2_ctrl_log_status); + +int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + if (sub->type == V4L2_EVENT_CTRL) + return v4l2_event_subscribe(fh, sub, 0); + return -EINVAL; +} +EXPORT_SYMBOL(v4l2_ctrl_subscribe_event); + +unsigned int v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait) +{ + struct v4l2_fh *fh = file->private_data; + + if (v4l2_event_pending(fh)) + return POLLPRI; + poll_wait(file, &fh->wait, wait); + return 0; +} +EXPORT_SYMBOL(v4l2_ctrl_poll); diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 96e9615663e9..041804b73ebd 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -788,7 +788,7 @@ static void __exit videodev_exit(void) unregister_chrdev_region(dev, VIDEO_NUM_DEVICES); } -module_init(videodev_init) +subsys_initcall(videodev_init); module_exit(videodev_exit) MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>"); diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 3f623859a337..5b2ec1fd2d0a 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -260,6 +260,8 @@ static const char *v4l2_ioctls[] = { [_IOC_NR(VIDIOC_ENCODER_CMD)] = "VIDIOC_ENCODER_CMD", [_IOC_NR(VIDIOC_TRY_ENCODER_CMD)] = "VIDIOC_TRY_ENCODER_CMD", + [_IOC_NR(VIDIOC_DECODER_CMD)] = "VIDIOC_DECODER_CMD", + [_IOC_NR(VIDIOC_TRY_DECODER_CMD)] = "VIDIOC_TRY_DECODER_CMD", [_IOC_NR(VIDIOC_DBG_S_REGISTER)] = "VIDIOC_DBG_S_REGISTER", [_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER", @@ -540,10 +542,12 @@ static long __video_do_ioctl(struct file *file, if (!ret) dbgarg(cmd, "driver=%s, card=%s, bus=%s, " "version=0x%08x, " - "capabilities=0x%08x\n", + "capabilities=0x%08x, " + "device_caps=0x%08x\n", cap->driver, cap->card, cap->bus_info, cap->version, - cap->capabilities); + cap->capabilities, + cap->device_caps); break; } @@ -1762,6 +1766,32 @@ static long __video_do_ioctl(struct file *file, dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); break; } + case VIDIOC_DECODER_CMD: + { + struct v4l2_decoder_cmd *p = arg; + + if (!ops->vidioc_decoder_cmd) + break; + if (ret_prio) { + ret = ret_prio; + break; + } + ret = ops->vidioc_decoder_cmd(file, fh, p); + if (!ret) + dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); + break; + } + case VIDIOC_TRY_DECODER_CMD: + { + struct v4l2_decoder_cmd *p = arg; + + if (!ops->vidioc_try_decoder_cmd) + break; + ret = ops->vidioc_try_decoder_cmd(file, fh, p); + if (!ret) + dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); + break; + } case VIDIOC_G_PARM: { struct v4l2_streamparm *p = arg; @@ -1909,7 +1939,13 @@ static long __video_do_ioctl(struct file *file, { if (!ops->vidioc_log_status) break; + if (vfd->v4l2_dev) + pr_info("%s: ================= START STATUS =================\n", + vfd->v4l2_dev->name); ret = ops->vidioc_log_status(file, fh); + if (vfd->v4l2_dev) + pr_info("%s: ================== END STATUS ==================\n", + vfd->v4l2_dev->name); break; } #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -2419,7 +2455,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, /* Handles IOCTL */ err = func(file, cmd, parg); if (err == -ENOIOCTLCMD) - err = -EINVAL; + err = -ENOTTY; if (has_array_args) { *kernel_ptr = user_ptr; diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index 41d118ee2de6..6fe88e965a8c 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -194,8 +194,16 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) } #endif - case VIDIOC_LOG_STATUS: - return v4l2_subdev_call(sd, core, log_status); + case VIDIOC_LOG_STATUS: { + int ret; + + pr_info("%s: ================= START STATUS =================\n", + sd->name); + ret = v4l2_subdev_call(sd, core, log_status); + pr_info("%s: ================== END STATUS ==================\n", + sd->name); + return ret; + } #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) case VIDIOC_SUBDEV_G_FMT: { diff --git a/drivers/media/video/videobuf2-vmalloc.c b/drivers/media/video/videobuf2-vmalloc.c index 4e789a178f8a..6b5ca6c70a46 100644 --- a/drivers/media/video/videobuf2-vmalloc.c +++ b/drivers/media/video/videobuf2-vmalloc.c @@ -10,6 +10,7 @@ * the Free Software Foundation. */ +#include <linux/io.h> #include <linux/module.h> #include <linux/mm.h> #include <linux/sched.h> @@ -22,6 +23,7 @@ struct vb2_vmalloc_buf { void *vaddr; struct page **pages; + struct vm_area_struct *vma; int write; unsigned long size; unsigned int n_pages; @@ -71,6 +73,8 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, struct vb2_vmalloc_buf *buf; unsigned long first, last; int n_pages, offset; + struct vm_area_struct *vma; + dma_addr_t physp; buf = kzalloc(sizeof(*buf), GFP_KERNEL); if (!buf) @@ -80,23 +84,37 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, offset = vaddr & ~PAGE_MASK; buf->size = size; - first = vaddr >> PAGE_SHIFT; - last = (vaddr + size - 1) >> PAGE_SHIFT; - buf->n_pages = last - first + 1; - buf->pages = kzalloc(buf->n_pages * sizeof(struct page *), GFP_KERNEL); - if (!buf->pages) - goto fail_pages_array_alloc; - /* current->mm->mmap_sem is taken by videobuf2 core */ - n_pages = get_user_pages(current, current->mm, vaddr & PAGE_MASK, - buf->n_pages, write, 1, /* force */ - buf->pages, NULL); - if (n_pages != buf->n_pages) - goto fail_get_user_pages; - - buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1, PAGE_KERNEL); - if (!buf->vaddr) - goto fail_get_user_pages; + vma = find_vma(current->mm, vaddr); + if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) { + if (vb2_get_contig_userptr(vaddr, size, &vma, &physp)) + goto fail_pages_array_alloc; + buf->vma = vma; + buf->vaddr = ioremap_nocache(physp, size); + if (!buf->vaddr) + goto fail_pages_array_alloc; + } else { + first = vaddr >> PAGE_SHIFT; + last = (vaddr + size - 1) >> PAGE_SHIFT; + buf->n_pages = last - first + 1; + buf->pages = kzalloc(buf->n_pages * sizeof(struct page *), + GFP_KERNEL); + if (!buf->pages) + goto fail_pages_array_alloc; + + /* current->mm->mmap_sem is taken by videobuf2 core */ + n_pages = get_user_pages(current, current->mm, + vaddr & PAGE_MASK, buf->n_pages, + write, 1, /* force */ + buf->pages, NULL); + if (n_pages != buf->n_pages) + goto fail_get_user_pages; + + buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1, + PAGE_KERNEL); + if (!buf->vaddr) + goto fail_get_user_pages; + } buf->vaddr += offset; return buf; @@ -120,14 +138,20 @@ static void vb2_vmalloc_put_userptr(void *buf_priv) unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK; unsigned int i; - if (vaddr) - vm_unmap_ram((void *)vaddr, buf->n_pages); - for (i = 0; i < buf->n_pages; ++i) { - if (buf->write) - set_page_dirty_lock(buf->pages[i]); - put_page(buf->pages[i]); + if (buf->pages) { + if (vaddr) + vm_unmap_ram((void *)vaddr, buf->n_pages); + for (i = 0; i < buf->n_pages; ++i) { + if (buf->write) + set_page_dirty_lock(buf->pages[i]); + put_page(buf->pages[i]); + } + kfree(buf->pages); + } else { + if (buf->vma) + vb2_put_vma(buf->vma); + iounmap(buf->vaddr); } - kfree(buf->pages); kfree(buf); } diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 7d754fbcccbf..5e8b0710105b 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -819,8 +819,9 @@ static int vidioc_querycap(struct file *file, void *priv, strcpy(cap->driver, "vivi"); strcpy(cap->card, "vivi"); strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | \ + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -958,14 +959,6 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) return vb2_streamoff(&dev->vb_vidq, i); } -static int vidioc_log_status(struct file *file, void *priv) -{ - struct vivi_dev *dev = video_drvdata(file); - - v4l2_ctrl_handler_log_status(&dev->ctrl_handler, dev->v4l2_dev.name); - return 0; -} - static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) { return 0; @@ -1008,17 +1001,6 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) return 0; } -static int vidioc_subscribe_event(struct v4l2_fh *fh, - struct v4l2_event_subscription *sub) -{ - switch (sub->type) { - case V4L2_EVENT_CTRL: - return v4l2_event_subscribe(fh, sub, 0); - default: - return -EINVAL; - } -} - /* --- controls ---------------------------------------------- */ static int vivi_g_volatile_ctrl(struct v4l2_ctrl *ctrl) @@ -1209,8 +1191,8 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = { .vidioc_s_input = vidioc_s_input, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, - .vidioc_log_status = vidioc_log_status, - .vidioc_subscribe_event = vidioc_subscribe_event, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; diff --git a/drivers/media/video/vp27smpx.c b/drivers/media/video/vp27smpx.c index c15efb6e7771..7cfbc9d94a48 100644 --- a/drivers/media/video/vp27smpx.c +++ b/drivers/media/video/vp27smpx.c @@ -208,15 +208,4 @@ static struct i2c_driver vp27smpx_driver = { .id_table = vp27smpx_id, }; -static __init int init_vp27smpx(void) -{ - return i2c_add_driver(&vp27smpx_driver); -} - -static __exit void exit_vp27smpx(void) -{ - i2c_del_driver(&vp27smpx_driver); -} - -module_init(init_vp27smpx); -module_exit(exit_vp27smpx); +module_i2c_driver(vp27smpx_driver); diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c index e5cad6ff64a1..2f67b4c5c823 100644 --- a/drivers/media/video/vpx3220.c +++ b/drivers/media/video/vpx3220.c @@ -588,15 +588,4 @@ static struct i2c_driver vpx3220_driver = { .id_table = vpx3220_id, }; -static __init int init_vpx3220(void) -{ - return i2c_add_driver(&vpx3220_driver); -} - -static __exit void exit_vpx3220(void) -{ - i2c_del_driver(&vpx3220_driver); -} - -module_init(init_vpx3220); -module_exit(exit_vpx3220); +module_i2c_driver(vpx3220_driver); diff --git a/drivers/media/video/vs6624.c b/drivers/media/video/vs6624.c new file mode 100644 index 000000000000..42ae9dc9c574 --- /dev/null +++ b/drivers/media/video/vs6624.c @@ -0,0 +1,928 @@ +/* + * vs6624.c ST VS6624 CMOS image sensor driver + * + * Copyright (c) 2011 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mediabus.h> + +#include "vs6624_regs.h" + +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 +#define QVGA_WIDTH 320 +#define QVGA_HEIGHT 240 +#define QQVGA_WIDTH 160 +#define QQVGA_HEIGHT 120 +#define CIF_WIDTH 352 +#define CIF_HEIGHT 288 +#define QCIF_WIDTH 176 +#define QCIF_HEIGHT 144 +#define QQCIF_WIDTH 88 +#define QQCIF_HEIGHT 72 + +#define MAX_FRAME_RATE 30 + +struct vs6624 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct v4l2_fract frame_rate; + struct v4l2_mbus_framefmt fmt; + unsigned ce_pin; +}; + +static const struct vs6624_format { + enum v4l2_mbus_pixelcode mbus_code; + enum v4l2_colorspace colorspace; +} vs6624_formats[] = { + { + .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + }, + { + .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + }, + { + .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, + .colorspace = V4L2_COLORSPACE_SRGB, + }, +}; + +static struct v4l2_mbus_framefmt vs6624_default_fmt = { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .code = V4L2_MBUS_FMT_UYVY8_2X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_JPEG, +}; + +static const u16 vs6624_p1[] = { + 0x8104, 0x03, + 0x8105, 0x01, + 0xc900, 0x03, + 0xc904, 0x47, + 0xc905, 0x10, + 0xc906, 0x80, + 0xc907, 0x3a, + 0x903a, 0x02, + 0x903b, 0x47, + 0x903c, 0x15, + 0xc908, 0x31, + 0xc909, 0xdc, + 0xc90a, 0x80, + 0xc90b, 0x44, + 0x9044, 0x02, + 0x9045, 0x31, + 0x9046, 0xe2, + 0xc90c, 0x07, + 0xc90d, 0xe0, + 0xc90e, 0x80, + 0xc90f, 0x47, + 0x9047, 0x90, + 0x9048, 0x83, + 0x9049, 0x81, + 0x904a, 0xe0, + 0x904b, 0x60, + 0x904c, 0x08, + 0x904d, 0x90, + 0x904e, 0xc0, + 0x904f, 0x43, + 0x9050, 0x74, + 0x9051, 0x01, + 0x9052, 0xf0, + 0x9053, 0x80, + 0x9054, 0x05, + 0x9055, 0xE4, + 0x9056, 0x90, + 0x9057, 0xc0, + 0x9058, 0x43, + 0x9059, 0xf0, + 0x905a, 0x02, + 0x905b, 0x07, + 0x905c, 0xec, + 0xc910, 0x5d, + 0xc911, 0xca, + 0xc912, 0x80, + 0xc913, 0x5d, + 0x905d, 0xa3, + 0x905e, 0x04, + 0x905f, 0xf0, + 0x9060, 0xa3, + 0x9061, 0x04, + 0x9062, 0xf0, + 0x9063, 0x22, + 0xc914, 0x72, + 0xc915, 0x92, + 0xc916, 0x80, + 0xc917, 0x64, + 0x9064, 0x74, + 0x9065, 0x01, + 0x9066, 0x02, + 0x9067, 0x72, + 0x9068, 0x95, + 0xc918, 0x47, + 0xc919, 0xf2, + 0xc91a, 0x81, + 0xc91b, 0x69, + 0x9169, 0x74, + 0x916a, 0x02, + 0x916b, 0xf0, + 0x916c, 0xec, + 0x916d, 0xb4, + 0x916e, 0x10, + 0x916f, 0x0a, + 0x9170, 0x90, + 0x9171, 0x80, + 0x9172, 0x16, + 0x9173, 0xe0, + 0x9174, 0x70, + 0x9175, 0x04, + 0x9176, 0x90, + 0x9177, 0xd3, + 0x9178, 0xc4, + 0x9179, 0xf0, + 0x917a, 0x22, + 0xc91c, 0x0a, + 0xc91d, 0xbe, + 0xc91e, 0x80, + 0xc91f, 0x73, + 0x9073, 0xfc, + 0x9074, 0xa3, + 0x9075, 0xe0, + 0x9076, 0xf5, + 0x9077, 0x82, + 0x9078, 0x8c, + 0x9079, 0x83, + 0x907a, 0xa3, + 0x907b, 0xa3, + 0x907c, 0xe0, + 0x907d, 0xfc, + 0x907e, 0xa3, + 0x907f, 0xe0, + 0x9080, 0xc3, + 0x9081, 0x9f, + 0x9082, 0xff, + 0x9083, 0xec, + 0x9084, 0x9e, + 0x9085, 0xfe, + 0x9086, 0x02, + 0x9087, 0x0a, + 0x9088, 0xea, + 0xc920, 0x47, + 0xc921, 0x38, + 0xc922, 0x80, + 0xc923, 0x89, + 0x9089, 0xec, + 0x908a, 0xd3, + 0x908b, 0x94, + 0x908c, 0x20, + 0x908d, 0x40, + 0x908e, 0x01, + 0x908f, 0x1c, + 0x9090, 0x90, + 0x9091, 0xd3, + 0x9092, 0xd4, + 0x9093, 0xec, + 0x9094, 0xf0, + 0x9095, 0x02, + 0x9096, 0x47, + 0x9097, 0x3d, + 0xc924, 0x45, + 0xc925, 0xca, + 0xc926, 0x80, + 0xc927, 0x98, + 0x9098, 0x12, + 0x9099, 0x77, + 0x909a, 0xd6, + 0x909b, 0x02, + 0x909c, 0x45, + 0x909d, 0xcd, + 0xc928, 0x20, + 0xc929, 0xd5, + 0xc92a, 0x80, + 0xc92b, 0x9e, + 0x909e, 0x90, + 0x909f, 0x82, + 0x90a0, 0x18, + 0x90a1, 0xe0, + 0x90a2, 0xb4, + 0x90a3, 0x03, + 0x90a4, 0x0e, + 0x90a5, 0x90, + 0x90a6, 0x83, + 0x90a7, 0xbf, + 0x90a8, 0xe0, + 0x90a9, 0x60, + 0x90aa, 0x08, + 0x90ab, 0x90, + 0x90ac, 0x81, + 0x90ad, 0xfc, + 0x90ae, 0xe0, + 0x90af, 0xff, + 0x90b0, 0xc3, + 0x90b1, 0x13, + 0x90b2, 0xf0, + 0x90b3, 0x90, + 0x90b4, 0x81, + 0x90b5, 0xfc, + 0x90b6, 0xe0, + 0x90b7, 0xff, + 0x90b8, 0x02, + 0x90b9, 0x20, + 0x90ba, 0xda, + 0xc92c, 0x70, + 0xc92d, 0xbc, + 0xc92e, 0x80, + 0xc92f, 0xbb, + 0x90bb, 0x90, + 0x90bc, 0x82, + 0x90bd, 0x18, + 0x90be, 0xe0, + 0x90bf, 0xb4, + 0x90c0, 0x03, + 0x90c1, 0x06, + 0x90c2, 0x90, + 0x90c3, 0xc1, + 0x90c4, 0x06, + 0x90c5, 0x74, + 0x90c6, 0x05, + 0x90c7, 0xf0, + 0x90c8, 0x90, + 0x90c9, 0xd3, + 0x90ca, 0xa0, + 0x90cb, 0x02, + 0x90cc, 0x70, + 0x90cd, 0xbf, + 0xc930, 0x72, + 0xc931, 0x21, + 0xc932, 0x81, + 0xc933, 0x3b, + 0x913b, 0x7d, + 0x913c, 0x02, + 0x913d, 0x7f, + 0x913e, 0x7b, + 0x913f, 0x02, + 0x9140, 0x72, + 0x9141, 0x25, + 0xc934, 0x28, + 0xc935, 0xae, + 0xc936, 0x80, + 0xc937, 0xd2, + 0x90d2, 0xf0, + 0x90d3, 0x90, + 0x90d4, 0xd2, + 0x90d5, 0x0a, + 0x90d6, 0x02, + 0x90d7, 0x28, + 0x90d8, 0xb4, + 0xc938, 0x28, + 0xc939, 0xb1, + 0xc93a, 0x80, + 0xc93b, 0xd9, + 0x90d9, 0x90, + 0x90da, 0x83, + 0x90db, 0xba, + 0x90dc, 0xe0, + 0x90dd, 0xff, + 0x90de, 0x90, + 0x90df, 0xd2, + 0x90e0, 0x08, + 0x90e1, 0xe0, + 0x90e2, 0xe4, + 0x90e3, 0xef, + 0x90e4, 0xf0, + 0x90e5, 0xa3, + 0x90e6, 0xe0, + 0x90e7, 0x74, + 0x90e8, 0xff, + 0x90e9, 0xf0, + 0x90ea, 0x90, + 0x90eb, 0xd2, + 0x90ec, 0x0a, + 0x90ed, 0x02, + 0x90ee, 0x28, + 0x90ef, 0xb4, + 0xc93c, 0x29, + 0xc93d, 0x79, + 0xc93e, 0x80, + 0xc93f, 0xf0, + 0x90f0, 0xf0, + 0x90f1, 0x90, + 0x90f2, 0xd2, + 0x90f3, 0x0e, + 0x90f4, 0x02, + 0x90f5, 0x29, + 0x90f6, 0x7f, + 0xc940, 0x29, + 0xc941, 0x7c, + 0xc942, 0x80, + 0xc943, 0xf7, + 0x90f7, 0x90, + 0x90f8, 0x83, + 0x90f9, 0xba, + 0x90fa, 0xe0, + 0x90fb, 0xff, + 0x90fc, 0x90, + 0x90fd, 0xd2, + 0x90fe, 0x0c, + 0x90ff, 0xe0, + 0x9100, 0xe4, + 0x9101, 0xef, + 0x9102, 0xf0, + 0x9103, 0xa3, + 0x9104, 0xe0, + 0x9105, 0x74, + 0x9106, 0xff, + 0x9107, 0xf0, + 0x9108, 0x90, + 0x9109, 0xd2, + 0x910a, 0x0e, + 0x910b, 0x02, + 0x910c, 0x29, + 0x910d, 0x7f, + 0xc944, 0x2a, + 0xc945, 0x42, + 0xc946, 0x81, + 0xc947, 0x0e, + 0x910e, 0xf0, + 0x910f, 0x90, + 0x9110, 0xd2, + 0x9111, 0x12, + 0x9112, 0x02, + 0x9113, 0x2a, + 0x9114, 0x48, + 0xc948, 0x2a, + 0xc949, 0x45, + 0xc94a, 0x81, + 0xc94b, 0x15, + 0x9115, 0x90, + 0x9116, 0x83, + 0x9117, 0xba, + 0x9118, 0xe0, + 0x9119, 0xff, + 0x911a, 0x90, + 0x911b, 0xd2, + 0x911c, 0x10, + 0x911d, 0xe0, + 0x911e, 0xe4, + 0x911f, 0xef, + 0x9120, 0xf0, + 0x9121, 0xa3, + 0x9122, 0xe0, + 0x9123, 0x74, + 0x9124, 0xff, + 0x9125, 0xf0, + 0x9126, 0x90, + 0x9127, 0xd2, + 0x9128, 0x12, + 0x9129, 0x02, + 0x912a, 0x2a, + 0x912b, 0x48, + 0xc900, 0x01, + 0x0000, 0x00, +}; + +static const u16 vs6624_p2[] = { + 0x806f, 0x01, + 0x058c, 0x01, + 0x0000, 0x00, +}; + +static const u16 vs6624_run_setup[] = { + 0x1d18, 0x00, /* Enableconstrainedwhitebalance */ + VS6624_PEAK_MIN_OUT_G_MSB, 0x3c, /* Damper PeakGain Output MSB */ + VS6624_PEAK_MIN_OUT_G_LSB, 0x66, /* Damper PeakGain Output LSB */ + VS6624_CM_LOW_THR_MSB, 0x65, /* Damper Low MSB */ + VS6624_CM_LOW_THR_LSB, 0xd1, /* Damper Low LSB */ + VS6624_CM_HIGH_THR_MSB, 0x66, /* Damper High MSB */ + VS6624_CM_HIGH_THR_LSB, 0x62, /* Damper High LSB */ + VS6624_CM_MIN_OUT_MSB, 0x00, /* Damper Min output MSB */ + VS6624_CM_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */ + VS6624_NORA_DISABLE, 0x00, /* Nora fDisable */ + VS6624_NORA_USAGE, 0x04, /* Nora usage */ + VS6624_NORA_LOW_THR_MSB, 0x63, /* Damper Low MSB Changed 0x63 to 0x65 */ + VS6624_NORA_LOW_THR_LSB, 0xd1, /* Damper Low LSB */ + VS6624_NORA_HIGH_THR_MSB, 0x68, /* Damper High MSB */ + VS6624_NORA_HIGH_THR_LSB, 0xdd, /* Damper High LSB */ + VS6624_NORA_MIN_OUT_MSB, 0x3a, /* Damper Min output MSB */ + VS6624_NORA_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */ + VS6624_F2B_DISABLE, 0x00, /* Disable */ + 0x1d8a, 0x30, /* MAXWeightHigh */ + 0x1d91, 0x62, /* fpDamperLowThresholdHigh MSB */ + 0x1d92, 0x4a, /* fpDamperLowThresholdHigh LSB */ + 0x1d95, 0x65, /* fpDamperHighThresholdHigh MSB */ + 0x1d96, 0x0e, /* fpDamperHighThresholdHigh LSB */ + 0x1da1, 0x3a, /* fpMinimumDamperOutputLow MSB */ + 0x1da2, 0xb8, /* fpMinimumDamperOutputLow LSB */ + 0x1e08, 0x06, /* MAXWeightLow */ + 0x1e0a, 0x0a, /* MAXWeightHigh */ + 0x1601, 0x3a, /* Red A MSB */ + 0x1602, 0x14, /* Red A LSB */ + 0x1605, 0x3b, /* Blue A MSB */ + 0x1606, 0x85, /* BLue A LSB */ + 0x1609, 0x3b, /* RED B MSB */ + 0x160a, 0x85, /* RED B LSB */ + 0x160d, 0x3a, /* Blue B MSB */ + 0x160e, 0x14, /* Blue B LSB */ + 0x1611, 0x30, /* Max Distance from Locus MSB */ + 0x1612, 0x8f, /* Max Distance from Locus MSB */ + 0x1614, 0x01, /* Enable constrainer */ + 0x0000, 0x00, +}; + +static const u16 vs6624_default[] = { + VS6624_CONTRAST0, 0x84, + VS6624_SATURATION0, 0x75, + VS6624_GAMMA0, 0x11, + VS6624_CONTRAST1, 0x84, + VS6624_SATURATION1, 0x75, + VS6624_GAMMA1, 0x11, + VS6624_MAN_RG, 0x80, + VS6624_MAN_GG, 0x80, + VS6624_MAN_BG, 0x80, + VS6624_WB_MODE, 0x1, + VS6624_EXPO_COMPENSATION, 0xfe, + VS6624_EXPO_METER, 0x0, + VS6624_LIGHT_FREQ, 0x64, + VS6624_PEAK_GAIN, 0xe, + VS6624_PEAK_LOW_THR, 0x28, + VS6624_HMIRROR0, 0x0, + VS6624_VFLIP0, 0x0, + VS6624_ZOOM_HSTEP0_MSB, 0x0, + VS6624_ZOOM_HSTEP0_LSB, 0x1, + VS6624_ZOOM_VSTEP0_MSB, 0x0, + VS6624_ZOOM_VSTEP0_LSB, 0x1, + VS6624_PAN_HSTEP0_MSB, 0x0, + VS6624_PAN_HSTEP0_LSB, 0xf, + VS6624_PAN_VSTEP0_MSB, 0x0, + VS6624_PAN_VSTEP0_LSB, 0xf, + VS6624_SENSOR_MODE, 0x1, + VS6624_SYNC_CODE_SETUP, 0x21, + VS6624_DISABLE_FR_DAMPER, 0x0, + VS6624_FR_DEN, 0x1, + VS6624_FR_NUM_LSB, 0xf, + VS6624_INIT_PIPE_SETUP, 0x0, + VS6624_IMG_FMT0, 0x0, + VS6624_YUV_SETUP, 0x1, + VS6624_IMAGE_SIZE0, 0x2, + 0x0000, 0x00, +}; + +static inline struct vs6624 *to_vs6624(struct v4l2_subdev *sd) +{ + return container_of(sd, struct vs6624, sd); +} +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct vs6624, hdl)->sd; +} + +static int vs6624_read(struct v4l2_subdev *sd, u16 index) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[2]; + + buf[0] = index >> 8; + buf[1] = index; + i2c_master_send(client, buf, 2); + i2c_master_recv(client, buf, 1); + + return buf[0]; +} + +static int vs6624_write(struct v4l2_subdev *sd, u16 index, + u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[3]; + + buf[0] = index >> 8; + buf[1] = index; + buf[2] = value; + + return i2c_master_send(client, buf, 3); +} + +static int vs6624_writeregs(struct v4l2_subdev *sd, const u16 *regs) +{ + u16 reg; + u8 data; + + while (*regs != 0x00) { + reg = *regs++; + data = *regs++; + + vs6624_write(sd, reg, data); + } + return 0; +} + +static int vs6624_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + vs6624_write(sd, VS6624_CONTRAST0, ctrl->val); + break; + case V4L2_CID_SATURATION: + vs6624_write(sd, VS6624_SATURATION0, ctrl->val); + break; + case V4L2_CID_HFLIP: + vs6624_write(sd, VS6624_HMIRROR0, ctrl->val); + break; + case V4L2_CID_VFLIP: + vs6624_write(sd, VS6624_VFLIP0, ctrl->val); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vs6624_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, + enum v4l2_mbus_pixelcode *code) +{ + if (index >= ARRAY_SIZE(vs6624_formats)) + return -EINVAL; + + *code = vs6624_formats[index].mbus_code; + return 0; +} + +static int vs6624_try_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + int index; + + for (index = 0; index < ARRAY_SIZE(vs6624_formats); index++) + if (vs6624_formats[index].mbus_code == fmt->code) + break; + if (index >= ARRAY_SIZE(vs6624_formats)) { + /* default to first format */ + index = 0; + fmt->code = vs6624_formats[0].mbus_code; + } + + /* sensor mode is VGA */ + if (fmt->width > VGA_WIDTH) + fmt->width = VGA_WIDTH; + if (fmt->height > VGA_HEIGHT) + fmt->height = VGA_HEIGHT; + fmt->width = fmt->width & (~3); + fmt->height = fmt->height & (~3); + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = vs6624_formats[index].colorspace; + return 0; +} + +static int vs6624_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct vs6624 *sensor = to_vs6624(sd); + int ret; + + ret = vs6624_try_mbus_fmt(sd, fmt); + if (ret) + return ret; + + /* set image format */ + switch (fmt->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + vs6624_write(sd, VS6624_IMG_FMT0, 0x0); + vs6624_write(sd, VS6624_YUV_SETUP, 0x1); + break; + case V4L2_MBUS_FMT_YUYV8_2X8: + vs6624_write(sd, VS6624_IMG_FMT0, 0x0); + vs6624_write(sd, VS6624_YUV_SETUP, 0x3); + break; + case V4L2_MBUS_FMT_RGB565_2X8_LE: + vs6624_write(sd, VS6624_IMG_FMT0, 0x4); + vs6624_write(sd, VS6624_RGB_SETUP, 0x0); + break; + default: + return -EINVAL; + } + + /* set image size */ + if ((fmt->width == VGA_WIDTH) && (fmt->height == VGA_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x2); + else if ((fmt->width == QVGA_WIDTH) && (fmt->height == QVGA_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x4); + else if ((fmt->width == QQVGA_WIDTH) && (fmt->height == QQVGA_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x6); + else if ((fmt->width == CIF_WIDTH) && (fmt->height == CIF_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x3); + else if ((fmt->width == QCIF_WIDTH) && (fmt->height == QCIF_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x5); + else if ((fmt->width == QQCIF_WIDTH) && (fmt->height == QQCIF_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x7); + else { + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x8); + vs6624_write(sd, VS6624_MAN_HSIZE0_MSB, fmt->width >> 8); + vs6624_write(sd, VS6624_MAN_HSIZE0_LSB, fmt->width & 0xFF); + vs6624_write(sd, VS6624_MAN_VSIZE0_MSB, fmt->height >> 8); + vs6624_write(sd, VS6624_MAN_VSIZE0_LSB, fmt->height & 0xFF); + vs6624_write(sd, VS6624_CROP_CTRL0, 0x1); + } + + sensor->fmt = *fmt; + + return 0; +} + +static int vs6624_g_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct vs6624 *sensor = to_vs6624(sd); + + *fmt = sensor->fmt; + return 0; +} + +static int vs6624_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct vs6624 *sensor = to_vs6624(sd); + struct v4l2_captureparm *cp = &parms->parm.capture; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(cp, 0, sizeof(*cp)); + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = sensor->frame_rate.denominator; + cp->timeperframe.denominator = sensor->frame_rate.numerator; + return 0; +} + +static int vs6624_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct vs6624 *sensor = to_vs6624(sd); + struct v4l2_captureparm *cp = &parms->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cp->extendedmode != 0) + return -EINVAL; + + if (tpf->numerator == 0 || tpf->denominator == 0 + || (tpf->denominator > tpf->numerator * MAX_FRAME_RATE)) { + /* reset to max frame rate */ + tpf->numerator = 1; + tpf->denominator = MAX_FRAME_RATE; + } + sensor->frame_rate.numerator = tpf->denominator; + sensor->frame_rate.denominator = tpf->numerator; + vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0); + vs6624_write(sd, VS6624_FR_NUM_MSB, + sensor->frame_rate.numerator >> 8); + vs6624_write(sd, VS6624_FR_NUM_LSB, + sensor->frame_rate.numerator & 0xFF); + vs6624_write(sd, VS6624_FR_DEN, + sensor->frame_rate.denominator & 0xFF); + return 0; +} + +static int vs6624_s_stream(struct v4l2_subdev *sd, int enable) +{ + if (enable) + vs6624_write(sd, VS6624_USER_CMD, 0x2); + else + vs6624_write(sd, VS6624_USER_CMD, 0x4); + udelay(100); + return 0; +} + +static int vs6624_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + int rev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + rev = (vs6624_read(sd, VS6624_FW_VSN_MAJOR) << 8) + | vs6624_read(sd, VS6624_FW_VSN_MINOR); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VS6624, rev); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vs6624_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = vs6624_read(sd, reg->reg & 0xffff); + reg->size = 1; + return 0; +} + +static int vs6624_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + vs6624_write(sd, reg->reg & 0xffff, reg->val & 0xff); + return 0; +} +#endif + +static const struct v4l2_ctrl_ops vs6624_ctrl_ops = { + .s_ctrl = vs6624_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops vs6624_core_ops = { + .g_chip_ident = vs6624_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = vs6624_g_register, + .s_register = vs6624_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops vs6624_video_ops = { + .enum_mbus_fmt = vs6624_enum_mbus_fmt, + .try_mbus_fmt = vs6624_try_mbus_fmt, + .s_mbus_fmt = vs6624_s_mbus_fmt, + .g_mbus_fmt = vs6624_g_mbus_fmt, + .s_parm = vs6624_s_parm, + .g_parm = vs6624_g_parm, + .s_stream = vs6624_s_stream, +}; + +static const struct v4l2_subdev_ops vs6624_ops = { + .core = &vs6624_core_ops, + .video = &vs6624_video_ops, +}; + +static int __devinit vs6624_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct vs6624 *sensor; + struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *hdl; + const unsigned *ce; + int ret; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -EIO; + + ce = client->dev.platform_data; + if (ce == NULL) + return -EINVAL; + + ret = gpio_request(*ce, "VS6624 Chip Enable"); + if (ret) { + v4l_err(client, "failed to request GPIO %d\n", *ce); + return ret; + } + gpio_direction_output(*ce, 1); + /* wait 100ms before any further i2c writes are performed */ + mdelay(100); + + sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); + if (sensor == NULL) { + gpio_free(*ce); + return -ENOMEM; + } + + sd = &sensor->sd; + v4l2_i2c_subdev_init(sd, client, &vs6624_ops); + + vs6624_writeregs(sd, vs6624_p1); + vs6624_write(sd, VS6624_MICRO_EN, 0x2); + vs6624_write(sd, VS6624_DIO_EN, 0x1); + mdelay(10); + vs6624_writeregs(sd, vs6624_p2); + + vs6624_writeregs(sd, vs6624_default); + vs6624_write(sd, VS6624_HSYNC_SETUP, 0xF); + vs6624_writeregs(sd, vs6624_run_setup); + + /* set frame rate */ + sensor->frame_rate.numerator = MAX_FRAME_RATE; + sensor->frame_rate.denominator = 1; + vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0); + vs6624_write(sd, VS6624_FR_NUM_MSB, + sensor->frame_rate.numerator >> 8); + vs6624_write(sd, VS6624_FR_NUM_LSB, + sensor->frame_rate.numerator & 0xFF); + vs6624_write(sd, VS6624_FR_DEN, + sensor->frame_rate.denominator & 0xFF); + + sensor->fmt = vs6624_default_fmt; + sensor->ce_pin = *ce; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + hdl = &sensor->hdl; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, + V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x87); + v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, + V4L2_CID_SATURATION, 0, 0xFF, 1, 0x78); + v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + /* hook the control handler into the driver */ + sd->ctrl_handler = hdl; + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + kfree(sensor); + gpio_free(*ce); + return err; + } + + /* initialize the hardware to the default control values */ + ret = v4l2_ctrl_handler_setup(hdl); + if (ret) { + v4l2_ctrl_handler_free(hdl); + kfree(sensor); + gpio_free(*ce); + } + return ret; +} + +static int __devexit vs6624_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct vs6624 *sensor = to_vs6624(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + gpio_free(sensor->ce_pin); + kfree(sensor); + return 0; +} + +static const struct i2c_device_id vs6624_id[] = { + {"vs6624", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, vs6624_id); + +static struct i2c_driver vs6624_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "vs6624", + }, + .probe = vs6624_probe, + .remove = __devexit_p(vs6624_remove), + .id_table = vs6624_id, +}; + +static __init int vs6624_init(void) +{ + return i2c_add_driver(&vs6624_driver); +} + +static __exit void vs6624_exit(void) +{ + i2c_del_driver(&vs6624_driver); +} + +module_init(vs6624_init); +module_exit(vs6624_exit); + +MODULE_DESCRIPTION("VS6624 sensor driver"); +MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/vs6624_regs.h b/drivers/media/video/vs6624_regs.h new file mode 100644 index 000000000000..6ba2ee25827e --- /dev/null +++ b/drivers/media/video/vs6624_regs.h @@ -0,0 +1,337 @@ +/* + * vs6624 - ST VS6624 CMOS image sensor registers + * + * Copyright (c) 2011 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _VS6624_REGS_H_ +#define _VS6624_REGS_H_ + +/* low level control registers */ +#define VS6624_MICRO_EN 0xC003 /* power enable for all MCU clock */ +#define VS6624_DIO_EN 0xC044 /* enable digital I/O */ +/* device parameters */ +#define VS6624_DEV_ID_MSB 0x0001 /* device id MSB */ +#define VS6624_DEV_ID_LSB 0x0002 /* device id LSB */ +#define VS6624_FW_VSN_MAJOR 0x0004 /* firmware version major */ +#define VS6624_FW_VSN_MINOR 0x0006 /* firmware version minor */ +#define VS6624_PATCH_VSN_MAJOR 0x0008 /* patch version major */ +#define VS6624_PATCH_VSN_MINOR 0x000A /* patch version minor */ +/* host interface manager control */ +#define VS6624_USER_CMD 0x0180 /* user level control of operating states */ +/* host interface manager status */ +#define VS6624_STATE 0x0202 /* current state of the mode manager */ +/* run mode control */ +#define VS6624_METER_ON 0x0280 /* if false AE and AWB are disabled */ +/* mode setup */ +#define VS6624_ACTIVE_PIPE_SETUP 0x0302 /* select the active bank for non view live mode */ +#define VS6624_SENSOR_MODE 0x0308 /* select the different sensor mode */ +/* pipe setup bank0 */ +#define VS6624_IMAGE_SIZE0 0x0380 /* required output dimension */ +#define VS6624_MAN_HSIZE0_MSB 0x0383 /* input required manual H size MSB */ +#define VS6624_MAN_HSIZE0_LSB 0x0384 /* input required manual H size LSB */ +#define VS6624_MAN_VSIZE0_MSB 0x0387 /* input required manual V size MSB */ +#define VS6624_MAN_VSIZE0_LSB 0x0388 /* input required manual V size LSB */ +#define VS6624_ZOOM_HSTEP0_MSB 0x038B /* set the zoom H step MSB */ +#define VS6624_ZOOM_HSTEP0_LSB 0x038C /* set the zoom H step LSB */ +#define VS6624_ZOOM_VSTEP0_MSB 0x038F /* set the zoom V step MSB */ +#define VS6624_ZOOM_VSTEP0_LSB 0x0390 /* set the zoom V step LSB */ +#define VS6624_ZOOM_CTRL0 0x0392 /* control zoon in, out and stop */ +#define VS6624_PAN_HSTEP0_MSB 0x0395 /* set the pan H step MSB */ +#define VS6624_PAN_HSTEP0_LSB 0x0396 /* set the pan H step LSB */ +#define VS6624_PAN_VSTEP0_MSB 0x0399 /* set the pan V step MSB */ +#define VS6624_PAN_VSTEP0_LSB 0x039A /* set the pan V step LSB */ +#define VS6624_PAN_CTRL0 0x039C /* control pan operation */ +#define VS6624_CROP_CTRL0 0x039E /* select cropping mode */ +#define VS6624_CROP_HSTART0_MSB 0x03A1 /* set the cropping H start address MSB */ +#define VS6624_CROP_HSTART0_LSB 0x03A2 /* set the cropping H start address LSB */ +#define VS6624_CROP_HSIZE0_MSB 0x03A5 /* set the cropping H size MSB */ +#define VS6624_CROP_HSIZE0_LSB 0x03A6 /* set the cropping H size LSB */ +#define VS6624_CROP_VSTART0_MSB 0x03A9 /* set the cropping V start address MSB */ +#define VS6624_CROP_VSTART0_LSB 0x03AA /* set the cropping V start address LSB */ +#define VS6624_CROP_VSIZE0_MSB 0x03AD /* set the cropping V size MSB */ +#define VS6624_CROP_VSIZE0_LSB 0x03AE /* set the cropping V size LSB */ +#define VS6624_IMG_FMT0 0x03B0 /* select required output image format */ +#define VS6624_BAYER_OUT_ALIGN0 0x03B2 /* set bayer output alignment */ +#define VS6624_CONTRAST0 0x03B4 /* contrast control for output */ +#define VS6624_SATURATION0 0x03B6 /* saturation control for output */ +#define VS6624_GAMMA0 0x03B8 /* gamma settings */ +#define VS6624_HMIRROR0 0x03BA /* horizontal image orientation flip */ +#define VS6624_VFLIP0 0x03BC /* vertical image orientation flip */ +#define VS6624_CHANNEL_ID0 0x03BE /* logical DMA channel number */ +/* pipe setup bank1 */ +#define VS6624_IMAGE_SIZE1 0x0400 /* required output dimension */ +#define VS6624_MAN_HSIZE1_MSB 0x0403 /* input required manual H size MSB */ +#define VS6624_MAN_HSIZE1_LSB 0x0404 /* input required manual H size LSB */ +#define VS6624_MAN_VSIZE1_MSB 0x0407 /* input required manual V size MSB */ +#define VS6624_MAN_VSIZE1_LSB 0x0408 /* input required manual V size LSB */ +#define VS6624_ZOOM_HSTEP1_MSB 0x040B /* set the zoom H step MSB */ +#define VS6624_ZOOM_HSTEP1_LSB 0x040C /* set the zoom H step LSB */ +#define VS6624_ZOOM_VSTEP1_MSB 0x040F /* set the zoom V step MSB */ +#define VS6624_ZOOM_VSTEP1_LSB 0x0410 /* set the zoom V step LSB */ +#define VS6624_ZOOM_CTRL1 0x0412 /* control zoon in, out and stop */ +#define VS6624_PAN_HSTEP1_MSB 0x0415 /* set the pan H step MSB */ +#define VS6624_PAN_HSTEP1_LSB 0x0416 /* set the pan H step LSB */ +#define VS6624_PAN_VSTEP1_MSB 0x0419 /* set the pan V step MSB */ +#define VS6624_PAN_VSTEP1_LSB 0x041A /* set the pan V step LSB */ +#define VS6624_PAN_CTRL1 0x041C /* control pan operation */ +#define VS6624_CROP_CTRL1 0x041E /* select cropping mode */ +#define VS6624_CROP_HSTART1_MSB 0x0421 /* set the cropping H start address MSB */ +#define VS6624_CROP_HSTART1_LSB 0x0422 /* set the cropping H start address LSB */ +#define VS6624_CROP_HSIZE1_MSB 0x0425 /* set the cropping H size MSB */ +#define VS6624_CROP_HSIZE1_LSB 0x0426 /* set the cropping H size LSB */ +#define VS6624_CROP_VSTART1_MSB 0x0429 /* set the cropping V start address MSB */ +#define VS6624_CROP_VSTART1_LSB 0x042A /* set the cropping V start address LSB */ +#define VS6624_CROP_VSIZE1_MSB 0x042D /* set the cropping V size MSB */ +#define VS6624_CROP_VSIZE1_LSB 0x042E /* set the cropping V size LSB */ +#define VS6624_IMG_FMT1 0x0430 /* select required output image format */ +#define VS6624_BAYER_OUT_ALIGN1 0x0432 /* set bayer output alignment */ +#define VS6624_CONTRAST1 0x0434 /* contrast control for output */ +#define VS6624_SATURATION1 0x0436 /* saturation control for output */ +#define VS6624_GAMMA1 0x0438 /* gamma settings */ +#define VS6624_HMIRROR1 0x043A /* horizontal image orientation flip */ +#define VS6624_VFLIP1 0x043C /* vertical image orientation flip */ +#define VS6624_CHANNEL_ID1 0x043E /* logical DMA channel number */ +/* view live control */ +#define VS6624_VIEW_LIVE_EN 0x0480 /* enable view live mode */ +#define VS6624_INIT_PIPE_SETUP 0x0482 /* select initial pipe setup bank */ +/* view live status */ +#define VS6624_CUR_PIPE_SETUP 0x0500 /* indicates most recently applied setup bank */ +/* power management */ +#define VS6624_TIME_TO_POWER_DOWN 0x0580 /* automatically transition time to stop mode */ +/* video timing parameter host inputs */ +#define VS6624_EXT_CLK_FREQ_NUM_MSB 0x0605 /* external clock frequency numerator MSB */ +#define VS6624_EXT_CLK_FREQ_NUM_LSB 0x0606 /* external clock frequency numerator LSB */ +#define VS6624_EXT_CLK_FREQ_DEN 0x0608 /* external clock frequency denominator */ +/* video timing control */ +#define VS6624_SYS_CLK_MODE 0x0880 /* decides system clock frequency */ +/* frame dimension parameter host inputs */ +#define VS6624_LIGHT_FREQ 0x0C80 /* AC frequency used for flicker free time */ +#define VS6624_FLICKER_COMPAT 0x0C82 /* flicker compatible frame length */ +/* static frame rate control */ +#define VS6624_FR_NUM_MSB 0x0D81 /* desired frame rate numerator MSB */ +#define VS6624_FR_NUM_LSB 0x0D82 /* desired frame rate numerator LSB */ +#define VS6624_FR_DEN 0x0D84 /* desired frame rate denominator */ +/* automatic frame rate control */ +#define VS6624_DISABLE_FR_DAMPER 0x0E80 /* defines frame rate mode */ +#define VS6624_MIN_DAMPER_OUT_MSB 0x0E8C /* minimum frame rate MSB */ +#define VS6624_MIN_DAMPER_OUT_LSB 0x0E8A /* minimum frame rate LSB */ +/* exposure controls */ +#define VS6624_EXPO_MODE 0x1180 /* exposure mode */ +#define VS6624_EXPO_METER 0x1182 /* weights to be associated with the zones */ +#define VS6624_EXPO_TIME_NUM 0x1184 /* exposure time numerator */ +#define VS6624_EXPO_TIME_DEN 0x1186 /* exposure time denominator */ +#define VS6624_EXPO_TIME_MSB 0x1189 /* exposure time for the Manual Mode MSB */ +#define VS6624_EXPO_TIME_LSB 0x118A /* exposure time for the Manual Mode LSB */ +#define VS6624_EXPO_COMPENSATION 0x1190 /* exposure compensation */ +#define VS6624_DIRECT_COARSE_MSB 0x1195 /* coarse integration lines for Direct Mode MSB */ +#define VS6624_DIRECT_COARSE_LSB 0x1196 /* coarse integration lines for Direct Mode LSB */ +#define VS6624_DIRECT_FINE_MSB 0x1199 /* fine integration pixels for Direct Mode MSB */ +#define VS6624_DIRECT_FINE_LSB 0x119A /* fine integration pixels for Direct Mode LSB */ +#define VS6624_DIRECT_ANAL_GAIN_MSB 0x119D /* analog gain for Direct Mode MSB */ +#define VS6624_DIRECT_ANAL_GAIN_LSB 0x119E /* analog gain for Direct Mode LSB */ +#define VS6624_DIRECT_DIGI_GAIN_MSB 0x11A1 /* digital gain for Direct Mode MSB */ +#define VS6624_DIRECT_DIGI_GAIN_LSB 0x11A2 /* digital gain for Direct Mode LSB */ +#define VS6624_FLASH_COARSE_MSB 0x11A5 /* coarse integration lines for Flash Gun Mode MSB */ +#define VS6624_FLASH_COARSE_LSB 0x11A6 /* coarse integration lines for Flash Gun Mode LSB */ +#define VS6624_FLASH_FINE_MSB 0x11A9 /* fine integration pixels for Flash Gun Mode MSB */ +#define VS6624_FLASH_FINE_LSB 0x11AA /* fine integration pixels for Flash Gun Mode LSB */ +#define VS6624_FLASH_ANAL_GAIN_MSB 0x11AD /* analog gain for Flash Gun Mode MSB */ +#define VS6624_FLASH_ANAL_GAIN_LSB 0x11AE /* analog gain for Flash Gun Mode LSB */ +#define VS6624_FLASH_DIGI_GAIN_MSB 0x11B1 /* digital gain for Flash Gun Mode MSB */ +#define VS6624_FLASH_DIGI_GAIN_LSB 0x11B2 /* digital gain for Flash Gun Mode LSB */ +#define VS6624_FREEZE_AE 0x11B4 /* freeze auto exposure */ +#define VS6624_MAX_INT_TIME_MSB 0x11B7 /* user maximum integration time MSB */ +#define VS6624_MAX_INT_TIME_LSB 0x11B8 /* user maximum integration time LSB */ +#define VS6624_FLASH_AG_THR_MSB 0x11BB /* recommend flash gun analog gain threshold MSB */ +#define VS6624_FLASH_AG_THR_LSB 0x11BC /* recommend flash gun analog gain threshold LSB */ +#define VS6624_ANTI_FLICKER_MODE 0x11C0 /* anti flicker mode */ +/* white balance control */ +#define VS6624_WB_MODE 0x1480 /* set white balance mode */ +#define VS6624_MAN_RG 0x1482 /* user setting for red channel gain */ +#define VS6624_MAN_GG 0x1484 /* user setting for green channel gain */ +#define VS6624_MAN_BG 0x1486 /* user setting for blue channel gain */ +#define VS6624_FLASH_RG_MSB 0x148B /* red gain for Flash Gun MSB */ +#define VS6624_FLASH_RG_LSB 0x148C /* red gain for Flash Gun LSB */ +#define VS6624_FLASH_GG_MSB 0x148F /* green gain for Flash Gun MSB */ +#define VS6624_FLASH_GG_LSB 0x1490 /* green gain for Flash Gun LSB */ +#define VS6624_FLASH_BG_MSB 0x1493 /* blue gain for Flash Gun MSB */ +#define VS6624_FLASH_BG_LSB 0x1494 /* blue gain for Flash Gun LSB */ +/* sensor setup */ +#define VS6624_BC_OFFSET 0x1990 /* Black Correction Offset */ +/* image stability */ +#define VS6624_STABLE_WB 0x1900 /* white balance stable */ +#define VS6624_STABLE_EXPO 0x1902 /* exposure stable */ +#define VS6624_STABLE 0x1906 /* system stable */ +/* flash control */ +#define VS6624_FLASH_MODE 0x1A80 /* flash mode */ +#define VS6624_FLASH_OFF_LINE_MSB 0x1A83 /* off line at flash pulse mode MSB */ +#define VS6624_FLASH_OFF_LINE_LSB 0x1A84 /* off line at flash pulse mode LSB */ +/* flash status */ +#define VS6624_FLASH_RECOM 0x1B00 /* flash gun is recommended */ +#define VS6624_FLASH_GRAB_COMPLETE 0x1B02 /* flash gun image has been grabbed */ +/* scythe filter controls */ +#define VS6624_SCYTHE_FILTER 0x1D80 /* disable scythe defect correction */ +/* jack filter controls */ +#define VS6624_JACK_FILTER 0x1E00 /* disable jack defect correction */ +/* demosaic control */ +#define VS6624_ANTI_ALIAS_FILTER 0x1E80 /* anti alias filter suppress */ +/* color matrix dampers */ +#define VS6624_CM_DISABLE 0x1F00 /* disable color matrix damper */ +#define VS6624_CM_LOW_THR_MSB 0x1F03 /* low threshold for exposure MSB */ +#define VS6624_CM_LOW_THR_LSB 0x1F04 /* low threshold for exposure LSB */ +#define VS6624_CM_HIGH_THR_MSB 0x1F07 /* high threshold for exposure MSB */ +#define VS6624_CM_HIGH_THR_LSB 0x1F08 /* high threshold for exposure LSB */ +#define VS6624_CM_MIN_OUT_MSB 0x1F0B /* minimum possible damper output MSB */ +#define VS6624_CM_MIN_OUT_LSB 0x1F0C /* minimum possible damper output LSB */ +/* peaking control */ +#define VS6624_PEAK_GAIN 0x2000 /* controls peaking gain */ +#define VS6624_PEAK_G_DISABLE 0x2002 /* disable peak gain damping */ +#define VS6624_PEAK_LOW_THR_G_MSB 0x2005 /* low threshold for exposure for gain MSB */ +#define VS6624_PEAK_LOW_THR_G_LSB 0x2006 /* low threshold for exposure for gain LSB */ +#define VS6624_PEAK_HIGH_THR_G_MSB 0x2009 /* high threshold for exposure for gain MSB */ +#define VS6624_PEAK_HIGH_THR_G_LSB 0x200A /* high threshold for exposure for gain LSB */ +#define VS6624_PEAK_MIN_OUT_G_MSB 0x200D /* minimum damper output for gain MSB */ +#define VS6624_PEAK_MIN_OUT_G_LSB 0x200E /* minimum damper output for gain LSB */ +#define VS6624_PEAK_LOW_THR 0x2010 /* adjust degree of coring */ +#define VS6624_PEAK_C_DISABLE 0x2012 /* disable coring damping */ +#define VS6624_PEAK_HIGH_THR 0x2014 /* adjust maximum gain */ +#define VS6624_PEAK_LOW_THR_C_MSB 0x2017 /* low threshold for exposure for coring MSB */ +#define VS6624_PEAK_LOW_THR_C_LSB 0x2018 /* low threshold for exposure for coring LSB */ +#define VS6624_PEAK_HIGH_THR_C_MSB 0x201B /* high threshold for exposure for coring MSB */ +#define VS6624_PEAK_HIGH_THR_C_LSB 0x201C /* high threshold for exposure for coring LSB */ +#define VS6624_PEAK_MIN_OUT_C_MSB 0x201F /* minimum damper output for coring MSB */ +#define VS6624_PEAK_MIN_OUT_C_LSB 0x2020 /* minimum damper output for coring LSB */ +/* pipe 0 RGB to YUV matrix manual control */ +#define VS6624_RYM0_MAN_CTRL 0x2180 /* enable manual RGB to YUV matrix */ +#define VS6624_RYM0_W00_MSB 0x2183 /* row 0 column 0 of YUV matrix MSB */ +#define VS6624_RYM0_W00_LSB 0x2184 /* row 0 column 0 of YUV matrix LSB */ +#define VS6624_RYM0_W01_MSB 0x2187 /* row 0 column 1 of YUV matrix MSB */ +#define VS6624_RYM0_W01_LSB 0x2188 /* row 0 column 1 of YUV matrix LSB */ +#define VS6624_RYM0_W02_MSB 0x218C /* row 0 column 2 of YUV matrix MSB */ +#define VS6624_RYM0_W02_LSB 0x218D /* row 0 column 2 of YUV matrix LSB */ +#define VS6624_RYM0_W10_MSB 0x2190 /* row 1 column 0 of YUV matrix MSB */ +#define VS6624_RYM0_W10_LSB 0x218F /* row 1 column 0 of YUV matrix LSB */ +#define VS6624_RYM0_W11_MSB 0x2193 /* row 1 column 1 of YUV matrix MSB */ +#define VS6624_RYM0_W11_LSB 0x2194 /* row 1 column 1 of YUV matrix LSB */ +#define VS6624_RYM0_W12_MSB 0x2197 /* row 1 column 2 of YUV matrix MSB */ +#define VS6624_RYM0_W12_LSB 0x2198 /* row 1 column 2 of YUV matrix LSB */ +#define VS6624_RYM0_W20_MSB 0x219B /* row 2 column 0 of YUV matrix MSB */ +#define VS6624_RYM0_W20_LSB 0x219C /* row 2 column 0 of YUV matrix LSB */ +#define VS6624_RYM0_W21_MSB 0x21A0 /* row 2 column 1 of YUV matrix MSB */ +#define VS6624_RYM0_W21_LSB 0x219F /* row 2 column 1 of YUV matrix LSB */ +#define VS6624_RYM0_W22_MSB 0x21A3 /* row 2 column 2 of YUV matrix MSB */ +#define VS6624_RYM0_W22_LSB 0x21A4 /* row 2 column 2 of YUV matrix LSB */ +#define VS6624_RYM0_YINY_MSB 0x21A7 /* Y in Y MSB */ +#define VS6624_RYM0_YINY_LSB 0x21A8 /* Y in Y LSB */ +#define VS6624_RYM0_YINCB_MSB 0x21AB /* Y in Cb MSB */ +#define VS6624_RYM0_YINCB_LSB 0x21AC /* Y in Cb LSB */ +#define VS6624_RYM0_YINCR_MSB 0x21B0 /* Y in Cr MSB */ +#define VS6624_RYM0_YINCR_LSB 0x21AF /* Y in Cr LSB */ +/* pipe 1 RGB to YUV matrix manual control */ +#define VS6624_RYM1_MAN_CTRL 0x2200 /* enable manual RGB to YUV matrix */ +#define VS6624_RYM1_W00_MSB 0x2203 /* row 0 column 0 of YUV matrix MSB */ +#define VS6624_RYM1_W00_LSB 0x2204 /* row 0 column 0 of YUV matrix LSB */ +#define VS6624_RYM1_W01_MSB 0x2207 /* row 0 column 1 of YUV matrix MSB */ +#define VS6624_RYM1_W01_LSB 0x2208 /* row 0 column 1 of YUV matrix LSB */ +#define VS6624_RYM1_W02_MSB 0x220C /* row 0 column 2 of YUV matrix MSB */ +#define VS6624_RYM1_W02_LSB 0x220D /* row 0 column 2 of YUV matrix LSB */ +#define VS6624_RYM1_W10_MSB 0x2210 /* row 1 column 0 of YUV matrix MSB */ +#define VS6624_RYM1_W10_LSB 0x220F /* row 1 column 0 of YUV matrix LSB */ +#define VS6624_RYM1_W11_MSB 0x2213 /* row 1 column 1 of YUV matrix MSB */ +#define VS6624_RYM1_W11_LSB 0x2214 /* row 1 column 1 of YUV matrix LSB */ +#define VS6624_RYM1_W12_MSB 0x2217 /* row 1 column 2 of YUV matrix MSB */ +#define VS6624_RYM1_W12_LSB 0x2218 /* row 1 column 2 of YUV matrix LSB */ +#define VS6624_RYM1_W20_MSB 0x221B /* row 2 column 0 of YUV matrix MSB */ +#define VS6624_RYM1_W20_LSB 0x221C /* row 2 column 0 of YUV matrix LSB */ +#define VS6624_RYM1_W21_MSB 0x2220 /* row 2 column 1 of YUV matrix MSB */ +#define VS6624_RYM1_W21_LSB 0x221F /* row 2 column 1 of YUV matrix LSB */ +#define VS6624_RYM1_W22_MSB 0x2223 /* row 2 column 2 of YUV matrix MSB */ +#define VS6624_RYM1_W22_LSB 0x2224 /* row 2 column 2 of YUV matrix LSB */ +#define VS6624_RYM1_YINY_MSB 0x2227 /* Y in Y MSB */ +#define VS6624_RYM1_YINY_LSB 0x2228 /* Y in Y LSB */ +#define VS6624_RYM1_YINCB_MSB 0x222B /* Y in Cb MSB */ +#define VS6624_RYM1_YINCB_LSB 0x222C /* Y in Cb LSB */ +#define VS6624_RYM1_YINCR_MSB 0x2220 /* Y in Cr MSB */ +#define VS6624_RYM1_YINCR_LSB 0x222F /* Y in Cr LSB */ +/* pipe 0 gamma manual control */ +#define VS6624_GAMMA_MAN_CTRL0 0x2280 /* enable manual gamma setup */ +#define VS6624_GAMMA_PEAK_R0 0x2282 /* peaked red channel gamma value */ +#define VS6624_GAMMA_PEAK_G0 0x2284 /* peaked green channel gamma value */ +#define VS6624_GAMMA_PEAK_B0 0x2286 /* peaked blue channel gamma value */ +#define VS6624_GAMMA_UNPEAK_R0 0x2288 /* unpeaked red channel gamma value */ +#define VS6624_GAMMA_UNPEAK_G0 0x228A /* unpeaked green channel gamma value */ +#define VS6624_GAMMA_UNPEAK_B0 0x228C /* unpeaked blue channel gamma value */ +/* pipe 1 gamma manual control */ +#define VS6624_GAMMA_MAN_CTRL1 0x2300 /* enable manual gamma setup */ +#define VS6624_GAMMA_PEAK_R1 0x2302 /* peaked red channel gamma value */ +#define VS6624_GAMMA_PEAK_G1 0x2304 /* peaked green channel gamma value */ +#define VS6624_GAMMA_PEAK_B1 0x2306 /* peaked blue channel gamma value */ +#define VS6624_GAMMA_UNPEAK_R1 0x2308 /* unpeaked red channel gamma value */ +#define VS6624_GAMMA_UNPEAK_G1 0x230A /* unpeaked green channel gamma value */ +#define VS6624_GAMMA_UNPEAK_B1 0x230C /* unpeaked blue channel gamma value */ +/* fade to black */ +#define VS6624_F2B_DISABLE 0x2480 /* disable fade to black */ +#define VS6624_F2B_BLACK_VAL_MSB 0x2483 /* black value MSB */ +#define VS6624_F2B_BLACK_VAL_LSB 0x2484 /* black value LSB */ +#define VS6624_F2B_LOW_THR_MSB 0x2487 /* low threshold for exposure MSB */ +#define VS6624_F2B_LOW_THR_LSB 0x2488 /* low threshold for exposure LSB */ +#define VS6624_F2B_HIGH_THR_MSB 0x248B /* high threshold for exposure MSB */ +#define VS6624_F2B_HIGH_THR_LSB 0x248C /* high threshold for exposure LSB */ +#define VS6624_F2B_MIN_OUT_MSB 0x248F /* minimum damper output MSB */ +#define VS6624_F2B_MIN_OUT_LSB 0x2490 /* minimum damper output LSB */ +/* output formatter control */ +#define VS6624_CODE_CK_EN 0x2580 /* code check enable */ +#define VS6624_BLANK_FMT 0x2582 /* blank format */ +#define VS6624_SYNC_CODE_SETUP 0x2584 /* sync code setup */ +#define VS6624_HSYNC_SETUP 0x2586 /* H sync setup */ +#define VS6624_VSYNC_SETUP 0x2588 /* V sync setup */ +#define VS6624_PCLK_SETUP 0x258A /* PCLK setup */ +#define VS6624_PCLK_EN 0x258C /* PCLK enable */ +#define VS6624_OPF_SP_SETUP 0x258E /* output formatter sp setup */ +#define VS6624_BLANK_DATA_MSB 0x2590 /* blank data MSB */ +#define VS6624_BLANK_DATA_LSB 0x2592 /* blank data LSB */ +#define VS6624_RGB_SETUP 0x2594 /* RGB setup */ +#define VS6624_YUV_SETUP 0x2596 /* YUV setup */ +#define VS6624_VSYNC_RIS_COARSE_H 0x2598 /* V sync rising coarse high */ +#define VS6624_VSYNC_RIS_COARSE_L 0x259A /* V sync rising coarse low */ +#define VS6624_VSYNC_RIS_FINE_H 0x259C /* V sync rising fine high */ +#define VS6624_VSYNC_RIS_FINE_L 0x259E /* V sync rising fine low */ +#define VS6624_VSYNC_FALL_COARSE_H 0x25A0 /* V sync falling coarse high */ +#define VS6624_VSYNC_FALL_COARSE_L 0x25A2 /* V sync falling coarse low */ +#define VS6624_VSYNC_FALL_FINE_H 0x25A4 /* V sync falling fine high */ +#define VS6624_VSYNC_FALL_FINE_L 0x25A6 /* V sync falling fine low */ +#define VS6624_HSYNC_RIS_H 0x25A8 /* H sync rising high */ +#define VS6624_HSYNC_RIS_L 0x25AA /* H sync rising low */ +#define VS6624_HSYNC_FALL_H 0x25AC /* H sync falling high */ +#define VS6624_HSYNC_FALL_L 0x25AE /* H sync falling low */ +#define VS6624_OUT_IF 0x25B0 /* output interface */ +#define VS6624_CCP_EXT_DATA 0x25B2 /* CCP extra data */ +/* NoRA controls */ +#define VS6624_NORA_DISABLE 0x2600 /* NoRA control mode */ +#define VS6624_NORA_USAGE 0x2602 /* usage */ +#define VS6624_NORA_SPLIT_KN 0x2604 /* split kn */ +#define VS6624_NORA_SPLIT_NI 0x2606 /* split ni */ +#define VS6624_NORA_TIGHT_G 0x2608 /* tight green */ +#define VS6624_NORA_DISABLE_NP 0x260A /* disable noro promoting */ +#define VS6624_NORA_LOW_THR_MSB 0x260D /* low threshold for exposure MSB */ +#define VS6624_NORA_LOW_THR_LSB 0x260E /* low threshold for exposure LSB */ +#define VS6624_NORA_HIGH_THR_MSB 0x2611 /* high threshold for exposure MSB */ +#define VS6624_NORA_HIGH_THR_LSB 0x2612 /* high threshold for exposure LSB */ +#define VS6624_NORA_MIN_OUT_MSB 0x2615 /* minimum damper output MSB */ +#define VS6624_NORA_MIN_OUT_LSB 0x2616 /* minimum damper output LSB */ + +#endif diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c index 453dbbd1e6e8..7fd7ac567e1a 100644 --- a/drivers/media/video/w9966.c +++ b/drivers/media/video/w9966.c @@ -129,9 +129,9 @@ MODULE_LICENSE("GPL"); MODULE_VERSION("0.33.1"); #ifdef MODULE -static const char *pardev[] = {[0 ... W9966_MAXCAMS] = ""}; +static char *pardev[] = {[0 ... W9966_MAXCAMS] = ""}; #else -static const char *pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"}; +static char *pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"}; #endif module_param_array(pardev, charp, NULL, 0); MODULE_PARM_DESC(pardev, "pardev: where to search for\n" diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c index a22f765e968a..3bb99e93febe 100644 --- a/drivers/media/video/wm8739.c +++ b/drivers/media/video/wm8739.c @@ -291,15 +291,4 @@ static struct i2c_driver wm8739_driver = { .id_table = wm8739_id, }; -static __init int init_wm8739(void) -{ - return i2c_add_driver(&wm8739_driver); -} - -static __exit void exit_wm8739(void) -{ - i2c_del_driver(&wm8739_driver); -} - -module_init(init_wm8739); -module_exit(exit_wm8739); +module_i2c_driver(wm8739_driver); diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c index 9cedb1e69b58..bee77ea9f49e 100644 --- a/drivers/media/video/wm8775.c +++ b/drivers/media/video/wm8775.c @@ -339,15 +339,4 @@ static struct i2c_driver wm8775_driver = { .id_table = wm8775_id, }; -static __init int init_wm8775(void) -{ - return i2c_add_driver(&wm8775_driver); -} - -static __exit void exit_wm8775(void) -{ - i2c_del_driver(&wm8775_driver); -} - -module_init(init_wm8775); -module_exit(exit_wm8775); +module_i2c_driver(wm8775_driver); diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index a7dc4672d996..a5c591ffe395 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -346,7 +346,7 @@ static int mpt_remove_dead_ioc_func(void *arg) if ((pdev == NULL)) return -1; - pci_remove_bus_device(pdev); + pci_stop_and_remove_bus_device(pdev); return 0; } diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 1489c3540f96..243e0c663c37 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -848,8 +848,9 @@ config MCP_SA11X0 # Chip drivers config MCP_UCB1200 - tristate "Support for UCB1200 / UCB1300" - depends on MCP + bool "Support for UCB1200 / UCB1300" + depends on MCP_SA11X0 + select MCP config MCP_UCB1200_TS tristate "Touchscreen interface support" diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c index 86cc3f7841cd..6acf2e03f2ba 100644 --- a/drivers/mfd/mcp-core.c +++ b/drivers/mfd/mcp-core.c @@ -19,7 +19,6 @@ #include <linux/string.h> #include <linux/mfd/mcp.h> -#include <mach/dma.h> #include <asm/system.h> @@ -48,39 +47,11 @@ static int mcp_bus_remove(struct device *dev) return 0; } -static int mcp_bus_suspend(struct device *dev, pm_message_t state) -{ - struct mcp *mcp = to_mcp(dev); - int ret = 0; - - if (dev->driver) { - struct mcp_driver *drv = to_mcp_driver(dev->driver); - - ret = drv->suspend(mcp, state); - } - return ret; -} - -static int mcp_bus_resume(struct device *dev) -{ - struct mcp *mcp = to_mcp(dev); - int ret = 0; - - if (dev->driver) { - struct mcp_driver *drv = to_mcp_driver(dev->driver); - - ret = drv->resume(mcp); - } - return ret; -} - static struct bus_type mcp_bus_type = { .name = "mcp", .match = mcp_bus_match, .probe = mcp_bus_probe, .remove = mcp_bus_remove, - .suspend = mcp_bus_suspend, - .resume = mcp_bus_resume, }; /** @@ -208,6 +179,7 @@ struct mcp *mcp_host_alloc(struct device *parent, size_t size) mcp = kzalloc(sizeof(struct mcp) + size, GFP_KERNEL); if (mcp) { spin_lock_init(&mcp->lock); + device_initialize(&mcp->attached_device); mcp->attached_device.parent = parent; mcp->attached_device.bus = &mcp_bus_type; mcp->attached_device.dma_mask = parent->dma_mask; @@ -217,18 +189,25 @@ struct mcp *mcp_host_alloc(struct device *parent, size_t size) } EXPORT_SYMBOL(mcp_host_alloc); -int mcp_host_register(struct mcp *mcp) +int mcp_host_add(struct mcp *mcp, void *pdata) { + mcp->attached_device.platform_data = pdata; dev_set_name(&mcp->attached_device, "mcp0"); - return device_register(&mcp->attached_device); + return device_add(&mcp->attached_device); +} +EXPORT_SYMBOL(mcp_host_add); + +void mcp_host_del(struct mcp *mcp) +{ + device_del(&mcp->attached_device); } -EXPORT_SYMBOL(mcp_host_register); +EXPORT_SYMBOL(mcp_host_del); -void mcp_host_unregister(struct mcp *mcp) +void mcp_host_free(struct mcp *mcp) { - device_unregister(&mcp->attached_device); + put_device(&mcp->attached_device); } -EXPORT_SYMBOL(mcp_host_unregister); +EXPORT_SYMBOL(mcp_host_free); int mcp_driver_register(struct mcp_driver *mcpdrv) { diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 02c53a0766c4..1c0ceacaa1f6 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -13,51 +13,61 @@ */ #include <linux/module.h> #include <linux/init.h> +#include <linux/io.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/spinlock.h> #include <linux/platform_device.h> +#include <linux/pm.h> #include <linux/mfd/mcp.h> -#include <mach/dma.h> #include <mach/hardware.h> #include <asm/mach-types.h> #include <asm/system.h> #include <mach/mcp.h> -#include <mach/assabet.h> - +#define DRIVER_NAME "sa11x0-mcp" struct mcp_sa11x0 { - u32 mccr0; - u32 mccr1; + void __iomem *base0; + void __iomem *base1; + u32 mccr0; + u32 mccr1; }; +/* Register offsets */ +#define MCCR0(m) ((m)->base0 + 0x00) +#define MCDR0(m) ((m)->base0 + 0x08) +#define MCDR1(m) ((m)->base0 + 0x0c) +#define MCDR2(m) ((m)->base0 + 0x10) +#define MCSR(m) ((m)->base0 + 0x18) +#define MCCR1(m) ((m)->base1 + 0x00) + #define priv(mcp) ((struct mcp_sa11x0 *)mcp_priv(mcp)) static void mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor) { - unsigned int mccr0; + struct mcp_sa11x0 *m = priv(mcp); divisor /= 32; - mccr0 = Ser4MCCR0 & ~0x00007f00; - mccr0 |= divisor << 8; - Ser4MCCR0 = mccr0; + m->mccr0 &= ~0x00007f00; + m->mccr0 |= divisor << 8; + writel_relaxed(m->mccr0, MCCR0(m)); } static void mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor) { - unsigned int mccr0; + struct mcp_sa11x0 *m = priv(mcp); divisor /= 32; - mccr0 = Ser4MCCR0 & ~0x0000007f; - mccr0 |= divisor; - Ser4MCCR0 = mccr0; + m->mccr0 &= ~0x0000007f; + m->mccr0 |= divisor; + writel_relaxed(m->mccr0, MCCR0(m)); } /* @@ -69,14 +79,15 @@ mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor) static void mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val) { + struct mcp_sa11x0 *m = priv(mcp); int ret = -ETIME; int i; - Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff); + writel_relaxed(reg << 17 | MCDR2_Wr | (val & 0xffff), MCDR2(m)); for (i = 0; i < 2; i++) { udelay(mcp->rw_timeout); - if (Ser4MCSR & MCSR_CWC) { + if (readl_relaxed(MCSR(m)) & MCSR_CWC) { ret = 0; break; } @@ -95,15 +106,16 @@ mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val) static unsigned int mcp_sa11x0_read(struct mcp *mcp, unsigned int reg) { + struct mcp_sa11x0 *m = priv(mcp); int ret = -ETIME; int i; - Ser4MCDR2 = reg << 17 | MCDR2_Rd; + writel_relaxed(reg << 17 | MCDR2_Rd, MCDR2(m)); for (i = 0; i < 2; i++) { udelay(mcp->rw_timeout); - if (Ser4MCSR & MCSR_CRC) { - ret = Ser4MCDR2 & 0xffff; + if (readl_relaxed(MCSR(m)) & MCSR_CRC) { + ret = readl_relaxed(MCDR2(m)) & 0xffff; break; } } @@ -116,13 +128,19 @@ mcp_sa11x0_read(struct mcp *mcp, unsigned int reg) static void mcp_sa11x0_enable(struct mcp *mcp) { - Ser4MCSR = -1; - Ser4MCCR0 |= MCCR0_MCE; + struct mcp_sa11x0 *m = priv(mcp); + + writel(-1, MCSR(m)); + m->mccr0 |= MCCR0_MCE; + writel_relaxed(m->mccr0, MCCR0(m)); } static void mcp_sa11x0_disable(struct mcp *mcp) { - Ser4MCCR0 &= ~MCCR0_MCE; + struct mcp_sa11x0 *m = priv(mcp); + + m->mccr0 &= ~MCCR0_MCE; + writel_relaxed(m->mccr0, MCCR0(m)); } /* @@ -137,55 +155,64 @@ static struct mcp_ops mcp_sa11x0 = { .disable = mcp_sa11x0_disable, }; -static int mcp_sa11x0_probe(struct platform_device *pdev) +static int mcp_sa11x0_probe(struct platform_device *dev) { - struct mcp_plat_data *data = pdev->dev.platform_data; + struct mcp_plat_data *data = dev->dev.platform_data; + struct resource *mem0, *mem1; + struct mcp_sa11x0 *m; struct mcp *mcp; int ret; if (!data) return -ENODEV; - if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp")) - return -EBUSY; + mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0); + mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1); + if (!mem0 || !mem1) + return -ENXIO; + + if (!request_mem_region(mem0->start, resource_size(mem0), + DRIVER_NAME)) { + ret = -EBUSY; + goto err_mem0; + } - mcp = mcp_host_alloc(&pdev->dev, sizeof(struct mcp_sa11x0)); + if (!request_mem_region(mem1->start, resource_size(mem1), + DRIVER_NAME)) { + ret = -EBUSY; + goto err_mem1; + } + + mcp = mcp_host_alloc(&dev->dev, sizeof(struct mcp_sa11x0)); if (!mcp) { ret = -ENOMEM; - goto release; + goto err_alloc; } mcp->owner = THIS_MODULE; mcp->ops = &mcp_sa11x0; mcp->sclk_rate = data->sclk_rate; - mcp->dma_audio_rd = DMA_Ser4MCP0Rd; - mcp->dma_audio_wr = DMA_Ser4MCP0Wr; - mcp->dma_telco_rd = DMA_Ser4MCP1Rd; - mcp->dma_telco_wr = DMA_Ser4MCP1Wr; - mcp->gpio_base = data->gpio_base; - platform_set_drvdata(pdev, mcp); + m = priv(mcp); + m->mccr0 = data->mccr0 | 0x7f7f; + m->mccr1 = data->mccr1; - if (machine_is_assabet()) { - ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); + m->base0 = ioremap(mem0->start, resource_size(mem0)); + m->base1 = ioremap(mem1->start, resource_size(mem1)); + if (!m->base0 || !m->base1) { + ret = -ENOMEM; + goto err_ioremap; } - /* - * Setup the PPC unit correctly. - */ - PPDR &= ~PPC_RXD4; - PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; - PSDR |= PPC_RXD4; - PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); - PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + platform_set_drvdata(dev, mcp); /* * Initialise device. Note that we initially * set the sampling rate to minimum. */ - Ser4MCSR = -1; - Ser4MCCR1 = data->mccr1; - Ser4MCCR0 = data->mccr0 | 0x7f7f; + writel_relaxed(-1, MCSR(m)); + writel_relaxed(m->mccr1, MCCR1(m)); + writel_relaxed(m->mccr0, MCCR0(m)); /* * Calculate the read/write timeout (us) from the bit clock @@ -195,62 +222,90 @@ static int mcp_sa11x0_probe(struct platform_device *pdev) mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / mcp->sclk_rate; - ret = mcp_host_register(mcp); + ret = mcp_host_add(mcp, data->codec_pdata); if (ret == 0) - goto out; + return 0; - release: - release_mem_region(0x80060000, 0x60); - platform_set_drvdata(pdev, NULL); + platform_set_drvdata(dev, NULL); - out: + err_ioremap: + iounmap(m->base1); + iounmap(m->base0); + mcp_host_free(mcp); + err_alloc: + release_mem_region(mem1->start, resource_size(mem1)); + err_mem1: + release_mem_region(mem0->start, resource_size(mem0)); + err_mem0: return ret; } static int mcp_sa11x0_remove(struct platform_device *dev) { struct mcp *mcp = platform_get_drvdata(dev); + struct mcp_sa11x0 *m = priv(mcp); + struct resource *mem0, *mem1; + + if (m->mccr0 & MCCR0_MCE) + dev_warn(&dev->dev, + "device left active (missing disable call?)\n"); + + mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0); + mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1); platform_set_drvdata(dev, NULL); - mcp_host_unregister(mcp); - release_mem_region(0x80060000, 0x60); + mcp_host_del(mcp); + iounmap(m->base1); + iounmap(m->base0); + mcp_host_free(mcp); + release_mem_region(mem1->start, resource_size(mem1)); + release_mem_region(mem0->start, resource_size(mem0)); return 0; } -static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int mcp_sa11x0_suspend(struct device *dev) { - struct mcp *mcp = platform_get_drvdata(dev); + struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev)); + + if (m->mccr0 & MCCR0_MCE) + dev_warn(dev, "device left active (missing disable call?)\n"); - priv(mcp)->mccr0 = Ser4MCCR0; - priv(mcp)->mccr1 = Ser4MCCR1; - Ser4MCCR0 &= ~MCCR0_MCE; + writel(m->mccr0 & ~MCCR0_MCE, MCCR0(m)); return 0; } -static int mcp_sa11x0_resume(struct platform_device *dev) +static int mcp_sa11x0_resume(struct device *dev) { - struct mcp *mcp = platform_get_drvdata(dev); + struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev)); - Ser4MCCR1 = priv(mcp)->mccr1; - Ser4MCCR0 = priv(mcp)->mccr0; + writel_relaxed(m->mccr1, MCCR1(m)); + writel_relaxed(m->mccr0, MCCR0(m)); return 0; } - -/* - * The driver for the SA11x0 MCP port. - */ -MODULE_ALIAS("platform:sa11x0-mcp"); +#endif + +static const struct dev_pm_ops mcp_sa11x0_pm_ops = { +#ifdef CONFIG_PM_SLEEP + .suspend = mcp_sa11x0_suspend, + .freeze = mcp_sa11x0_suspend, + .poweroff = mcp_sa11x0_suspend, + .resume_noirq = mcp_sa11x0_resume, + .thaw_noirq = mcp_sa11x0_resume, + .restore_noirq = mcp_sa11x0_resume, +#endif +}; static struct platform_driver mcp_sa11x0_driver = { .probe = mcp_sa11x0_probe, .remove = mcp_sa11x0_remove, - .suspend = mcp_sa11x0_suspend, - .resume = mcp_sa11x0_resume, .driver = { - .name = "sa11x0-mcp", + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .pm = &mcp_sa11x0_pm_ops, }, }; @@ -259,6 +314,7 @@ static struct platform_driver mcp_sa11x0_driver = { */ module_platform_driver(mcp_sa11x0_driver); +MODULE_ALIAS("platform:" DRIVER_NAME); MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); MODULE_DESCRIPTION("SA11x0 multimedia communications port driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/ucb1x00-assabet.c b/drivers/mfd/ucb1x00-assabet.c index cea9da60850d..b63c0756a669 100644 --- a/drivers/mfd/ucb1x00-assabet.c +++ b/drivers/mfd/ucb1x00-assabet.c @@ -11,14 +11,15 @@ */ #include <linux/module.h> #include <linux/init.h> +#include <linux/device.h> +#include <linux/err.h> #include <linux/fs.h> +#include <linux/gpio_keys.h> +#include <linux/input.h> +#include <linux/platform_device.h> #include <linux/proc_fs.h> -#include <linux/device.h> #include <linux/mfd/ucb1x00.h> -#include <mach/dma.h> - - #define UCB1X00_ATTR(name,input)\ static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \ char *buf) \ @@ -38,14 +39,45 @@ UCB1X00_ATTR(batt_temp, UCB_ADC_INP_AD2); static int ucb1x00_assabet_add(struct ucb1x00_dev *dev) { - device_create_file(&dev->ucb->dev, &dev_attr_vbatt); - device_create_file(&dev->ucb->dev, &dev_attr_vcharger); - device_create_file(&dev->ucb->dev, &dev_attr_batt_temp); + struct ucb1x00 *ucb = dev->ucb; + struct platform_device *pdev; + struct gpio_keys_platform_data keys; + static struct gpio_keys_button buttons[6]; + unsigned i; + + memset(buttons, 0, sizeof(buttons)); + memset(&keys, 0, sizeof(keys)); + + for (i = 0; i < ARRAY_SIZE(buttons); i++) { + buttons[i].code = BTN_0 + i; + buttons[i].gpio = ucb->gpio.base + i; + buttons[i].type = EV_KEY; + buttons[i].can_disable = true; + } + + keys.buttons = buttons; + keys.nbuttons = ARRAY_SIZE(buttons); + keys.poll_interval = 50; + keys.name = "ucb1x00"; + + pdev = platform_device_register_data(&ucb->dev, "gpio-keys", -1, + &keys, sizeof(keys)); + + device_create_file(&ucb->dev, &dev_attr_vbatt); + device_create_file(&ucb->dev, &dev_attr_vcharger); + device_create_file(&ucb->dev, &dev_attr_batt_temp); + + dev->priv = pdev; return 0; } static void ucb1x00_assabet_remove(struct ucb1x00_dev *dev) { + struct platform_device *pdev = dev->priv; + + if (!IS_ERR(pdev)) + platform_device_unregister(pdev); + device_remove_file(&dev->ucb->dev, &dev_attr_batt_temp); device_remove_file(&dev->ucb->dev, &dev_attr_vcharger); device_remove_file(&dev->ucb->dev, &dev_attr_vbatt); diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index febc90cdef7e..70f02daeb22a 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -23,14 +23,12 @@ #include <linux/init.h> #include <linux/errno.h> #include <linux/interrupt.h> +#include <linux/irq.h> #include <linux/device.h> #include <linux/mutex.h> #include <linux/mfd/ucb1x00.h> +#include <linux/pm.h> #include <linux/gpio.h> -#include <linux/semaphore.h> - -#include <mach/dma.h> -#include <mach/hardware.h> static DEFINE_MUTEX(ucb1x00_mutex); static LIST_HEAD(ucb1x00_drivers); @@ -102,7 +100,7 @@ void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int set, unsigned int clear) * ucb1x00_enable must have been called to enable the comms * before using this function. * - * This function does not take any semaphores or spinlocks. + * This function does not take any mutexes or spinlocks. */ unsigned int ucb1x00_io_read(struct ucb1x00 *ucb) { @@ -120,14 +118,22 @@ static void ucb1x00_gpio_set(struct gpio_chip *chip, unsigned offset, int value) else ucb->io_out &= ~(1 << offset); + ucb1x00_enable(ucb); ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); + ucb1x00_disable(ucb); spin_unlock_irqrestore(&ucb->io_lock, flags); } static int ucb1x00_gpio_get(struct gpio_chip *chip, unsigned offset) { struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio); - return ucb1x00_reg_read(ucb, UCB_IO_DATA) & (1 << offset); + unsigned val; + + ucb1x00_enable(ucb); + val = ucb1x00_reg_read(ucb, UCB_IO_DATA); + ucb1x00_disable(ucb); + + return val & (1 << offset); } static int ucb1x00_gpio_direction_input(struct gpio_chip *chip, unsigned offset) @@ -137,7 +143,9 @@ static int ucb1x00_gpio_direction_input(struct gpio_chip *chip, unsigned offset) spin_lock_irqsave(&ucb->io_lock, flags); ucb->io_dir &= ~(1 << offset); + ucb1x00_enable(ucb); ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); + ucb1x00_disable(ucb); spin_unlock_irqrestore(&ucb->io_lock, flags); return 0; @@ -157,6 +165,7 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset else ucb->io_out &= ~mask; + ucb1x00_enable(ucb); if (old != ucb->io_out) ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); @@ -164,11 +173,19 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset ucb->io_dir |= mask; ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); } + ucb1x00_disable(ucb); spin_unlock_irqrestore(&ucb->io_lock, flags); return 0; } +static int ucb1x00_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio); + + return ucb->irq_base > 0 ? ucb->irq_base + offset : -ENXIO; +} + /* * UCB1300 data sheet says we must: * 1. enable ADC => 5us (including reference startup time) @@ -186,7 +203,7 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset * Any code wishing to use the ADC converter must call this * function prior to using it. * - * This function takes the ADC semaphore to prevent two or more + * This function takes the ADC mutex to prevent two or more * concurrent uses, and therefore may sleep. As a result, it * can only be called from process context, not interrupt * context. @@ -196,7 +213,7 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset */ void ucb1x00_adc_enable(struct ucb1x00 *ucb) { - down(&ucb->adc_sem); + mutex_lock(&ucb->adc_mutex); ucb->adc_cr |= UCB_ADC_ENA; @@ -218,7 +235,7 @@ void ucb1x00_adc_enable(struct ucb1x00 *ucb) * complete (2 frames max without sync). * * If called for a synchronised ADC conversion, it may sleep - * with the ADC semaphore held. + * with the ADC mutex held. */ unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync) { @@ -246,7 +263,7 @@ unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync) * ucb1x00_adc_disable - disable the ADC converter * @ucb: UCB1x00 structure describing chip * - * Disable the ADC converter and release the ADC semaphore. + * Disable the ADC converter and release the ADC mutex. */ void ucb1x00_adc_disable(struct ucb1x00 *ucb) { @@ -254,7 +271,7 @@ void ucb1x00_adc_disable(struct ucb1x00 *ucb) ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr); ucb1x00_disable(ucb); - up(&ucb->adc_sem); + mutex_unlock(&ucb->adc_mutex); } /* @@ -265,10 +282,9 @@ void ucb1x00_adc_disable(struct ucb1x00 *ucb) * SIBCLK to talk to the chip. We leave the clock running until * we have finished processing all interrupts from the chip. */ -static irqreturn_t ucb1x00_irq(int irqnr, void *devid) +static void ucb1x00_irq(unsigned int irq, struct irq_desc *desc) { - struct ucb1x00 *ucb = devid; - struct ucb1x00_irq *irq; + struct ucb1x00 *ucb = irq_desc_get_handler_data(desc); unsigned int isr, i; ucb1x00_enable(ucb); @@ -276,157 +292,104 @@ static irqreturn_t ucb1x00_irq(int irqnr, void *devid) ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr); ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); - for (i = 0, irq = ucb->irq_handler; i < 16 && isr; i++, isr >>= 1, irq++) - if (isr & 1 && irq->fn) - irq->fn(i, irq->devid); + for (i = 0; i < 16 && isr; i++, isr >>= 1, irq++) + if (isr & 1) + generic_handle_irq(ucb->irq_base + i); ucb1x00_disable(ucb); - - return IRQ_HANDLED; } -/** - * ucb1x00_hook_irq - hook a UCB1x00 interrupt - * @ucb: UCB1x00 structure describing chip - * @idx: interrupt index - * @fn: function to call when interrupt is triggered - * @devid: device id to pass to interrupt handler - * - * Hook the specified interrupt. You can only register one handler - * for each interrupt source. The interrupt source is not enabled - * by this function; use ucb1x00_enable_irq instead. - * - * Interrupt handlers will be called with other interrupts enabled. - * - * Returns zero on success, or one of the following errors: - * -EINVAL if the interrupt index is invalid - * -EBUSY if the interrupt has already been hooked - */ -int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid) +static void ucb1x00_irq_update(struct ucb1x00 *ucb, unsigned mask) { - struct ucb1x00_irq *irq; - int ret = -EINVAL; - - if (idx < 16) { - irq = ucb->irq_handler + idx; - ret = -EBUSY; - - spin_lock_irq(&ucb->lock); - if (irq->fn == NULL) { - irq->devid = devid; - irq->fn = fn; - ret = 0; - } - spin_unlock_irq(&ucb->lock); - } - return ret; + ucb1x00_enable(ucb); + if (ucb->irq_ris_enbl & mask) + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & + ucb->irq_mask); + if (ucb->irq_fal_enbl & mask) + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & + ucb->irq_mask); + ucb1x00_disable(ucb); } -/** - * ucb1x00_enable_irq - enable an UCB1x00 interrupt source - * @ucb: UCB1x00 structure describing chip - * @idx: interrupt index - * @edges: interrupt edges to enable - * - * Enable the specified interrupt to trigger on %UCB_RISING, - * %UCB_FALLING or both edges. The interrupt should have been - * hooked by ucb1x00_hook_irq. - */ -void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) +static void ucb1x00_irq_noop(struct irq_data *data) { - unsigned long flags; +} - if (idx < 16) { - spin_lock_irqsave(&ucb->lock, flags); +static void ucb1x00_irq_mask(struct irq_data *data) +{ + struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); + unsigned mask = 1 << (data->irq - ucb->irq_base); - ucb1x00_enable(ucb); - if (edges & UCB_RISING) { - ucb->irq_ris_enbl |= 1 << idx; - ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); - } - if (edges & UCB_FALLING) { - ucb->irq_fal_enbl |= 1 << idx; - ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); - } - ucb1x00_disable(ucb); - spin_unlock_irqrestore(&ucb->lock, flags); - } + raw_spin_lock(&ucb->irq_lock); + ucb->irq_mask &= ~mask; + ucb1x00_irq_update(ucb, mask); + raw_spin_unlock(&ucb->irq_lock); } -/** - * ucb1x00_disable_irq - disable an UCB1x00 interrupt source - * @ucb: UCB1x00 structure describing chip - * @edges: interrupt edges to disable - * - * Disable the specified interrupt triggering on the specified - * (%UCB_RISING, %UCB_FALLING or both) edges. - */ -void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) +static void ucb1x00_irq_unmask(struct irq_data *data) { - unsigned long flags; + struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); + unsigned mask = 1 << (data->irq - ucb->irq_base); - if (idx < 16) { - spin_lock_irqsave(&ucb->lock, flags); - - ucb1x00_enable(ucb); - if (edges & UCB_RISING) { - ucb->irq_ris_enbl &= ~(1 << idx); - ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); - } - if (edges & UCB_FALLING) { - ucb->irq_fal_enbl &= ~(1 << idx); - ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); - } - ucb1x00_disable(ucb); - spin_unlock_irqrestore(&ucb->lock, flags); - } + raw_spin_lock(&ucb->irq_lock); + ucb->irq_mask |= mask; + ucb1x00_irq_update(ucb, mask); + raw_spin_unlock(&ucb->irq_lock); } -/** - * ucb1x00_free_irq - disable and free the specified UCB1x00 interrupt - * @ucb: UCB1x00 structure describing chip - * @idx: interrupt index - * @devid: device id. - * - * Disable the interrupt source and remove the handler. devid must - * match the devid passed when hooking the interrupt. - * - * Returns zero on success, or one of the following errors: - * -EINVAL if the interrupt index is invalid - * -ENOENT if devid does not match - */ -int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid) +static int ucb1x00_irq_set_type(struct irq_data *data, unsigned int type) { - struct ucb1x00_irq *irq; - int ret; + struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); + unsigned mask = 1 << (data->irq - ucb->irq_base); - if (idx >= 16) - goto bad; + raw_spin_lock(&ucb->irq_lock); + if (type & IRQ_TYPE_EDGE_RISING) + ucb->irq_ris_enbl |= mask; + else + ucb->irq_ris_enbl &= ~mask; - irq = ucb->irq_handler + idx; - ret = -ENOENT; + if (type & IRQ_TYPE_EDGE_FALLING) + ucb->irq_fal_enbl |= mask; + else + ucb->irq_fal_enbl &= ~mask; + if (ucb->irq_mask & mask) { + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & + ucb->irq_mask); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & + ucb->irq_mask); + } + raw_spin_unlock(&ucb->irq_lock); - spin_lock_irq(&ucb->lock); - if (irq->devid == devid) { - ucb->irq_ris_enbl &= ~(1 << idx); - ucb->irq_fal_enbl &= ~(1 << idx); + return 0; +} - ucb1x00_enable(ucb); - ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); - ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); - ucb1x00_disable(ucb); +static int ucb1x00_irq_set_wake(struct irq_data *data, unsigned int on) +{ + struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); + struct ucb1x00_plat_data *pdata = ucb->mcp->attached_device.platform_data; + unsigned mask = 1 << (data->irq - ucb->irq_base); - irq->fn = NULL; - irq->devid = NULL; - ret = 0; - } - spin_unlock_irq(&ucb->lock); - return ret; + if (!pdata || !pdata->can_wakeup) + return -EINVAL; -bad: - printk(KERN_ERR "Freeing bad UCB1x00 irq %d\n", idx); - return -EINVAL; + raw_spin_lock(&ucb->irq_lock); + if (on) + ucb->irq_wake |= mask; + else + ucb->irq_wake &= ~mask; + raw_spin_unlock(&ucb->irq_lock); + + return 0; } +static struct irq_chip ucb1x00_irqchip = { + .name = "ucb1x00", + .irq_ack = ucb1x00_irq_noop, + .irq_mask = ucb1x00_irq_mask, + .irq_unmask = ucb1x00_irq_unmask, + .irq_set_type = ucb1x00_irq_set_type, + .irq_set_wake = ucb1x00_irq_set_wake, +}; + static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv) { struct ucb1x00_dev *dev; @@ -440,8 +403,8 @@ static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv) ret = drv->add(dev); if (ret == 0) { - list_add(&dev->dev_node, &ucb->devs); - list_add(&dev->drv_node, &drv->devs); + list_add_tail(&dev->dev_node, &ucb->devs); + list_add_tail(&dev->drv_node, &drv->devs); } else { kfree(dev); } @@ -533,98 +496,126 @@ static struct class ucb1x00_class = { static int ucb1x00_probe(struct mcp *mcp) { - struct ucb1x00 *ucb; + struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data; struct ucb1x00_driver *drv; - unsigned int id; + struct ucb1x00 *ucb; + unsigned id, i, irq_base; int ret = -ENODEV; - int temp; + + /* Tell the platform to deassert the UCB1x00 reset */ + if (pdata && pdata->reset) + pdata->reset(UCB_RST_PROBE); mcp_enable(mcp); id = mcp_reg_read(mcp, UCB_ID); + mcp_disable(mcp); if (id != UCB_ID_1200 && id != UCB_ID_1300 && id != UCB_ID_TC35143) { printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id); - goto err_disable; + goto out; } ucb = kzalloc(sizeof(struct ucb1x00), GFP_KERNEL); ret = -ENOMEM; if (!ucb) - goto err_disable; - + goto out; + device_initialize(&ucb->dev); ucb->dev.class = &ucb1x00_class; ucb->dev.parent = &mcp->attached_device; dev_set_name(&ucb->dev, "ucb1x00"); - spin_lock_init(&ucb->lock); + raw_spin_lock_init(&ucb->irq_lock); spin_lock_init(&ucb->io_lock); - sema_init(&ucb->adc_sem, 1); + mutex_init(&ucb->adc_mutex); ucb->id = id; ucb->mcp = mcp; + + ret = device_add(&ucb->dev); + if (ret) + goto err_dev_add; + + ucb1x00_enable(ucb); ucb->irq = ucb1x00_detect_irq(ucb); + ucb1x00_disable(ucb); if (ucb->irq == NO_IRQ) { - printk(KERN_ERR "UCB1x00: IRQ probe failed\n"); + dev_err(&ucb->dev, "IRQ probe failed\n"); ret = -ENODEV; - goto err_free; + goto err_no_irq; } ucb->gpio.base = -1; - if (mcp->gpio_base != 0) { + irq_base = pdata ? pdata->irq_base : 0; + ucb->irq_base = irq_alloc_descs(-1, irq_base, 16, -1); + if (ucb->irq_base < 0) { + dev_err(&ucb->dev, "unable to allocate 16 irqs: %d\n", + ucb->irq_base); + goto err_irq_alloc; + } + + for (i = 0; i < 16; i++) { + unsigned irq = ucb->irq_base + i; + + irq_set_chip_and_handler(irq, &ucb1x00_irqchip, handle_edge_irq); + irq_set_chip_data(irq, ucb); + set_irq_flags(irq, IRQF_VALID | IRQ_NOREQUEST); + } + + irq_set_irq_type(ucb->irq, IRQ_TYPE_EDGE_RISING); + irq_set_handler_data(ucb->irq, ucb); + irq_set_chained_handler(ucb->irq, ucb1x00_irq); + + if (pdata && pdata->gpio_base) { ucb->gpio.label = dev_name(&ucb->dev); - ucb->gpio.base = mcp->gpio_base; + ucb->gpio.dev = &ucb->dev; + ucb->gpio.owner = THIS_MODULE; + ucb->gpio.base = pdata->gpio_base; ucb->gpio.ngpio = 10; ucb->gpio.set = ucb1x00_gpio_set; ucb->gpio.get = ucb1x00_gpio_get; ucb->gpio.direction_input = ucb1x00_gpio_direction_input; ucb->gpio.direction_output = ucb1x00_gpio_direction_output; + ucb->gpio.to_irq = ucb1x00_to_irq; ret = gpiochip_add(&ucb->gpio); if (ret) - goto err_free; + goto err_gpio_add; } else dev_info(&ucb->dev, "gpio_base not set so no gpiolib support"); - ret = request_irq(ucb->irq, ucb1x00_irq, IRQF_TRIGGER_RISING, - "UCB1x00", ucb); - if (ret) { - printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n", - ucb->irq, ret); - goto err_gpio; - } - mcp_set_drvdata(mcp, ucb); - ret = device_register(&ucb->dev); - if (ret) - goto err_irq; - + if (pdata) + device_set_wakeup_capable(&ucb->dev, pdata->can_wakeup); INIT_LIST_HEAD(&ucb->devs); mutex_lock(&ucb1x00_mutex); - list_add(&ucb->node, &ucb1x00_devices); + list_add_tail(&ucb->node, &ucb1x00_devices); list_for_each_entry(drv, &ucb1x00_drivers, node) { ucb1x00_add_dev(ucb, drv); } mutex_unlock(&ucb1x00_mutex); - goto out; + return ret; - err_irq: - free_irq(ucb->irq, ucb); - err_gpio: - if (ucb->gpio.base != -1) - temp = gpiochip_remove(&ucb->gpio); - err_free: - kfree(ucb); - err_disable: - mcp_disable(mcp); + err_gpio_add: + irq_set_chained_handler(ucb->irq, NULL); + err_irq_alloc: + if (ucb->irq_base > 0) + irq_free_descs(ucb->irq_base, 16); + err_no_irq: + device_del(&ucb->dev); + err_dev_add: + put_device(&ucb->dev); out: + if (pdata && pdata->reset) + pdata->reset(UCB_RST_PROBE_FAIL); return ret; } static void ucb1x00_remove(struct mcp *mcp) { + struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data; struct ucb1x00 *ucb = mcp_get_drvdata(mcp); struct list_head *l, *n; int ret; @@ -643,8 +634,12 @@ static void ucb1x00_remove(struct mcp *mcp) dev_err(&ucb->dev, "Can't remove gpio chip: %d\n", ret); } - free_irq(ucb->irq, ucb); + irq_set_chained_handler(ucb->irq, NULL); + irq_free_descs(ucb->irq_base, 16); device_unregister(&ucb->dev); + + if (pdata && pdata->reset) + pdata->reset(UCB_RST_REMOVE); } int ucb1x00_register_driver(struct ucb1x00_driver *drv) @@ -653,7 +648,7 @@ int ucb1x00_register_driver(struct ucb1x00_driver *drv) INIT_LIST_HEAD(&drv->devs); mutex_lock(&ucb1x00_mutex); - list_add(&drv->node, &ucb1x00_drivers); + list_add_tail(&drv->node, &ucb1x00_drivers); list_for_each_entry(ucb, &ucb1x00_devices, node) { ucb1x00_add_dev(ucb, drv); } @@ -674,44 +669,86 @@ void ucb1x00_unregister_driver(struct ucb1x00_driver *drv) mutex_unlock(&ucb1x00_mutex); } -static int ucb1x00_suspend(struct mcp *mcp, pm_message_t state) +static int ucb1x00_suspend(struct device *dev) { - struct ucb1x00 *ucb = mcp_get_drvdata(mcp); - struct ucb1x00_dev *dev; + struct ucb1x00_plat_data *pdata = dev->platform_data; + struct ucb1x00 *ucb = dev_get_drvdata(dev); + struct ucb1x00_dev *udev; mutex_lock(&ucb1x00_mutex); - list_for_each_entry(dev, &ucb->devs, dev_node) { - if (dev->drv->suspend) - dev->drv->suspend(dev, state); + list_for_each_entry(udev, &ucb->devs, dev_node) { + if (udev->drv->suspend) + udev->drv->suspend(udev); } mutex_unlock(&ucb1x00_mutex); + + if (ucb->irq_wake) { + unsigned long flags; + + raw_spin_lock_irqsave(&ucb->irq_lock, flags); + ucb1x00_enable(ucb); + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & + ucb->irq_wake); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & + ucb->irq_wake); + ucb1x00_disable(ucb); + raw_spin_unlock_irqrestore(&ucb->irq_lock, flags); + + enable_irq_wake(ucb->irq); + } else if (pdata && pdata->reset) + pdata->reset(UCB_RST_SUSPEND); + return 0; } -static int ucb1x00_resume(struct mcp *mcp) +static int ucb1x00_resume(struct device *dev) { - struct ucb1x00 *ucb = mcp_get_drvdata(mcp); - struct ucb1x00_dev *dev; + struct ucb1x00_plat_data *pdata = dev->platform_data; + struct ucb1x00 *ucb = dev_get_drvdata(dev); + struct ucb1x00_dev *udev; + + if (!ucb->irq_wake && pdata && pdata->reset) + pdata->reset(UCB_RST_RESUME); + ucb1x00_enable(ucb); ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); + + if (ucb->irq_wake) { + unsigned long flags; + + raw_spin_lock_irqsave(&ucb->irq_lock, flags); + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & + ucb->irq_mask); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & + ucb->irq_mask); + raw_spin_unlock_irqrestore(&ucb->irq_lock, flags); + + disable_irq_wake(ucb->irq); + } + ucb1x00_disable(ucb); + mutex_lock(&ucb1x00_mutex); - list_for_each_entry(dev, &ucb->devs, dev_node) { - if (dev->drv->resume) - dev->drv->resume(dev); + list_for_each_entry(udev, &ucb->devs, dev_node) { + if (udev->drv->resume) + udev->drv->resume(udev); } mutex_unlock(&ucb1x00_mutex); return 0; } +static const struct dev_pm_ops ucb1x00_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ucb1x00_suspend, ucb1x00_resume) +}; + static struct mcp_driver ucb1x00_driver = { .drv = { .name = "ucb1x00", + .owner = THIS_MODULE, + .pm = &ucb1x00_pm_ops, }, .probe = ucb1x00_probe, .remove = ucb1x00_remove, - .suspend = ucb1x00_suspend, - .resume = ucb1x00_resume, }; static int __init ucb1x00_init(void) @@ -742,14 +779,10 @@ EXPORT_SYMBOL(ucb1x00_adc_enable); EXPORT_SYMBOL(ucb1x00_adc_read); EXPORT_SYMBOL(ucb1x00_adc_disable); -EXPORT_SYMBOL(ucb1x00_hook_irq); -EXPORT_SYMBOL(ucb1x00_free_irq); -EXPORT_SYMBOL(ucb1x00_enable_irq); -EXPORT_SYMBOL(ucb1x00_disable_irq); - EXPORT_SYMBOL(ucb1x00_register_driver); EXPORT_SYMBOL(ucb1x00_unregister_driver); +MODULE_ALIAS("mcp:ucb1x00"); MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); MODULE_DESCRIPTION("UCB1x00 core driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c index 63a3cbdfa3f3..1e0e20c0e082 100644 --- a/drivers/mfd/ucb1x00-ts.c +++ b/drivers/mfd/ucb1x00-ts.c @@ -20,8 +20,9 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> -#include <linux/smp.h> +#include <linux/interrupt.h> #include <linux/sched.h> +#include <linux/spinlock.h> #include <linux/completion.h> #include <linux/delay.h> #include <linux/string.h> @@ -32,7 +33,6 @@ #include <linux/kthread.h> #include <linux/mfd/ucb1x00.h> -#include <mach/dma.h> #include <mach/collie.h> #include <asm/mach-types.h> @@ -42,6 +42,8 @@ struct ucb1x00_ts { struct input_dev *idev; struct ucb1x00 *ucb; + spinlock_t irq_lock; + unsigned irq_disabled; wait_queue_head_t irq_wait; struct task_struct *rtask; u16 x_res; @@ -238,7 +240,12 @@ static int ucb1x00_thread(void *_ts) if (ucb1x00_ts_pen_down(ts)) { set_current_state(TASK_INTERRUPTIBLE); - ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, machine_is_collie() ? UCB_RISING : UCB_FALLING); + spin_lock_irq(&ts->irq_lock); + if (ts->irq_disabled) { + ts->irq_disabled = 0; + enable_irq(ts->ucb->irq_base + UCB_IRQ_TSPX); + } + spin_unlock_irq(&ts->irq_lock); ucb1x00_disable(ts->ucb); /* @@ -281,23 +288,37 @@ static int ucb1x00_thread(void *_ts) * We only detect touch screen _touches_ with this interrupt * handler, and even then we just schedule our task. */ -static void ucb1x00_ts_irq(int idx, void *id) +static irqreturn_t ucb1x00_ts_irq(int irq, void *id) { struct ucb1x00_ts *ts = id; - ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); + spin_lock(&ts->irq_lock); + ts->irq_disabled = 1; + disable_irq_nosync(ts->ucb->irq_base + UCB_IRQ_TSPX); + spin_unlock(&ts->irq_lock); wake_up(&ts->irq_wait); + + return IRQ_HANDLED; } static int ucb1x00_ts_open(struct input_dev *idev) { struct ucb1x00_ts *ts = input_get_drvdata(idev); + unsigned long flags = 0; int ret = 0; BUG_ON(ts->rtask); + if (machine_is_collie()) + flags = IRQF_TRIGGER_RISING; + else + flags = IRQF_TRIGGER_FALLING; + + ts->irq_disabled = 0; + init_waitqueue_head(&ts->irq_wait); - ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts); + ret = request_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ucb1x00_ts_irq, + flags, "ucb1x00-ts", ts); if (ret < 0) goto out; @@ -314,7 +335,7 @@ static int ucb1x00_ts_open(struct input_dev *idev) if (!IS_ERR(ts->rtask)) { ret = 0; } else { - ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); + free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts); ts->rtask = NULL; ret = -EFAULT; } @@ -334,7 +355,7 @@ static void ucb1x00_ts_close(struct input_dev *idev) kthread_stop(ts->rtask); ucb1x00_enable(ts->ucb); - ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); + free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts); ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0); ucb1x00_disable(ts->ucb); } @@ -359,11 +380,13 @@ static int ucb1x00_ts_add(struct ucb1x00_dev *dev) ts->ucb = dev->ucb; ts->idev = idev; ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC; + spin_lock_init(&ts->irq_lock); idev->name = "Touchscreen panel"; idev->id.product = ts->ucb->id; idev->open = ucb1x00_ts_open; idev->close = ucb1x00_ts_close; + idev->dev.parent = &ts->ucb->dev; idev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index f5e54fae8ada..838056c3493a 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -1631,7 +1631,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID); if (ret < 0) { dev_err(wm831x->dev, "Failed to read parent ID: %d\n", ret); - goto err_regmap; + goto err; } switch (ret) { case 0x6204: @@ -1640,20 +1640,20 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) default: dev_err(wm831x->dev, "Device is not a WM831x: ID %x\n", ret); ret = -EINVAL; - goto err_regmap; + goto err; } ret = wm831x_reg_read(wm831x, WM831X_REVISION); if (ret < 0) { dev_err(wm831x->dev, "Failed to read revision: %d\n", ret); - goto err_regmap; + goto err; } rev = (ret & WM831X_PARENT_REV_MASK) >> WM831X_PARENT_REV_SHIFT; ret = wm831x_reg_read(wm831x, WM831X_RESET_ID); if (ret < 0) { dev_err(wm831x->dev, "Failed to read device ID: %d\n", ret); - goto err_regmap; + goto err; } /* Some engineering samples do not have the ID set, rely on @@ -1728,7 +1728,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) default: dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret); ret = -EINVAL; - goto err_regmap; + goto err; } /* This will need revisiting in future but is OK for all @@ -1742,7 +1742,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) ret = wm831x_reg_read(wm831x, WM831X_SECURITY_KEY); if (ret < 0) { dev_err(wm831x->dev, "Failed to read security key: %d\n", ret); - goto err_regmap; + goto err; } if (ret != 0) { dev_warn(wm831x->dev, "Security key had non-zero value %x\n", @@ -1755,7 +1755,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) ret = pdata->pre_init(wm831x); if (ret != 0) { dev_err(wm831x->dev, "pre_init() failed: %d\n", ret); - goto err_regmap; + goto err; } } @@ -1778,7 +1778,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) ret = wm831x_irq_init(wm831x, irq); if (ret != 0) - goto err_regmap; + goto err; wm831x_auxadc_init(wm831x); @@ -1874,9 +1874,8 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) err_irq: wm831x_irq_exit(wm831x); -err_regmap: +err: mfd_remove_devices(wm831x->dev); - regmap_exit(wm831x->regmap); return ret; } @@ -1887,7 +1886,6 @@ void wm831x_device_exit(struct wm831x *wm831x) if (wm831x->irq_base) free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x); wm831x_irq_exit(wm831x); - regmap_exit(wm831x->regmap); } int wm831x_device_suspend(struct wm831x *wm831x) diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c index cb15609b0a48..2b29caebc9cf 100644 --- a/drivers/mfd/wm831x-i2c.c +++ b/drivers/mfd/wm831x-i2c.c @@ -37,7 +37,7 @@ static int wm831x_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, wm831x); wm831x->dev = &i2c->dev; - wm831x->regmap = regmap_init_i2c(i2c, &wm831x_regmap_config); + wm831x->regmap = devm_regmap_init_i2c(i2c, &wm831x_regmap_config); if (IS_ERR(wm831x->regmap)) { ret = PTR_ERR(wm831x->regmap); dev_err(wm831x->dev, "Failed to allocate register map: %d\n", diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c index 62ef3254105f..745c87945664 100644 --- a/drivers/mfd/wm831x-spi.c +++ b/drivers/mfd/wm831x-spi.c @@ -40,7 +40,7 @@ static int __devinit wm831x_spi_probe(struct spi_device *spi) dev_set_drvdata(&spi->dev, wm831x); wm831x->dev = &spi->dev; - wm831x->regmap = regmap_init_spi(spi, &wm831x_regmap_config); + wm831x->regmap = devm_regmap_init_spi(spi, &wm831x_regmap_config); if (IS_ERR(wm831x->regmap)) { ret = PTR_ERR(wm831x->regmap); dev_err(wm831x->dev, "Failed to allocate register map: %d\n", diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c index 2204893444a6..237764ae5f9b 100644 --- a/drivers/mfd/wm8400-core.c +++ b/drivers/mfd/wm8400-core.c @@ -350,7 +350,7 @@ static int wm8400_i2c_probe(struct i2c_client *i2c, goto err; } - wm8400->regmap = regmap_init_i2c(i2c, &wm8400_regmap_config); + wm8400->regmap = devm_regmap_init_i2c(i2c, &wm8400_regmap_config); if (IS_ERR(wm8400->regmap)) { ret = PTR_ERR(wm8400->regmap); goto err; @@ -361,12 +361,10 @@ static int wm8400_i2c_probe(struct i2c_client *i2c, ret = wm8400_init(wm8400, i2c->dev.platform_data); if (ret != 0) - goto map_err; + goto err; return 0; -map_err: - regmap_exit(wm8400->regmap); err: return ret; } @@ -376,7 +374,6 @@ static int wm8400_i2c_remove(struct i2c_client *i2c) struct wm8400 *wm8400 = i2c_get_clientdata(i2c); wm8400_release(wm8400); - regmap_exit(wm8400->regmap); return 0; } diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index a04b3c108c8c..98733d408fee 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -359,15 +359,38 @@ static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo) } #endif +static const __devinitdata struct reg_default wm8994_revc_patch[] = { + { 0x102, 0x3 }, + { 0x56, 0x3 }, + { 0x817, 0x0 }, + { 0x102, 0x0 }, +}; + +static const __devinitdata struct reg_default wm8958_reva_patch[] = { + { 0x102, 0x3 }, + { 0xcb, 0x81 }, + { 0x817, 0x0 }, + { 0x102, 0x0 }, +}; + +static const __devinitdata struct reg_default wm1811_reva_patch[] = { + { 0x102, 0x3 }, + { 0x56, 0x7 }, + { 0x5d, 0x7e }, + { 0x5e, 0x0 }, + { 0x102, 0x0 }, +}; + /* * Instantiate the generic non-control parts of the device. */ -static int wm8994_device_init(struct wm8994 *wm8994, int irq) +static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq) { struct wm8994_pdata *pdata = wm8994->dev->platform_data; struct regmap_config *regmap_config; + const struct reg_default *regmap_patch = NULL; const char *devname; - int ret, i; + int ret, i, patch_regs; int pulls = 0; dev_set_drvdata(wm8994->dev, wm8994); @@ -379,7 +402,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) NULL, 0); if (ret != 0) { dev_err(wm8994->dev, "Failed to add children: %d\n", ret); - goto err_regmap; + goto err; } switch (wm8994->type) { @@ -394,7 +417,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) break; default: BUG(); - goto err_regmap; + goto err; } wm8994->supplies = devm_kzalloc(wm8994->dev, @@ -402,7 +425,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) wm8994->num_supplies, GFP_KERNEL); if (!wm8994->supplies) { ret = -ENOMEM; - goto err_regmap; + goto err; } switch (wm8994->type) { @@ -420,14 +443,14 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) break; default: BUG(); - goto err_regmap; + goto err; } ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies, wm8994->supplies); if (ret != 0) { dev_err(wm8994->dev, "Failed to get supplies: %d\n", ret); - goto err_regmap; + goto err; } ret = regulator_bulk_enable(wm8994->num_supplies, @@ -488,15 +511,44 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) "revision %c not fully supported\n", 'A' + wm8994->revision); break; + case 2: + case 3: + regmap_patch = wm8994_revc_patch; + patch_regs = ARRAY_SIZE(wm8994_revc_patch); + break; + default: + break; + } + break; + + case WM8958: + switch (wm8994->revision) { + case 0: + regmap_patch = wm8958_reva_patch; + patch_regs = ARRAY_SIZE(wm8958_reva_patch); + break; default: break; } break; + case WM1811: /* Revision C did not change the relevant layer */ if (wm8994->revision > 1) wm8994->revision++; + switch (wm8994->revision) { + case 0: + case 1: + case 2: + case 3: + regmap_patch = wm1811_reva_patch; + patch_regs = ARRAY_SIZE(wm1811_reva_patch); + break; + default: + break; + } break; + default: break; } @@ -526,6 +578,16 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) return ret; } + if (regmap_patch) { + ret = regmap_register_patch(wm8994->regmap, regmap_patch, + patch_regs); + if (ret != 0) { + dev_err(wm8994->dev, "Failed to register patch: %d\n", + ret); + goto err; + } + } + if (pdata) { wm8994->irq_base = pdata->irq_base; wm8994->gpio_base = pdata->gpio_base; @@ -588,13 +650,12 @@ err_enable: wm8994->supplies); err_get: regulator_bulk_free(wm8994->num_supplies, wm8994->supplies); -err_regmap: - regmap_exit(wm8994->regmap); +err: mfd_remove_devices(wm8994->dev); return ret; } -static void wm8994_device_exit(struct wm8994 *wm8994) +static __devexit void wm8994_device_exit(struct wm8994 *wm8994) { pm_runtime_disable(wm8994->dev); mfd_remove_devices(wm8994->dev); @@ -602,7 +663,6 @@ static void wm8994_device_exit(struct wm8994 *wm8994) regulator_bulk_disable(wm8994->num_supplies, wm8994->supplies); regulator_bulk_free(wm8994->num_supplies, wm8994->supplies); - regmap_exit(wm8994->regmap); } static const struct of_device_id wm8994_of_match[] = { @@ -613,8 +673,8 @@ static const struct of_device_id wm8994_of_match[] = { }; MODULE_DEVICE_TABLE(of, wm8994_of_match); -static int wm8994_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static __devinit int wm8994_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) { struct wm8994 *wm8994; int ret; @@ -628,7 +688,7 @@ static int wm8994_i2c_probe(struct i2c_client *i2c, wm8994->irq = i2c->irq; wm8994->type = id->driver_data; - wm8994->regmap = regmap_init_i2c(i2c, &wm8994_base_regmap_config); + wm8994->regmap = devm_regmap_init_i2c(i2c, &wm8994_base_regmap_config); if (IS_ERR(wm8994->regmap)) { ret = PTR_ERR(wm8994->regmap); dev_err(wm8994->dev, "Failed to allocate register map: %d\n", @@ -639,7 +699,7 @@ static int wm8994_i2c_probe(struct i2c_client *i2c, return wm8994_device_init(wm8994, i2c->irq); } -static int wm8994_i2c_remove(struct i2c_client *i2c) +static __devexit int wm8994_i2c_remove(struct i2c_client *i2c) { struct wm8994 *wm8994 = i2c_get_clientdata(i2c); @@ -668,7 +728,7 @@ static struct i2c_driver wm8994_i2c_driver = { .of_match_table = wm8994_of_match, }, .probe = wm8994_i2c_probe, - .remove = wm8994_i2c_remove, + .remove = __devexit_p(wm8994_i2c_remove), .id_table = wm8994_i2c_id, }; diff --git a/drivers/mfd/wm8994-regmap.c b/drivers/mfd/wm8994-regmap.c index bc0c5096539a..7605b6095453 100644 --- a/drivers/mfd/wm8994-regmap.c +++ b/drivers/mfd/wm8994-regmap.c @@ -15,6 +15,7 @@ #include <linux/mfd/wm8994/core.h> #include <linux/mfd/wm8994/registers.h> #include <linux/regmap.h> +#include <linux/device.h> #include "wm8994.h" diff --git a/drivers/misc/atmel_tclib.c b/drivers/misc/atmel_tclib.c index 4bcfc3759734..c8d8e38d0d8a 100644 --- a/drivers/misc/atmel_tclib.c +++ b/drivers/misc/atmel_tclib.c @@ -6,12 +6,10 @@ #include <linux/ioport.h> #include <linux/kernel.h> #include <linux/platform_device.h> +#include <linux/module.h> #include <linux/slab.h> #include <linux/export.h> - -/* Number of bytes to reserve for the iomem resource */ -#define ATMEL_TC_IOMEM_SIZE 256 - +#include <linux/of.h> /* * This is a thin library to solve the problem of how to portably allocate @@ -48,10 +46,17 @@ struct atmel_tc *atmel_tc_alloc(unsigned block, const char *name) struct atmel_tc *tc; struct platform_device *pdev = NULL; struct resource *r; + size_t size; spin_lock(&tc_list_lock); list_for_each_entry(tc, &tc_list, node) { - if (tc->pdev->id == block) { + if (tc->pdev->dev.of_node) { + if (of_alias_get_id(tc->pdev->dev.of_node, "tcb") + == block) { + pdev = tc->pdev; + break; + } + } else if (tc->pdev->id == block) { pdev = tc->pdev; break; } @@ -61,11 +66,15 @@ struct atmel_tc *atmel_tc_alloc(unsigned block, const char *name) goto fail; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - r = request_mem_region(r->start, ATMEL_TC_IOMEM_SIZE, name); if (!r) goto fail; - tc->regs = ioremap(r->start, ATMEL_TC_IOMEM_SIZE); + size = resource_size(r); + r = request_mem_region(r->start, size, name); + if (!r) + goto fail; + + tc->regs = ioremap(r->start, size); if (!tc->regs) goto fail_ioremap; @@ -76,7 +85,7 @@ out: return tc; fail_ioremap: - release_mem_region(r->start, ATMEL_TC_IOMEM_SIZE); + release_mem_region(r->start, size); fail: tc = NULL; goto out; @@ -96,7 +105,7 @@ void atmel_tc_free(struct atmel_tc *tc) spin_lock(&tc_list_lock); if (tc->regs) { iounmap(tc->regs); - release_mem_region(tc->iomem->start, ATMEL_TC_IOMEM_SIZE); + release_mem_region(tc->iomem->start, resource_size(tc->iomem)); tc->regs = NULL; tc->iomem = NULL; } @@ -104,6 +113,30 @@ void atmel_tc_free(struct atmel_tc *tc) } EXPORT_SYMBOL_GPL(atmel_tc_free); +#if defined(CONFIG_OF) +static struct atmel_tcb_config tcb_rm9200_config = { + .counter_width = 16, +}; + +static struct atmel_tcb_config tcb_sam9x5_config = { + .counter_width = 32, +}; + +static const struct of_device_id atmel_tcb_dt_ids[] = { + { + .compatible = "atmel,at91rm9200-tcb", + .data = &tcb_rm9200_config, + }, { + .compatible = "atmel,at91sam9x5-tcb", + .data = &tcb_sam9x5_config, + }, { + /* sentinel */ + } +}; + +MODULE_DEVICE_TABLE(of, atmel_tcb_dt_ids); +#endif + static int __init tc_probe(struct platform_device *pdev) { struct atmel_tc *tc; @@ -129,6 +162,14 @@ static int __init tc_probe(struct platform_device *pdev) return -EINVAL; } + /* Now take SoC information if available */ + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(atmel_tcb_dt_ids, pdev->dev.of_node); + if (match) + tc->tcb_config = match->data; + } + tc->clk[0] = clk; tc->clk[1] = clk_get(&pdev->dev, "t1_clk"); if (IS_ERR(tc->clk[1])) @@ -153,7 +194,10 @@ static int __init tc_probe(struct platform_device *pdev) } static struct platform_driver tc_driver = { - .driver.name = "atmel_tcb", + .driver = { + .name = "atmel_tcb", + .of_match_table = of_match_ptr(atmel_tcb_dt_ids), + }, }; static int __init tc_init(void) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 00fcbed1afd2..ecbee9bf87b2 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -395,7 +395,7 @@ config MMC_SPI config MMC_S3C tristate "Samsung S3C SD/MMC Card Interface support" - depends on ARCH_S3C2410 + depends on ARCH_S3C24XX help This selects a driver for the MCI interface found in Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs. diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c index 947faa5d2ce4..efdb81d21c44 100644 --- a/drivers/mmc/host/at91_mci.c +++ b/drivers/mmc/host/at91_mci.c @@ -86,7 +86,6 @@ static inline int at91mci_is_mci1rev2xx(void) { return ( cpu_is_at91sam9260() || cpu_is_at91sam9263() - || cpu_is_at91cap9() || cpu_is_at91sam9rl() || cpu_is_at91sam9g10() || cpu_is_at91sam9g20() diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 11e589cd8233..983e244eca76 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -53,6 +53,8 @@ static unsigned int fmax = 515633; * @sdio: variant supports SDIO * @st_clkdiv: true if using a ST-specific clock divider algorithm * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register + * @pwrreg_powerup: power up value for MMCIPOWER register + * @signal_direction: input/out direction of bus signals can be indicated */ struct variant_data { unsigned int clkreg; @@ -63,18 +65,22 @@ struct variant_data { bool sdio; bool st_clkdiv; bool blksz_datactrl16; + u32 pwrreg_powerup; + bool signal_direction; }; static struct variant_data variant_arm = { .fifosize = 16 * 4, .fifohalfsize = 8 * 4, .datalength_bits = 16, + .pwrreg_powerup = MCI_PWR_UP, }; static struct variant_data variant_arm_extended_fifo = { .fifosize = 128 * 4, .fifohalfsize = 64 * 4, .datalength_bits = 16, + .pwrreg_powerup = MCI_PWR_UP, }; static struct variant_data variant_u300 = { @@ -83,6 +89,8 @@ static struct variant_data variant_u300 = { .clkreg_enable = MCI_ST_U300_HWFCEN, .datalength_bits = 16, .sdio = true, + .pwrreg_powerup = MCI_PWR_ON, + .signal_direction = true, }; static struct variant_data variant_ux500 = { @@ -93,6 +101,8 @@ static struct variant_data variant_ux500 = { .datalength_bits = 24, .sdio = true, .st_clkdiv = true, + .pwrreg_powerup = MCI_PWR_ON, + .signal_direction = true, }; static struct variant_data variant_ux500v2 = { @@ -104,11 +114,35 @@ static struct variant_data variant_ux500v2 = { .sdio = true, .st_clkdiv = true, .blksz_datactrl16 = true, + .pwrreg_powerup = MCI_PWR_ON, + .signal_direction = true, }; /* * This must be called with host->lock held */ +static void mmci_write_clkreg(struct mmci_host *host, u32 clk) +{ + if (host->clk_reg != clk) { + host->clk_reg = clk; + writel(clk, host->base + MMCICLOCK); + } +} + +/* + * This must be called with host->lock held + */ +static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr) +{ + if (host->pwr_reg != pwr) { + host->pwr_reg = pwr; + writel(pwr, host->base + MMCIPOWER); + } +} + +/* + * This must be called with host->lock held + */ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) { struct variant_data *variant = host->variant; @@ -153,7 +187,7 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8) clk |= MCI_ST_8BIT_BUS; - writel(clk, host->base + MMCICLOCK); + mmci_write_clkreg(host, clk); } static void @@ -166,14 +200,10 @@ mmci_request_end(struct mmci_host *host, struct mmc_request *mrq) host->mrq = NULL; host->cmd = NULL; - /* - * Need to drop the host lock here; mmc_request_done may call - * back into the driver... - */ - spin_unlock(&host->lock); - pm_runtime_put(mmc_dev(host->mmc)); mmc_request_done(host->mmc, mrq); - spin_lock(&host->lock); + + pm_runtime_mark_last_busy(mmc_dev(host->mmc)); + pm_runtime_put_autosuspend(mmc_dev(host->mmc)); } static void mmci_set_mask1(struct mmci_host *host, unsigned int mask) @@ -607,6 +637,11 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) if (data->flags & MMC_DATA_READ) datactrl |= MCI_DPSM_DIRECTION; + /* The ST Micro variants has a special bit to enable SDIO */ + if (variant->sdio && host->mmc->card) + if (mmc_card_sdio(host->mmc->card)) + datactrl |= MCI_ST_DPSM_SDIOEN; + /* * Attempt to use DMA operation mode, if this * should fail, fall back to PIO mode @@ -635,11 +670,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) irqmask = MCI_TXFIFOHALFEMPTYMASK; } - /* The ST Micro variants has a special bit to enable SDIO */ - if (variant->sdio && host->mmc->card) - if (mmc_card_sdio(host->mmc->card)) - datactrl |= MCI_ST_DPSM_SDIOEN; - writel(datactrl, base + MMCIDATACTRL); writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0); mmci_set_mask1(host, irqmask); @@ -786,7 +816,24 @@ static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int rema if (count <= 0) break; - readsl(base + MMCIFIFO, ptr, count >> 2); + /* + * SDIO especially may want to send something that is + * not divisible by 4 (as opposed to card sectors + * etc). Therefore make sure to always read the last bytes + * while only doing full 32-bit reads towards the FIFO. + */ + if (unlikely(count & 0x3)) { + if (count < 4) { + unsigned char buf[4]; + readsl(base + MMCIFIFO, buf, 1); + memcpy(ptr, buf, count); + } else { + readsl(base + MMCIFIFO, ptr, count >> 2); + count &= ~0x3; + } + } else { + readsl(base + MMCIFIFO, ptr, count >> 2); + } ptr += count; remain -= count; @@ -821,14 +868,13 @@ static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int rem */ if (variant->sdio && mmc_card_sdio(host->mmc->card)) { + u32 clk; if (count < 8) - writel(readl(host->base + MMCICLOCK) & - ~variant->clkreg_enable, - host->base + MMCICLOCK); + clk = host->clk_reg & ~variant->clkreg_enable; else - writel(readl(host->base + MMCICLOCK) | - variant->clkreg_enable, - host->base + MMCICLOCK); + clk = host->clk_reg | variant->clkreg_enable; + + mmci_write_clkreg(host, clk); } /* @@ -1015,10 +1061,17 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct mmci_host *host = mmc_priv(mmc); + struct variant_data *variant = host->variant; u32 pwr = 0; unsigned long flags; int ret; + pm_runtime_get_sync(mmc_dev(mmc)); + + if (host->plat->ios_handler && + host->plat->ios_handler(mmc_dev(mmc), ios)) + dev_err(mmc_dev(mmc), "platform ios_handler failed\n"); + switch (ios->power_mode) { case MMC_POWER_OFF: if (host->vcc) @@ -1035,22 +1088,38 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) * power should be rare so we print an error * and return here. */ - return; + goto out; } } - if (host->plat->vdd_handler) - pwr |= host->plat->vdd_handler(mmc_dev(mmc), ios->vdd, - ios->power_mode); - /* The ST version does not have this, fall through to POWER_ON */ - if (host->hw_designer != AMBA_VENDOR_ST) { - pwr |= MCI_PWR_UP; - break; - } + /* + * The ST Micro variant doesn't have the PL180s MCI_PWR_UP + * and instead uses MCI_PWR_ON so apply whatever value is + * configured in the variant data. + */ + pwr |= variant->pwrreg_powerup; + + break; case MMC_POWER_ON: pwr |= MCI_PWR_ON; break; } + if (variant->signal_direction && ios->power_mode != MMC_POWER_OFF) { + /* + * The ST Micro variant has some additional bits + * indicating signal direction for the signals in + * the SD/MMC bus and feedback-clock usage. + */ + pwr |= host->plat->sigdir; + + if (ios->bus_width == MMC_BUS_WIDTH_4) + pwr &= ~MCI_ST_DATA74DIREN; + else if (ios->bus_width == MMC_BUS_WIDTH_1) + pwr &= (~MCI_ST_DATA74DIREN & + ~MCI_ST_DATA31DIREN & + ~MCI_ST_DATA2DIREN); + } + if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) { if (host->hw_designer != AMBA_VENDOR_ST) pwr |= MCI_ROD; @@ -1066,13 +1135,13 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) spin_lock_irqsave(&host->lock, flags); mmci_set_clkreg(host, ios->clock); - - if (host->pwr != pwr) { - host->pwr = pwr; - writel(pwr, host->base + MMCIPOWER); - } + mmci_write_pwrreg(host, pwr); spin_unlock_irqrestore(&host->lock, flags); + + out: + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); } static int mmci_get_ro(struct mmc_host *mmc) @@ -1326,7 +1395,7 @@ static int __devinit mmci_probe(struct amba_device *dev, if (ret) goto unmap; - if (dev->irq[1] == NO_IRQ) + if (dev->irq[1] == NO_IRQ || !dev->irq[1]) host->singleirq = true; else { ret = request_irq(dev->irq[1], mmci_pio_irq, IRQF_SHARED, @@ -1346,6 +1415,8 @@ static int __devinit mmci_probe(struct amba_device *dev, mmci_dma_setup(host); + pm_runtime_set_autosuspend_delay(&dev->dev, 50); + pm_runtime_use_autosuspend(&dev->dev); pm_runtime_put(&dev->dev); mmc_add_host(mmc); @@ -1430,43 +1501,49 @@ static int __devexit mmci_remove(struct amba_device *dev) return 0; } -#ifdef CONFIG_PM -static int mmci_suspend(struct amba_device *dev, pm_message_t state) +#ifdef CONFIG_SUSPEND +static int mmci_suspend(struct device *dev) { - struct mmc_host *mmc = amba_get_drvdata(dev); + struct amba_device *adev = to_amba_device(dev); + struct mmc_host *mmc = amba_get_drvdata(adev); int ret = 0; if (mmc) { struct mmci_host *host = mmc_priv(mmc); ret = mmc_suspend_host(mmc); - if (ret == 0) + if (ret == 0) { + pm_runtime_get_sync(dev); writel(0, host->base + MMCIMASK0); + } } return ret; } -static int mmci_resume(struct amba_device *dev) +static int mmci_resume(struct device *dev) { - struct mmc_host *mmc = amba_get_drvdata(dev); + struct amba_device *adev = to_amba_device(dev); + struct mmc_host *mmc = amba_get_drvdata(adev); int ret = 0; if (mmc) { struct mmci_host *host = mmc_priv(mmc); writel(MCI_IRQENABLE, host->base + MMCIMASK0); + pm_runtime_put(dev); ret = mmc_resume_host(mmc); } return ret; } -#else -#define mmci_suspend NULL -#define mmci_resume NULL #endif +static const struct dev_pm_ops mmci_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mmci_suspend, mmci_resume) +}; + static struct amba_id mmci_ids[] = { { .id = 0x00041180, @@ -1512,26 +1589,15 @@ MODULE_DEVICE_TABLE(amba, mmci_ids); static struct amba_driver mmci_driver = { .drv = { .name = DRIVER_NAME, + .pm = &mmci_dev_pm_ops, }, .probe = mmci_probe, .remove = __devexit_p(mmci_remove), - .suspend = mmci_suspend, - .resume = mmci_resume, .id_table = mmci_ids, }; -static int __init mmci_init(void) -{ - return amba_driver_register(&mmci_driver); -} - -static void __exit mmci_exit(void) -{ - amba_driver_unregister(&mmci_driver); -} +module_amba_driver(mmci_driver); -module_init(mmci_init); -module_exit(mmci_exit); module_param(fmax, uint, 0444); MODULE_DESCRIPTION("ARM PrimeCell PL180/181 Multimedia Card Interface driver"); diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 79e4143ab9df..d437ccf62d6b 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -13,16 +13,6 @@ #define MCI_PWR_ON 0x03 #define MCI_OD (1 << 6) #define MCI_ROD (1 << 7) -/* - * The ST Micro version does not have ROD and reuse the voltage registers - * for direction settings - */ -#define MCI_ST_DATA2DIREN (1 << 2) -#define MCI_ST_CMDDIREN (1 << 3) -#define MCI_ST_DATA0DIREN (1 << 4) -#define MCI_ST_DATA31DIREN (1 << 5) -#define MCI_ST_FBCLKEN (1 << 7) -#define MCI_ST_DATA74DIREN (1 << 8) #define MMCICLOCK 0x004 #define MCI_CLK_ENABLE (1 << 8) @@ -160,7 +150,7 @@ (MCI_RXFIFOHALFFULLMASK | MCI_RXDATAAVLBLMASK | \ MCI_TXFIFOHALFEMPTYMASK) -#define NR_SG 16 +#define NR_SG 128 struct clk; struct variant_data; @@ -189,7 +179,8 @@ struct mmci_host { unsigned int mclk; unsigned int cclk; - u32 pwr; + u32 pwr_reg; + u32 clk_reg; struct mmci_platform_data *plat; struct variant_data *variant; diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 0be4e2013632..6193a0d7bde5 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -464,7 +464,7 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev) err = PTR_ERR(clk); goto err_clk_get; } - clk_enable(clk); + clk_prepare_enable(clk); pltfm_host->clk = clk; if (!is_imx25_esdhc(imx_data)) @@ -559,7 +559,7 @@ no_card_detect_irq: gpio_free(boarddata->wp_gpio); no_card_detect_pin: no_board_data: - clk_disable(pltfm_host->clk); + clk_disable_unprepare(pltfm_host->clk); clk_put(pltfm_host->clk); err_clk_get: kfree(imx_data); @@ -586,7 +586,7 @@ static int __devexit sdhci_esdhc_imx_remove(struct platform_device *pdev) gpio_free(boarddata->cd_gpio); } - clk_disable(pltfm_host->clk); + clk_disable_unprepare(pltfm_host->clk); clk_put(pltfm_host->clk); kfree(imx_data); diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 1af756ee0f9a..b19e7d435f8d 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -518,9 +518,6 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) if (pdata->cd_type == S3C_SDHCI_CD_PERMANENT) host->mmc->caps = MMC_CAP_NONREMOVABLE; - if (pdata->host_caps) - host->mmc->caps |= pdata->host_caps; - if (pdata->pm_caps) host->mmc->pm_caps |= pdata->pm_caps; @@ -544,6 +541,9 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) if (pdata->host_caps) host->mmc->caps |= pdata->host_caps; + if (pdata->host_caps2) + host->mmc->caps2 |= pdata->host_caps2; + ret = sdhci_add_host(host); if (ret) { dev_err(dev, "sdhci_add_host() failed\n"); diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 78a36eba4df0..cb348569454b 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -23,7 +23,6 @@ #include <linux/gpio.h> #include <linux/mmc/card.h> #include <linux/mmc/host.h> -#include <linux/module.h> #include <asm/gpio.h> diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 1be621841400..284cf3433720 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -1,6 +1,6 @@ menuconfig MTD tristate "Memory Technology Device (MTD) support" - depends on HAS_IOMEM + depends on GENERIC_IO help Memory Technology Devices are flash, RAM and similar chips, often used for solid state file systems on embedded devices. This option diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index e1e122f2f929..9bcd1f415f43 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -2526,12 +2526,10 @@ static void cfi_intelext_restore_locks(struct mtd_info *mtd) if (!region->lockmap) continue; - for (block = 0; block < region->numblocks; block++) { + for_each_clear_bit(block, region->lockmap, region->numblocks) { len = region->erasesize; adr = region->offset + block * len; - - if (!test_bit(block, region->lockmap)) - cfi_intelext_unlock(mtd, adr, len); + cfi_intelext_unlock(mtd, adr, len); } } } diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 37b05c3f2792..8d3dac40d7e6 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -1,5 +1,6 @@ menu "Self-contained MTD device drivers" depends on MTD!=n + depends on HAS_IOMEM config MTD_PMC551 tristate "Ramix PMC551 PCI Mezzanine RAM card support" diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 6c5c431c64af..8af67cfd671a 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -1,5 +1,6 @@ menu "Mapping drivers for chip access" depends on MTD!=n + depends on HAS_IOMEM config MTD_COMPLEX_MAPPINGS bool "Support non-linear mappings of flash chips" diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index 502821997707..cbc3b7867910 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -23,106 +23,6 @@ #include <asm/sizes.h> #include <asm/mach/flash.h> -#if 0 -/* - * This is here for documentation purposes only - until these people - * submit their machine types. It will be gone January 2005. - */ -static struct mtd_partition consus_partitions[] = { - { - .name = "Consus boot firmware", - .offset = 0, - .size = 0x00040000, - .mask_flags = MTD_WRITABLE, /* force read-only */ - }, { - .name = "Consus kernel", - .offset = 0x00040000, - .size = 0x00100000, - .mask_flags = 0, - }, { - .name = "Consus disk", - .offset = 0x00140000, - /* The rest (up to 16M) for jffs. We could put 0 and - make it find the size automatically, but right now - i have 32 megs. jffs will use all 32 megs if given - the chance, and this leads to horrible problems - when you try to re-flash the image because blob - won't erase the whole partition. */ - .size = 0x01000000 - 0x00140000, - .mask_flags = 0, - }, { - /* this disk is a secondary disk, which can be used as - needed, for simplicity, make it the size of the other - consus partition, although realistically it could be - the remainder of the disk (depending on the file - system used) */ - .name = "Consus disk2", - .offset = 0x01000000, - .size = 0x01000000 - 0x00140000, - .mask_flags = 0, - } -}; - -/* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */ -static struct mtd_partition frodo_partitions[] = -{ - { - .name = "bootloader", - .size = 0x00040000, - .offset = 0x00000000, - .mask_flags = MTD_WRITEABLE - }, { - .name = "bootloader params", - .size = 0x00040000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE - }, { - .name = "kernel", - .size = 0x00100000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE - }, { - .name = "ramdisk", - .size = 0x00400000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE - }, { - .name = "file system", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND - } -}; - -static struct mtd_partition jornada56x_partitions[] = { - { - .name = "bootldr", - .size = 0x00040000, - .offset = 0, - .mask_flags = MTD_WRITEABLE, - }, { - .name = "rootfs", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND, - } -}; - -static void jornada56x_set_vpp(int vpp) -{ - if (vpp) - GPSR = GPIO_GPIO26; - else - GPCR = GPIO_GPIO26; - GPDR |= GPIO_GPIO26; -} - -/* - * Machine Phys Size set_vpp - * Consus : SA1100_CS0_PHYS SZ_32M - * Frodo : SA1100_CS0_PHYS SZ_32M - * Jornada56x: SA1100_CS0_PHYS SZ_32M jornada56x_set_vpp - */ -#endif - struct sa_subdev_info { char name[16]; struct map_info map; @@ -373,21 +273,9 @@ static int __exit sa1100_mtd_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static void sa1100_mtd_shutdown(struct platform_device *dev) -{ - struct sa_info *info = platform_get_drvdata(dev); - if (info && mtd_suspend(info->mtd) == 0) - mtd_resume(info->mtd); -} -#else -#define sa1100_mtd_shutdown NULL -#endif - static struct platform_driver sa1100_mtd_driver = { .probe = sa1100_mtd_probe, .remove = __exit_p(sa1100_mtd_remove), - .shutdown = sa1100_mtd_shutdown, .driver = { .name = "sa1100-mtd", .owner = THIS_MODULE, diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 50c6a1e7f675..c57ae92ebda4 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -31,13 +31,13 @@ #include <linux/compat.h> #include <linux/mount.h> #include <linux/blkpg.h> +#include <linux/magic.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> #include <linux/mtd/map.h> #include <asm/uaccess.h> -#define MTD_INODE_FS_MAGIC 0x11307854 static DEFINE_MUTEX(mtd_mutex); static struct vfsmount *mtd_inode_mnt __read_mostly; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 3b1d6da874e0..a3c4de551ebe 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -187,7 +187,7 @@ config MTD_NAND_PPCHAMELEONEVB config MTD_NAND_S3C2410 tristate "NAND Flash support for Samsung S3C SoCs" - depends on ARCH_S3C2410 || ARCH_S3C64XX + depends on ARCH_S3C24XX || ARCH_S3C64XX help This enables the NAND flash controller on the S3C24xx and S3C64xx SoCs @@ -246,6 +246,7 @@ config MTD_NAND_BCM_UMI_HWCS config MTD_NAND_DISKONCHIP tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)" depends on EXPERIMENTAL + depends on HAS_IOMEM select REED_SOLOMON select REED_SOLOMON_DEC16 help @@ -431,6 +432,7 @@ config MTD_NAND_GPMI_NAND config MTD_NAND_PLATFORM tristate "Support for generic platform NAND driver" + depends on HAS_IOMEM help This implements a generic NAND driver for on-SOC platform devices. You will need to provide platform-specific functions diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index 3197e9764fcd..73416951f4c1 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -26,7 +26,7 @@ #include <asm/io.h> #include <mach/hardware.h> #include <asm/sizes.h> -#include <asm/gpio.h> +#include <linux/gpio.h> #include <plat/board-ams-delta.h> /* @@ -34,8 +34,6 @@ */ static struct mtd_info *ams_delta_mtd = NULL; -#define NAND_MASK (AMS_DELTA_LATCH2_NAND_NRE | AMS_DELTA_LATCH2_NAND_NWE | AMS_DELTA_LATCH2_NAND_CLE | AMS_DELTA_LATCH2_NAND_ALE | AMS_DELTA_LATCH2_NAND_NCE | AMS_DELTA_LATCH2_NAND_NWP) - /* * Define partitions for flash devices */ @@ -68,10 +66,9 @@ static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte) writew(0, io_base + OMAP_MPUIO_IO_CNTL); writew(byte, this->IO_ADDR_W); - ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NWE, 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 0); ndelay(40); - ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NWE, - AMS_DELTA_LATCH2_NAND_NWE); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 1); } static u_char ams_delta_read_byte(struct mtd_info *mtd) @@ -80,12 +77,11 @@ static u_char ams_delta_read_byte(struct mtd_info *mtd) struct nand_chip *this = mtd->priv; void __iomem *io_base = this->priv; - ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NRE, 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 0); ndelay(40); writew(~0, io_base + OMAP_MPUIO_IO_CNTL); res = readw(this->IO_ADDR_R); - ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NRE, - AMS_DELTA_LATCH2_NAND_NRE); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 1); return res; } @@ -132,15 +128,12 @@ static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd, { if (ctrl & NAND_CTRL_CHANGE) { - unsigned long bits; - - bits = (~ctrl & NAND_NCE) ? AMS_DELTA_LATCH2_NAND_NCE : 0; - bits |= (ctrl & NAND_CLE) ? AMS_DELTA_LATCH2_NAND_CLE : 0; - bits |= (ctrl & NAND_ALE) ? AMS_DELTA_LATCH2_NAND_ALE : 0; - - ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_CLE | - AMS_DELTA_LATCH2_NAND_ALE | - AMS_DELTA_LATCH2_NAND_NCE, bits); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NCE, + (ctrl & NAND_NCE) == 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_CLE, + (ctrl & NAND_CLE) != 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_ALE, + (ctrl & NAND_ALE) != 0); } if (cmd != NAND_CMD_NONE) @@ -152,6 +145,39 @@ static int ams_delta_nand_ready(struct mtd_info *mtd) return gpio_get_value(AMS_DELTA_GPIO_PIN_NAND_RB); } +static const struct gpio _mandatory_gpio[] = { + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_NCE, + .flags = GPIOF_OUT_INIT_HIGH, + .label = "nand_nce", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_NRE, + .flags = GPIOF_OUT_INIT_HIGH, + .label = "nand_nre", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_NWP, + .flags = GPIOF_OUT_INIT_HIGH, + .label = "nand_nwp", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_NWE, + .flags = GPIOF_OUT_INIT_HIGH, + .label = "nand_nwe", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_ALE, + .flags = GPIOF_OUT_INIT_LOW, + .label = "nand_ale", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_CLE, + .flags = GPIOF_OUT_INIT_LOW, + .label = "nand_cle", + }, +}; + /* * Main initialization routine */ @@ -223,10 +249,9 @@ static int __devinit ams_delta_init(struct platform_device *pdev) platform_set_drvdata(pdev, io_base); /* Set chip enabled, but */ - ams_delta_latch2_write(NAND_MASK, AMS_DELTA_LATCH2_NAND_NRE | - AMS_DELTA_LATCH2_NAND_NWE | - AMS_DELTA_LATCH2_NAND_NCE | - AMS_DELTA_LATCH2_NAND_NWP); + err = gpio_request_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); + if (err) + goto out_gpio; /* Scan to find existence of the device */ if (nand_scan(ams_delta_mtd, 1)) { @@ -241,7 +266,10 @@ static int __devinit ams_delta_init(struct platform_device *pdev) goto out; out_mtd: + gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); +out_gpio: platform_set_drvdata(pdev, NULL); + gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB); iounmap(io_base); out_release_io: release_mem_region(res->start, resource_size(res)); @@ -262,6 +290,8 @@ static int __devexit ams_delta_cleanup(struct platform_device *pdev) /* Release resources, unregister device */ nand_release(ams_delta_mtd); + gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); + gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB); iounmap(io_base); release_mem_region(res->start, resource_size(res)); diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index 772ad2966619..91467bb03634 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -1,6 +1,7 @@ menuconfig MTD_ONENAND tristate "OneNAND Device Support" depends on MTD + depends on HAS_IOMEM help This enables support for accessing all type of OneNAND flash devices. For further information see diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 115749f20f9e..0fde9fc7d2e5 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -945,12 +945,8 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) goto out_free; err = -ENOMEM; - ubi->peb_buf1 = vmalloc(ubi->peb_size); - if (!ubi->peb_buf1) - goto out_free; - - ubi->peb_buf2 = vmalloc(ubi->peb_size); - if (!ubi->peb_buf2) + ubi->peb_buf = vmalloc(ubi->peb_size); + if (!ubi->peb_buf) goto out_free; err = ubi_debugging_init_dev(ubi); @@ -1029,8 +1025,7 @@ out_detach: out_debugging: ubi_debugging_exit_dev(ubi); out_free: - vfree(ubi->peb_buf1); - vfree(ubi->peb_buf2); + vfree(ubi->peb_buf); if (ref) put_device(&ubi->dev); else @@ -1101,8 +1096,7 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) vfree(ubi->vtbl); put_mtd_device(ubi->mtd); ubi_debugging_exit_dev(ubi); - vfree(ubi->peb_buf1); - vfree(ubi->peb_buf2); + vfree(ubi->peb_buf); ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num); put_device(&ubi->dev); return 0; diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index cd26da8ad225..2455d620d96b 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -529,18 +529,18 @@ retry: data_size = offset + len; mutex_lock(&ubi->buf_mutex); - memset(ubi->peb_buf1 + offset, 0xFF, len); + memset(ubi->peb_buf + offset, 0xFF, len); /* Read everything before the area where the write failure happened */ if (offset > 0) { - err = ubi_io_read_data(ubi, ubi->peb_buf1, pnum, 0, offset); + err = ubi_io_read_data(ubi, ubi->peb_buf, pnum, 0, offset); if (err && err != UBI_IO_BITFLIPS) goto out_unlock; } - memcpy(ubi->peb_buf1 + offset, buf, len); + memcpy(ubi->peb_buf + offset, buf, len); - err = ubi_io_write_data(ubi, ubi->peb_buf1, new_pnum, 0, data_size); + err = ubi_io_write_data(ubi, ubi->peb_buf, new_pnum, 0, data_size); if (err) { mutex_unlock(&ubi->buf_mutex); goto write_error; @@ -979,7 +979,7 @@ static int is_error_sane(int err) * physical eraseblock @to. The @vid_hdr buffer may be changed by this * function. Returns: * o %0 in case of success; - * o %MOVE_CANCEL_RACE, %MOVE_TARGET_WR_ERR, %MOVE_CANCEL_BITFLIPS, etc; + * o %MOVE_CANCEL_RACE, %MOVE_TARGET_WR_ERR, %MOVE_TARGET_BITFLIPS, etc; * o a negative error code in case of failure. */ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, @@ -1053,13 +1053,13 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, /* * OK, now the LEB is locked and we can safely start moving it. Since - * this function utilizes the @ubi->peb_buf1 buffer which is shared + * this function utilizes the @ubi->peb_buf buffer which is shared * with some other functions - we lock the buffer by taking the * @ubi->buf_mutex. */ mutex_lock(&ubi->buf_mutex); dbg_wl("read %d bytes of data", aldata_size); - err = ubi_io_read_data(ubi, ubi->peb_buf1, from, 0, aldata_size); + err = ubi_io_read_data(ubi, ubi->peb_buf, from, 0, aldata_size); if (err && err != UBI_IO_BITFLIPS) { ubi_warn("error %d while reading data from PEB %d", err, from); @@ -1079,10 +1079,10 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, */ if (vid_hdr->vol_type == UBI_VID_DYNAMIC) aldata_size = data_size = - ubi_calc_data_len(ubi, ubi->peb_buf1, data_size); + ubi_calc_data_len(ubi, ubi->peb_buf, data_size); cond_resched(); - crc = crc32(UBI_CRC32_INIT, ubi->peb_buf1, data_size); + crc = crc32(UBI_CRC32_INIT, ubi->peb_buf, data_size); cond_resched(); /* @@ -1116,12 +1116,12 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, if (is_error_sane(err)) err = MOVE_TARGET_RD_ERR; } else - err = MOVE_CANCEL_BITFLIPS; + err = MOVE_TARGET_BITFLIPS; goto out_unlock_buf; } if (data_size > 0) { - err = ubi_io_write_data(ubi, ubi->peb_buf1, to, 0, aldata_size); + err = ubi_io_write_data(ubi, ubi->peb_buf, to, 0, aldata_size); if (err) { if (err == -EIO) err = MOVE_TARGET_WR_ERR; @@ -1134,8 +1134,8 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, * We've written the data and are going to read it back to make * sure it was written correctly. */ - - err = ubi_io_read_data(ubi, ubi->peb_buf2, to, 0, aldata_size); + memset(ubi->peb_buf, 0xFF, aldata_size); + err = ubi_io_read_data(ubi, ubi->peb_buf, to, 0, aldata_size); if (err) { if (err != UBI_IO_BITFLIPS) { ubi_warn("error %d while reading data back " @@ -1143,13 +1143,13 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, if (is_error_sane(err)) err = MOVE_TARGET_RD_ERR; } else - err = MOVE_CANCEL_BITFLIPS; + err = MOVE_TARGET_BITFLIPS; goto out_unlock_buf; } cond_resched(); - if (memcmp(ubi->peb_buf1, ubi->peb_buf2, aldata_size)) { + if (crc != crc32(UBI_CRC32_INIT, ubi->peb_buf, data_size)) { ubi_warn("read data back from PEB %d and it is " "different", to); err = -EINVAL; diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 5cde4e5ca3e5..43f1a0011a55 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -431,11 +431,11 @@ static int torture_peb(struct ubi_device *ubi, int pnum) goto out; /* Make sure the PEB contains only 0xFF bytes */ - err = ubi_io_read(ubi, ubi->peb_buf1, pnum, 0, ubi->peb_size); + err = ubi_io_read(ubi, ubi->peb_buf, pnum, 0, ubi->peb_size); if (err) goto out; - err = ubi_check_pattern(ubi->peb_buf1, 0xFF, ubi->peb_size); + err = ubi_check_pattern(ubi->peb_buf, 0xFF, ubi->peb_size); if (err == 0) { ubi_err("erased PEB %d, but a non-0xFF byte found", pnum); @@ -444,17 +444,17 @@ static int torture_peb(struct ubi_device *ubi, int pnum) } /* Write a pattern and check it */ - memset(ubi->peb_buf1, patterns[i], ubi->peb_size); - err = ubi_io_write(ubi, ubi->peb_buf1, pnum, 0, ubi->peb_size); + memset(ubi->peb_buf, patterns[i], ubi->peb_size); + err = ubi_io_write(ubi, ubi->peb_buf, pnum, 0, ubi->peb_size); if (err) goto out; - memset(ubi->peb_buf1, ~patterns[i], ubi->peb_size); - err = ubi_io_read(ubi, ubi->peb_buf1, pnum, 0, ubi->peb_size); + memset(ubi->peb_buf, ~patterns[i], ubi->peb_size); + err = ubi_io_read(ubi, ubi->peb_buf, pnum, 0, ubi->peb_size); if (err) goto out; - err = ubi_check_pattern(ubi->peb_buf1, patterns[i], + err = ubi_check_pattern(ubi->peb_buf, patterns[i], ubi->peb_size); if (err == 0) { ubi_err("pattern %x checking failed for PEB %d", diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index 0cb17d936b5a..12c43b44f815 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -789,9 +789,9 @@ static int check_corruption(struct ubi_device *ubi, struct ubi_vid_hdr *vid_hdr, int err; mutex_lock(&ubi->buf_mutex); - memset(ubi->peb_buf1, 0x00, ubi->leb_size); + memset(ubi->peb_buf, 0x00, ubi->leb_size); - err = ubi_io_read(ubi, ubi->peb_buf1, pnum, ubi->leb_start, + err = ubi_io_read(ubi, ubi->peb_buf, pnum, ubi->leb_start, ubi->leb_size); if (err == UBI_IO_BITFLIPS || mtd_is_eccerr(err)) { /* @@ -808,7 +808,7 @@ static int check_corruption(struct ubi_device *ubi, struct ubi_vid_hdr *vid_hdr, if (err) goto out_unlock; - if (ubi_check_pattern(ubi->peb_buf1, 0xFF, ubi->leb_size)) + if (ubi_check_pattern(ubi->peb_buf, 0xFF, ubi->leb_size)) goto out_unlock; ubi_err("PEB %d contains corrupted VID header, and the data does not " @@ -818,7 +818,7 @@ static int check_corruption(struct ubi_device *ubi, struct ubi_vid_hdr *vid_hdr, dbg_msg("hexdump of PEB %d offset %d, length %d", pnum, ubi->leb_start, ubi->leb_size); ubi_dbg_print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, - ubi->peb_buf1, ubi->leb_size, 1); + ubi->peb_buf, ubi->leb_size, 1); err = 1; out_unlock: @@ -1174,7 +1174,7 @@ struct ubi_scan_info *ubi_scan(struct ubi_device *ubi) ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); if (!ech) - goto out_slab; + goto out_si; vidh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); if (!vidh) @@ -1235,8 +1235,6 @@ out_vidh: ubi_free_vid_hdr(ubi, vidh); out_ech: kfree(ech); -out_slab: - kmem_cache_destroy(si->scan_leb_slab); out_si: ubi_scan_destroy_si(si); return ERR_PTR(err); @@ -1325,7 +1323,9 @@ void ubi_scan_destroy_si(struct ubi_scan_info *si) } } - kmem_cache_destroy(si->scan_leb_slab); + if (si->scan_leb_slab) + kmem_cache_destroy(si->scan_leb_slab); + kfree(si); } diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index d51d75d34446..b162790790a9 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -118,7 +118,7 @@ enum { * PEB * MOVE_TARGET_WR_ERR: canceled because there was a write error to the target * PEB - * MOVE_CANCEL_BITFLIPS: canceled because a bit-flip was detected in the + * MOVE_TARGET_BITFLIPS: canceled because a bit-flip was detected in the * target PEB * MOVE_RETRY: retry scrubbing the PEB */ @@ -127,7 +127,7 @@ enum { MOVE_SOURCE_RD_ERR, MOVE_TARGET_RD_ERR, MOVE_TARGET_WR_ERR, - MOVE_CANCEL_BITFLIPS, + MOVE_TARGET_BITFLIPS, MOVE_RETRY, }; @@ -387,9 +387,8 @@ struct ubi_wl_entry; * time (MTD write buffer size) * @mtd: MTD device descriptor * - * @peb_buf1: a buffer of PEB size used for different purposes - * @peb_buf2: another buffer of PEB size used for different purposes - * @buf_mutex: protects @peb_buf1 and @peb_buf2 + * @peb_buf: a buffer of PEB size used for different purposes + * @buf_mutex: protects @peb_buf * @ckvol_mutex: serializes static volume checking when opening * * @dbg: debugging information for this UBI device @@ -471,8 +470,7 @@ struct ubi_device { int max_write_size; struct mtd_info *mtd; - void *peb_buf1; - void *peb_buf2; + void *peb_buf; struct mutex buf_mutex; struct mutex ckvol_mutex; diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 0696e36b0539..7c1a9bf8ac86 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -350,18 +350,19 @@ static void prot_queue_add(struct ubi_device *ubi, struct ubi_wl_entry *e) /** * find_wl_entry - find wear-leveling entry closest to certain erase counter. * @root: the RB-tree where to look for - * @max: highest possible erase counter + * @diff: maximum possible difference from the smallest erase counter * * This function looks for a wear leveling entry with erase counter closest to - * @max and less than @max. + * min + @diff, where min is the smallest erase counter. */ -static struct ubi_wl_entry *find_wl_entry(struct rb_root *root, int max) +static struct ubi_wl_entry *find_wl_entry(struct rb_root *root, int diff) { struct rb_node *p; struct ubi_wl_entry *e; + int max; e = rb_entry(rb_first(root), struct ubi_wl_entry, u.rb); - max += e->ec; + max = e->ec + diff; p = root->rb_node; while (p) { @@ -389,7 +390,7 @@ static struct ubi_wl_entry *find_wl_entry(struct rb_root *root, int max) */ int ubi_wl_get_peb(struct ubi_device *ubi, int dtype) { - int err, medium_ec; + int err; struct ubi_wl_entry *e, *first, *last; ubi_assert(dtype == UBI_LONGTERM || dtype == UBI_SHORTTERM || @@ -427,7 +428,7 @@ retry: * For unknown data we pick a physical eraseblock with medium * erase counter. But we by no means can pick a physical * eraseblock with erase counter greater or equivalent than the - * lowest erase counter plus %WL_FREE_MAX_DIFF. + * lowest erase counter plus %WL_FREE_MAX_DIFF/2. */ first = rb_entry(rb_first(&ubi->free), struct ubi_wl_entry, u.rb); @@ -436,10 +437,8 @@ retry: if (last->ec - first->ec < WL_FREE_MAX_DIFF) e = rb_entry(ubi->free.rb_node, struct ubi_wl_entry, u.rb); - else { - medium_ec = (first->ec + WL_FREE_MAX_DIFF)/2; - e = find_wl_entry(&ubi->free, medium_ec); - } + else + e = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF/2); break; case UBI_SHORTTERM: /* @@ -799,7 +798,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, scrubbing = 1; goto out_not_moved; } - if (err == MOVE_CANCEL_BITFLIPS || err == MOVE_TARGET_WR_ERR || + if (err == MOVE_TARGET_BITFLIPS || err == MOVE_TARGET_WR_ERR || err == MOVE_TARGET_RD_ERR) { /* * Target PEB had bit-flips or write error - torture it. diff --git a/drivers/net/Space.c b/drivers/net/Space.c index 068c3563e00f..88bbd8ffa7fe 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -190,8 +190,10 @@ static struct devprobe2 isa_probes[] __initdata = { {seeq8005_probe, 0}, #endif #ifdef CONFIG_CS89x0 +#ifndef CONFIG_CS89x0_PLATFORM {cs89x0_probe, 0}, #endif +#endif #ifdef CONFIG_AT1700 {at1700_probe, 0}, #endif diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 0730203a19f2..b920d829692a 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2573,12 +2573,16 @@ re_arm: static int bond_has_this_ip(struct bonding *bond, __be32 ip) { struct vlan_entry *vlan; + struct net_device *vlan_dev; - if (ip == bond->master_ip) + if (ip == bond_confirm_addr(bond->dev, 0, ip)) return 1; list_for_each_entry(vlan, &bond->vlan_list, vlan_list) { - if (ip == vlan->vlan_ip) + rcu_read_lock(); + vlan_dev = __vlan_find_dev_deep(bond->dev, vlan->vlan_id); + rcu_read_unlock(); + if (vlan_dev && ip == bond_confirm_addr(vlan_dev, 0, ip)) return 1; } @@ -2620,17 +2624,19 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave) int i, vlan_id; __be32 *targets = bond->params.arp_targets; struct vlan_entry *vlan; - struct net_device *vlan_dev; + struct net_device *vlan_dev = NULL; struct rtable *rt; for (i = 0; (i < BOND_MAX_ARP_TARGETS); i++) { + __be32 addr; if (!targets[i]) break; pr_debug("basa: target %x\n", targets[i]); if (!bond_vlan_used(bond)) { pr_debug("basa: empty vlan: arp_send\n"); + addr = bond_confirm_addr(bond->dev, targets[i], 0); bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i], - bond->master_ip, 0); + addr, 0); continue; } @@ -2655,8 +2661,9 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave) if (rt->dst.dev == bond->dev) { ip_rt_put(rt); pr_debug("basa: rtdev == bond->dev: arp_send\n"); + addr = bond_confirm_addr(bond->dev, targets[i], 0); bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i], - bond->master_ip, 0); + addr, 0); continue; } @@ -2674,10 +2681,11 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave) } } - if (vlan_id) { + if (vlan_id && vlan_dev) { ip_rt_put(rt); + addr = bond_confirm_addr(vlan_dev, targets[i], 0); bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i], - vlan->vlan_ip, vlan_id); + addr, vlan_id); continue; } @@ -3299,68 +3307,10 @@ static int bond_netdev_event(struct notifier_block *this, return NOTIFY_DONE; } -/* - * bond_inetaddr_event: handle inetaddr notifier chain events. - * - * We keep track of device IPs primarily to use as source addresses in - * ARP monitor probes (rather than spewing out broadcasts all the time). - * - * We track one IP for the main device (if it has one), plus one per VLAN. - */ -static int bond_inetaddr_event(struct notifier_block *this, unsigned long event, void *ptr) -{ - struct in_ifaddr *ifa = ptr; - struct net_device *vlan_dev, *event_dev = ifa->ifa_dev->dev; - struct bond_net *bn = net_generic(dev_net(event_dev), bond_net_id); - struct bonding *bond; - struct vlan_entry *vlan; - - /* we only care about primary address */ - if(ifa->ifa_flags & IFA_F_SECONDARY) - return NOTIFY_DONE; - - list_for_each_entry(bond, &bn->dev_list, bond_list) { - if (bond->dev == event_dev) { - switch (event) { - case NETDEV_UP: - bond->master_ip = ifa->ifa_local; - return NOTIFY_OK; - case NETDEV_DOWN: - bond->master_ip = 0; - return NOTIFY_OK; - default: - return NOTIFY_DONE; - } - } - - list_for_each_entry(vlan, &bond->vlan_list, vlan_list) { - vlan_dev = __vlan_find_dev_deep(bond->dev, - vlan->vlan_id); - if (vlan_dev == event_dev) { - switch (event) { - case NETDEV_UP: - vlan->vlan_ip = ifa->ifa_local; - return NOTIFY_OK; - case NETDEV_DOWN: - vlan->vlan_ip = 0; - return NOTIFY_OK; - default: - return NOTIFY_DONE; - } - } - } - } - return NOTIFY_DONE; -} - static struct notifier_block bond_netdev_notifier = { .notifier_call = bond_netdev_event, }; -static struct notifier_block bond_inetaddr_notifier = { - .notifier_call = bond_inetaddr_event, -}; - /*---------------------------- Hashing Policies -----------------------------*/ /* @@ -4929,7 +4879,6 @@ static int __init bonding_init(void) } register_netdevice_notifier(&bond_netdev_notifier); - register_inetaddr_notifier(&bond_inetaddr_notifier); out: return res; err: @@ -4943,7 +4892,6 @@ err_link: static void __exit bonding_exit(void) { unregister_netdevice_notifier(&bond_netdev_notifier); - unregister_inetaddr_notifier(&bond_inetaddr_notifier); bond_destroy_debugfs(); diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index 1aecc37e5b4d..9f2bae6616d3 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -21,6 +21,7 @@ #include <linux/cpumask.h> #include <linux/in6.h> #include <linux/netpoll.h> +#include <linux/inetdevice.h> #include "bond_3ad.h" #include "bond_alb.h" @@ -166,7 +167,6 @@ struct bond_parm_tbl { struct vlan_entry { struct list_head vlan_list; - __be32 vlan_ip; unsigned short vlan_id; }; @@ -232,7 +232,6 @@ struct bonding { struct list_head bond_list; struct netdev_hw_addr_list mc_list; int (*xmit_hash_policy)(struct sk_buff *, int); - __be32 master_ip; u16 rr_tx_counter; struct ad_bond_info ad_info; struct alb_bond_info alb_info; @@ -378,6 +377,21 @@ static inline bool bond_is_slave_inactive(struct slave *slave) return slave->inactive; } +static inline __be32 bond_confirm_addr(struct net_device *dev, __be32 dst, __be32 local) +{ + struct in_device *in_dev; + __be32 addr = 0; + + rcu_read_lock(); + in_dev = __in_dev_get_rcu(dev); + + if (in_dev) + addr = inet_confirm_addr(in_dev, dst, local, RT_SCOPE_HOST); + + rcu_read_unlock(); + return addr; +} + struct bond_net; struct vlan_entry *bond_next_vlan(struct bonding *bond, struct vlan_entry *curr); diff --git a/drivers/net/ethernet/broadcom/cnic.c b/drivers/net/ethernet/broadcom/cnic.c index 7b65716b8734..c95e7b5e2b85 100644 --- a/drivers/net/ethernet/broadcom/cnic.c +++ b/drivers/net/ethernet/broadcom/cnic.c @@ -47,6 +47,7 @@ #include "bnx2x/bnx2x_hsi.h" #include "../../../scsi/bnx2i/57xx_iscsi_constants.h" #include "../../../scsi/bnx2i/57xx_iscsi_hsi.h" +#include "../../../scsi/bnx2fc/bnx2fc_constants.h" #include "cnic.h" #include "cnic_defs.h" @@ -2547,7 +2548,7 @@ static void cnic_bnx2x_kwqe_err(struct cnic_dev *dev, struct kwqe *kwqe) } kcqe.kcqe_op_flag = kcqe_op << KCQE_FLAGS_OPCODE_SHIFT; kcqe.kcqe_op_flag |= KCQE_FLAGS_LAYER_MASK_L5_FCOE; - kcqe.kcqe_info1 = FCOE_KCQE_COMPLETION_STATUS_NIC_ERROR; + kcqe.kcqe_info1 = FCOE_KCQE_COMPLETION_STATUS_PARITY_ERROR; kcqe.kcqe_info2 = cid; kcqe.kcqe_info0 = l5_cid; @@ -2558,7 +2559,7 @@ static void cnic_bnx2x_kwqe_err(struct cnic_dev *dev, struct kwqe *kwqe) kcqe.kcqe_op_flag = (opcode + 0x10) << KCQE_FLAGS_OPCODE_SHIFT; kcqe.kcqe_op_flag |= KCQE_FLAGS_LAYER_MASK_L5_ISCSI; - kcqe.kcqe_info1 = ISCSI_KCQE_COMPLETION_STATUS_NIC_ERROR; + kcqe.kcqe_info1 = ISCSI_KCQE_COMPLETION_STATUS_PARITY_ERR; kcqe.kcqe_info2 = cid; cnic_get_l5_cid(cp, BNX2X_SW_CID(cid), &kcqe.kcqe_info0); @@ -2577,7 +2578,7 @@ static void cnic_bnx2x_kwqe_err(struct cnic_dev *dev, struct kwqe *kwqe) kcqe.kcqe_op_flag = (kcqe_op << KCQE_FLAGS_OPCODE_SHIFT) | KCQE_FLAGS_LAYER_MASK_L4; - l4kcqe->status = L4_KCQE_COMPLETION_STATUS_NIC_ERROR; + l4kcqe->status = L4_KCQE_COMPLETION_STATUS_PARITY_ERROR; l4kcqe->cid = cid; cnic_get_l5_cid(cp, BNX2X_SW_CID(cid), &l4kcqe->conn_id); } else { @@ -3933,7 +3934,8 @@ static void cnic_cm_process_kcqe(struct cnic_dev *dev, struct kcqe *kcqe) case L4_KCQE_OPCODE_VALUE_CONNECT_COMPLETE: if (l4kcqe->status == 0) set_bit(SK_F_OFFLD_COMPLETE, &csk->flags); - else if (l4kcqe->status == L4_KCQE_COMPLETION_STATUS_NIC_ERROR) + else if (l4kcqe->status == + L4_KCQE_COMPLETION_STATUS_PARITY_ERROR) set_bit(SK_F_HW_ERR, &csk->flags); smp_mb__before_clear_bit(); @@ -3946,7 +3948,7 @@ static void cnic_cm_process_kcqe(struct cnic_dev *dev, struct kcqe *kcqe) case L4_KCQE_OPCODE_VALUE_RESET_COMP: case L5CM_RAMROD_CMD_ID_SEARCHER_DELETE: case L5CM_RAMROD_CMD_ID_TERMINATE_OFFLOAD: - if (l4kcqe->status == L4_KCQE_COMPLETION_STATUS_NIC_ERROR) + if (l4kcqe->status == L4_KCQE_COMPLETION_STATUS_PARITY_ERROR) set_bit(SK_F_HW_ERR, &csk->flags); cp->close_conn(csk, opcode); diff --git a/drivers/net/ethernet/broadcom/cnic_defs.h b/drivers/net/ethernet/broadcom/cnic_defs.h index 06ca00266d70..382c98b0cc0c 100644 --- a/drivers/net/ethernet/broadcom/cnic_defs.h +++ b/drivers/net/ethernet/broadcom/cnic_defs.h @@ -35,16 +35,6 @@ #define L5CM_RAMROD_CMD_ID_SEARCHER_DELETE (L5CM_RAMROD_CMD_ID_BASE + 14) #define L5CM_RAMROD_CMD_ID_TERMINATE_OFFLOAD (L5CM_RAMROD_CMD_ID_BASE + 15) -#define FCOE_KCQE_OPCODE_INIT_FUNC (0x10) -#define FCOE_KCQE_OPCODE_DESTROY_FUNC (0x11) -#define FCOE_KCQE_OPCODE_STAT_FUNC (0x12) -#define FCOE_KCQE_OPCODE_OFFLOAD_CONN (0x15) -#define FCOE_KCQE_OPCODE_ENABLE_CONN (0x16) -#define FCOE_KCQE_OPCODE_DISABLE_CONN (0x17) -#define FCOE_KCQE_OPCODE_DESTROY_CONN (0x18) -#define FCOE_KCQE_OPCODE_CQ_EVENT_NOTIFICATION (0x20) -#define FCOE_KCQE_OPCODE_FCOE_ERROR (0x21) - #define FCOE_RAMROD_CMD_ID_INIT_FUNC (FCOE_KCQE_OPCODE_INIT_FUNC) #define FCOE_RAMROD_CMD_ID_DESTROY_FUNC (FCOE_KCQE_OPCODE_DESTROY_FUNC) #define FCOE_RAMROD_CMD_ID_STAT_FUNC (FCOE_KCQE_OPCODE_STAT_FUNC) @@ -54,23 +44,6 @@ #define FCOE_RAMROD_CMD_ID_DESTROY_CONN (FCOE_KCQE_OPCODE_DESTROY_CONN) #define FCOE_RAMROD_CMD_ID_TERMINATE_CONN (0x81) -#define FCOE_KWQE_OPCODE_INIT1 (0) -#define FCOE_KWQE_OPCODE_INIT2 (1) -#define FCOE_KWQE_OPCODE_INIT3 (2) -#define FCOE_KWQE_OPCODE_OFFLOAD_CONN1 (3) -#define FCOE_KWQE_OPCODE_OFFLOAD_CONN2 (4) -#define FCOE_KWQE_OPCODE_OFFLOAD_CONN3 (5) -#define FCOE_KWQE_OPCODE_OFFLOAD_CONN4 (6) -#define FCOE_KWQE_OPCODE_ENABLE_CONN (7) -#define FCOE_KWQE_OPCODE_DISABLE_CONN (8) -#define FCOE_KWQE_OPCODE_DESTROY_CONN (9) -#define FCOE_KWQE_OPCODE_DESTROY (10) -#define FCOE_KWQE_OPCODE_STAT (11) - -#define FCOE_KCQE_COMPLETION_STATUS_ERROR (0x1) -#define FCOE_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAILURE (0x3) -#define FCOE_KCQE_COMPLETION_STATUS_NIC_ERROR (0x5) - /* KCQ (kernel completion queue) response op codes */ #define L4_KCQE_OPCODE_VALUE_CLOSE_COMP (53) #define L4_KCQE_OPCODE_VALUE_RESET_COMP (54) @@ -87,6 +60,7 @@ /* KCQ (kernel completion queue) completion status */ #define L4_KCQE_COMPLETION_STATUS_SUCCESS (0) #define L4_KCQE_COMPLETION_STATUS_NIC_ERROR (4) +#define L4_KCQE_COMPLETION_STATUS_PARITY_ERROR (0x81) #define L4_KCQE_COMPLETION_STATUS_TIMEOUT (0x93) #define L4_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAIL (0x83) diff --git a/drivers/net/ethernet/broadcom/cnic_if.h b/drivers/net/ethernet/broadcom/cnic_if.h index 60deb84d36bd..289274e546be 100644 --- a/drivers/net/ethernet/broadcom/cnic_if.h +++ b/drivers/net/ethernet/broadcom/cnic_if.h @@ -12,8 +12,8 @@ #ifndef CNIC_IF_H #define CNIC_IF_H -#define CNIC_MODULE_VERSION "2.5.9" -#define CNIC_MODULE_RELDATE "Feb 8, 2012" +#define CNIC_MODULE_VERSION "2.5.10" +#define CNIC_MODULE_RELDATE "March 21, 2012" #define CNIC_ULP_RDMA 0 #define CNIC_ULP_ISCSI 1 diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index b0657466041d..7b71387cf93c 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -89,10 +89,10 @@ static inline void _tg3_flag_clear(enum TG3_FLAGS flag, unsigned long *bits) #define DRV_MODULE_NAME "tg3" #define TG3_MAJ_NUM 3 -#define TG3_MIN_NUM 122 +#define TG3_MIN_NUM 123 #define DRV_MODULE_VERSION \ __stringify(TG3_MAJ_NUM) "." __stringify(TG3_MIN_NUM) -#define DRV_MODULE_RELDATE "December 7, 2011" +#define DRV_MODULE_RELDATE "March 21, 2012" #define RESET_KIND_SHUTDOWN 0 #define RESET_KIND_INIT 1 @@ -5953,8 +5953,10 @@ next_pkt_nopost: tpr->rx_std_prod_idx = std_prod_idx & tp->rx_std_ring_mask; tpr->rx_jmb_prod_idx = jmb_prod_idx & tp->rx_jmb_ring_mask; - if (tnapi != &tp->napi[1]) + if (tnapi != &tp->napi[1]) { + tp->rx_refill = true; napi_schedule(&tp->napi[1].napi); + } } return received; @@ -6134,6 +6136,7 @@ static int tg3_poll_work(struct tg3_napi *tnapi, int work_done, int budget) u32 std_prod_idx = dpr->rx_std_prod_idx; u32 jmb_prod_idx = dpr->rx_jmb_prod_idx; + tp->rx_refill = false; for (i = 1; i < tp->irq_cnt; i++) err |= tg3_rx_prodring_xfer(tp, dpr, &tp->napi[i].prodring); @@ -6197,9 +6200,25 @@ static int tg3_poll_msix(struct napi_struct *napi, int budget) /* check for RX/TX work to do */ if (likely(sblk->idx[0].tx_consumer == tnapi->tx_cons && *(tnapi->rx_rcb_prod_idx) == tnapi->rx_rcb_ptr)) { + + /* This test here is not race free, but will reduce + * the number of interrupts by looping again. + */ + if (tnapi == &tp->napi[1] && tp->rx_refill) + continue; + napi_complete(napi); /* Reenable interrupts. */ tw32_mailbox(tnapi->int_mbox, tnapi->last_tag << 24); + + /* This test here is synchronized by napi_schedule() + * and napi_complete() to close the race condition. + */ + if (unlikely(tnapi == &tp->napi[1] && tp->rx_refill)) { + tw32(HOSTCC_MODE, tp->coalesce_mode | + HOSTCC_MODE_ENABLE | + tnapi->coal_now); + } mmiowb(); break; } diff --git a/drivers/net/ethernet/broadcom/tg3.h b/drivers/net/ethernet/broadcom/tg3.h index 66bcfca55261..93865f899a4f 100644 --- a/drivers/net/ethernet/broadcom/tg3.h +++ b/drivers/net/ethernet/broadcom/tg3.h @@ -3007,6 +3007,7 @@ struct tg3 { u32 rx_std_max_post; u32 rx_offset; u32 rx_pkt_map_sz; + bool rx_refill; /* begin "everything else" cacheline(s) section */ diff --git a/drivers/net/ethernet/cirrus/Kconfig b/drivers/net/ethernet/cirrus/Kconfig index 1f8648f099c7..8388e36cf08f 100644 --- a/drivers/net/ethernet/cirrus/Kconfig +++ b/drivers/net/ethernet/cirrus/Kconfig @@ -5,8 +5,7 @@ config NET_VENDOR_CIRRUS bool "Cirrus devices" default y - depends on ISA || EISA || MACH_IXDP2351 || ARCH_IXDP2X01 \ - || MACH_MX31ADS || MACH_QQ2440 || (ARM && ARCH_EP93XX) || MAC + depends on ISA || EISA || ARM || MAC ---help--- If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from @@ -21,8 +20,7 @@ if NET_VENDOR_CIRRUS config CS89x0 tristate "CS89x0 support" - depends on (ISA || EISA || MACH_IXDP2351 \ - || ARCH_IXDP2X01 || MACH_MX31ADS || MACH_QQ2440) + depends on ISA || EISA || ARM ---help--- Support for CS89x0 chipset based Ethernet cards. If you have a network (Ethernet) card of this type, say Y and read the @@ -33,10 +31,15 @@ config CS89x0 To compile this driver as a module, choose M here. The module will be called cs89x0. -config CS89x0_NONISA_IRQ - def_bool y - depends on CS89x0 != n - depends on MACH_IXDP2351 || ARCH_IXDP2X01 || MACH_MX31ADS || MACH_QQ2440 +config CS89x0_PLATFORM + bool "CS89x0 platform driver support" + depends on CS89x0 + help + Say Y to compile the cs89x0 driver as a platform driver. This + makes this driver suitable for use on certain evaluation boards + such as the iMX21ADS. + + If you are unsure, say N. config EP93XX_ETH tristate "EP93xx Ethernet support" diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c index d5ff93653e4c..30fee428c489 100644 --- a/drivers/net/ethernet/cirrus/cs89x0.c +++ b/drivers/net/ethernet/cirrus/cs89x0.c @@ -100,9 +100,6 @@ */ -/* Always include 'config.h' first in case the user wants to turn on - or override something. */ -#include <linux/module.h> /* * Set this to zero to disable DMA code @@ -131,9 +128,12 @@ */ +#include <linux/module.h> +#include <linux/printk.h> #include <linux/errno.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> +#include <linux/platform_device.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/fcntl.h> @@ -151,6 +151,7 @@ #include <asm/system.h> #include <asm/io.h> #include <asm/irq.h> +#include <linux/atomic.h> #if ALLOW_DMA #include <asm/dma.h> #endif @@ -174,26 +175,20 @@ static char version[] __initdata = them to system IRQ numbers. This mapping is card specific and is set to the configuration of the Cirrus Eval board for this chip. */ #if defined(CONFIG_MACH_IXDP2351) +#define CS89x0_NONISA_IRQ static unsigned int netcard_portlist[] __used __initdata = {IXDP2351_VIRT_CS8900_BASE, 0}; static unsigned int cs8900_irq_map[] = {IRQ_IXDP2351_CS8900, 0, 0, 0}; #elif defined(CONFIG_ARCH_IXDP2X01) +#define CS89x0_NONISA_IRQ static unsigned int netcard_portlist[] __used __initdata = {IXDP2X01_CS8900_VIRT_BASE, 0}; static unsigned int cs8900_irq_map[] = {IRQ_IXDP2X01_CS8900, 0, 0, 0}; -#elif defined(CONFIG_MACH_QQ2440) -#include <mach/qq2440.h> -static unsigned int netcard_portlist[] __used __initdata = { QQ2440_CS8900_VIRT_BASE + 0x300, 0 }; -static unsigned int cs8900_irq_map[] = { QQ2440_CS8900_IRQ, 0, 0, 0 }; -#elif defined(CONFIG_MACH_MX31ADS) -#include <mach/board-mx31ads.h> -static unsigned int netcard_portlist[] __used __initdata = { - PBC_BASE_ADDRESS + PBC_CS8900A_IOBASE + 0x300, 0 -}; -static unsigned cs8900_irq_map[] = {EXPIO_INT_ENET_INT, 0, 0, 0}; #else +#ifndef CONFIG_CS89x0_PLATFORM static unsigned int netcard_portlist[] __used __initdata = { 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0}; static unsigned int cs8900_irq_map[] = {10,11,12,5}; #endif +#endif #if DEBUGGING static unsigned int net_debug = DEBUGGING; @@ -236,11 +231,16 @@ struct net_local { unsigned char *end_dma_buff; /* points to the end of the buffer */ unsigned char *rx_dma_ptr; /* points to the next packet */ #endif +#ifdef CONFIG_CS89x0_PLATFORM + void __iomem *virt_addr;/* Virtual address for accessing the CS89x0. */ + unsigned long phys_addr;/* Physical address for accessing the CS89x0. */ + unsigned long size; /* Length of CS89x0 memory region. */ +#endif }; /* Index to functions, as function prototypes. */ -static int cs89x0_probe1(struct net_device *dev, int ioaddr, int modular); +static int cs89x0_probe1(struct net_device *dev, unsigned long ioaddr, int modular); static int net_open(struct net_device *dev); static netdev_tx_t net_send_packet(struct sk_buff *skb, struct net_device *dev); static irqreturn_t net_interrupt(int irq, void *dev_id); @@ -294,6 +294,7 @@ static int __init media_fn(char *str) __setup("cs89x0_media=", media_fn); +#ifndef CONFIG_CS89x0_PLATFORM /* Check for a network adaptor of this type, and return '0' iff one exists. If dev->base_addr == 0, probe all likely locations. If dev->base_addr == 1, always return failure. @@ -343,6 +344,7 @@ out: return ERR_PTR(err); } #endif +#endif #if defined(CONFIG_MACH_IXDP2351) static u16 @@ -504,7 +506,7 @@ static const struct net_device_ops net_ops = { */ static int __init -cs89x0_probe1(struct net_device *dev, int ioaddr, int modular) +cs89x0_probe1(struct net_device *dev, unsigned long ioaddr, int modular) { struct net_local *lp = netdev_priv(dev); static unsigned version_printed; @@ -529,15 +531,12 @@ cs89x0_probe1(struct net_device *dev, int ioaddr, int modular) lp->force = g_cs89x0_media__force; #endif -#if defined(CONFIG_MACH_QQ2440) - lp->force |= FORCE_RJ45 | FORCE_FULL; -#endif } /* Grab the region so we can find another board if autoIRQ fails. */ /* WTF is going on here? */ if (!request_region(ioaddr & ~3, NETCARD_IO_EXTENT, DRV_NAME)) { - printk(KERN_ERR "%s: request_region(0x%x, 0x%x) failed\n", + printk(KERN_ERR "%s: request_region(0x%lx, 0x%x) failed\n", DRV_NAME, ioaddr, NETCARD_IO_EXTENT); retval = -EBUSY; goto out1; @@ -549,7 +548,7 @@ cs89x0_probe1(struct net_device *dev, int ioaddr, int modular) will skip the test for the ADD_PORT. */ if (ioaddr & 1) { if (net_debug > 1) - printk(KERN_INFO "%s: odd ioaddr 0x%x\n", dev->name, ioaddr); + printk(KERN_INFO "%s: odd ioaddr 0x%lx\n", dev->name, ioaddr); if ((ioaddr & 2) != 2) if ((readword(ioaddr & ~3, ADD_PORT) & ADD_MASK) != ADD_SIG) { printk(KERN_ERR "%s: bad signature 0x%x\n", @@ -560,13 +559,13 @@ cs89x0_probe1(struct net_device *dev, int ioaddr, int modular) } ioaddr &= ~3; - printk(KERN_DEBUG "PP_addr at %x[%x]: 0x%x\n", + printk(KERN_DEBUG "PP_addr at %lx[%x]: 0x%x\n", ioaddr, ADD_PORT, readword(ioaddr, ADD_PORT)); writeword(ioaddr, ADD_PORT, PP_ChipID); tmp = readword(ioaddr, DATA_PORT); if (tmp != CHIP_EISA_ID_SIG) { - printk(KERN_DEBUG "%s: incorrect signature at %x[%x]: 0x%x!=" + printk(KERN_DEBUG "%s: incorrect signature at %lx[%x]: 0x%x!=" CHIP_EISA_ID_SIG_STR "\n", dev->name, ioaddr, DATA_PORT, tmp); retval = -ENODEV; @@ -736,8 +735,9 @@ cs89x0_probe1(struct net_device *dev, int ioaddr, int modular) dev->irq = i; } else { i = lp->isa_config & INT_NO_MASK; +#ifndef CONFIG_CS89x0_PLATFORM if (lp->chip_type == CS8900) { -#ifdef CONFIG_CS89x0_NONISA_IRQ +#ifdef CS89x0_NONISA_IRQ i = cs8900_irq_map[0]; #else /* Translate the IRQ using the IRQ mapping table. */ @@ -758,6 +758,7 @@ cs89x0_probe1(struct net_device *dev, int ioaddr, int modular) } #endif } +#endif if (!dev->irq) dev->irq = i; } @@ -1168,6 +1169,7 @@ write_irq(struct net_device *dev, int chip_type, int irq) int i; if (chip_type == CS8900) { +#ifndef CONFIG_CS89x0_PLATFORM /* Search the mapping table for the corresponding IRQ pin. */ for (i = 0; i != ARRAY_SIZE(cs8900_irq_map); i++) if (cs8900_irq_map[i] == irq) @@ -1175,6 +1177,10 @@ write_irq(struct net_device *dev, int chip_type, int irq) /* Not found */ if (i == ARRAY_SIZE(cs8900_irq_map)) i = 3; +#else + /* INTRQ0 pin is used for interrupt generation. */ + i = 0; +#endif writereg(dev, PP_CS8900_ISAINT, i); } else { writereg(dev, PP_CS8920_ISAINT, irq); @@ -1228,7 +1234,7 @@ net_open(struct net_device *dev) } else { -#ifndef CONFIG_CS89x0_NONISA_IRQ +#if !defined(CS89x0_NONISA_IRQ) && !defined(CONFIG_CS89x0_PLATFORM) if (((1 << dev->irq) & lp->irq_map) == 0) { printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x\n", dev->name, dev->irq, lp->irq_map); @@ -1746,7 +1752,7 @@ static int set_mac_address(struct net_device *dev, void *p) return 0; } -#ifdef MODULE +#if defined(MODULE) && !defined(CONFIG_CS89x0_PLATFORM) static struct net_device *dev_cs89x0; @@ -1900,7 +1906,97 @@ cleanup_module(void) release_region(dev_cs89x0->base_addr, NETCARD_IO_EXTENT); free_netdev(dev_cs89x0); } -#endif /* MODULE */ +#endif /* MODULE && !CONFIG_CS89x0_PLATFORM */ + +#ifdef CONFIG_CS89x0_PLATFORM +static int __init cs89x0_platform_probe(struct platform_device *pdev) +{ + struct net_device *dev = alloc_etherdev(sizeof(struct net_local)); + struct net_local *lp; + struct resource *mem_res; + int err; + + if (!dev) + return -ENOMEM; + + lp = netdev_priv(dev); + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dev->irq = platform_get_irq(pdev, 0); + if (mem_res == NULL || dev->irq <= 0) { + dev_warn(&dev->dev, "memory/interrupt resource missing.\n"); + err = -ENXIO; + goto free; + } + + lp->phys_addr = mem_res->start; + lp->size = resource_size(mem_res); + if (!request_mem_region(lp->phys_addr, lp->size, DRV_NAME)) { + dev_warn(&dev->dev, "request_mem_region() failed.\n"); + err = -EBUSY; + goto free; + } + + lp->virt_addr = ioremap(lp->phys_addr, lp->size); + if (!lp->virt_addr) { + dev_warn(&dev->dev, "ioremap() failed.\n"); + err = -ENOMEM; + goto release; + } + + err = cs89x0_probe1(dev, (unsigned long)lp->virt_addr, 0); + if (err) { + dev_warn(&dev->dev, "no cs8900 or cs8920 detected.\n"); + goto unmap; + } + + platform_set_drvdata(pdev, dev); + return 0; + +unmap: + iounmap(lp->virt_addr); +release: + release_mem_region(lp->phys_addr, lp->size); +free: + free_netdev(dev); + return err; +} + +static int cs89x0_platform_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct net_local *lp = netdev_priv(dev); + + unregister_netdev(dev); + iounmap(lp->virt_addr); + release_mem_region(lp->phys_addr, lp->size); + free_netdev(dev); + return 0; +} + +static struct platform_driver cs89x0_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .remove = cs89x0_platform_remove, +}; + +static int __init cs89x0_init(void) +{ + return platform_driver_probe(&cs89x0_driver, cs89x0_platform_probe); +} + +module_init(cs89x0_init); + +static void __exit cs89x0_cleanup(void) +{ + platform_driver_unregister(&cs89x0_driver); +} + +module_exit(cs89x0_cleanup); + +#endif /* CONFIG_CS89x0_PLATFORM */ /* * Local variables: diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index d9428f0e738a..e7bed5303997 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -968,7 +968,6 @@ static int gfar_probe(struct platform_device *ofdev) struct gfar_private *priv = NULL; struct gfar __iomem *regs = NULL; int err = 0, i, grp_idx = 0; - int len_devname; u32 rstat = 0, tstat = 0, rqueue = 0, tqueue = 0; u32 isrg = 0; u32 __iomem *baddr; @@ -1169,40 +1168,16 @@ static int gfar_probe(struct platform_device *ofdev) priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); /* fill out IRQ number and name fields */ - len_devname = strlen(dev->name); for (i = 0; i < priv->num_grps; i++) { - strncpy(&priv->gfargrp[i].int_name_tx[0], dev->name, - len_devname); if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) { - strncpy(&priv->gfargrp[i].int_name_tx[len_devname], - "_g", sizeof("_g")); - priv->gfargrp[i].int_name_tx[ - strlen(priv->gfargrp[i].int_name_tx)] = i+48; - strncpy(&priv->gfargrp[i].int_name_tx[strlen( - priv->gfargrp[i].int_name_tx)], - "_tx", sizeof("_tx") + 1); - - strncpy(&priv->gfargrp[i].int_name_rx[0], dev->name, - len_devname); - strncpy(&priv->gfargrp[i].int_name_rx[len_devname], - "_g", sizeof("_g")); - priv->gfargrp[i].int_name_rx[ - strlen(priv->gfargrp[i].int_name_rx)] = i+48; - strncpy(&priv->gfargrp[i].int_name_rx[strlen( - priv->gfargrp[i].int_name_rx)], - "_rx", sizeof("_rx") + 1); - - strncpy(&priv->gfargrp[i].int_name_er[0], dev->name, - len_devname); - strncpy(&priv->gfargrp[i].int_name_er[len_devname], - "_g", sizeof("_g")); - priv->gfargrp[i].int_name_er[strlen( - priv->gfargrp[i].int_name_er)] = i+48; - strncpy(&priv->gfargrp[i].int_name_er[strlen(\ - priv->gfargrp[i].int_name_er)], - "_er", sizeof("_er") + 1); + sprintf(priv->gfargrp[i].int_name_tx, "%s%s%c%s", + dev->name, "_g", '0' + i, "_tx"); + sprintf(priv->gfargrp[i].int_name_rx, "%s%s%c%s", + dev->name, "_g", '0' + i, "_rx"); + sprintf(priv->gfargrp[i].int_name_er, "%s%s%c%s", + dev->name, "_g", '0' + i, "_er"); } else - priv->gfargrp[i].int_name_tx[len_devname] = '\0'; + strcpy(priv->gfargrp[i].int_name_tx, dev->name); } /* Initialize the filer table */ diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h index fc2488adca36..4c9f8d487dbb 100644 --- a/drivers/net/ethernet/freescale/gianfar.h +++ b/drivers/net/ethernet/freescale/gianfar.h @@ -517,7 +517,7 @@ extern const char gfar_driver_version[]; #define RXFCB_PERR_MASK 0x000c #define RXFCB_PERR_BADL3 0x0008 -#define GFAR_INT_NAME_MAX IFNAMSIZ + 4 +#define GFAR_INT_NAME_MAX (IFNAMSIZ + 6) /* '_g#_xx' */ struct txbd8 { diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index 82c2c86a1951..423a1a2a702e 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -95,6 +95,10 @@ static int disable_msi = 0; module_param(disable_msi, int, 0); MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)"); +static int legacy_pme = 0; +module_param(legacy_pme, int, 0); +MODULE_PARM_DESC(legacy_pme, "Legacy power management"); + static DEFINE_PCI_DEVICE_TABLE(sky2_id_table) = { { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) }, /* SK-9Sxx */ { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) }, /* SK-9Exx */ @@ -867,6 +871,13 @@ static void sky2_wol_init(struct sky2_port *sky2) /* Disable PiG firmware */ sky2_write16(hw, B0_CTST, Y2_HW_WOL_OFF); + /* Needed by some broken BIOSes, use PCI rather than PCI-e for WOL */ + if (legacy_pme) { + u32 reg1 = sky2_pci_read32(hw, PCI_DEV_REG1); + reg1 |= PCI_Y2_PME_LEGACY; + sky2_pci_write32(hw, PCI_DEV_REG1, reg1); + } + /* block receiver */ sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET); sky2_read32(hw, B0_CTST); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 2b5af22419a5..385a4d5c7c25 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -36,8 +36,8 @@ #define _QLCNIC_LINUX_MAJOR 5 #define _QLCNIC_LINUX_MINOR 0 -#define _QLCNIC_LINUX_SUBVERSION 25 -#define QLCNIC_LINUX_VERSIONID "5.0.26" +#define _QLCNIC_LINUX_SUBVERSION 27 +#define QLCNIC_LINUX_VERSIONID "5.0.27" #define QLCNIC_DRV_IDC_VER 0x01 #define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\ (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION)) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 81bb1a69e69f..75c32e875fef 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -1458,8 +1458,10 @@ qlcnic_reset_context(struct qlcnic_adapter *adapter) if (netif_running(netdev)) { err = qlcnic_attach(adapter); - if (!err) + if (!err) { __qlcnic_up(adapter, netdev); + qlcnic_restore_indev_addr(netdev, NETDEV_UP); + } } netif_device_attach(netdev); diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index 1dc4fad593e7..fee449355014 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -2280,7 +2280,7 @@ static int __devinit smc_drv_probe(struct platform_device *pdev) if (ret) goto out_release_io; #if defined(CONFIG_SA1100_ASSABET) - NCR_0 |= NCR_ENET_OSC_EN; + neponset_ncr_set(NCR_ENET_OSC_EN); #endif platform_set_drvdata(pdev, ndev); ret = smc_enable_device(pdev); diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig index e535137eb2d0..468047866c8c 100644 --- a/drivers/net/irda/Kconfig +++ b/drivers/net/irda/Kconfig @@ -356,7 +356,7 @@ config VLSI_FIR config SA1100_FIR tristate "SA1100 Internal IR" - depends on ARCH_SA1100 && IRDA + depends on ARCH_SA1100 && IRDA && DMA_SA11X0 config VIA_FIR tristate "VIA VT8231/VT1211 SIR/MIR/FIR" diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c index da2705061a60..a0d1913a58d3 100644 --- a/drivers/net/irda/sa1100_ir.c +++ b/drivers/net/irda/sa1100_ir.c @@ -15,7 +15,7 @@ * This driver takes one kernel command line parameter, sa1100ir=, with * the following options: * max_rate:baudrate - set the maximum baud rate - * power_leve:level - set the transmitter power level + * power_level:level - set the transmitter power level * tx_lpm:0|1 - set transmit low power mode */ #include <linux/module.h> @@ -30,13 +30,13 @@ #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/sa11x0-dma.h> #include <net/irda/irda.h> #include <net/irda/wrapper.h> #include <net/irda/irda_device.h> -#include <asm/irq.h> -#include <mach/dma.h> #include <mach/hardware.h> #include <asm/mach/irda.h> @@ -44,8 +44,15 @@ static int power_level = 3; static int tx_lpm; static int max_rate = 4000000; +struct sa1100_buf { + struct device *dev; + struct sk_buff *skb; + struct scatterlist sg; + struct dma_chan *chan; + dma_cookie_t cookie; +}; + struct sa1100_irda { - unsigned char hscr0; unsigned char utcr4; unsigned char power; unsigned char open; @@ -53,12 +60,8 @@ struct sa1100_irda { int speed; int newspeed; - struct sk_buff *txskb; - struct sk_buff *rxskb; - dma_addr_t txbuf_dma; - dma_addr_t rxbuf_dma; - dma_regs_t *txdma; - dma_regs_t *rxdma; + struct sa1100_buf dma_rx; + struct sa1100_buf dma_tx; struct device *dev; struct irda_platform_data *pdata; @@ -67,23 +70,103 @@ struct sa1100_irda { iobuff_t tx_buff; iobuff_t rx_buff; + + int (*tx_start)(struct sk_buff *, struct net_device *, struct sa1100_irda *); + irqreturn_t (*irq)(struct net_device *, struct sa1100_irda *); }; +static int sa1100_irda_set_speed(struct sa1100_irda *, int); + #define IS_FIR(si) ((si)->speed >= 4000000) #define HPSIR_MAX_RXLEN 2047 +static struct dma_slave_config sa1100_irda_sir_tx = { + .direction = DMA_TO_DEVICE, + .dst_addr = __PREG(Ser2UTDR), + .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .dst_maxburst = 4, +}; + +static struct dma_slave_config sa1100_irda_fir_rx = { + .direction = DMA_FROM_DEVICE, + .src_addr = __PREG(Ser2HSDR), + .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .src_maxburst = 8, +}; + +static struct dma_slave_config sa1100_irda_fir_tx = { + .direction = DMA_TO_DEVICE, + .dst_addr = __PREG(Ser2HSDR), + .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .dst_maxburst = 8, +}; + +static unsigned sa1100_irda_dma_xferred(struct sa1100_buf *buf) +{ + struct dma_chan *chan = buf->chan; + struct dma_tx_state state; + enum dma_status status; + + status = chan->device->device_tx_status(chan, buf->cookie, &state); + if (status != DMA_PAUSED) + return 0; + + return sg_dma_len(&buf->sg) - state.residue; +} + +static int sa1100_irda_dma_request(struct device *dev, struct sa1100_buf *buf, + const char *name, struct dma_slave_config *cfg) +{ + dma_cap_mask_t m; + int ret; + + dma_cap_zero(m); + dma_cap_set(DMA_SLAVE, m); + + buf->chan = dma_request_channel(m, sa11x0_dma_filter_fn, (void *)name); + if (!buf->chan) { + dev_err(dev, "unable to request DMA channel for %s\n", + name); + return -ENOENT; + } + + ret = dmaengine_slave_config(buf->chan, cfg); + if (ret) + dev_warn(dev, "DMA slave_config for %s returned %d\n", + name, ret); + + buf->dev = buf->chan->device->dev; + + return 0; +} + +static void sa1100_irda_dma_start(struct sa1100_buf *buf, + enum dma_transfer_direction dir, dma_async_tx_callback cb, void *cb_p) +{ + struct dma_async_tx_descriptor *desc; + struct dma_chan *chan = buf->chan; + + desc = chan->device->device_prep_slave_sg(chan, &buf->sg, 1, dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (desc) { + desc->callback = cb; + desc->callback_param = cb_p; + buf->cookie = dmaengine_submit(desc); + dma_async_issue_pending(chan); + } +} + /* * Allocate and map the receive buffer, unless it is already allocated. */ static int sa1100_irda_rx_alloc(struct sa1100_irda *si) { - if (si->rxskb) + if (si->dma_rx.skb) return 0; - si->rxskb = alloc_skb(HPSIR_MAX_RXLEN + 1, GFP_ATOMIC); - - if (!si->rxskb) { + si->dma_rx.skb = alloc_skb(HPSIR_MAX_RXLEN + 1, GFP_ATOMIC); + if (!si->dma_rx.skb) { printk(KERN_ERR "sa1100_ir: out of memory for RX SKB\n"); return -ENOMEM; } @@ -92,11 +175,14 @@ static int sa1100_irda_rx_alloc(struct sa1100_irda *si) * Align any IP headers that may be contained * within the frame. */ - skb_reserve(si->rxskb, 1); + skb_reserve(si->dma_rx.skb, 1); + + sg_set_buf(&si->dma_rx.sg, si->dma_rx.skb->data, HPSIR_MAX_RXLEN); + if (dma_map_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE) == 0) { + dev_kfree_skb_any(si->dma_rx.skb); + return -ENOMEM; + } - si->rxbuf_dma = dma_map_single(si->dev, si->rxskb->data, - HPSIR_MAX_RXLEN, - DMA_FROM_DEVICE); return 0; } @@ -106,7 +192,7 @@ static int sa1100_irda_rx_alloc(struct sa1100_irda *si) */ static void sa1100_irda_rx_dma_start(struct sa1100_irda *si) { - if (!si->rxskb) { + if (!si->dma_rx.skb) { printk(KERN_ERR "sa1100_ir: rx buffer went missing\n"); return; } @@ -114,254 +200,87 @@ static void sa1100_irda_rx_dma_start(struct sa1100_irda *si) /* * First empty receive FIFO */ - Ser2HSCR0 = si->hscr0 | HSCR0_HSSP; + Ser2HSCR0 = HSCR0_HSSP; /* * Enable the DMA, receiver and receive interrupt. */ - sa1100_clear_dma(si->rxdma); - sa1100_start_dma(si->rxdma, si->rxbuf_dma, HPSIR_MAX_RXLEN); - Ser2HSCR0 = si->hscr0 | HSCR0_HSSP | HSCR0_RXE; + dmaengine_terminate_all(si->dma_rx.chan); + sa1100_irda_dma_start(&si->dma_rx, DMA_DEV_TO_MEM, NULL, NULL); + + Ser2HSCR0 = HSCR0_HSSP | HSCR0_RXE; } -/* - * Set the IrDA communications speed. - */ -static int sa1100_irda_set_speed(struct sa1100_irda *si, int speed) +static void sa1100_irda_check_speed(struct sa1100_irda *si) { - unsigned long flags; - int brd, ret = -EINVAL; - - switch (speed) { - case 9600: case 19200: case 38400: - case 57600: case 115200: - brd = 3686400 / (16 * speed) - 1; - - /* - * Stop the receive DMA. - */ - if (IS_FIR(si)) - sa1100_stop_dma(si->rxdma); - - local_irq_save(flags); - - Ser2UTCR3 = 0; - Ser2HSCR0 = HSCR0_UART; - - Ser2UTCR1 = brd >> 8; - Ser2UTCR2 = brd; - - /* - * Clear status register - */ - Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; - Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE; - - if (si->pdata->set_speed) - si->pdata->set_speed(si->dev, speed); - - si->speed = speed; - - local_irq_restore(flags); - ret = 0; - break; - - case 4000000: - local_irq_save(flags); - - si->hscr0 = 0; - - Ser2HSSR0 = 0xff; - Ser2HSCR0 = si->hscr0 | HSCR0_HSSP; - Ser2UTCR3 = 0; - - si->speed = speed; - - if (si->pdata->set_speed) - si->pdata->set_speed(si->dev, speed); - - sa1100_irda_rx_alloc(si); - sa1100_irda_rx_dma_start(si); - - local_irq_restore(flags); - - break; - - default: - break; + if (si->newspeed) { + sa1100_irda_set_speed(si, si->newspeed); + si->newspeed = 0; } - - return ret; } /* - * Control the power state of the IrDA transmitter. - * State: - * 0 - off - * 1 - short range, lowest power - * 2 - medium range, medium power - * 3 - maximum range, high power - * - * Currently, only assabet is known to support this. + * HP-SIR format support. */ -static int -__sa1100_irda_set_power(struct sa1100_irda *si, unsigned int state) +static void sa1100_irda_sirtxdma_irq(void *id) { - int ret = 0; - if (si->pdata->set_power) - ret = si->pdata->set_power(si->dev, state); - return ret; -} - -static inline int -sa1100_set_power(struct sa1100_irda *si, unsigned int state) -{ - int ret; - - ret = __sa1100_irda_set_power(si, state); - if (ret == 0) - si->power = state; + struct net_device *dev = id; + struct sa1100_irda *si = netdev_priv(dev); - return ret; -} + dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE); + dev_kfree_skb(si->dma_tx.skb); + si->dma_tx.skb = NULL; -static int sa1100_irda_startup(struct sa1100_irda *si) -{ - int ret; + dev->stats.tx_packets++; + dev->stats.tx_bytes += sg_dma_len(&si->dma_tx.sg); - /* - * Ensure that the ports for this device are setup correctly. - */ - if (si->pdata->startup) { - ret = si->pdata->startup(si->dev); - if (ret) - return ret; - } - - /* - * Configure PPC for IRDA - we want to drive TXD2 low. - * We also want to drive this pin low during sleep. - */ - PPSR &= ~PPC_TXD2; - PSDR &= ~PPC_TXD2; - PPDR |= PPC_TXD2; - - /* - * Enable HP-SIR modulation, and ensure that the port is disabled. - */ - Ser2UTCR3 = 0; - Ser2HSCR0 = HSCR0_UART; - Ser2UTCR4 = si->utcr4; - Ser2UTCR0 = UTCR0_8BitData; - Ser2HSCR2 = HSCR2_TrDataH | HSCR2_RcDataL; + /* We need to ensure that the transmitter has finished. */ + do + rmb(); + while (Ser2UTSR1 & UTSR1_TBY); /* - * Clear status register + * Ok, we've finished transmitting. Now enable the receiver. + * Sometimes we get a receive IRQ immediately after a transmit... */ Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; + Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE; - ret = sa1100_irda_set_speed(si, si->speed = 9600); - if (ret) { - Ser2UTCR3 = 0; - Ser2HSCR0 = 0; - - if (si->pdata->shutdown) - si->pdata->shutdown(si->dev); - } - - return ret; -} - -static void sa1100_irda_shutdown(struct sa1100_irda *si) -{ - /* - * Stop all DMA activity. - */ - sa1100_stop_dma(si->rxdma); - sa1100_stop_dma(si->txdma); - - /* Disable the port. */ - Ser2UTCR3 = 0; - Ser2HSCR0 = 0; + sa1100_irda_check_speed(si); - if (si->pdata->shutdown) - si->pdata->shutdown(si->dev); + /* I'm hungry! */ + netif_wake_queue(dev); } -#ifdef CONFIG_PM -/* - * Suspend the IrDA interface. - */ -static int sa1100_irda_suspend(struct platform_device *pdev, pm_message_t state) +static int sa1100_irda_sir_tx_start(struct sk_buff *skb, struct net_device *dev, + struct sa1100_irda *si) { - struct net_device *dev = platform_get_drvdata(pdev); - struct sa1100_irda *si; - - if (!dev) - return 0; - - si = netdev_priv(dev); - if (si->open) { - /* - * Stop the transmit queue - */ - netif_device_detach(dev); - disable_irq(dev->irq); - sa1100_irda_shutdown(si); - __sa1100_irda_set_power(si, 0); + si->tx_buff.data = si->tx_buff.head; + si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data, + si->tx_buff.truesize); + + si->dma_tx.skb = skb; + sg_set_buf(&si->dma_tx.sg, si->tx_buff.data, si->tx_buff.len); + if (dma_map_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE) == 0) { + si->dma_tx.skb = NULL; + netif_wake_queue(dev); + dev->stats.tx_dropped++; + return NETDEV_TX_OK; } - return 0; -} - -/* - * Resume the IrDA interface. - */ -static int sa1100_irda_resume(struct platform_device *pdev) -{ - struct net_device *dev = platform_get_drvdata(pdev); - struct sa1100_irda *si; - - if (!dev) - return 0; + sa1100_irda_dma_start(&si->dma_tx, DMA_MEM_TO_DEV, sa1100_irda_sirtxdma_irq, dev); - si = netdev_priv(dev); - if (si->open) { - /* - * If we missed a speed change, initialise at the new speed - * directly. It is debatable whether this is actually - * required, but in the interests of continuing from where - * we left off it is desirable. The converse argument is - * that we should re-negotiate at 9600 baud again. - */ - if (si->newspeed) { - si->speed = si->newspeed; - si->newspeed = 0; - } - - sa1100_irda_startup(si); - __sa1100_irda_set_power(si, si->power); - enable_irq(dev->irq); - - /* - * This automatically wakes up the queue - */ - netif_device_attach(dev); - } + /* + * The mean turn-around time is enforced by XBOF padding, + * so we don't have to do anything special here. + */ + Ser2UTCR3 = UTCR3_TXE; - return 0; + return NETDEV_TX_OK; } -#else -#define sa1100_irda_suspend NULL -#define sa1100_irda_resume NULL -#endif -/* - * HP-SIR format interrupt service routines. - */ -static void sa1100_irda_hpsir_irq(struct net_device *dev) +static irqreturn_t sa1100_irda_sir_irq(struct net_device *dev, struct sa1100_irda *si) { - struct sa1100_irda *si = netdev_priv(dev); int status; status = Ser2UTSR0; @@ -414,51 +333,96 @@ static void sa1100_irda_hpsir_irq(struct net_device *dev) } - if (status & UTSR0_TFS && si->tx_buff.len) { - /* - * Transmitter FIFO is not full - */ - do { - Ser2UTDR = *si->tx_buff.data++; - si->tx_buff.len -= 1; - } while (Ser2UTSR1 & UTSR1_TNF && si->tx_buff.len); + return IRQ_HANDLED; +} - if (si->tx_buff.len == 0) { - dev->stats.tx_packets++; - dev->stats.tx_bytes += si->tx_buff.data - - si->tx_buff.head; +/* + * FIR format support. + */ +static void sa1100_irda_firtxdma_irq(void *id) +{ + struct net_device *dev = id; + struct sa1100_irda *si = netdev_priv(dev); + struct sk_buff *skb; - /* - * We need to ensure that the transmitter has - * finished. - */ - do - rmb(); - while (Ser2UTSR1 & UTSR1_TBY); + /* + * Wait for the transmission to complete. Unfortunately, + * the hardware doesn't give us an interrupt to indicate + * "end of frame". + */ + do + rmb(); + while (!(Ser2HSSR0 & HSSR0_TUR) || Ser2HSSR1 & HSSR1_TBY); - /* - * Ok, we've finished transmitting. Now enable - * the receiver. Sometimes we get a receive IRQ - * immediately after a transmit... - */ - Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; - Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE; + /* + * Clear the transmit underrun bit. + */ + Ser2HSSR0 = HSSR0_TUR; - if (si->newspeed) { - sa1100_irda_set_speed(si, si->newspeed); - si->newspeed = 0; - } + /* + * Do we need to change speed? Note that we're lazy + * here - we don't free the old dma_rx.skb. We don't need + * to allocate a buffer either. + */ + sa1100_irda_check_speed(si); - /* I'm hungry! */ - netif_wake_queue(dev); - } + /* + * Start reception. This disables the transmitter for + * us. This will be using the existing RX buffer. + */ + sa1100_irda_rx_dma_start(si); + + /* Account and free the packet. */ + skb = si->dma_tx.skb; + if (skb) { + dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, + DMA_TO_DEVICE); + dev->stats.tx_packets ++; + dev->stats.tx_bytes += skb->len; + dev_kfree_skb_irq(skb); + si->dma_tx.skb = NULL; } + + /* + * Make sure that the TX queue is available for sending + * (for retries). TX has priority over RX at all times. + */ + netif_wake_queue(dev); +} + +static int sa1100_irda_fir_tx_start(struct sk_buff *skb, struct net_device *dev, + struct sa1100_irda *si) +{ + int mtt = irda_get_mtt(skb); + + si->dma_tx.skb = skb; + sg_set_buf(&si->dma_tx.sg, skb->data, skb->len); + if (dma_map_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE) == 0) { + si->dma_tx.skb = NULL; + netif_wake_queue(dev); + dev->stats.tx_dropped++; + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + sa1100_irda_dma_start(&si->dma_tx, DMA_MEM_TO_DEV, sa1100_irda_firtxdma_irq, dev); + + /* + * If we have a mean turn-around time, impose the specified + * specified delay. We could shorten this by timing from + * the point we received the packet. + */ + if (mtt) + udelay(mtt); + + Ser2HSCR0 = HSCR0_HSSP | HSCR0_TXE; + + return NETDEV_TX_OK; } static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev) { - struct sk_buff *skb = si->rxskb; - dma_addr_t dma_addr; + struct sk_buff *skb = si->dma_rx.skb; unsigned int len, stat, data; if (!skb) { @@ -469,11 +433,10 @@ static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev /* * Get the current data position. */ - dma_addr = sa1100_get_dma_pos(si->rxdma); - len = dma_addr - si->rxbuf_dma; + len = sa1100_irda_dma_xferred(&si->dma_rx); if (len > HPSIR_MAX_RXLEN) len = HPSIR_MAX_RXLEN; - dma_unmap_single(si->dev, si->rxbuf_dma, len, DMA_FROM_DEVICE); + dma_unmap_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE); do { /* @@ -501,7 +464,7 @@ static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev } while (Ser2HSSR0 & HSSR0_EIF); if (stat & HSSR1_EOF) { - si->rxskb = NULL; + si->dma_rx.skb = NULL; skb_put(skb, len); skb->dev = dev; @@ -518,28 +481,23 @@ static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev netif_rx(skb); } else { /* - * Remap the buffer. + * Remap the buffer - it was previously mapped, and we + * hope that this succeeds. */ - si->rxbuf_dma = dma_map_single(si->dev, si->rxskb->data, - HPSIR_MAX_RXLEN, - DMA_FROM_DEVICE); + dma_map_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE); } } /* - * FIR format interrupt service routine. We only have to - * handle RX events; transmit events go via the TX DMA handler. - * - * No matter what, we disable RX, process, and the restart RX. + * We only have to handle RX events here; transmit events go via the TX + * DMA handler. We disable RX, process, and the restart RX. */ -static void sa1100_irda_fir_irq(struct net_device *dev) +static irqreturn_t sa1100_irda_fir_irq(struct net_device *dev, struct sa1100_irda *si) { - struct sa1100_irda *si = netdev_priv(dev); - /* * Stop RX DMA */ - sa1100_stop_dma(si->rxdma); + dmaengine_pause(si->dma_rx.chan); /* * Framing error - we throw away the packet completely. @@ -555,7 +513,7 @@ static void sa1100_irda_fir_irq(struct net_device *dev) /* * Clear out the DMA... */ - Ser2HSCR0 = si->hscr0 | HSCR0_HSSP; + Ser2HSCR0 = HSCR0_HSSP; /* * Clear selected status bits now, so we @@ -577,74 +535,124 @@ static void sa1100_irda_fir_irq(struct net_device *dev) * No matter what happens, we must restart reception. */ sa1100_irda_rx_dma_start(si); -} -static irqreturn_t sa1100_irda_irq(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - if (IS_FIR(((struct sa1100_irda *)netdev_priv(dev)))) - sa1100_irda_fir_irq(dev); - else - sa1100_irda_hpsir_irq(dev); return IRQ_HANDLED; } /* - * TX DMA completion handler. + * Set the IrDA communications speed. */ -static void sa1100_irda_txdma_irq(void *id) +static int sa1100_irda_set_speed(struct sa1100_irda *si, int speed) { - struct net_device *dev = id; - struct sa1100_irda *si = netdev_priv(dev); - struct sk_buff *skb = si->txskb; + unsigned long flags; + int brd, ret = -EINVAL; - si->txskb = NULL; + switch (speed) { + case 9600: case 19200: case 38400: + case 57600: case 115200: + brd = 3686400 / (16 * speed) - 1; - /* - * Wait for the transmission to complete. Unfortunately, - * the hardware doesn't give us an interrupt to indicate - * "end of frame". - */ - do - rmb(); - while (!(Ser2HSSR0 & HSSR0_TUR) || Ser2HSSR1 & HSSR1_TBY); + /* Stop the receive DMA, and configure transmit. */ + if (IS_FIR(si)) { + dmaengine_terminate_all(si->dma_rx.chan); + dmaengine_slave_config(si->dma_tx.chan, + &sa1100_irda_sir_tx); + } - /* - * Clear the transmit underrun bit. - */ - Ser2HSSR0 = HSSR0_TUR; + local_irq_save(flags); - /* - * Do we need to change speed? Note that we're lazy - * here - we don't free the old rxskb. We don't need - * to allocate a buffer either. - */ - if (si->newspeed) { - sa1100_irda_set_speed(si, si->newspeed); - si->newspeed = 0; - } + Ser2UTCR3 = 0; + Ser2HSCR0 = HSCR0_UART; - /* - * Start reception. This disables the transmitter for - * us. This will be using the existing RX buffer. - */ - sa1100_irda_rx_dma_start(si); + Ser2UTCR1 = brd >> 8; + Ser2UTCR2 = brd; - /* - * Account and free the packet. - */ - if (skb) { - dma_unmap_single(si->dev, si->txbuf_dma, skb->len, DMA_TO_DEVICE); - dev->stats.tx_packets ++; - dev->stats.tx_bytes += skb->len; - dev_kfree_skb_irq(skb); + /* + * Clear status register + */ + Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; + Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE; + + if (si->pdata->set_speed) + si->pdata->set_speed(si->dev, speed); + + si->speed = speed; + si->tx_start = sa1100_irda_sir_tx_start; + si->irq = sa1100_irda_sir_irq; + + local_irq_restore(flags); + ret = 0; + break; + + case 4000000: + if (!IS_FIR(si)) + dmaengine_slave_config(si->dma_tx.chan, + &sa1100_irda_fir_tx); + + local_irq_save(flags); + + Ser2HSSR0 = 0xff; + Ser2HSCR0 = HSCR0_HSSP; + Ser2UTCR3 = 0; + + si->speed = speed; + si->tx_start = sa1100_irda_fir_tx_start; + si->irq = sa1100_irda_fir_irq; + + if (si->pdata->set_speed) + si->pdata->set_speed(si->dev, speed); + + sa1100_irda_rx_alloc(si); + sa1100_irda_rx_dma_start(si); + + local_irq_restore(flags); + + break; + + default: + break; } - /* - * Make sure that the TX queue is available for sending - * (for retries). TX has priority over RX at all times. - */ - netif_wake_queue(dev); + return ret; +} + +/* + * Control the power state of the IrDA transmitter. + * State: + * 0 - off + * 1 - short range, lowest power + * 2 - medium range, medium power + * 3 - maximum range, high power + * + * Currently, only assabet is known to support this. + */ +static int +__sa1100_irda_set_power(struct sa1100_irda *si, unsigned int state) +{ + int ret = 0; + if (si->pdata->set_power) + ret = si->pdata->set_power(si->dev, state); + return ret; +} + +static inline int +sa1100_set_power(struct sa1100_irda *si, unsigned int state) +{ + int ret; + + ret = __sa1100_irda_set_power(si, state); + if (ret == 0) + si->power = state; + + return ret; +} + +static irqreturn_t sa1100_irda_irq(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct sa1100_irda *si = netdev_priv(dev); + + return si->irq(dev, si); } static int sa1100_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) @@ -660,62 +668,19 @@ static int sa1100_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) if (speed != si->speed && speed != -1) si->newspeed = speed; - /* - * If this is an empty frame, we can bypass a lot. - */ + /* If this is an empty frame, we can bypass a lot. */ if (skb->len == 0) { - if (si->newspeed) { - si->newspeed = 0; - sa1100_irda_set_speed(si, speed); - } + sa1100_irda_check_speed(si); dev_kfree_skb(skb); return NETDEV_TX_OK; } - if (!IS_FIR(si)) { - netif_stop_queue(dev); - - si->tx_buff.data = si->tx_buff.head; - si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data, - si->tx_buff.truesize); - - /* - * Set the transmit interrupt enable. This will fire - * off an interrupt immediately. Note that we disable - * the receiver so we won't get spurious characteres - * received. - */ - Ser2UTCR3 = UTCR3_TIE | UTCR3_TXE; - - dev_kfree_skb(skb); - } else { - int mtt = irda_get_mtt(skb); - - /* - * We must not be transmitting... - */ - BUG_ON(si->txskb); - - netif_stop_queue(dev); - - si->txskb = skb; - si->txbuf_dma = dma_map_single(si->dev, skb->data, - skb->len, DMA_TO_DEVICE); - - sa1100_start_dma(si->txdma, si->txbuf_dma, skb->len); - - /* - * If we have a mean turn-around time, impose the specified - * specified delay. We could shorten this by timing from - * the point we received the packet. - */ - if (mtt) - udelay(mtt); + netif_stop_queue(dev); - Ser2HSCR0 = si->hscr0 | HSCR0_HSSP | HSCR0_TXE; - } + /* We must not already have a skb to transmit... */ + BUG_ON(si->dma_tx.skb); - return NETDEV_TX_OK; + return si->tx_start(skb, dev, si); } static int @@ -762,6 +727,69 @@ sa1100_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) return ret; } +static int sa1100_irda_startup(struct sa1100_irda *si) +{ + int ret; + + /* + * Ensure that the ports for this device are setup correctly. + */ + if (si->pdata->startup) { + ret = si->pdata->startup(si->dev); + if (ret) + return ret; + } + + /* + * Configure PPC for IRDA - we want to drive TXD2 low. + * We also want to drive this pin low during sleep. + */ + PPSR &= ~PPC_TXD2; + PSDR &= ~PPC_TXD2; + PPDR |= PPC_TXD2; + + /* + * Enable HP-SIR modulation, and ensure that the port is disabled. + */ + Ser2UTCR3 = 0; + Ser2HSCR0 = HSCR0_UART; + Ser2UTCR4 = si->utcr4; + Ser2UTCR0 = UTCR0_8BitData; + Ser2HSCR2 = HSCR2_TrDataH | HSCR2_RcDataL; + + /* + * Clear status register + */ + Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; + + ret = sa1100_irda_set_speed(si, si->speed = 9600); + if (ret) { + Ser2UTCR3 = 0; + Ser2HSCR0 = 0; + + if (si->pdata->shutdown) + si->pdata->shutdown(si->dev); + } + + return ret; +} + +static void sa1100_irda_shutdown(struct sa1100_irda *si) +{ + /* + * Stop all DMA activity. + */ + dmaengine_terminate_all(si->dma_rx.chan); + dmaengine_terminate_all(si->dma_tx.chan); + + /* Disable the port. */ + Ser2UTCR3 = 0; + Ser2HSCR0 = 0; + + if (si->pdata->shutdown) + si->pdata->shutdown(si->dev); +} + static int sa1100_irda_start(struct net_device *dev) { struct sa1100_irda *si = netdev_priv(dev); @@ -769,26 +797,17 @@ static int sa1100_irda_start(struct net_device *dev) si->speed = 9600; - err = request_irq(dev->irq, sa1100_irda_irq, 0, dev->name, dev); - if (err) - goto err_irq; - - err = sa1100_request_dma(DMA_Ser2HSSPRd, "IrDA receive", - NULL, NULL, &si->rxdma); + err = sa1100_irda_dma_request(si->dev, &si->dma_rx, "Ser2ICPRc", + &sa1100_irda_fir_rx); if (err) goto err_rx_dma; - err = sa1100_request_dma(DMA_Ser2HSSPWr, "IrDA transmit", - sa1100_irda_txdma_irq, dev, &si->txdma); + err = sa1100_irda_dma_request(si->dev, &si->dma_tx, "Ser2ICPTr", + &sa1100_irda_sir_tx); if (err) goto err_tx_dma; /* - * The interrupt must remain disabled for now. - */ - disable_irq(dev->irq); - - /* * Setup the serial port for the specified speed. */ err = sa1100_irda_startup(si); @@ -803,44 +822,60 @@ static int sa1100_irda_start(struct net_device *dev) if (!si->irlap) goto err_irlap; + err = request_irq(dev->irq, sa1100_irda_irq, 0, dev->name, dev); + if (err) + goto err_irq; + /* * Now enable the interrupt and start the queue */ si->open = 1; sa1100_set_power(si, power_level); /* low power mode */ - enable_irq(dev->irq); + netif_start_queue(dev); return 0; +err_irq: + irlap_close(si->irlap); err_irlap: si->open = 0; sa1100_irda_shutdown(si); err_startup: - sa1100_free_dma(si->txdma); + dma_release_channel(si->dma_tx.chan); err_tx_dma: - sa1100_free_dma(si->rxdma); + dma_release_channel(si->dma_rx.chan); err_rx_dma: - free_irq(dev->irq, dev); -err_irq: return err; } static int sa1100_irda_stop(struct net_device *dev) { struct sa1100_irda *si = netdev_priv(dev); + struct sk_buff *skb; + + netif_stop_queue(dev); - disable_irq(dev->irq); + si->open = 0; sa1100_irda_shutdown(si); /* - * If we have been doing DMA receive, make sure we + * If we have been doing any DMA activity, make sure we * tidy that up cleanly. */ - if (si->rxskb) { - dma_unmap_single(si->dev, si->rxbuf_dma, HPSIR_MAX_RXLEN, - DMA_FROM_DEVICE); - dev_kfree_skb(si->rxskb); - si->rxskb = NULL; + skb = si->dma_rx.skb; + if (skb) { + dma_unmap_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, + DMA_FROM_DEVICE); + dev_kfree_skb(skb); + si->dma_rx.skb = NULL; + } + + skb = si->dma_tx.skb; + if (skb) { + dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, + DMA_TO_DEVICE); + dev_kfree_skb(skb); + si->dma_tx.skb = NULL; } /* Stop IrLAP */ @@ -849,14 +884,11 @@ static int sa1100_irda_stop(struct net_device *dev) si->irlap = NULL; } - netif_stop_queue(dev); - si->open = 0; - /* * Free resources */ - sa1100_free_dma(si->txdma); - sa1100_free_dma(si->rxdma); + dma_release_channel(si->dma_tx.chan); + dma_release_channel(si->dma_rx.chan); free_irq(dev->irq, dev); sa1100_set_power(si, 0); @@ -888,11 +920,15 @@ static int sa1100_irda_probe(struct platform_device *pdev) struct net_device *dev; struct sa1100_irda *si; unsigned int baudrate_mask; - int err; + int err, irq; if (!pdev->dev.platform_data) return -EINVAL; + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return irq < 0 ? irq : -ENXIO; + err = request_mem_region(__PREG(Ser2UTCR0), 0x24, "IrDA") ? 0 : -EBUSY; if (err) goto err_mem_1; @@ -907,22 +943,27 @@ static int sa1100_irda_probe(struct platform_device *pdev) if (!dev) goto err_mem_4; + SET_NETDEV_DEV(dev, &pdev->dev); + si = netdev_priv(dev); si->dev = &pdev->dev; si->pdata = pdev->dev.platform_data; + sg_init_table(&si->dma_rx.sg, 1); + sg_init_table(&si->dma_tx.sg, 1); + /* * Initialise the HP-SIR buffers */ err = sa1100_irda_init_iobuf(&si->rx_buff, 14384); if (err) goto err_mem_5; - err = sa1100_irda_init_iobuf(&si->tx_buff, 4000); + err = sa1100_irda_init_iobuf(&si->tx_buff, IRDA_SIR_MAX_FRAME); if (err) goto err_mem_5; dev->netdev_ops = &sa1100_irda_netdev_ops; - dev->irq = IRQ_Ser2ICP; + dev->irq = irq; irda_init_max_qos_capabilies(&si->qos); @@ -996,6 +1037,74 @@ static int sa1100_irda_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +/* + * Suspend the IrDA interface. + */ +static int sa1100_irda_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct sa1100_irda *si; + + if (!dev) + return 0; + + si = netdev_priv(dev); + if (si->open) { + /* + * Stop the transmit queue + */ + netif_device_detach(dev); + disable_irq(dev->irq); + sa1100_irda_shutdown(si); + __sa1100_irda_set_power(si, 0); + } + + return 0; +} + +/* + * Resume the IrDA interface. + */ +static int sa1100_irda_resume(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct sa1100_irda *si; + + if (!dev) + return 0; + + si = netdev_priv(dev); + if (si->open) { + /* + * If we missed a speed change, initialise at the new speed + * directly. It is debatable whether this is actually + * required, but in the interests of continuing from where + * we left off it is desirable. The converse argument is + * that we should re-negotiate at 9600 baud again. + */ + if (si->newspeed) { + si->speed = si->newspeed; + si->newspeed = 0; + } + + sa1100_irda_startup(si); + __sa1100_irda_set_power(si, si->power); + enable_irq(dev->irq); + + /* + * This automatically wakes up the queue + */ + netif_device_attach(dev); + } + + return 0; +} +#else +#define sa1100_irda_suspend NULL +#define sa1100_irda_resume NULL +#endif + static struct platform_driver sa1100ir_driver = { .probe = sa1100_irda_probe, .remove = sa1100_irda_remove, diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c index 790cbdea7392..3886b30ed373 100644 --- a/drivers/net/usb/cdc-phonet.c +++ b/drivers/net/usb/cdc-phonet.c @@ -164,12 +164,14 @@ static void rx_complete(struct urb *req) /* Can't use pskb_pull() on page in IRQ */ memcpy(skb_put(skb, 1), page_address(page), 1); skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, - page, 1, req->actual_length); + page, 1, req->actual_length, + req->actual_length); page = NULL; } } else { skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, - page, 0, req->actual_length); + page, 0, req->actual_length, + req->actual_length); page = NULL; } if (req->actual_length < PAGE_SIZE) diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index aac68f5195c0..552d24bf862e 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -409,6 +409,42 @@ static const struct usb_device_id products[] = { .bInterfaceProtocol = 0xff, .driver_info = (unsigned long)&qmi_wwan_force_int4, }, + { /* ZTE (Vodafone) K3565-Z */ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x19d2, + .idProduct = 0x0063, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0xff, + .bInterfaceProtocol = 0xff, + .driver_info = (unsigned long)&qmi_wwan_force_int4, + }, + { /* ZTE (Vodafone) K3570-Z */ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x19d2, + .idProduct = 0x1008, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0xff, + .bInterfaceProtocol = 0xff, + .driver_info = (unsigned long)&qmi_wwan_force_int4, + }, + { /* ZTE (Vodafone) K3571-Z */ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x19d2, + .idProduct = 0x1010, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0xff, + .bInterfaceProtocol = 0xff, + .driver_info = (unsigned long)&qmi_wwan_force_int4, + }, + { /* ZTE (Vodafone) K4505-Z */ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x19d2, + .idProduct = 0x0104, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0xff, + .bInterfaceProtocol = 0xff, + .driver_info = (unsigned long)&qmi_wwan_force_int4, + }, {QMI_GOBI_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ {QMI_GOBI_DEVICE(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */ {QMI_GOBI_DEVICE(0x03f0, 0x371d)}, /* HP un2430 Mobile Broadband Module */ diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 4b8b52ca09d8..b7b3f5b0d406 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -493,6 +493,7 @@ block: if (netif_running (dev->net) && !test_bit (EVENT_RX_HALT, &dev->flags)) { rx_submit (dev, urb, GFP_ATOMIC); + usb_mark_last_busy(dev->udev); return; } usb_free_urb (urb); @@ -589,6 +590,14 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q) entry = (struct skb_data *) skb->cb; urb = entry->urb; + /* + * Get reference count of the URB to avoid it to be + * freed during usb_unlink_urb, which may trigger + * use-after-free problem inside usb_unlink_urb since + * usb_unlink_urb is always racing with .complete + * handler(include defer_bh). + */ + usb_get_urb(urb); spin_unlock_irqrestore(&q->lock, flags); // during some PM-driven resume scenarios, // these (async) unlinks complete immediately @@ -597,6 +606,7 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q) netdev_dbg(dev->net, "unlink urb err, %d\n", retval); else count++; + usb_put_urb(urb); spin_lock_irqsave(&q->lock, flags); } spin_unlock_irqrestore (&q->lock, flags); @@ -1028,7 +1038,6 @@ static void tx_complete (struct urb *urb) } usb_autopm_put_interface_async(dev->intf); - urb->dev = NULL; entry->state = tx_done; defer_bh(dev, skb, &dev->txq); } diff --git a/drivers/net/wireless/iwlegacy/3945.c b/drivers/net/wireless/iwlegacy/3945.c index c5b1d199e0bc..b25c01be0d90 100644 --- a/drivers/net/wireless/iwlegacy/3945.c +++ b/drivers/net/wireless/iwlegacy/3945.c @@ -499,7 +499,8 @@ il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb, le32_to_cpu(rx_end->status), stats); skb_add_rx_frag(skb, 0, rxb->page, - (void *)rx_hdr->payload - (void *)pkt, len); + (void *)rx_hdr->payload - (void *)pkt, len, + len); il_update_stats(il, false, fc, len); memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 7b54dbb338be..17f1c6853182 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -596,7 +596,8 @@ il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr, return; } - skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), len); + skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), len, + len); il_update_stats(il, false, fc, len); memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c index 44c6f712b77d..f4b84d1596e3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c @@ -796,7 +796,7 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv, offset = (void *)hdr - rxb_addr(rxb); p = rxb_steal_page(rxb); - skb_add_rx_frag(skb, 0, p, offset, len); + skb_add_rx_frag(skb, 0, p, offset, len, len); iwl_update_stats(priv, false, fc, len); diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index b16175032327..663b32c2e931 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -47,6 +47,7 @@ #include <xen/xenbus.h> #include <xen/events.h> #include <xen/page.h> +#include <xen/platform_pci.h> #include <xen/grant_table.h> #include <xen/interface/io/netif.h> @@ -1964,6 +1965,9 @@ static int __init netif_init(void) if (xen_initial_domain()) return 0; + if (!xen_platform_pci_unplug) + return -ENODEV; + printk(KERN_INFO "Initialising Xen virtual ethernet driver.\n"); return xenbus_register_frontend(&netfront_driver); diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 20fbebd49db3..343ad29e211c 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -253,7 +253,7 @@ static struct amba_device *of_amba_device_create(struct device_node *node, if (!of_device_is_available(node)) return NULL; - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = amba_device_alloc(NULL, 0, 0); if (!dev) return NULL; @@ -283,14 +283,14 @@ static struct amba_device *of_amba_device_create(struct device_node *node, if (ret) goto err_free; - ret = amba_device_register(dev, &iomem_resource); + ret = amba_device_add(dev, &iomem_resource); if (ret) goto err_free; return dev; err_free: - kfree(dev); + amba_device_put(dev); return NULL; } #else /* CONFIG_ARM_AMBA */ diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c index 7ff10c1e8664..0610e91bceb2 100644 --- a/drivers/parisc/dino.c +++ b/drivers/parisc/dino.c @@ -553,7 +553,6 @@ dino_fixup_bus(struct pci_bus *bus) struct list_head *ln; struct pci_dev *dev; struct dino_device *dino_dev = DINO_DEV(parisc_walk_tree(bus->bridge)); - int port_base = HBA_PORT_BASE(dino_dev->hba.hba_num); DBG(KERN_WARNING "%s(0x%p) bus %d platform_data 0x%p\n", __func__, bus, bus->secondary, @@ -599,8 +598,6 @@ dino_fixup_bus(struct pci_bus *bus) list_for_each(ln, &bus->devices) { - int i; - dev = pci_dev_b(ln); if (is_card_dino(&dino_dev->hba.dev->id)) dino_card_fixup(dev); @@ -612,21 +609,6 @@ dino_fixup_bus(struct pci_bus *bus) if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) continue; - /* Adjust the I/O Port space addresses */ - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - struct resource *res = &dev->resource[i]; - if (res->flags & IORESOURCE_IO) { - res->start |= port_base; - res->end |= port_base; - } -#ifdef __LP64__ - /* Sign Extend MMIO addresses */ - else if (res->flags & IORESOURCE_MEM) { - res->start |= F_EXTEND(0UL); - res->end |= F_EXTEND(0UL); - } -#endif - } /* null out the ROM resource if there is one (we don't * care about an expansion rom on parisc, since it * usually contains (x86) bios code) */ @@ -991,11 +973,14 @@ static int __init dino_probe(struct parisc_device *dev) dev->dev.platform_data = dino_dev; - pci_add_resource(&resources, &dino_dev->hba.io_space); + pci_add_resource_offset(&resources, &dino_dev->hba.io_space, + HBA_PORT_BASE(dino_dev->hba.hba_num)); if (dino_dev->hba.lmmio_space.flags) - pci_add_resource(&resources, &dino_dev->hba.lmmio_space); + pci_add_resource_offset(&resources, &dino_dev->hba.lmmio_space, + dino_dev->hba.lmmio_space_offset); if (dino_dev->hba.elmmio_space.flags) - pci_add_resource(&resources, &dino_dev->hba.elmmio_space); + pci_add_resource_offset(&resources, &dino_dev->hba.elmmio_space, + dino_dev->hba.lmmio_space_offset); if (dino_dev->hba.gmmio_space.flags) pci_add_resource(&resources, &dino_dev->hba.gmmio_space); diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c index d5f3d753a108..e8857647e210 100644 --- a/drivers/parisc/lba_pci.c +++ b/drivers/parisc/lba_pci.c @@ -635,7 +635,6 @@ lba_fixup_bus(struct pci_bus *bus) u16 status; #endif struct lba_device *ldev = LBA_DEV(parisc_walk_tree(bus->bridge)); - int lba_portbase = HBA_PORT_BASE(ldev->hba.hba_num); DBG("lba_fixup_bus(0x%p) bus %d platform_data 0x%p\n", bus, bus->secondary, bus->bridge->platform_data); @@ -726,27 +725,6 @@ lba_fixup_bus(struct pci_bus *bus) if (!res->start) continue; - if (res->flags & IORESOURCE_IO) { - DBG("lba_fixup_bus() I/O Ports [%lx/%lx] -> ", - res->start, res->end); - res->start |= lba_portbase; - res->end |= lba_portbase; - DBG("[%lx/%lx]\n", res->start, res->end); - } else if (res->flags & IORESOURCE_MEM) { - /* - ** Convert PCI (IO_VIEW) addresses to - ** processor (PA_VIEW) addresses - */ - DBG("lba_fixup_bus() MMIO [%lx/%lx] -> ", - res->start, res->end); - res->start = PCI_HOST_ADDR(HBA_DATA(ldev), res->start); - res->end = PCI_HOST_ADDR(HBA_DATA(ldev), res->end); - DBG("[%lx/%lx]\n", res->start, res->end); - } else { - DBG("lba_fixup_bus() WTF? 0x%lx [%lx/%lx] XXX", - res->flags, res->start, res->end); - } - /* ** FIXME: this will result in whinging for devices ** that share expansion ROMs (think quad tulip), but @@ -1514,11 +1492,14 @@ lba_driver_probe(struct parisc_device *dev) lba_dev->hba.lmmio_space.flags = 0; } - pci_add_resource(&resources, &lba_dev->hba.io_space); + pci_add_resource_offset(&resources, &lba_dev->hba.io_space, + HBA_PORT_BASE(lba_dev->hba.hba_num)); if (lba_dev->hba.elmmio_space.start) - pci_add_resource(&resources, &lba_dev->hba.elmmio_space); + pci_add_resource_offset(&resources, &lba_dev->hba.elmmio_space, + lba_dev->hba.lmmio_space_offset); if (lba_dev->hba.lmmio_space.flags) - pci_add_resource(&resources, &lba_dev->hba.lmmio_space); + pci_add_resource_offset(&resources, &lba_dev->hba.lmmio_space, + lba_dev->hba.lmmio_space_offset); if (lba_dev->hba.gmmio_space.flags) pci_add_resource(&resources, &lba_dev->hba.gmmio_space); diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 37856f7c7781..848bfb84c04c 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -31,6 +31,19 @@ config PCI_DEBUG When in doubt, say N. +config PCI_REALLOC_ENABLE_AUTO + bool "Enable PCI resource re-allocation detection" + depends on PCI + help + Say Y here if you want the PCI core to detect if PCI resource + re-allocation needs to be enabled. You can always use pci=realloc=on + or pci=realloc=off to override it. Note this feature is a no-op + unless PCI_IOV support is also enabled; in that case it will + automatically re-allocate PCI resources if SR-IOV BARs have not + been allocated by the BIOS. + + When in doubt, say N. + config PCI_STUB tristate "PCI Stub driver" depends on PCI diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 398f5d859791..4ce5ef2f2826 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -18,28 +18,36 @@ #include "pci.h" -void pci_add_resource(struct list_head *resources, struct resource *res) +void pci_add_resource_offset(struct list_head *resources, struct resource *res, + resource_size_t offset) { - struct pci_bus_resource *bus_res; + struct pci_host_bridge_window *window; - bus_res = kzalloc(sizeof(struct pci_bus_resource), GFP_KERNEL); - if (!bus_res) { - printk(KERN_ERR "PCI: can't add bus resource %pR\n", res); + window = kzalloc(sizeof(struct pci_host_bridge_window), GFP_KERNEL); + if (!window) { + printk(KERN_ERR "PCI: can't add host bridge window %pR\n", res); return; } - bus_res->res = res; - list_add_tail(&bus_res->list, resources); + window->res = res; + window->offset = offset; + list_add_tail(&window->list, resources); +} +EXPORT_SYMBOL(pci_add_resource_offset); + +void pci_add_resource(struct list_head *resources, struct resource *res) +{ + pci_add_resource_offset(resources, res, 0); } EXPORT_SYMBOL(pci_add_resource); void pci_free_resource_list(struct list_head *resources) { - struct pci_bus_resource *bus_res, *tmp; + struct pci_host_bridge_window *window, *tmp; - list_for_each_entry_safe(bus_res, tmp, resources, list) { - list_del(&bus_res->list); - kfree(bus_res); + list_for_each_entry_safe(window, tmp, resources, list) { + list_del(&window->list); + kfree(window); } } EXPORT_SYMBOL(pci_free_resource_list); diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 9ddf69e3bbef..806c44fa645a 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -800,20 +800,10 @@ static int __ref enable_device(struct acpiphp_slot *slot) if (slot->flags & SLOT_ENABLED) goto err_exit; - /* sanity check: dev should be NULL when hot-plugged in */ - dev = pci_get_slot(bus, PCI_DEVFN(slot->device, 0)); - if (dev) { - /* This case shouldn't happen */ - err("pci_dev structure already exists.\n"); - pci_dev_put(dev); - retval = -1; - goto err_exit; - } - num = pci_scan_slot(bus, PCI_DEVFN(slot->device, 0)); if (num == 0) { - err("No new device found\n"); - retval = -1; + /* Maybe only part of funcs are added. */ + dbg("No new device found\n"); goto err_exit; } @@ -848,11 +838,16 @@ static int __ref enable_device(struct acpiphp_slot *slot) pci_bus_add_devices(bus); + slot->flags |= SLOT_ENABLED; list_for_each_entry(func, &slot->funcs, sibling) { dev = pci_get_slot(bus, PCI_DEVFN(slot->device, func->function)); - if (!dev) + if (!dev) { + /* Do not set SLOT_ENABLED flag if some funcs + are not added. */ + slot->flags &= (~SLOT_ENABLED); continue; + } if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE && dev->hdr_type != PCI_HEADER_TYPE_CARDBUS) { @@ -867,7 +862,6 @@ static int __ref enable_device(struct acpiphp_slot *slot) pci_dev_put(dev); } - slot->flags |= SLOT_ENABLED; err_exit: return retval; @@ -892,9 +886,12 @@ static int disable_device(struct acpiphp_slot *slot) { struct acpiphp_func *func; struct pci_dev *pdev; + struct pci_bus *bus = slot->bridge->pci_bus; - /* is this slot already disabled? */ - if (!(slot->flags & SLOT_ENABLED)) + /* The slot will be enabled when func 0 is added, so check + func 0 before disable the slot. */ + pdev = pci_get_slot(bus, PCI_DEVFN(slot->device, 0)); + if (!pdev) goto err_exit; list_for_each_entry(func, &slot->funcs, sibling) { @@ -913,7 +910,7 @@ static int disable_device(struct acpiphp_slot *slot) disable_bridges(pdev->subordinate); pci_disable_device(pdev); } - pci_remove_bus_device(pdev); + __pci_remove_bus_device(pdev); pci_dev_put(pdev); } } @@ -1070,7 +1067,7 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus) res->end) { /* Could not assign a required resources * for this device, remove it */ - pci_remove_bus_device(dev); + pci_stop_and_remove_bus_device(dev); break; } } diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c index 829c327cfb5e..ae853ccd0cd5 100644 --- a/drivers/pci/hotplug/cpci_hotplug_pci.c +++ b/drivers/pci/hotplug/cpci_hotplug_pci.c @@ -341,7 +341,7 @@ int cpci_unconfigure_slot(struct slot* slot) dev = pci_get_slot(slot->bus, PCI_DEVFN(PCI_SLOT(slot->devfn), i)); if (dev) { - pci_remove_bus_device(dev); + pci_stop_and_remove_bus_device(dev); pci_dev_put(dev); } } diff --git a/drivers/pci/hotplug/cpcihp_generic.c b/drivers/pci/hotplug/cpcihp_generic.c index fb3f84661bdc..81af764c629b 100644 --- a/drivers/pci/hotplug/cpcihp_generic.c +++ b/drivers/pci/hotplug/cpcihp_generic.c @@ -62,7 +62,7 @@ #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) /* local variables */ -static int debug; +static bool debug; static char *bridge; static u8 bridge_busnr; static u8 bridge_slot; diff --git a/drivers/pci/hotplug/cpqphp_pci.c b/drivers/pci/hotplug/cpqphp_pci.c index 6173b9a4544e..1c8494021a42 100644 --- a/drivers/pci/hotplug/cpqphp_pci.c +++ b/drivers/pci/hotplug/cpqphp_pci.c @@ -127,7 +127,7 @@ int cpqhp_unconfigure_device(struct pci_func* func) struct pci_dev* temp = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, j)); if (temp) { pci_dev_put(temp); - pci_remove_bus_device(temp); + pci_stop_and_remove_bus_device(temp); } } return 0; diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c index 17d10e2e8fb6..a019c9a712be 100644 --- a/drivers/pci/hotplug/fakephp.c +++ b/drivers/pci/hotplug/fakephp.c @@ -40,7 +40,7 @@ static ssize_t legacy_show(struct kobject *kobj, struct attribute *attr, static void remove_callback(void *data) { - pci_remove_bus_device((struct pci_dev *)data); + pci_stop_and_remove_bus_device((struct pci_dev *)data); } static ssize_t legacy_store(struct kobject *kobj, struct attribute *attr, diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c index 5506e0e8fbc0..4fda7e6a86a7 100644 --- a/drivers/pci/hotplug/ibmphp_core.c +++ b/drivers/pci/hotplug/ibmphp_core.c @@ -721,7 +721,7 @@ static void ibm_unconfigure_device(struct pci_func *func) for (j = 0; j < 0x08; j++) { temp = pci_get_bus_and_slot(func->busno, (func->device << 3) | j); if (temp) { - pci_remove_bus_device(temp); + pci_stop_and_remove_bus_device(temp); pci_dev_put(temp); } } diff --git a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c index 2850e64dedae..714ca5c4ed50 100644 --- a/drivers/pci/hotplug/ibmphp_ebda.c +++ b/drivers/pci/hotplug/ibmphp_ebda.c @@ -368,8 +368,10 @@ int __init ibmphp_access_ebda (void) debug ("rio blk id: %x\n", blk_id); rio_table_ptr = kzalloc(sizeof(struct rio_table_hdr), GFP_KERNEL); - if (!rio_table_ptr) - return -ENOMEM; + if (!rio_table_ptr) { + rc = -ENOMEM; + goto out; + } rio_table_ptr->ver_num = readb (io_mem + offset); rio_table_ptr->scal_count = readb (io_mem + offset + 1); rio_table_ptr->riodev_count = readb (io_mem + offset + 2); diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index bcdbb1643621..a960faec1021 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -241,34 +241,79 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) return retval; } -static inline int check_link_active(struct controller *ctrl) +static bool check_link_active(struct controller *ctrl) { - u16 link_status; + bool ret = false; + u16 lnk_status; - if (pciehp_readw(ctrl, PCI_EXP_LNKSTA, &link_status)) - return 0; - return !!(link_status & PCI_EXP_LNKSTA_DLLLA); + if (pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status)) + return ret; + + ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA); + + if (ret) + ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status); + + return ret; } -static void pcie_wait_link_active(struct controller *ctrl) +static void __pcie_wait_link_active(struct controller *ctrl, bool active) { int timeout = 1000; - if (check_link_active(ctrl)) + if (check_link_active(ctrl) == active) return; while (timeout > 0) { msleep(10); timeout -= 10; - if (check_link_active(ctrl)) + if (check_link_active(ctrl) == active) return; } - ctrl_dbg(ctrl, "Data Link Layer Link Active not set in 1000 msec\n"); + ctrl_dbg(ctrl, "Data Link Layer Link Active not %s in 1000 msec\n", + active ? "set" : "cleared"); +} + +static void pcie_wait_link_active(struct controller *ctrl) +{ + __pcie_wait_link_active(ctrl, true); +} + +static void pcie_wait_link_not_active(struct controller *ctrl) +{ + __pcie_wait_link_active(ctrl, false); +} + +static bool pci_bus_check_dev(struct pci_bus *bus, int devfn) +{ + u32 l; + int count = 0; + int delay = 1000, step = 20; + bool found = false; + + do { + found = pci_bus_read_dev_vendor_id(bus, devfn, &l, 0); + count++; + + if (found) + break; + + msleep(step); + delay -= step; + } while (delay > 0); + + if (count > 1 && pciehp_debug) + printk(KERN_DEBUG "pci %04x:%02x:%02x.%d id reading try %d times with interval %d ms to get %08x\n", + pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), + PCI_FUNC(devfn), count, step, l); + + return found; } int pciehp_check_link_status(struct controller *ctrl) { u16 lnk_status; int retval = 0; + bool found = false; /* * Data Link Layer Link Active Reporting must be capable for @@ -280,13 +325,10 @@ int pciehp_check_link_status(struct controller *ctrl) else msleep(1000); - /* - * Need to wait for 1000 ms after Data Link Layer Link Active - * (DLLLA) bit reads 1b before sending configuration request. - * We need it before checking Link Training (LT) bit becuase - * LT is still set even after DLLLA bit is set on some platform. - */ - msleep(1000); + /* wait 100ms before read pci conf, and try in 1s */ + msleep(100); + found = pci_bus_check_dev(ctrl->pcie->port->subordinate, + PCI_DEVFN(0, 0)); retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status); if (retval) { @@ -302,19 +344,50 @@ int pciehp_check_link_status(struct controller *ctrl) return retval; } - /* - * If the port supports Link speeds greater than 5.0 GT/s, we - * must wait for 100 ms after Link training completes before - * sending configuration request. - */ - if (ctrl->pcie->port->subordinate->max_bus_speed > PCIE_SPEED_5_0GT) - msleep(100); - pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status); + if (!found && !retval) + retval = -1; + return retval; } +static int __pciehp_link_set(struct controller *ctrl, bool enable) +{ + u16 lnk_ctrl; + int retval = 0; + + retval = pciehp_readw(ctrl, PCI_EXP_LNKCTL, &lnk_ctrl); + if (retval) { + ctrl_err(ctrl, "Cannot read LNKCTRL register\n"); + return retval; + } + + if (enable) + lnk_ctrl &= ~PCI_EXP_LNKCTL_LD; + else + lnk_ctrl |= PCI_EXP_LNKCTL_LD; + + retval = pciehp_writew(ctrl, PCI_EXP_LNKCTL, lnk_ctrl); + if (retval) { + ctrl_err(ctrl, "Cannot write LNKCTRL register\n"); + return retval; + } + ctrl_dbg(ctrl, "%s: lnk_ctrl = %x\n", __func__, lnk_ctrl); + + return retval; +} + +static int pciehp_link_enable(struct controller *ctrl) +{ + return __pciehp_link_set(ctrl, true); +} + +static int pciehp_link_disable(struct controller *ctrl) +{ + return __pciehp_link_set(ctrl, false); +} + int pciehp_get_attention_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; @@ -533,6 +606,10 @@ int pciehp_power_on_slot(struct slot * slot) ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); + retval = pciehp_link_enable(ctrl); + if (retval) + ctrl_err(ctrl, "%s: Can not enable the link!\n", __func__); + return retval; } @@ -543,6 +620,14 @@ int pciehp_power_off_slot(struct slot * slot) u16 cmd_mask; int retval; + /* Disable the link at first */ + pciehp_link_disable(ctrl); + /* wait the link is down */ + if (ctrl->link_active_reporting) + pcie_wait_link_not_active(ctrl); + else + msleep(1000); + slot_cmd = POWER_OFF; cmd_mask = PCI_EXP_SLTCTL_PCC; retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index a4031dfe938e..47d9dc06b109 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -141,7 +141,7 @@ int pciehp_unconfigure_device(struct slot *p_slot) break; } } - pci_remove_bus_device(temp); + pci_stop_and_remove_bus_device(temp); /* * Ensure that no new Requests will be generated from * the device. diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c index c56a9413e1af..1e117c2a3cad 100644 --- a/drivers/pci/hotplug/rpadlpar_core.c +++ b/drivers/pci/hotplug/rpadlpar_core.c @@ -389,7 +389,7 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn) BUG_ON(!bus->self); pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self)); eeh_remove_bus_device(bus->self); - pci_remove_bus_device(bus->self); + pci_stop_and_remove_bus_device(bus->self); return 0; } diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index 72d507b6a2aa..de573113c102 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -554,7 +554,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot) PCI_FUNC(func))); if (dev) { sn_bus_free_data(dev); - pci_remove_bus_device(dev); + pci_stop_and_remove_bus_device(dev); pci_dev_put(dev); } } diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c index a2ccfcd3c298..df7e4bfadae3 100644 --- a/drivers/pci/hotplug/shpchp_pci.c +++ b/drivers/pci/hotplug/shpchp_pci.c @@ -124,7 +124,7 @@ int shpchp_unconfigure_device(struct slot *p_slot) break; } } - pci_remove_bus_device(temp); + pci_stop_and_remove_bus_device(temp); pci_dev_put(temp); } return rc; diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 0dab5ecf61bb..6554e1a0f634 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -142,7 +142,7 @@ failed2: failed1: pci_dev_put(dev); mutex_lock(&iov->dev->sriov->lock); - pci_remove_bus_device(virtfn); + pci_stop_and_remove_bus_device(virtfn); virtfn_remove_bus(dev->bus, virtfn_bus(dev, id)); mutex_unlock(&iov->dev->sriov->lock); @@ -173,10 +173,16 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset) sprintf(buf, "virtfn%u", id); sysfs_remove_link(&dev->dev.kobj, buf); - sysfs_remove_link(&virtfn->dev.kobj, "physfn"); + /* + * pci_stop_dev() could have been called for this virtfn already, + * so the directory for the virtfn may have been removed before. + * Double check to avoid spurious sysfs warnings. + */ + if (virtfn->dev.kobj.sd) + sysfs_remove_link(&virtfn->dev.kobj, "physfn"); mutex_lock(&iov->dev->sriov->lock); - pci_remove_bus_device(virtfn); + pci_stop_and_remove_bus_device(virtfn); virtfn_remove_bus(dev->bus, virtfn_bus(dev, id)); mutex_unlock(&iov->dev->sriov->lock); diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 8d9616b821ca..6b54b23b990b 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -419,6 +419,16 @@ static void pci_device_shutdown(struct device *dev) drv->shutdown(pci_dev); pci_msi_shutdown(pci_dev); pci_msix_shutdown(pci_dev); + + /* + * Devices may be enabled to wake up by runtime PM, but they need not + * be supposed to wake up the system from its "power off" state (e.g. + * ACPI S5). Therefore disable wakeup for all devices that aren't + * supposed to wake up the system at this point. The state argument + * will be ignored by pci_enable_wake(). + */ + if (!device_may_wakeup(dev)) + pci_enable_wake(pci_dev, PCI_UNKNOWN, false); } #ifdef CONFIG_PM diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index a3cd8cad532a..a55e248618cd 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -330,7 +330,7 @@ static void remove_callback(struct device *dev) struct pci_dev *pdev = to_pci_dev(dev); mutex_lock(&pci_remove_rescan_mutex); - pci_remove_bus_device(pdev); + pci_stop_and_remove_bus_device(pdev); mutex_unlock(&pci_remove_rescan_mutex); } @@ -366,7 +366,10 @@ dev_bus_rescan_store(struct device *dev, struct device_attribute *attr, if (val) { mutex_lock(&pci_remove_rescan_mutex); - pci_rescan_bus(bus); + if (!pci_is_root_bus(bus) && list_empty(&bus->devices)) + pci_rescan_bus_bridge_resize(bus->self); + else + pci_rescan_bus(bus); mutex_unlock(&pci_remove_rescan_mutex); } return count; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 053670e09e2b..815674415267 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -94,6 +94,9 @@ u8 pci_cache_line_size; */ unsigned int pcibios_max_latency = 255; +/* If set, the PCIe ARI capability will not be used. */ +static bool pcie_ari_disabled; + /** * pci_bus_max_busnr - returns maximum PCI bus number of given bus' children * @bus: pointer to PCI bus structure to search @@ -825,6 +828,19 @@ EXPORT_SYMBOL(pci_choose_state); #define pcie_cap_has_sltctl2(type, flags) \ ((flags & PCI_EXP_FLAGS_VERS) > 1) +static struct pci_cap_saved_state *pci_find_saved_cap( + struct pci_dev *pci_dev, char cap) +{ + struct pci_cap_saved_state *tmp; + struct hlist_node *pos; + + hlist_for_each_entry(tmp, pos, &pci_dev->saved_cap_space, next) { + if (tmp->cap.cap_nr == cap) + return tmp; + } + return NULL; +} + static int pci_save_pcie_state(struct pci_dev *dev) { int pos, i = 0; @@ -959,6 +975,7 @@ void pci_restore_state(struct pci_dev *dev) { int i; u32 val; + int tries; if (!dev->state_saved) return; @@ -973,12 +990,16 @@ void pci_restore_state(struct pci_dev *dev) */ for (i = 15; i >= 0; i--) { pci_read_config_dword(dev, i * 4, &val); - if (val != dev->saved_config_space[i]) { + tries = 10; + while (tries && val != dev->saved_config_space[i]) { dev_dbg(&dev->dev, "restoring config " "space at offset %#x (was %#x, writing %#x)\n", i, val, (int)dev->saved_config_space[i]); pci_write_config_dword(dev,i * 4, dev->saved_config_space[i]); + pci_read_config_dword(dev, i * 4, &val); + mdelay(10); + tries--; } } pci_restore_pcix_state(dev); @@ -1864,6 +1885,12 @@ void platform_pci_wakeup_init(struct pci_dev *dev) platform_pci_sleep_wake(dev, false); } +static void pci_add_saved_cap(struct pci_dev *pci_dev, + struct pci_cap_saved_state *new_cap) +{ + hlist_add_head(&new_cap->next, &pci_dev->saved_cap_space); +} + /** * pci_add_save_buffer - allocate buffer for saving given capability registers * @dev: the PCI device @@ -1911,6 +1938,15 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev) "unable to preallocate PCI-X save buffer\n"); } +void pci_free_cap_save_buffers(struct pci_dev *dev) +{ + struct pci_cap_saved_state *tmp; + struct hlist_node *pos, *n; + + hlist_for_each_entry_safe(tmp, pos, n, &dev->saved_cap_space, next) + kfree(tmp); +} + /** * pci_enable_ari - enable ARI forwarding if hardware support it * @dev: the PCI device @@ -1922,7 +1958,7 @@ void pci_enable_ari(struct pci_dev *dev) u16 flags, ctrl; struct pci_dev *bridge; - if (!pci_is_pcie(dev) || dev->devfn) + if (pcie_ari_disabled || !pci_is_pcie(dev) || dev->devfn) return; pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI); @@ -3661,6 +3697,68 @@ int pci_is_reassigndev(struct pci_dev *dev) return (pci_specified_resource_alignment(dev) != 0); } +/* + * This function disables memory decoding and releases memory resources + * of the device specified by kernel's boot parameter 'pci=resource_alignment='. + * It also rounds up size to specified alignment. + * Later on, the kernel will assign page-aligned memory resource back + * to the device. + */ +void pci_reassigndev_resource_alignment(struct pci_dev *dev) +{ + int i; + struct resource *r; + resource_size_t align, size; + u16 command; + + if (!pci_is_reassigndev(dev)) + return; + + if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL && + (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) { + dev_warn(&dev->dev, + "Can't reassign resources to host bridge.\n"); + return; + } + + dev_info(&dev->dev, + "Disabling memory decoding and releasing memory resources.\n"); + pci_read_config_word(dev, PCI_COMMAND, &command); + command &= ~PCI_COMMAND_MEMORY; + pci_write_config_word(dev, PCI_COMMAND, command); + + align = pci_specified_resource_alignment(dev); + for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) { + r = &dev->resource[i]; + if (!(r->flags & IORESOURCE_MEM)) + continue; + size = resource_size(r); + if (size < align) { + size = align; + dev_info(&dev->dev, + "Rounding up size of resource #%d to %#llx.\n", + i, (unsigned long long)size); + } + r->end = size - 1; + r->start = 0; + } + /* Need to disable bridge's resource window, + * to enable the kernel to reassign new resource + * window later on. + */ + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && + (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { + for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { + r = &dev->resource[i]; + if (!(r->flags & IORESOURCE_MEM)) + continue; + r->end = resource_size(r) - 1; + r->start = 0; + } + pci_disable_bridge_window(dev); + } +} + ssize_t pci_set_resource_alignment_param(const char *buf, size_t count) { if (count > RESOURCE_ALIGNMENT_PARAM_SIZE - 1) @@ -3739,10 +3837,14 @@ static int __init pci_setup(char *str) pci_no_msi(); } else if (!strcmp(str, "noaer")) { pci_no_aer(); + } else if (!strncmp(str, "realloc=", 8)) { + pci_realloc_get_opt(str + 8); } else if (!strncmp(str, "realloc", 7)) { - pci_realloc(); + pci_realloc_get_opt("on"); } else if (!strcmp(str, "nodomains")) { pci_no_domains(); + } else if (!strncmp(str, "noari", 5)) { + pcie_ari_disabled = true; } else if (!strncmp(str, "cbiosize=", 9)) { pci_cardbus_io_size = memparse(str + 9, &str); } else if (!strncmp(str, "cbmemsize=", 10)) { diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 1009a5e88e53..e4943479b234 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -73,6 +73,7 @@ extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign); extern void pci_pm_init(struct pci_dev *dev); extern void platform_pci_wakeup_init(struct pci_dev *dev); extern void pci_allocate_cap_save_buffers(struct pci_dev *dev); +void pci_free_cap_save_buffers(struct pci_dev *dev); static inline void pci_wakeup_event(struct pci_dev *dev) { @@ -148,7 +149,7 @@ static inline void pci_no_msi(void) { } static inline void pci_msi_init_pci_dev(struct pci_dev *dev) { } #endif -extern void pci_realloc(void); +void pci_realloc_get_opt(char *); static inline int pci_no_d1d2(struct pci_dev *dev) { @@ -207,6 +208,8 @@ enum pci_bar_type { pci_bar_mem64, /* A 64-bit memory BAR */ }; +bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl, + int crs_timeout); extern int pci_setup_device(struct pci_dev *dev); extern int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, struct resource *res, unsigned int reg); @@ -225,11 +228,8 @@ static inline int pci_ari_enabled(struct pci_bus *bus) return bus->self && bus->self->ari_enabled; } -#ifdef CONFIG_PCI_QUIRKS -extern int pci_is_reassigndev(struct pci_dev *dev); -resource_size_t pci_specified_resource_alignment(struct pci_dev *dev); +void pci_reassigndev_resource_alignment(struct pci_dev *dev); extern void pci_disable_bridge_window(struct pci_dev *dev); -#endif /* Single Root I/O Virtualization */ struct pci_sriov { diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig index 72962cc92e0a..6c8bc5809787 100644 --- a/drivers/pci/pcie/Kconfig +++ b/drivers/pci/pcie/Kconfig @@ -55,6 +55,31 @@ config PCIEASPM_DEBUG This enables PCI Express ASPM debug support. It will add per-device interface to control ASPM. +choice + prompt "Default ASPM policy" + default PCIEASPM_DEFAULT + depends on PCIEASPM + +config PCIEASPM_DEFAULT + bool "BIOS default" + depends on PCIEASPM + help + Use the BIOS defaults for PCI Express ASPM. + +config PCIEASPM_POWERSAVE + bool "Powersave" + depends on PCIEASPM + help + Enable PCI Express ASPM L0s and L1 where possible, even if the + BIOS did not. + +config PCIEASPM_PERFORMANCE + bool "Performance" + depends on PCIEASPM + help + Disable PCI Express ASPM L0s and L1, even if the BIOS enabled them. +endchoice + config PCIE_PME def_bool y depends on PCIEPORTBUS && PM_RUNTIME && EXPERIMENTAL && ACPI diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 24f049e73952..4bdef24cd412 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -76,7 +76,15 @@ static LIST_HEAD(link_list); #define POLICY_DEFAULT 0 /* BIOS default setting */ #define POLICY_PERFORMANCE 1 /* high performance */ #define POLICY_POWERSAVE 2 /* high power saving */ + +#ifdef CONFIG_PCIEASPM_PERFORMANCE +static int aspm_policy = POLICY_PERFORMANCE; +#elif defined CONFIG_PCIEASPM_POWERSAVE +static int aspm_policy = POLICY_POWERSAVE; +#else static int aspm_policy; +#endif + static const char *policy_str[] = { [POLICY_DEFAULT] = "default", [POLICY_PERFORMANCE] = "performance", diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index bd00a01aef14..eea2ca2375e6 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -34,6 +34,18 @@ struct pci_dev; extern void pcie_clear_root_pme_status(struct pci_dev *dev); +#ifdef CONFIG_HOTPLUG_PCI_PCIE +extern bool pciehp_msi_disabled; + +static inline bool pciehp_no_msi(void) +{ + return pciehp_msi_disabled; +} + +#else /* !CONFIG_HOTPLUG_PCI_PCIE */ +static inline bool pciehp_no_msi(void) { return false; } +#endif /* !CONFIG_HOTPLUG_PCI_PCIE */ + #ifdef CONFIG_PCIE_PME extern bool pcie_pme_msi_disabled; diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 595654a1a6a6..2f589a54f9bd 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -19,6 +19,17 @@ #include "../pci.h" #include "portdrv.h" +bool pciehp_msi_disabled; + +static int __init pciehp_setup(char *str) +{ + if (!strncmp(str, "nomsi", 5)) + pciehp_msi_disabled = true; + + return 1; +} +__setup("pcie_hp=", pciehp_setup); + /** * release_pcie_device - free PCI Express port service device structure * @dev: Port service device to release @@ -189,8 +200,9 @@ static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask) { int i, irq = -1; - /* We have to use INTx if MSI cannot be used for PCIe PME. */ - if ((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) { + /* We have to use INTx if MSI cannot be used for PCIe PME or pciehp. */ + if (((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) || + ((mask & PCIE_PORT_SERVICE_HP) && pciehp_no_msi())) { if (dev->pin) irq = dev->irq; goto no_msi; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 71eac9cd724d..5e1ca3c58a7d 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -15,6 +15,8 @@ #define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ #define CARDBUS_RESERVE_BUSNR 3 +static LIST_HEAD(pci_host_bridges); + /* Ugh. Need to stop exporting this to modules. */ LIST_HEAD(pci_root_buses); EXPORT_SYMBOL(pci_root_buses); @@ -42,6 +44,82 @@ int no_pci_devices(void) } EXPORT_SYMBOL(no_pci_devices); +static struct pci_host_bridge *pci_host_bridge(struct pci_dev *dev) +{ + struct pci_bus *bus; + struct pci_host_bridge *bridge; + + bus = dev->bus; + while (bus->parent) + bus = bus->parent; + + list_for_each_entry(bridge, &pci_host_bridges, list) { + if (bridge->bus == bus) + return bridge; + } + + return NULL; +} + +static bool resource_contains(struct resource *res1, struct resource *res2) +{ + return res1->start <= res2->start && res1->end >= res2->end; +} + +void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) +{ + struct pci_host_bridge *bridge = pci_host_bridge(dev); + struct pci_host_bridge_window *window; + resource_size_t offset = 0; + + list_for_each_entry(window, &bridge->windows, list) { + if (resource_type(res) != resource_type(window->res)) + continue; + + if (resource_contains(window->res, res)) { + offset = window->offset; + break; + } + } + + region->start = res->start - offset; + region->end = res->end - offset; +} +EXPORT_SYMBOL(pcibios_resource_to_bus); + +static bool region_contains(struct pci_bus_region *region1, + struct pci_bus_region *region2) +{ + return region1->start <= region2->start && region1->end >= region2->end; +} + +void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, + struct pci_bus_region *region) +{ + struct pci_host_bridge *bridge = pci_host_bridge(dev); + struct pci_host_bridge_window *window; + struct pci_bus_region bus_region; + resource_size_t offset = 0; + + list_for_each_entry(window, &bridge->windows, list) { + if (resource_type(res) != resource_type(window->res)) + continue; + + bus_region.start = window->res->start - window->offset; + bus_region.end = window->res->end - window->offset; + + if (region_contains(&bus_region, region)) { + offset = window->offset; + break; + } + } + + res->start = region->start + offset; + res->end = region->end + offset; +} +EXPORT_SYMBOL(pcibios_bus_to_resource); + /* * PCI Bus Class */ @@ -135,6 +213,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, { u32 l, sz, mask; u16 orig_cmd; + struct pci_bus_region region; mask = type ? PCI_ROM_ADDRESS_MASK : ~0; @@ -214,11 +293,13 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, /* Address above 32-bit boundary; disable the BAR */ pci_write_config_dword(dev, pos, 0); pci_write_config_dword(dev, pos + 4, 0); - res->start = 0; - res->end = sz64; + region.start = 0; + region.end = sz64; + pcibios_bus_to_resource(dev, res, ®ion); } else { - res->start = l64; - res->end = l64 + sz64; + region.start = l64; + region.end = l64 + sz64; + pcibios_bus_to_resource(dev, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, "reg %x: %pR\n", pos, res); } @@ -228,8 +309,9 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, if (!sz) goto fail; - res->start = l; - res->end = l + sz; + region.start = l; + region.end = l + sz; + pcibios_bus_to_resource(dev, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, "reg %x: %pR\n", pos, res); } @@ -266,7 +348,8 @@ static void __devinit pci_read_bridge_io(struct pci_bus *child) struct pci_dev *dev = child->self; u8 io_base_lo, io_limit_lo; unsigned long base, limit; - struct resource *res; + struct pci_bus_region region; + struct resource *res, res2; res = child->resource[0]; pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); @@ -284,10 +367,14 @@ static void __devinit pci_read_bridge_io(struct pci_bus *child) if (base && base <= limit) { res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO; + res2.flags = res->flags; + region.start = base; + region.end = limit + 0xfff; + pcibios_bus_to_resource(dev, &res2, ®ion); if (!res->start) - res->start = base; + res->start = res2.start; if (!res->end) - res->end = limit + 0xfff; + res->end = res2.end; dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); } } @@ -297,6 +384,7 @@ static void __devinit pci_read_bridge_mmio(struct pci_bus *child) struct pci_dev *dev = child->self; u16 mem_base_lo, mem_limit_lo; unsigned long base, limit; + struct pci_bus_region region; struct resource *res; res = child->resource[1]; @@ -306,8 +394,9 @@ static void __devinit pci_read_bridge_mmio(struct pci_bus *child) limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16; if (base && base <= limit) { res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM; - res->start = base; - res->end = limit + 0xfffff; + region.start = base; + region.end = limit + 0xfffff; + pcibios_bus_to_resource(dev, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); } } @@ -317,6 +406,7 @@ static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child) struct pci_dev *dev = child->self; u16 mem_base_lo, mem_limit_lo; unsigned long base, limit; + struct pci_bus_region region; struct resource *res; res = child->resource[2]; @@ -353,8 +443,9 @@ static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child) IORESOURCE_MEM | IORESOURCE_PREFETCH; if (res->flags & PCI_PREF_RANGE_TYPE_64) res->flags |= IORESOURCE_MEM_64; - res->start = base; - res->end = limit + 0xfffff; + region.start = base; + region.end = limit + 0xfffff; + pcibios_bus_to_resource(dev, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); } } @@ -900,6 +991,8 @@ int pci_setup_device(struct pci_dev *dev) u8 hdr_type; struct pci_slot *slot; int pos = 0; + struct pci_bus_region region; + struct resource *res; if (pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type)) return -EIO; @@ -926,12 +1019,10 @@ int pci_setup_device(struct pci_dev *dev) pci_read_config_dword(dev, PCI_CLASS_REVISION, &class); dev->revision = class & 0xff; - class >>= 8; /* upper 3 bytes */ - dev->class = class; - class >>= 8; + dev->class = class >> 8; /* upper 3 bytes */ - dev_printk(KERN_DEBUG, &dev->dev, "[%04x:%04x] type %d class %#08x\n", - dev->vendor, dev->device, dev->hdr_type, class); + dev_printk(KERN_DEBUG, &dev->dev, "[%04x:%04x] type %02x class %#08x\n", + dev->vendor, dev->device, dev->hdr_type, dev->class); /* need to have dev->class ready */ dev->cfg_size = pci_cfg_space_size(dev); @@ -963,20 +1054,28 @@ int pci_setup_device(struct pci_dev *dev) u8 progif; pci_read_config_byte(dev, PCI_CLASS_PROG, &progif); if ((progif & 1) == 0) { - dev->resource[0].start = 0x1F0; - dev->resource[0].end = 0x1F7; - dev->resource[0].flags = LEGACY_IO_RESOURCE; - dev->resource[1].start = 0x3F6; - dev->resource[1].end = 0x3F6; - dev->resource[1].flags = LEGACY_IO_RESOURCE; + region.start = 0x1F0; + region.end = 0x1F7; + res = &dev->resource[0]; + res->flags = LEGACY_IO_RESOURCE; + pcibios_bus_to_resource(dev, res, ®ion); + region.start = 0x3F6; + region.end = 0x3F6; + res = &dev->resource[1]; + res->flags = LEGACY_IO_RESOURCE; + pcibios_bus_to_resource(dev, res, ®ion); } if ((progif & 4) == 0) { - dev->resource[2].start = 0x170; - dev->resource[2].end = 0x177; - dev->resource[2].flags = LEGACY_IO_RESOURCE; - dev->resource[3].start = 0x376; - dev->resource[3].end = 0x376; - dev->resource[3].flags = LEGACY_IO_RESOURCE; + region.start = 0x170; + region.end = 0x177; + res = &dev->resource[2]; + res->flags = LEGACY_IO_RESOURCE; + pcibios_bus_to_resource(dev, res, ®ion); + region.start = 0x376; + region.end = 0x376; + res = &dev->resource[3]; + res->flags = LEGACY_IO_RESOURCE; + pcibios_bus_to_resource(dev, res, ®ion); } } break; @@ -1013,8 +1112,8 @@ int pci_setup_device(struct pci_dev *dev) return -EIO; bad: - dev_err(&dev->dev, "ignoring class %02x (doesn't match header " - "type %02x)\n", class, dev->hdr_type); + dev_err(&dev->dev, "ignoring class %#08x (doesn't match header " + "type %02x)\n", dev->class, dev->hdr_type); dev->class = PCI_CLASS_NOT_DEFINED; } @@ -1026,6 +1125,7 @@ static void pci_release_capabilities(struct pci_dev *dev) { pci_vpd_release(dev); pci_iov_release(dev); + pci_free_cap_save_buffers(dev); } /** @@ -1118,40 +1218,54 @@ struct pci_dev *alloc_pci_dev(void) } EXPORT_SYMBOL(alloc_pci_dev); -/* - * Read the config data for a PCI device, sanity-check it - * and fill in the dev structure... - */ -static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) +bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l, + int crs_timeout) { - struct pci_dev *dev; - u32 l; int delay = 1; - if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l)) - return NULL; + if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l)) + return false; /* some broken boards return 0 or ~0 if a slot is empty: */ - if (l == 0xffffffff || l == 0x00000000 || - l == 0x0000ffff || l == 0xffff0000) - return NULL; + if (*l == 0xffffffff || *l == 0x00000000 || + *l == 0x0000ffff || *l == 0xffff0000) + return false; /* Configuration request Retry Status */ - while (l == 0xffff0001) { + while (*l == 0xffff0001) { + if (!crs_timeout) + return false; + msleep(delay); delay *= 2; - if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l)) - return NULL; + if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l)) + return false; /* Card hasn't responded in 60 seconds? Must be stuck. */ - if (delay > 60 * 1000) { + if (delay > crs_timeout) { printk(KERN_WARNING "pci %04x:%02x:%02x.%d: not " "responding\n", pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn)); - return NULL; + return false; } } + return true; +} +EXPORT_SYMBOL(pci_bus_read_dev_vendor_id); + +/* + * Read the config data for a PCI device, sanity-check it + * and fill in the dev structure... + */ +static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) +{ + struct pci_dev *dev; + u32 l; + + if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000)) + return NULL; + dev = alloc_pci_dev(); if (!dev) return NULL; @@ -1212,6 +1326,9 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) /* Fix up broken headers */ pci_fixup_device(pci_fixup_header, dev); + /* moved out from quirk header fixup code */ + pci_reassigndev_resource_alignment(dev); + /* Clear the state_saved flag. */ dev->state_saved = false; @@ -1530,21 +1647,27 @@ unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus) struct pci_bus *pci_create_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources) { - int error, i; + int error; + struct pci_host_bridge *bridge; struct pci_bus *b, *b2; struct device *dev; - struct pci_bus_resource *bus_res, *n; + struct pci_host_bridge_window *window, *n; struct resource *res; + resource_size_t offset; + char bus_addr[64]; + char *fmt; + + bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); + if (!bridge) + return NULL; b = pci_alloc_bus(); if (!b) - return NULL; + goto err_bus; dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - kfree(b); - return NULL; - } + if (!dev) + goto err_dev; b->sysdata = sysdata; b->ops = ops; @@ -1556,10 +1679,6 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, goto err_out; } - down_write(&pci_bus_sem); - list_add_tail(&b->node, &pci_root_buses); - up_write(&pci_bus_sem); - dev->parent = parent; dev->release = pci_release_bus_bridge_dev; dev_set_name(dev, "pci%04x:%02x", pci_domain_nr(b), bus); @@ -1585,31 +1704,53 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, b->number = b->secondary = bus; - /* Add initial resources to the bus */ - list_for_each_entry_safe(bus_res, n, resources, list) - list_move_tail(&bus_res->list, &b->resources); + bridge->bus = b; + INIT_LIST_HEAD(&bridge->windows); if (parent) dev_info(parent, "PCI host bridge to bus %s\n", dev_name(&b->dev)); else printk(KERN_INFO "PCI host bridge to bus %s\n", dev_name(&b->dev)); - pci_bus_for_each_resource(b, res, i) { - if (res) - dev_info(&b->dev, "root bus resource %pR\n", res); + /* Add initial resources to the bus */ + list_for_each_entry_safe(window, n, resources, list) { + list_move_tail(&window->list, &bridge->windows); + res = window->res; + offset = window->offset; + pci_bus_add_resource(b, res, 0); + if (offset) { + if (resource_type(res) == IORESOURCE_IO) + fmt = " (bus address [%#06llx-%#06llx])"; + else + fmt = " (bus address [%#010llx-%#010llx])"; + snprintf(bus_addr, sizeof(bus_addr), fmt, + (unsigned long long) (res->start - offset), + (unsigned long long) (res->end - offset)); + } else + bus_addr[0] = '\0'; + dev_info(&b->dev, "root bus resource %pR%s\n", res, bus_addr); } + down_write(&pci_bus_sem); + list_add_tail(&bridge->list, &pci_host_bridges); + list_add_tail(&b->node, &pci_root_buses); + up_write(&pci_bus_sem); + return b; class_dev_reg_err: device_unregister(dev); dev_reg_err: down_write(&pci_bus_sem); + list_del(&bridge->list); list_del(&b->node); up_write(&pci_bus_sem); err_out: kfree(dev); +err_dev: kfree(b); +err_bus: + kfree(bridge); return NULL; } @@ -1667,36 +1808,29 @@ EXPORT_SYMBOL(pci_scan_bus); #ifdef CONFIG_HOTPLUG /** - * pci_rescan_bus - scan a PCI bus for devices. - * @bus: PCI bus to scan + * pci_rescan_bus_bridge_resize - scan a PCI bus for devices. + * @bridge: PCI bridge for the bus to scan * - * Scan a PCI bus and child buses for new devices, adds them, - * and enables them. + * Scan a PCI bus and child buses for new devices, add them, + * and enable them, resizing bridge mmio/io resource if necessary + * and possible. The caller must ensure the child devices are already + * removed for resizing to occur. * * Returns the max number of subordinate bus discovered. */ -unsigned int __ref pci_rescan_bus(struct pci_bus *bus) +unsigned int __ref pci_rescan_bus_bridge_resize(struct pci_dev *bridge) { unsigned int max; - struct pci_dev *dev; + struct pci_bus *bus = bridge->subordinate; max = pci_scan_child_bus(bus); - down_read(&pci_bus_sem); - list_for_each_entry(dev, &bus->devices, bus_list) - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || - dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) - if (dev->subordinate) - pci_bus_size_bridges(dev->subordinate); - up_read(&pci_bus_sem); + pci_assign_unassigned_bridge_resources(bridge); - pci_bus_assign_resources(bus); - pci_enable_bridges(bus); pci_bus_add_devices(bus); return max; } -EXPORT_SYMBOL_GPL(pci_rescan_bus); EXPORT_SYMBOL(pci_add_new_bus); EXPORT_SYMBOL(pci_scan_slot); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index f722c5f6951a..4bf71028556b 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -26,73 +26,12 @@ #include <linux/dmi.h> #include <linux/pci-aspm.h> #include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/ktime.h> #include <asm/dma.h> /* isa_dma_bridge_buggy */ #include "pci.h" /* - * This quirk function disables memory decoding and releases memory resources - * of the device specified by kernel's boot parameter 'pci=resource_alignment='. - * It also rounds up size to specified alignment. - * Later on, the kernel will assign page-aligned memory resource back - * to the device. - */ -static void __devinit quirk_resource_alignment(struct pci_dev *dev) -{ - int i; - struct resource *r; - resource_size_t align, size; - u16 command; - - if (!pci_is_reassigndev(dev)) - return; - - if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL && - (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) { - dev_warn(&dev->dev, - "Can't reassign resources to host bridge.\n"); - return; - } - - dev_info(&dev->dev, - "Disabling memory decoding and releasing memory resources.\n"); - pci_read_config_word(dev, PCI_COMMAND, &command); - command &= ~PCI_COMMAND_MEMORY; - pci_write_config_word(dev, PCI_COMMAND, command); - - align = pci_specified_resource_alignment(dev); - for (i=0; i < PCI_BRIDGE_RESOURCES; i++) { - r = &dev->resource[i]; - if (!(r->flags & IORESOURCE_MEM)) - continue; - size = resource_size(r); - if (size < align) { - size = align; - dev_info(&dev->dev, - "Rounding up size of resource #%d to %#llx.\n", - i, (unsigned long long)size); - } - r->end = size - 1; - r->start = 0; - } - /* Need to disable bridge's resource window, - * to enable the kernel to reassign new resource - * window later on. - */ - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && - (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { - for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { - r = &dev->resource[i]; - if (!(r->flags & IORESOURCE_MEM)) - continue; - r->end = resource_size(r) - 1; - r->start = 0; - } - pci_disable_bridge_window(dev); - } -} -DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_resource_alignment); - -/* * Decoding should be disabled for a PCI device during BAR sizing to avoid * conflict. But doing so may cause problems on host bridge and perhaps other * key system devices. For devices that need to have mmio decoding always-on, @@ -100,10 +39,10 @@ DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_resource_alignment); */ static void __devinit quirk_mmio_always_on(struct pci_dev *dev) { - if ((dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) - dev->mmio_always_on = 1; + dev->mmio_always_on = 1; } -DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, quirk_mmio_always_on); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_BRIDGE_HOST, 8, quirk_mmio_always_on); /* The Mellanox Tavor device gives false positive parity errors * Mark this device with a broken_parity_status, to allow @@ -1002,12 +941,12 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C597_0, quirk_vt */ static void quirk_cardbus_legacy(struct pci_dev *dev) { - if ((PCI_CLASS_BRIDGE_CARDBUS << 8) ^ dev->class) - return; pci_write_config_dword(dev, PCI_CB_LEGACY_MODE_BASE, 0); } -DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy); -DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy); +DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_BRIDGE_CARDBUS, 8, quirk_cardbus_legacy); +DECLARE_PCI_FIXUP_CLASS_RESUME_EARLY(PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_BRIDGE_CARDBUS, 8, quirk_cardbus_legacy); /* * Following the PCI ordering rules is optional on the AMD762. I'm not @@ -1164,17 +1103,20 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_10, qui static void __devinit quirk_no_ata_d3(struct pci_dev *pdev) { - /* Quirk the legacy ATA devices only. The AHCI ones are ok */ - if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) - pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3; + pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3; } -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_ANY_ID, quirk_no_ata_d3); -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_ATI, PCI_ANY_ID, quirk_no_ata_d3); +/* Quirk the legacy ATA devices only. The AHCI ones are ok */ +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_ANY_ID, + PCI_CLASS_STORAGE_IDE, 8, quirk_no_ata_d3); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_ATI, PCI_ANY_ID, + PCI_CLASS_STORAGE_IDE, 8, quirk_no_ata_d3); /* ALi loses some register settings that we cannot then restore */ -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AL, PCI_ANY_ID, quirk_no_ata_d3); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_AL, PCI_ANY_ID, + PCI_CLASS_STORAGE_IDE, 8, quirk_no_ata_d3); /* VIA comes back fine but we need to keep it alive or ACPI GTM failures occur when mode detecting */ -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_VIA, PCI_ANY_ID, quirk_no_ata_d3); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_VIA, PCI_ANY_ID, + PCI_CLASS_STORAGE_IDE, 8, quirk_no_ata_d3); /* This was originally an Alpha specific thing, but it really fits here. * The i82375 PCI/EISA bridge appears as non-classified. Fix that. @@ -1873,8 +1815,7 @@ static void __devinit quirk_netmos(struct pci_dev *dev) case PCI_DEVICE_ID_NETMOS_9745: case PCI_DEVICE_ID_NETMOS_9845: case PCI_DEVICE_ID_NETMOS_9855: - if ((dev->class >> 8) == PCI_CLASS_COMMUNICATION_SERIAL && - num_parallel) { + if (num_parallel) { dev_info(&dev->dev, "Netmos %04x (%u parallel, " "%u serial); changing class SERIAL to OTHER " "(use parport_serial)\n", @@ -1884,7 +1825,8 @@ static void __devinit quirk_netmos(struct pci_dev *dev) } } } -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETMOS, PCI_ANY_ID, quirk_netmos); +DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_NETMOS, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_SERIAL, 8, quirk_netmos); static void __devinit quirk_e100_interrupt(struct pci_dev *dev) { @@ -1952,7 +1894,8 @@ static void __devinit quirk_e100_interrupt(struct pci_dev *dev) iounmap(csr); } -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, quirk_e100_interrupt); +DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, + PCI_CLASS_NETWORK_ETHERNET, 8, quirk_e100_interrupt); /* * The 82575 and 82598 may experience data corruption issues when transitioning @@ -2834,12 +2777,11 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x3c28, vtd_mask_spec_errors); static void __devinit fixup_ti816x_class(struct pci_dev* dev) { /* TI 816x devices do not have class code set when in PCIe boot mode */ - if (dev->class == PCI_CLASS_NOT_DEFINED) { - dev_info(&dev->dev, "Setting PCI class for 816x PCIe device\n"); - dev->class = PCI_CLASS_MULTIMEDIA_VIDEO; - } + dev_info(&dev->dev, "Setting PCI class for 816x PCIe device\n"); + dev->class = PCI_CLASS_MULTIMEDIA_VIDEO; } -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_TI, 0xb800, fixup_ti816x_class); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_TI, 0xb800, + PCI_CLASS_NOT_DEFINED, 0, fixup_ti816x_class); /* Some PCIe devices do not work reliably with the claimed maximum * payload size supported. @@ -2924,17 +2866,73 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f8, quirk_intel_mc_errata); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f9, quirk_intel_mc_errata); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65fa, quirk_intel_mc_errata); + +static void do_one_fixup_debug(void (*fn)(struct pci_dev *dev), struct pci_dev *dev) +{ + ktime_t calltime, delta, rettime; + unsigned long long duration; + + printk(KERN_DEBUG "calling %pF @ %i for %s\n", + fn, task_pid_nr(current), dev_name(&dev->dev)); + calltime = ktime_get(); + fn(dev); + rettime = ktime_get(); + delta = ktime_sub(rettime, calltime); + duration = (unsigned long long) ktime_to_ns(delta) >> 10; + printk(KERN_DEBUG "pci fixup %pF returned after %lld usecs for %s\n", + fn, duration, dev_name(&dev->dev)); +} + +/* + * Some BIOS implementations leave the Intel GPU interrupts enabled, + * even though no one is handling them (f.e. i915 driver is never loaded). + * Additionally the interrupt destination is not set up properly + * and the interrupt ends up -somewhere-. + * + * These spurious interrupts are "sticky" and the kernel disables + * the (shared) interrupt line after 100.000+ generated interrupts. + * + * Fix it by disabling the still enabled interrupts. + * This resolves crashes often seen on monitor unplug. + */ +#define I915_DEIER_REG 0x4400c +static void __devinit disable_igfx_irq(struct pci_dev *dev) +{ + void __iomem *regs = pci_iomap(dev, 0, 0); + if (regs == NULL) { + dev_warn(&dev->dev, "igfx quirk: Can't iomap PCI device\n"); + return; + } + + /* Check if any interrupt line is still enabled */ + if (readl(regs + I915_DEIER_REG) != 0) { + dev_warn(&dev->dev, "BIOS left Intel GPU interrupts enabled; " + "disabling\n"); + + writel(0, regs + I915_DEIER_REG); + } + + pci_iounmap(dev, regs); +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0102, disable_igfx_irq); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq); + static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end) { - while (f < end) { - if ((f->vendor == dev->vendor || f->vendor == (u16) PCI_ANY_ID) && - (f->device == dev->device || f->device == (u16) PCI_ANY_ID)) { + for (; f < end; f++) + if ((f->class == (u32) (dev->class >> f->class_shift) || + f->class == (u32) PCI_ANY_ID) && + (f->vendor == dev->vendor || + f->vendor == (u16) PCI_ANY_ID) && + (f->device == dev->device || + f->device == (u16) PCI_ANY_ID)) { dev_dbg(&dev->dev, "calling %pF\n", f->hook); - f->hook(dev); + if (initcall_debug) + do_one_fixup_debug(f->hook, dev); + else + f->hook(dev); } - f++; - } } extern struct pci_fixup __start_pci_fixups_early[]; diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index ef8b18c48f26..fd77e2bde2e8 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -79,7 +79,7 @@ EXPORT_SYMBOL(pci_remove_bus); static void __pci_remove_behind_bridge(struct pci_dev *dev); /** - * pci_remove_bus_device - remove a PCI device and any children + * pci_stop_and_remove_bus_device - remove a PCI device and any children * @dev: the device to remove * * Remove a PCI device from the device lists, informing the drivers @@ -90,7 +90,7 @@ static void __pci_remove_behind_bridge(struct pci_dev *dev); * device lists, remove the /proc entry, and notify userspace * (/sbin/hotplug). */ -static void __pci_remove_bus_device(struct pci_dev *dev) +void __pci_remove_bus_device(struct pci_dev *dev) { if (dev->subordinate) { struct pci_bus *b = dev->subordinate; @@ -102,7 +102,9 @@ static void __pci_remove_bus_device(struct pci_dev *dev) pci_destroy_dev(dev); } -void pci_remove_bus_device(struct pci_dev *dev) +EXPORT_SYMBOL(__pci_remove_bus_device); + +void pci_stop_and_remove_bus_device(struct pci_dev *dev) { pci_stop_bus_device(dev); __pci_remove_bus_device(dev); @@ -127,14 +129,15 @@ static void pci_stop_behind_bridge(struct pci_dev *dev) } /** - * pci_remove_behind_bridge - remove all devices behind a PCI bridge + * pci_stop_and_remove_behind_bridge - stop and remove all devices behind + * a PCI bridge * @dev: PCI bridge device * * Remove all devices on the bus, except for the parent bridge. * This also removes any child buses, and any devices they may * contain in a depth-first manner. */ -void pci_remove_behind_bridge(struct pci_dev *dev) +void pci_stop_and_remove_behind_bridge(struct pci_dev *dev) { pci_stop_behind_bridge(dev); __pci_remove_behind_bridge(dev); @@ -144,7 +147,15 @@ static void pci_stop_bus_devices(struct pci_bus *bus) { struct list_head *l, *n; - list_for_each_safe(l, n, &bus->devices) { + /* + * VFs could be removed by pci_stop_and_remove_bus_device() in the + * pci_stop_bus_devices() code path for PF. + * aka, bus->devices get updated in the process. + * but VFs are inserted after PFs when SRIOV is enabled for PF, + * We can iterate the list backwards to get prev valid PF instead + * of removed VF. + */ + list_for_each_prev_safe(l, n, &bus->devices) { struct pci_dev *dev = pci_dev_b(l); pci_stop_bus_device(dev); } @@ -166,6 +177,6 @@ void pci_stop_bus_device(struct pci_dev *dev) pci_stop_dev(dev); } -EXPORT_SYMBOL(pci_remove_bus_device); -EXPORT_SYMBOL(pci_remove_behind_bridge); +EXPORT_SYMBOL(pci_stop_and_remove_bus_device); +EXPORT_SYMBOL(pci_stop_and_remove_behind_bridge); EXPORT_SYMBOL_GPL(pci_stop_bus_device); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 86b69f85f900..8fa2d4be88de 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -25,10 +25,13 @@ #include <linux/ioport.h> #include <linux/cache.h> #include <linux/slab.h> +#include <asm-generic/pci-bridge.h> #include "pci.h" -struct resource_list_x { - struct resource_list_x *next; +unsigned int pci_flags; + +struct pci_dev_resource { + struct list_head list; struct resource *res; struct pci_dev *dev; resource_size_t start; @@ -38,21 +41,14 @@ struct resource_list_x { unsigned long flags; }; -#define free_list(type, head) do { \ - struct type *list, *tmp; \ - for (list = (head)->next; list;) { \ - tmp = list; \ - list = list->next; \ - kfree(tmp); \ - } \ - (head)->next = NULL; \ -} while (0) - -int pci_realloc_enable = 0; -#define pci_realloc_enabled() pci_realloc_enable -void pci_realloc(void) +static void free_list(struct list_head *head) { - pci_realloc_enable = 1; + struct pci_dev_resource *dev_res, *tmp; + + list_for_each_entry_safe(dev_res, tmp, head, list) { + list_del(&dev_res->list); + kfree(dev_res); + } } /** @@ -64,21 +60,18 @@ void pci_realloc(void) * @add_size: additional size to be optionally added * to the resource */ -static void add_to_list(struct resource_list_x *head, +static int add_to_list(struct list_head *head, struct pci_dev *dev, struct resource *res, resource_size_t add_size, resource_size_t min_align) { - struct resource_list_x *list = head; - struct resource_list_x *ln = list->next; - struct resource_list_x *tmp; + struct pci_dev_resource *tmp; - tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); + tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); if (!tmp) { pr_warning("add_to_list: kmalloc() failed!\n"); - return; + return -ENOMEM; } - tmp->next = ln; tmp->res = res; tmp->dev = dev; tmp->start = res->start; @@ -86,19 +79,100 @@ static void add_to_list(struct resource_list_x *head, tmp->flags = res->flags; tmp->add_size = add_size; tmp->min_align = min_align; - list->next = tmp; + + list_add(&tmp->list, head); + + return 0; } -static void add_to_failed_list(struct resource_list_x *head, - struct pci_dev *dev, struct resource *res) +static void remove_from_list(struct list_head *head, + struct resource *res) { - add_to_list(head, dev, res, - 0 /* dont care */, - 0 /* dont care */); + struct pci_dev_resource *dev_res, *tmp; + + list_for_each_entry_safe(dev_res, tmp, head, list) { + if (dev_res->res == res) { + list_del(&dev_res->list); + kfree(dev_res); + break; + } + } +} + +static resource_size_t get_res_add_size(struct list_head *head, + struct resource *res) +{ + struct pci_dev_resource *dev_res; + + list_for_each_entry(dev_res, head, list) { + if (dev_res->res == res) { + int idx = res - &dev_res->dev->resource[0]; + + dev_printk(KERN_DEBUG, &dev_res->dev->dev, + "res[%d]=%pR get_res_add_size add_size %llx\n", + idx, dev_res->res, + (unsigned long long)dev_res->add_size); + + return dev_res->add_size; + } + } + + return 0; +} + +/* Sort resources by alignment */ +static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head) +{ + int i; + + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + struct resource *r; + struct pci_dev_resource *dev_res, *tmp; + resource_size_t r_align; + struct list_head *n; + + r = &dev->resource[i]; + + if (r->flags & IORESOURCE_PCI_FIXED) + continue; + + if (!(r->flags) || r->parent) + continue; + + r_align = pci_resource_alignment(dev, r); + if (!r_align) { + dev_warn(&dev->dev, "BAR %d: %pR has bogus alignment\n", + i, r); + continue; + } + + tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); + if (!tmp) + panic("pdev_sort_resources(): " + "kmalloc() failed!\n"); + tmp->res = r; + tmp->dev = dev; + + /* fallback is smallest one or list is empty*/ + n = head; + list_for_each_entry(dev_res, head, list) { + resource_size_t align; + + align = pci_resource_alignment(dev_res->dev, + dev_res->res); + + if (r_align > align) { + n = &dev_res->list; + break; + } + } + /* Insert it just before n*/ + list_add_tail(&tmp->list, n); + } } static void __dev_sort_resources(struct pci_dev *dev, - struct resource_list *head) + struct list_head *head) { u16 class = dev->class >> 8; @@ -136,49 +210,54 @@ static inline void reset_resource(struct resource *res) * additional resources for the element, provided the element * is in the head list. */ -static void reassign_resources_sorted(struct resource_list_x *realloc_head, - struct resource_list *head) +static void reassign_resources_sorted(struct list_head *realloc_head, + struct list_head *head) { struct resource *res; - struct resource_list_x *list, *tmp, *prev; - struct resource_list *hlist; + struct pci_dev_resource *add_res, *tmp; + struct pci_dev_resource *dev_res; resource_size_t add_size; int idx; - prev = realloc_head; - for (list = realloc_head->next; list;) { - res = list->res; + list_for_each_entry_safe(add_res, tmp, realloc_head, list) { + bool found_match = false; + + res = add_res->res; /* skip resource that has been reset */ if (!res->flags) goto out; /* skip this resource if not found in head list */ - for (hlist = head->next; hlist && hlist->res != res; - hlist = hlist->next); - if (!hlist) { /* just skip */ - prev = list; - list = list->next; - continue; + list_for_each_entry(dev_res, head, list) { + if (dev_res->res == res) { + found_match = true; + break; + } } + if (!found_match)/* just skip */ + continue; - idx = res - &list->dev->resource[0]; - add_size=list->add_size; + idx = res - &add_res->dev->resource[0]; + add_size = add_res->add_size; if (!resource_size(res)) { - res->start = list->start; + res->start = add_res->start; res->end = res->start + add_size - 1; - if(pci_assign_resource(list->dev, idx)) + if (pci_assign_resource(add_res->dev, idx)) reset_resource(res); } else { - resource_size_t align = list->min_align; - res->flags |= list->flags & (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN); - if (pci_reassign_resource(list->dev, idx, add_size, align)) - dev_printk(KERN_DEBUG, &list->dev->dev, "failed to add optional resources res=%pR\n", - res); + resource_size_t align = add_res->min_align; + res->flags |= add_res->flags & + (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN); + if (pci_reassign_resource(add_res->dev, idx, + add_size, align)) + dev_printk(KERN_DEBUG, &add_res->dev->dev, + "failed to add %llx res[%d]=%pR\n", + (unsigned long long)add_size, + idx, res); } out: - tmp = list; - prev->next = list = list->next; - kfree(tmp); + list_del(&add_res->list); + kfree(add_res); } } @@ -192,35 +271,99 @@ out: * Satisfy resource requests of each element in the list. Add * requests that could not satisfied to the failed_list. */ -static void assign_requested_resources_sorted(struct resource_list *head, - struct resource_list_x *fail_head) +static void assign_requested_resources_sorted(struct list_head *head, + struct list_head *fail_head) { struct resource *res; - struct resource_list *list; + struct pci_dev_resource *dev_res; int idx; - for (list = head->next; list; list = list->next) { - res = list->res; - idx = res - &list->dev->resource[0]; - if (resource_size(res) && pci_assign_resource(list->dev, idx)) { - if (fail_head && !pci_is_root_bus(list->dev->bus)) { + list_for_each_entry(dev_res, head, list) { + res = dev_res->res; + idx = res - &dev_res->dev->resource[0]; + if (resource_size(res) && + pci_assign_resource(dev_res->dev, idx)) { + if (fail_head && !pci_is_root_bus(dev_res->dev->bus)) { /* * if the failed res is for ROM BAR, and it will * be enabled later, don't add it to the list */ if (!((idx == PCI_ROM_RESOURCE) && (!(res->flags & IORESOURCE_ROM_ENABLE)))) - add_to_failed_list(fail_head, list->dev, res); + add_to_list(fail_head, + dev_res->dev, res, + 0 /* dont care */, + 0 /* dont care */); } reset_resource(res); } } } -static void __assign_resources_sorted(struct resource_list *head, - struct resource_list_x *realloc_head, - struct resource_list_x *fail_head) +static void __assign_resources_sorted(struct list_head *head, + struct list_head *realloc_head, + struct list_head *fail_head) { + /* + * Should not assign requested resources at first. + * they could be adjacent, so later reassign can not reallocate + * them one by one in parent resource window. + * Try to assign requested + add_size at begining + * if could do that, could get out early. + * if could not do that, we still try to assign requested at first, + * then try to reassign add_size for some resources. + */ + LIST_HEAD(save_head); + LIST_HEAD(local_fail_head); + struct pci_dev_resource *save_res; + struct pci_dev_resource *dev_res; + + /* Check if optional add_size is there */ + if (!realloc_head || list_empty(realloc_head)) + goto requested_and_reassign; + + /* Save original start, end, flags etc at first */ + list_for_each_entry(dev_res, head, list) { + if (add_to_list(&save_head, dev_res->dev, dev_res->res, 0, 0)) { + free_list(&save_head); + goto requested_and_reassign; + } + } + + /* Update res in head list with add_size in realloc_head list */ + list_for_each_entry(dev_res, head, list) + dev_res->res->end += get_res_add_size(realloc_head, + dev_res->res); + + /* Try updated head list with add_size added */ + assign_requested_resources_sorted(head, &local_fail_head); + + /* all assigned with add_size ? */ + if (list_empty(&local_fail_head)) { + /* Remove head list from realloc_head list */ + list_for_each_entry(dev_res, head, list) + remove_from_list(realloc_head, dev_res->res); + free_list(&save_head); + free_list(head); + return; + } + + free_list(&local_fail_head); + /* Release assigned resource */ + list_for_each_entry(dev_res, head, list) + if (dev_res->res->parent) + release_resource(dev_res->res); + /* Restore start/end/flags from saved list */ + list_for_each_entry(save_res, &save_head, list) { + struct resource *res = save_res->res; + + res->start = save_res->start; + res->end = save_res->end; + res->flags = save_res->flags; + } + free_list(&save_head); + +requested_and_reassign: /* Satisfy the must-have resource requests */ assign_requested_resources_sorted(head, fail_head); @@ -228,28 +371,27 @@ static void __assign_resources_sorted(struct resource_list *head, requests */ if (realloc_head) reassign_resources_sorted(realloc_head, head); - free_list(resource_list, head); + free_list(head); } static void pdev_assign_resources_sorted(struct pci_dev *dev, - struct resource_list_x *fail_head) + struct list_head *add_head, + struct list_head *fail_head) { - struct resource_list head; + LIST_HEAD(head); - head.next = NULL; __dev_sort_resources(dev, &head); - __assign_resources_sorted(&head, NULL, fail_head); + __assign_resources_sorted(&head, add_head, fail_head); } static void pbus_assign_resources_sorted(const struct pci_bus *bus, - struct resource_list_x *realloc_head, - struct resource_list_x *fail_head) + struct list_head *realloc_head, + struct list_head *fail_head) { struct pci_dev *dev; - struct resource_list head; + LIST_HEAD(head); - head.next = NULL; list_for_each_entry(dev, &bus->devices, bus_list) __dev_sort_resources(dev, &head); @@ -548,20 +690,6 @@ static resource_size_t calculate_memsize(resource_size_t size, return size; } -static resource_size_t get_res_add_size(struct resource_list_x *realloc_head, - struct resource *res) -{ - struct resource_list_x *list; - - /* check if it is in realloc_head list */ - for (list = realloc_head->next; list && list->res != res; - list = list->next); - if (list) - return list->add_size; - - return 0; -} - /** * pbus_size_io() - size the io window of a given bus * @@ -576,7 +704,7 @@ static resource_size_t get_res_add_size(struct resource_list_x *realloc_head, * We must be careful with the ISA aliasing though. */ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, - resource_size_t add_size, struct resource_list_x *realloc_head) + resource_size_t add_size, struct list_head *realloc_head) { struct pci_dev *dev; struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO); @@ -612,7 +740,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, if (children_add_size > add_size) add_size = children_add_size; size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 : - calculate_iosize(size, min_size+add_size, size1, + calculate_iosize(size, min_size, add_size + size1, resource_size(b_res), 4096); if (!size0 && !size1) { if (b_res->start || b_res->end) @@ -626,8 +754,12 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, b_res->start = 4096; b_res->end = b_res->start + size0 - 1; b_res->flags |= IORESOURCE_STARTALIGN; - if (size1 > size0 && realloc_head) + if (size1 > size0 && realloc_head) { add_to_list(realloc_head, bus->self, b_res, size1-size0, 4096); + dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window " + "%pR to [bus %02x-%02x] add_size %lx\n", b_res, + bus->secondary, bus->subordinate, size1-size0); + } } /** @@ -644,7 +776,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long type, resource_size_t min_size, resource_size_t add_size, - struct resource_list_x *realloc_head) + struct list_head *realloc_head) { struct pci_dev *dev; resource_size_t min_align, align, size, size0, size1; @@ -726,7 +858,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, if (children_add_size > add_size) add_size = children_add_size; size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 : - calculate_memsize(size, min_size+add_size, 0, + calculate_memsize(size, min_size, add_size, resource_size(b_res), min_align); if (!size0 && !size1) { if (b_res->start || b_res->end) @@ -739,8 +871,12 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, b_res->start = min_align; b_res->end = size0 + min_align - 1; b_res->flags |= IORESOURCE_STARTALIGN | mem64_mask; - if (size1 > size0 && realloc_head) + if (size1 > size0 && realloc_head) { add_to_list(realloc_head, bus->self, b_res, size1-size0, min_align); + dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window " + "%pR to [bus %02x-%02x] add_size %llx\n", b_res, + bus->secondary, bus->subordinate, (unsigned long long)size1-size0); + } return 1; } @@ -754,25 +890,48 @@ unsigned long pci_cardbus_resource_alignment(struct resource *res) } static void pci_bus_size_cardbus(struct pci_bus *bus, - struct resource_list_x *realloc_head) + struct list_head *realloc_head) { struct pci_dev *bridge = bus->self; struct resource *b_res = &bridge->resource[PCI_BRIDGE_RESOURCES]; + resource_size_t b_res_3_size = pci_cardbus_mem_size * 2; u16 ctrl; + if (b_res[0].parent) + goto handle_b_res_1; /* * Reserve some resources for CardBus. We reserve * a fixed amount of bus space for CardBus bridges. */ - b_res[0].start = 0; - b_res[0].flags |= IORESOURCE_IO | IORESOURCE_SIZEALIGN; - if (realloc_head) - add_to_list(realloc_head, bridge, b_res, pci_cardbus_io_size, 0 /* dont care */); + b_res[0].start = pci_cardbus_io_size; + b_res[0].end = b_res[0].start + pci_cardbus_io_size - 1; + b_res[0].flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN; + if (realloc_head) { + b_res[0].end -= pci_cardbus_io_size; + add_to_list(realloc_head, bridge, b_res, pci_cardbus_io_size, + pci_cardbus_io_size); + } - b_res[1].start = 0; - b_res[1].flags |= IORESOURCE_IO | IORESOURCE_SIZEALIGN; - if (realloc_head) - add_to_list(realloc_head, bridge, b_res+1, pci_cardbus_io_size, 0 /* dont care */); +handle_b_res_1: + if (b_res[1].parent) + goto handle_b_res_2; + b_res[1].start = pci_cardbus_io_size; + b_res[1].end = b_res[1].start + pci_cardbus_io_size - 1; + b_res[1].flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN; + if (realloc_head) { + b_res[1].end -= pci_cardbus_io_size; + add_to_list(realloc_head, bridge, b_res+1, pci_cardbus_io_size, + pci_cardbus_io_size); + } + +handle_b_res_2: + /* MEM1 must not be pref mmio */ + pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); + if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM1) { + ctrl &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM1; + pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl); + pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); + } /* * Check whether prefetchable memory is supported @@ -785,38 +944,46 @@ static void pci_bus_size_cardbus(struct pci_bus *bus, pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); } + if (b_res[2].parent) + goto handle_b_res_3; /* * If we have prefetchable memory support, allocate * two regions. Otherwise, allocate one region of * twice the size. */ if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) { - b_res[2].start = 0; - b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_SIZEALIGN; - if (realloc_head) - add_to_list(realloc_head, bridge, b_res+2, pci_cardbus_mem_size, 0 /* dont care */); - - b_res[3].start = 0; - b_res[3].flags |= IORESOURCE_MEM | IORESOURCE_SIZEALIGN; - if (realloc_head) - add_to_list(realloc_head, bridge, b_res+3, pci_cardbus_mem_size, 0 /* dont care */); - } else { - b_res[3].start = 0; - b_res[3].flags |= IORESOURCE_MEM | IORESOURCE_SIZEALIGN; - if (realloc_head) - add_to_list(realloc_head, bridge, b_res+3, pci_cardbus_mem_size * 2, 0 /* dont care */); + b_res[2].start = pci_cardbus_mem_size; + b_res[2].end = b_res[2].start + pci_cardbus_mem_size - 1; + b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH | + IORESOURCE_STARTALIGN; + if (realloc_head) { + b_res[2].end -= pci_cardbus_mem_size; + add_to_list(realloc_head, bridge, b_res+2, + pci_cardbus_mem_size, pci_cardbus_mem_size); + } + + /* reduce that to half */ + b_res_3_size = pci_cardbus_mem_size; + } + +handle_b_res_3: + if (b_res[3].parent) + goto handle_done; + b_res[3].start = pci_cardbus_mem_size; + b_res[3].end = b_res[3].start + b_res_3_size - 1; + b_res[3].flags |= IORESOURCE_MEM | IORESOURCE_STARTALIGN; + if (realloc_head) { + b_res[3].end -= b_res_3_size; + add_to_list(realloc_head, bridge, b_res+3, b_res_3_size, + pci_cardbus_mem_size); } - /* set the size of the resource to zero, so that the resource does not - * get assigned during required-resource allocation cycle but gets assigned - * during the optional-resource allocation cycle. - */ - b_res[0].start = b_res[1].start = b_res[2].start = b_res[3].start = 1; - b_res[0].end = b_res[1].end = b_res[2].end = b_res[3].end = 0; +handle_done: + ; } void __ref __pci_bus_size_bridges(struct pci_bus *bus, - struct resource_list_x *realloc_head) + struct list_head *realloc_head) { struct pci_dev *dev; unsigned long mask, prefmask; @@ -858,7 +1025,8 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus, * Follow thru */ default: - pbus_size_io(bus, 0, additional_io_size, realloc_head); + pbus_size_io(bus, realloc_head ? 0 : additional_io_size, + additional_io_size, realloc_head); /* If the bridge supports prefetchable range, size it separately. If it doesn't, or its prefetchable window has already been allocated by arch code, try @@ -866,11 +1034,15 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus, resources. */ mask = IORESOURCE_MEM; prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH; - if (pbus_size_mem(bus, prefmask, prefmask, 0, additional_mem_size, realloc_head)) + if (pbus_size_mem(bus, prefmask, prefmask, + realloc_head ? 0 : additional_mem_size, + additional_mem_size, realloc_head)) mask = prefmask; /* Success, size non-prefetch only. */ else additional_mem_size += additional_mem_size; - pbus_size_mem(bus, mask, IORESOURCE_MEM, 0, additional_mem_size, realloc_head); + pbus_size_mem(bus, mask, IORESOURCE_MEM, + realloc_head ? 0 : additional_mem_size, + additional_mem_size, realloc_head); break; } } @@ -882,8 +1054,8 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus) EXPORT_SYMBOL(pci_bus_size_bridges); static void __ref __pci_bus_assign_resources(const struct pci_bus *bus, - struct resource_list_x *realloc_head, - struct resource_list_x *fail_head) + struct list_head *realloc_head, + struct list_head *fail_head) { struct pci_bus *b; struct pci_dev *dev; @@ -922,17 +1094,19 @@ void __ref pci_bus_assign_resources(const struct pci_bus *bus) EXPORT_SYMBOL(pci_bus_assign_resources); static void __ref __pci_bridge_assign_resources(const struct pci_dev *bridge, - struct resource_list_x *fail_head) + struct list_head *add_head, + struct list_head *fail_head) { struct pci_bus *b; - pdev_assign_resources_sorted((struct pci_dev *)bridge, fail_head); + pdev_assign_resources_sorted((struct pci_dev *)bridge, + add_head, fail_head); b = bridge->subordinate; if (!b) return; - __pci_bus_assign_resources(b, NULL, fail_head); + __pci_bus_assign_resources(b, add_head, fail_head); switch (bridge->class >> 8) { case PCI_CLASS_BRIDGE_PCI: @@ -1095,6 +1269,58 @@ static int __init pci_get_max_depth(void) return depth; } +/* + * -1: undefined, will auto detect later + * 0: disabled by user + * 1: disabled by auto detect + * 2: enabled by user + * 3: enabled by auto detect + */ +enum enable_type { + undefined = -1, + user_disabled, + auto_disabled, + user_enabled, + auto_enabled, +}; + +static enum enable_type pci_realloc_enable __initdata = undefined; +void __init pci_realloc_get_opt(char *str) +{ + if (!strncmp(str, "off", 3)) + pci_realloc_enable = user_disabled; + else if (!strncmp(str, "on", 2)) + pci_realloc_enable = user_enabled; +} +static bool __init pci_realloc_enabled(void) +{ + return pci_realloc_enable >= user_enabled; +} + +static void __init pci_realloc_detect(void) +{ +#if defined(CONFIG_PCI_IOV) && defined(CONFIG_PCI_REALLOC_ENABLE_AUTO) + struct pci_dev *dev = NULL; + + if (pci_realloc_enable != undefined) + return; + + for_each_pci_dev(dev) { + int i; + + for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) { + struct resource *r = &dev->resource[i]; + + /* Not assigned, or rejected by kernel ? */ + if (r->flags && !r->start) { + pci_realloc_enable = auto_enabled; + + return; + } + } + } +#endif +} /* * first try will not touch pci bridge res @@ -1105,59 +1331,57 @@ void __init pci_assign_unassigned_resources(void) { struct pci_bus *bus; - struct resource_list_x realloc_list; /* list of resources that + LIST_HEAD(realloc_head); /* list of resources that want additional resources */ + struct list_head *add_list = NULL; int tried_times = 0; enum release_type rel_type = leaf_only; - struct resource_list_x head, *list; + LIST_HEAD(fail_head); + struct pci_dev_resource *fail_res; unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH; - unsigned long failed_type; - int max_depth = pci_get_max_depth(); - int pci_try_num; - + int pci_try_num = 1; - head.next = NULL; - realloc_list.next = NULL; + /* don't realloc if asked to do so */ + pci_realloc_detect(); + if (pci_realloc_enabled()) { + int max_depth = pci_get_max_depth(); - pci_try_num = max_depth + 1; - printk(KERN_DEBUG "PCI: max bus depth: %d pci_try_num: %d\n", - max_depth, pci_try_num); + pci_try_num = max_depth + 1; + printk(KERN_DEBUG "PCI: max bus depth: %d pci_try_num: %d\n", + max_depth, pci_try_num); + } again: + /* + * last try will use add_list, otherwise will try good to have as + * must have, so can realloc parent bridge resource + */ + if (tried_times + 1 == pci_try_num) + add_list = &realloc_head; /* Depth first, calculate sizes and alignments of all subordinate buses. */ list_for_each_entry(bus, &pci_root_buses, node) - __pci_bus_size_bridges(bus, &realloc_list); + __pci_bus_size_bridges(bus, add_list); /* Depth last, allocate resources and update the hardware. */ list_for_each_entry(bus, &pci_root_buses, node) - __pci_bus_assign_resources(bus, &realloc_list, &head); - BUG_ON(realloc_list.next); + __pci_bus_assign_resources(bus, add_list, &fail_head); + if (add_list) + BUG_ON(!list_empty(add_list)); tried_times++; /* any device complain? */ - if (!head.next) + if (list_empty(&fail_head)) goto enable_and_dump; - /* don't realloc if asked to do so */ - if (!pci_realloc_enabled()) { - free_list(resource_list_x, &head); - goto enable_and_dump; - } + if (tried_times >= pci_try_num) { + if (pci_realloc_enable == undefined) + printk(KERN_INFO "Some PCI device resources are unassigned, try booting with pci=realloc\n"); + else if (pci_realloc_enable == auto_enabled) + printk(KERN_INFO "Automatically enabled pci realloc, if you have problem, try booting with pci=realloc=off\n"); - failed_type = 0; - for (list = head.next; list;) { - failed_type |= list->flags; - list = list->next; - } - /* - * io port are tight, don't try extra - * or if reach the limit, don't want to try more - */ - failed_type &= type_mask; - if ((failed_type == IORESOURCE_IO) || (tried_times >= pci_try_num)) { - free_list(resource_list_x, &head); + free_list(&fail_head); goto enable_and_dump; } @@ -1172,25 +1396,23 @@ again: * Try to release leaf bridge's resources that doesn't fit resource of * child device under that bridge */ - for (list = head.next; list;) { - bus = list->dev->bus; - pci_bus_release_bridge_resources(bus, list->flags & type_mask, - rel_type); - list = list->next; + list_for_each_entry(fail_res, &fail_head, list) { + bus = fail_res->dev->bus; + pci_bus_release_bridge_resources(bus, + fail_res->flags & type_mask, + rel_type); } /* restore size and flags */ - for (list = head.next; list;) { - struct resource *res = list->res; + list_for_each_entry(fail_res, &fail_head, list) { + struct resource *res = fail_res->res; - res->start = list->start; - res->end = list->end; - res->flags = list->flags; - if (list->dev->subordinate) + res->start = fail_res->start; + res->end = fail_res->end; + res->flags = fail_res->flags; + if (fail_res->dev->subordinate) res->flags = 0; - - list = list->next; } - free_list(resource_list_x, &head); + free_list(&fail_head); goto again; @@ -1207,26 +1429,27 @@ enable_and_dump: void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) { struct pci_bus *parent = bridge->subordinate; + LIST_HEAD(add_list); /* list of resources that + want additional resources */ int tried_times = 0; - struct resource_list_x head, *list; + LIST_HEAD(fail_head); + struct pci_dev_resource *fail_res; int retval; unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH; - head.next = NULL; - again: - pci_bus_size_bridges(parent); - __pci_bridge_assign_resources(bridge, &head); - + __pci_bus_size_bridges(parent, &add_list); + __pci_bridge_assign_resources(bridge, &add_list, &fail_head); + BUG_ON(!list_empty(&add_list)); tried_times++; - if (!head.next) + if (list_empty(&fail_head)) goto enable_all; if (tried_times >= 2) { /* still fail, don't need to try more */ - free_list(resource_list_x, &head); + free_list(&fail_head); goto enable_all; } @@ -1237,27 +1460,24 @@ again: * Try to release leaf bridge's resources that doesn't fit resource of * child device under that bridge */ - for (list = head.next; list;) { - struct pci_bus *bus = list->dev->bus; - unsigned long flags = list->flags; + list_for_each_entry(fail_res, &fail_head, list) { + struct pci_bus *bus = fail_res->dev->bus; + unsigned long flags = fail_res->flags; pci_bus_release_bridge_resources(bus, flags & type_mask, whole_subtree); - list = list->next; } /* restore size and flags */ - for (list = head.next; list;) { - struct resource *res = list->res; + list_for_each_entry(fail_res, &fail_head, list) { + struct resource *res = fail_res->res; - res->start = list->start; - res->end = list->end; - res->flags = list->flags; - if (list->dev->subordinate) + res->start = fail_res->start; + res->end = fail_res->end; + res->flags = fail_res->flags; + if (fail_res->dev->subordinate) res->flags = 0; - - list = list->next; } - free_list(resource_list_x, &head); + free_list(&fail_head); goto again; @@ -1267,3 +1487,41 @@ enable_all: pci_enable_bridges(parent); } EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); + +#ifdef CONFIG_HOTPLUG +/** + * pci_rescan_bus - scan a PCI bus for devices. + * @bus: PCI bus to scan + * + * Scan a PCI bus and child buses for new devices, adds them, + * and enables them. + * + * Returns the max number of subordinate bus discovered. + */ +unsigned int __ref pci_rescan_bus(struct pci_bus *bus) +{ + unsigned int max; + struct pci_dev *dev; + LIST_HEAD(add_list); /* list of resources that + want additional resources */ + + max = pci_scan_child_bus(bus); + + down_read(&pci_bus_sem); + list_for_each_entry(dev, &bus->devices, bus_list) + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || + dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) + if (dev->subordinate) + __pci_bus_size_bridges(dev->subordinate, + &add_list); + up_read(&pci_bus_sem); + __pci_bus_assign_resources(bus, &add_list, NULL); + BUG_ON(!list_empty(&add_list)); + + pci_enable_bridges(bus); + pci_bus_add_devices(bus); + + return max; +} +EXPORT_SYMBOL_GPL(pci_rescan_bus); +#endif diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index b66bfdbd21f7..eea85dafc763 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -114,7 +114,6 @@ int pci_claim_resource(struct pci_dev *dev, int resource) } EXPORT_SYMBOL(pci_claim_resource); -#ifdef CONFIG_PCI_QUIRKS void pci_disable_bridge_window(struct pci_dev *dev) { dev_info(&dev->dev, "disabling bridge mem windows\n"); @@ -127,9 +126,6 @@ void pci_disable_bridge_window(struct pci_dev *dev) pci_write_config_dword(dev, PCI_PREF_MEMORY_BASE, 0x0000fff0); pci_write_config_dword(dev, PCI_PREF_BASE_UPPER32, 0xffffffff); } -#endif /* CONFIG_PCI_QUIRKS */ - - static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, int resno, resource_size_t size, resource_size_t align) @@ -158,22 +154,44 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, return ret; } +/* + * Generic function that returns a value indicating that the device's + * original BIOS BAR address was not saved and so is not available for + * reinstatement. + * + * Can be over-ridden by architecture specific code that implements + * reinstatement functionality rather than leaving it disabled when + * normal allocation attempts fail. + */ +resource_size_t __weak pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx) +{ + return 0; +} + static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev, int resno, resource_size_t size) { struct resource *root, *conflict; - resource_size_t start, end; + resource_size_t fw_addr, start, end; int ret = 0; - if (res->flags & IORESOURCE_IO) - root = &ioport_resource; - else - root = &iomem_resource; + fw_addr = pcibios_retrieve_fw_addr(dev, resno); + if (!fw_addr) + return 1; start = res->start; end = res->end; - res->start = dev->fw_addr[resno]; + res->start = fw_addr; res->end = res->start + size - 1; + + root = pci_find_parent_resource(dev, res); + if (!root) { + if (res->flags & IORESOURCE_IO) + root = &ioport_resource; + else + root = &iomem_resource; + } + dev_info(&dev->dev, "BAR %d: trying firmware assignment %pR\n", resno, res); conflict = request_resource_conflict(root, res); @@ -228,16 +246,17 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz int ret; if (!res->parent) { - dev_info(&dev->dev, "BAR %d: can't reassign an unassigned resouce %pR " + dev_info(&dev->dev, "BAR %d: can't reassign an unassigned resource %pR " "\n", resno, res); return -EINVAL; } - new_size = resource_size(res) + addsize + min_align; + /* already aligned with min_align */ + new_size = resource_size(res) + addsize; ret = _pci_assign_resource(dev, resno, new_size, min_align); if (!ret) { res->flags &= ~IORESOURCE_STARTALIGN; - dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res); + dev_info(&dev->dev, "BAR %d: reassigned %pR\n", resno, res); if (resno < PCI_BRIDGE_RESOURCES) pci_update_resource(dev, resno); } @@ -267,7 +286,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) * where firmware left it. That at least has a chance of * working, which is better than just leaving it disabled. */ - if (ret < 0 && dev->fw_addr[resno]) + if (ret < 0) ret = pci_revert_fw_address(res, dev, resno, size); if (!ret) { @@ -279,53 +298,6 @@ int pci_assign_resource(struct pci_dev *dev, int resno) return ret; } - -/* Sort resources by alignment */ -void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head) -{ - int i; - - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - struct resource *r; - struct resource_list *list, *tmp; - resource_size_t r_align; - - r = &dev->resource[i]; - - if (r->flags & IORESOURCE_PCI_FIXED) - continue; - - if (!(r->flags) || r->parent) - continue; - - r_align = pci_resource_alignment(dev, r); - if (!r_align) { - dev_warn(&dev->dev, "BAR %d: %pR has bogus alignment\n", - i, r); - continue; - } - for (list = head; ; list = list->next) { - resource_size_t align = 0; - struct resource_list *ln = list->next; - - if (ln) - align = pci_resource_alignment(ln->dev, ln->res); - - if (r_align > align) { - tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); - if (!tmp) - panic("pdev_sort_resources(): " - "kmalloc() failed!\n"); - tmp->next = ln; - tmp->res = r; - tmp->dev = dev; - list->next = tmp; - break; - } - } - } -} - int pci_enable_resources(struct pci_dev *dev, int mask) { u16 cmd, old_cmd; diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c index 401090110922..fd00ff02ab4d 100644 --- a/drivers/pci/xen-pcifront.c +++ b/drivers/pci/xen-pcifront.c @@ -544,7 +544,7 @@ static void free_root_bus_devs(struct pci_bus *bus) dev = container_of(bus->devices.next, struct pci_dev, bus_list); dev_dbg(&dev->dev, "removing device\n"); - pci_remove_bus_device(dev); + pci_stop_and_remove_bus_device(dev); } } @@ -1044,7 +1044,7 @@ static int pcifront_detach_devices(struct pcifront_device *pdev) domain, bus, slot, func); continue; } - pci_remove_bus_device(pci_dev); + pci_stop_and_remove_bus_device(pci_dev); pci_dev_put(pci_dev); dev_dbg(&pdev->xdev->dev, diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index f9e3fb3a285b..bba3ab2066ee 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -183,10 +183,14 @@ config PCMCIA_BCM63XX config PCMCIA_SOC_COMMON tristate +config PCMCIA_SA11XX_BASE + tristate + config PCMCIA_SA1100 tristate "SA1100 support" depends on ARM && ARCH_SA1100 && PCMCIA select PCMCIA_SOC_COMMON + select PCMCIA_SA11XX_BASE help Say Y here to include support for SA11x0-based PCMCIA or CF sockets, found on HP iPAQs, Yopy, and other StrongARM(R)/ @@ -196,8 +200,9 @@ config PCMCIA_SA1100 config PCMCIA_SA1111 tristate "SA1111 support" - depends on ARM && ARCH_SA1100 && SA1111 && PCMCIA + depends on ARM && SA1111 && PCMCIA select PCMCIA_SOC_COMMON + select PCMCIA_SA11XX_BASE if ARCH_SA1100 help Say Y here to include support for SA1111-based PCMCIA or CF sockets, found on the Jornada 720, Graphicsmaster and other @@ -213,6 +218,7 @@ config PCMCIA_PXA2XX || ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2 \ || MACH_VPAC270 || MACH_BALLOON3 || MACH_COLIBRI \ || MACH_COLIBRI320) + select PCMCIA_SA1111 if ARCH_LUBBOCK && SA1111 select PCMCIA_SOC_COMMON help Say Y here to include support for the PXA2xx PCMCIA controller diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index ec543a4ff2e4..47525de6a631 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -25,8 +25,9 @@ obj-$(CONFIG_I82092) += i82092.o obj-$(CONFIG_TCIC) += tcic.o obj-$(CONFIG_PCMCIA_M8XX) += m8xx_pcmcia.o obj-$(CONFIG_PCMCIA_SOC_COMMON) += soc_common.o -obj-$(CONFIG_PCMCIA_SA1100) += sa11xx_base.o sa1100_cs.o -obj-$(CONFIG_PCMCIA_SA1111) += sa11xx_base.o sa1111_cs.o +obj-$(CONFIG_PCMCIA_SA11XX_BASE) += sa11xx_base.o +obj-$(CONFIG_PCMCIA_SA1100) += sa1100_cs.o +obj-$(CONFIG_PCMCIA_SA1111) += sa1111_cs.o obj-$(CONFIG_M32R_PCC) += m32r_pcc.o obj-$(CONFIG_M32R_CFC) += m32r_cfc.o obj-$(CONFIG_PCMCIA_BCM63XX) += bcm63xx_pcmcia.o @@ -39,9 +40,10 @@ obj-$(CONFIG_ELECTRA_CF) += electra_cf.o obj-$(CONFIG_PCMCIA_ALCHEMY_DEVBOARD) += db1xxx_ss.o sa1111_cs-y += sa1111_generic.o -sa1111_cs-$(CONFIG_ASSABET_NEPONSET) += sa1100_neponset.o -sa1111_cs-$(CONFIG_SA1100_BADGE4) += sa1100_badge4.o -sa1111_cs-$(CONFIG_SA1100_JORNADA720) += sa1100_jornada720.o +sa1111_cs-$(CONFIG_ASSABET_NEPONSET) += sa1111_neponset.o +sa1111_cs-$(CONFIG_SA1100_BADGE4) += sa1111_badge4.o +sa1111_cs-$(CONFIG_SA1100_JORNADA720) += sa1111_jornada720.o +sa1111_cs-$(CONFIG_ARCH_LUBBOCK) += sa1111_lubbock.o sa1100_cs-y += sa1100_generic.o sa1100_cs-$(CONFIG_SA1100_ASSABET) += sa1100_assabet.o @@ -52,9 +54,7 @@ sa1100_cs-$(CONFIG_SA1100_NANOENGINE) += sa1100_nanoengine.o sa1100_cs-$(CONFIG_SA1100_SHANNON) += sa1100_shannon.o sa1100_cs-$(CONFIG_SA1100_SIMPAD) += sa1100_simpad.o -pxa2xx_lubbock_cs-y += pxa2xx_lubbock.o sa1111_generic.o pxa2xx_cm_x2xx_cs-y += pxa2xx_cm_x2xx.o pxa2xx_cm_x255.o pxa2xx_cm_x270.o -pxa2xx-obj-$(CONFIG_ARCH_LUBBOCK) += pxa2xx_lubbock_cs.o pxa2xx-obj-$(CONFIG_MACH_MAINSTONE) += pxa2xx_mainstone.o pxa2xx-obj-$(CONFIG_PXA_SHARPSL) += pxa2xx_sharpsl.o pxa2xx-obj-$(CONFIG_MACH_ARMCORE) += pxa2xx_cm_x2xx_cs.o diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index 4902206f53d9..1dd68f502634 100644 --- a/drivers/pcmcia/at91_cf.c +++ b/drivers/pcmcia/at91_cf.c @@ -26,6 +26,7 @@ #include <mach/board.h> #include <mach/at91rm9200_mc.h> +#include <mach/at91_ramc.h> /* @@ -156,7 +157,7 @@ static int at91_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io) /* * Use 16 bit accesses unless/until we need 8-bit i/o space. */ - csr = at91_sys_read(AT91_SMC_CSR(cf->board->chipselect)) & ~AT91_SMC_DBW; + csr = at91_ramc_read(0, AT91_SMC_CSR(cf->board->chipselect)) & ~AT91_SMC_DBW; /* * NOTE: this CF controller ignores IOIS16, so we can't really do @@ -175,7 +176,7 @@ static int at91_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io) csr |= AT91_SMC_DBW_16; pr_debug("%s: 16bit i/o bus\n", driver_name); } - at91_sys_write(AT91_SMC_CSR(cf->board->chipselect), csr); + at91_ramc_write(0, AT91_SMC_CSR(cf->board->chipselect), csr); io->start = cf->socket.io_offset; io->stop = io->start + SZ_2K - 1; diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c index 9a58862f1401..6e75153c5b4f 100644 --- a/drivers/pcmcia/cardbus.c +++ b/drivers/pcmcia/cardbus.c @@ -108,5 +108,5 @@ void cb_free(struct pcmcia_socket *s) struct pci_dev *bridge = s->cb_dev; if (bridge) - pci_remove_behind_bridge(bridge); + pci_stop_and_remove_behind_bridge(bridge); } diff --git a/drivers/pcmcia/pxa2xx_balloon3.c b/drivers/pcmcia/pxa2xx_balloon3.c index 22a75e610f12..2ef576c5b69d 100644 --- a/drivers/pcmcia/pxa2xx_balloon3.c +++ b/drivers/pcmcia/pxa2xx_balloon3.c @@ -29,15 +29,6 @@ #include "soc_common.h" -/* - * These are a list of interrupt sources that provokes a polled - * check of status - */ -static struct pcmcia_irqs irqs[] = { - { 0, BALLOON3_S0_CD_IRQ, "PCMCIA0 CD" }, - { 0, BALLOON3_BP_NSTSCHG_IRQ, "PCMCIA0 STSCHG" }, -}; - static int balloon3_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { uint16_t ver; @@ -49,12 +40,12 @@ static int balloon3_pcmcia_hw_init(struct soc_pcmcia_socket *skt) ver); skt->socket.pci_irq = BALLOON3_BP_CF_NRDY_IRQ; - return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); -} + skt->stat[SOC_STAT_CD].gpio = BALLOON3_GPIO_S0_CD; + skt->stat[SOC_STAT_CD].name = "PCMCIA0 CD"; + skt->stat[SOC_STAT_BVD1].irq = BALLOON3_BP_NSTSCHG_IRQ; + skt->stat[SOC_STAT_BVD1].name = "PCMCIA0 STSCHG"; -static void balloon3_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) -{ - soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); + return 0; } static unsigned long balloon3_pcmcia_status[2] = { @@ -85,13 +76,11 @@ static void balloon3_pcmcia_socket_state(struct soc_pcmcia_socket *skt, disable_irq(BALLOON3_BP_NSTSCHG_IRQ); } - state->detect = !gpio_get_value(BALLOON3_GPIO_S0_CD); state->ready = !!(status & BALLOON3_CF_nIRQ); state->bvd1 = !!(status & BALLOON3_CF_nSTSCHG_BVD1); state->bvd2 = 0; /* not available */ state->vs_3v = 1; /* Always true its a CF card */ state->vs_Xv = 0; /* not available */ - state->wrprot = 0; /* not available */ } static int balloon3_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, @@ -106,7 +95,6 @@ static int balloon3_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, static struct pcmcia_low_level balloon3_pcmcia_ops = { .owner = THIS_MODULE, .hw_init = balloon3_pcmcia_hw_init, - .hw_shutdown = balloon3_pcmcia_hw_shutdown, .socket_state = balloon3_pcmcia_socket_state, .configure_socket = balloon3_pcmcia_configure_socket, .first = 0, diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c index 64d433ec4fc6..66a54222bbf4 100644 --- a/drivers/pcmcia/pxa2xx_base.c +++ b/drivers/pcmcia/pxa2xx_base.c @@ -318,10 +318,7 @@ static int pxa2xx_drv_pcmcia_probe(struct platform_device *dev) skt->nr = ops->first + i; skt->clk = clk; - skt->ops = ops; - skt->socket.owner = ops->owner; - skt->socket.dev.parent = &dev->dev; - skt->socket.pci_irq = NO_IRQ; + soc_pcmcia_init_one(skt, ops, &dev->dev); ret = pxa2xx_drv_pcmcia_add_one(skt); if (ret) diff --git a/drivers/pcmcia/pxa2xx_cm_x255.c b/drivers/pcmcia/pxa2xx_cm_x255.c index 31ab6ddf52c9..da40908b29dd 100644 --- a/drivers/pcmcia/pxa2xx_cm_x255.c +++ b/drivers/pcmcia/pxa2xx_cm_x255.c @@ -25,17 +25,6 @@ #define GPIO_PCMCIA_S1_RDYINT (8) #define GPIO_PCMCIA_RESET (9) -#define PCMCIA_S0_CD_VALID gpio_to_irq(GPIO_PCMCIA_S0_CD_VALID) -#define PCMCIA_S1_CD_VALID gpio_to_irq(GPIO_PCMCIA_S1_CD_VALID) -#define PCMCIA_S0_RDYINT gpio_to_irq(GPIO_PCMCIA_S0_RDYINT) -#define PCMCIA_S1_RDYINT gpio_to_irq(GPIO_PCMCIA_S1_RDYINT) - - -static struct pcmcia_irqs irqs[] = { - { .sock = 0, .str = "PCMCIA0 CD" }, - { .sock = 1, .str = "PCMCIA1 CD" }, -}; - static int cmx255_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { int ret = gpio_request(GPIO_PCMCIA_RESET, "PCCard reset"); @@ -43,19 +32,23 @@ static int cmx255_pcmcia_hw_init(struct soc_pcmcia_socket *skt) return ret; gpio_direction_output(GPIO_PCMCIA_RESET, 0); - skt->socket.pci_irq = skt->nr == 0 ? PCMCIA_S0_RDYINT : PCMCIA_S1_RDYINT; - irqs[0].irq = PCMCIA_S0_CD_VALID; - irqs[1].irq = PCMCIA_S1_CD_VALID; - ret = soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); - if (!ret) - gpio_free(GPIO_PCMCIA_RESET); + if (skt->nr == 0) { + skt->stat[SOC_STAT_CD].gpio = GPIO_PCMCIA_S0_CD_VALID; + skt->stat[SOC_STAT_CD].name = "PCMCIA0 CD"; + skt->stat[SOC_STAT_RDY].gpio = GPIO_PCMCIA_S0_RDYINT; + skt->stat[SOC_STAT_RDY].name = "PCMCIA0 RDY"; + } else { + skt->stat[SOC_STAT_CD].gpio = GPIO_PCMCIA_S1_CD_VALID; + skt->stat[SOC_STAT_CD].name = "PCMCIA1 CD"; + skt->stat[SOC_STAT_RDY].gpio = GPIO_PCMCIA_S1_RDYINT; + skt->stat[SOC_STAT_RDY].name = "PCMCIA1 RDY"; + } - return ret; + return 0; } static void cmx255_pcmcia_shutdown(struct soc_pcmcia_socket *skt) { - soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); gpio_free(GPIO_PCMCIA_RESET); } @@ -63,16 +56,8 @@ static void cmx255_pcmcia_shutdown(struct soc_pcmcia_socket *skt) static void cmx255_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { - int cd = skt->nr ? GPIO_PCMCIA_S1_CD_VALID : GPIO_PCMCIA_S0_CD_VALID; - int rdy = skt->nr ? GPIO_PCMCIA_S1_RDYINT : GPIO_PCMCIA_S0_RDYINT; - - state->detect = !gpio_get_value(cd); - state->ready = !!gpio_get_value(rdy); - state->bvd1 = 1; - state->bvd2 = 1; state->vs_3v = 0; state->vs_Xv = 0; - state->wrprot = 0; /* not available */ } diff --git a/drivers/pcmcia/pxa2xx_cm_x270.c b/drivers/pcmcia/pxa2xx_cm_x270.c index 3dc7621a0767..f59223f2307d 100644 --- a/drivers/pcmcia/pxa2xx_cm_x270.c +++ b/drivers/pcmcia/pxa2xx_cm_x270.c @@ -22,14 +22,6 @@ #define GPIO_PCMCIA_S0_RDYINT (82) #define GPIO_PCMCIA_RESET (53) -#define PCMCIA_S0_CD_VALID gpio_to_irq(GPIO_PCMCIA_S0_CD_VALID) -#define PCMCIA_S0_RDYINT gpio_to_irq(GPIO_PCMCIA_S0_RDYINT) - - -static struct pcmcia_irqs irqs[] = { - { .sock = 0, .str = "PCMCIA0 CD" }, -}; - static int cmx270_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { int ret = gpio_request(GPIO_PCMCIA_RESET, "PCCard reset"); @@ -37,18 +29,16 @@ static int cmx270_pcmcia_hw_init(struct soc_pcmcia_socket *skt) return ret; gpio_direction_output(GPIO_PCMCIA_RESET, 0); - skt->socket.pci_irq = PCMCIA_S0_RDYINT; - irqs[0].irq = PCMCIA_S0_CD_VALID; - ret = soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); - if (!ret) - gpio_free(GPIO_PCMCIA_RESET); + skt->stat[SOC_STAT_CD].gpio = GPIO_PCMCIA_S0_CD_VALID; + skt->stat[SOC_STAT_CD].name = "PCMCIA0 CD"; + skt->stat[SOC_STAT_RDY].gpio = GPIO_PCMCIA_S0_RDYINT; + skt->stat[SOC_STAT_RDY].name = "PCMCIA0 RDY"; return ret; } static void cmx270_pcmcia_shutdown(struct soc_pcmcia_socket *skt) { - soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); gpio_free(GPIO_PCMCIA_RESET); } @@ -56,13 +46,8 @@ static void cmx270_pcmcia_shutdown(struct soc_pcmcia_socket *skt) static void cmx270_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { - state->detect = (gpio_get_value(GPIO_PCMCIA_S0_CD_VALID) == 0) ? 1 : 0; - state->ready = (gpio_get_value(GPIO_PCMCIA_S0_RDYINT) == 0) ? 0 : 1; - state->bvd1 = 1; - state->bvd2 = 1; state->vs_3v = 0; state->vs_Xv = 0; - state->wrprot = 0; /* not available */ } diff --git a/drivers/pcmcia/pxa2xx_colibri.c b/drivers/pcmcia/pxa2xx_colibri.c index c6dec572a05d..4dee7b2a8032 100644 --- a/drivers/pcmcia/pxa2xx_colibri.c +++ b/drivers/pcmcia/pxa2xx_colibri.c @@ -53,13 +53,6 @@ static struct gpio colibri_pcmcia_gpios[] = { { 0, GPIOF_INIT_HIGH,"PCMCIA Reset" }, }; -static struct pcmcia_irqs colibri_irqs[] = { - { - .sock = 0, - .str = "PCMCIA CD" - }, -}; - static int colibri_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { int ret; @@ -69,19 +62,10 @@ static int colibri_pcmcia_hw_init(struct soc_pcmcia_socket *skt) if (ret) goto err1; - colibri_irqs[0].irq = gpio_to_irq(colibri_pcmcia_gpios[DETECT].gpio); skt->socket.pci_irq = gpio_to_irq(colibri_pcmcia_gpios[READY].gpio); + skt->stat[SOC_STAT_CD].irq = gpio_to_irq(colibri_pcmcia_gpios[DETECT].gpio); + skt->stat[SOC_STAT_CD].name = "PCMCIA CD"; - ret = soc_pcmcia_request_irqs(skt, colibri_irqs, - ARRAY_SIZE(colibri_irqs)); - if (ret) - goto err2; - - return ret; - -err2: - gpio_free_array(colibri_pcmcia_gpios, - ARRAY_SIZE(colibri_pcmcia_gpios)); err1: return ret; } @@ -100,7 +84,6 @@ static void colibri_pcmcia_socket_state(struct soc_pcmcia_socket *skt, state->ready = !!gpio_get_value(colibri_pcmcia_gpios[READY].gpio); state->bvd1 = !!gpio_get_value(colibri_pcmcia_gpios[BVD1].gpio); state->bvd2 = !!gpio_get_value(colibri_pcmcia_gpios[BVD2].gpio); - state->wrprot = 0; state->vs_3v = 1; state->vs_Xv = 0; } diff --git a/drivers/pcmcia/pxa2xx_e740.c b/drivers/pcmcia/pxa2xx_e740.c index 17cd2ce7428f..8751a323b448 100644 --- a/drivers/pcmcia/pxa2xx_e740.c +++ b/drivers/pcmcia/pxa2xx_e740.c @@ -23,53 +23,27 @@ #include "soc_common.h" -static struct pcmcia_irqs cd_irqs[] = { - { - .sock = 0, - .str = "CF card detect" - }, - { - .sock = 1, - .str = "Wifi switch" - }, -}; - static int e740_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { - if (skt->nr == 0) - skt->socket.pci_irq = gpio_to_irq(GPIO_E740_PCMCIA_RDY0); - else - skt->socket.pci_irq = gpio_to_irq(GPIO_E740_PCMCIA_RDY1); - - cd_irqs[0].irq = gpio_to_irq(GPIO_E740_PCMCIA_CD0); - cd_irqs[1].irq = gpio_to_irq(GPIO_E740_PCMCIA_CD1); - - return soc_pcmcia_request_irqs(skt, &cd_irqs[skt->nr], 1); -} + if (skt->nr == 0) { + skt->stat[SOC_STAT_CD].gpio = GPIO_E740_PCMCIA_CD0; + skt->stat[SOC_STAT_CD].name = "CF card detect"; + skt->stat[SOC_STAT_RDY].gpio = GPIO_E740_PCMCIA_RDY0; + skt->stat[SOC_STAT_RDY].name = "CF ready"; + } else { + skt->stat[SOC_STAT_CD].gpio = GPIO_E740_PCMCIA_CD1; + skt->stat[SOC_STAT_CD].name = "Wifi switch"; + skt->stat[SOC_STAT_RDY].gpio = GPIO_E740_PCMCIA_RDY1; + skt->stat[SOC_STAT_RDY].name = "Wifi ready"; + } -/* - * Release all resources. - */ -static void e740_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) -{ - soc_pcmcia_free_irqs(skt, &cd_irqs[skt->nr], 1); + return 0; } static void e740_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { - if (skt->nr == 0) { - state->detect = gpio_get_value(GPIO_E740_PCMCIA_CD0) ? 0 : 1; - state->ready = gpio_get_value(GPIO_E740_PCMCIA_RDY0) ? 1 : 0; - } else { - state->detect = gpio_get_value(GPIO_E740_PCMCIA_CD1) ? 0 : 1; - state->ready = gpio_get_value(GPIO_E740_PCMCIA_RDY1) ? 1 : 0; - } - state->vs_3v = 1; - state->bvd1 = 1; - state->bvd2 = 1; - state->wrprot = 0; state->vs_Xv = 0; } @@ -109,32 +83,11 @@ static int e740_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, return 0; } -/* - * Enable card status IRQs on (re-)initialisation. This can - * be called at initialisation, power management event, or - * pcmcia event. - */ -static void e740_pcmcia_socket_init(struct soc_pcmcia_socket *skt) -{ - soc_pcmcia_enable_irqs(skt, cd_irqs, ARRAY_SIZE(cd_irqs)); -} - -/* - * Disable card status IRQs on suspend. - */ -static void e740_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) -{ - soc_pcmcia_disable_irqs(skt, cd_irqs, ARRAY_SIZE(cd_irqs)); -} - static struct pcmcia_low_level e740_pcmcia_ops = { .owner = THIS_MODULE, .hw_init = e740_pcmcia_hw_init, - .hw_shutdown = e740_pcmcia_hw_shutdown, .socket_state = e740_pcmcia_socket_state, .configure_socket = e740_pcmcia_configure_socket, - .socket_init = e740_pcmcia_socket_init, - .socket_suspend = e740_pcmcia_socket_suspend, .nr = 2, }; diff --git a/drivers/pcmcia/pxa2xx_mainstone.c b/drivers/pcmcia/pxa2xx_mainstone.c index aded706c0b9f..7e32e25cdcb2 100644 --- a/drivers/pcmcia/pxa2xx_mainstone.c +++ b/drivers/pcmcia/pxa2xx_mainstone.c @@ -30,27 +30,26 @@ #include "soc_common.h" -static struct pcmcia_irqs irqs[] = { - { 0, MAINSTONE_S0_CD_IRQ, "PCMCIA0 CD" }, - { 1, MAINSTONE_S1_CD_IRQ, "PCMCIA1 CD" }, - { 0, MAINSTONE_S0_STSCHG_IRQ, "PCMCIA0 STSCHG" }, - { 1, MAINSTONE_S1_STSCHG_IRQ, "PCMCIA1 STSCHG" }, -}; - static int mst_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { /* * Setup default state of GPIO outputs * before we enable them as outputs. */ - - skt->socket.pci_irq = (skt->nr == 0) ? MAINSTONE_S0_IRQ : MAINSTONE_S1_IRQ; - return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); -} - -static void mst_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) -{ - soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); + if (skt->nr == 0) { + skt->socket.pci_irq = MAINSTONE_S0_IRQ; + skt->stat[SOC_STAT_CD].irq = MAINSTONE_S0_CD_IRQ; + skt->stat[SOC_STAT_CD].name = "PCMCIA0 CD"; + skt->stat[SOC_STAT_BVD1].irq = MAINSTONE_S0_STSCHG_IRQ; + skt->stat[SOC_STAT_BVD1].name = "PCMCIA0 STSCHG"; + } else { + skt->socket.pci_irq = MAINSTONE_S1_IRQ; + skt->stat[SOC_STAT_CD].irq = MAINSTONE_S1_CD_IRQ; + skt->stat[SOC_STAT_CD].name = "PCMCIA1 CD"; + skt->stat[SOC_STAT_BVD1].irq = MAINSTONE_S1_STSCHG_IRQ; + skt->stat[SOC_STAT_BVD1].name = "PCMCIA1 STSCHG"; + } + return 0; } static unsigned long mst_pcmcia_status[2]; @@ -84,7 +83,6 @@ static void mst_pcmcia_socket_state(struct soc_pcmcia_socket *skt, state->bvd2 = (status & MST_PCMCIA_nSPKR_BVD2) ? 1 : 0; state->vs_3v = (status & MST_PCMCIA_nVS1) ? 0 : 1; state->vs_Xv = (status & MST_PCMCIA_nVS2) ? 0 : 1; - state->wrprot = 0; /* not available */ } static int mst_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, @@ -131,7 +129,6 @@ static int mst_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, static struct pcmcia_low_level mst_pcmcia_ops __initdata = { .owner = THIS_MODULE, .hw_init = mst_pcmcia_hw_init, - .hw_shutdown = mst_pcmcia_hw_shutdown, .socket_state = mst_pcmcia_socket_state, .configure_socket = mst_pcmcia_configure_socket, .nr = 2, diff --git a/drivers/pcmcia/pxa2xx_palmld.c b/drivers/pcmcia/pxa2xx_palmld.c index 6a8e011a8c13..ed7d4dbc39fa 100644 --- a/drivers/pcmcia/pxa2xx_palmld.c +++ b/drivers/pcmcia/pxa2xx_palmld.c @@ -23,7 +23,6 @@ static struct gpio palmld_pcmcia_gpios[] = { { GPIO_NR_PALMLD_PCMCIA_POWER, GPIOF_INIT_LOW, "PCMCIA Power" }, { GPIO_NR_PALMLD_PCMCIA_RESET, GPIOF_INIT_HIGH,"PCMCIA Reset" }, - { GPIO_NR_PALMLD_PCMCIA_READY, GPIOF_IN, "PCMCIA Ready" }, }; static int palmld_pcmcia_hw_init(struct soc_pcmcia_socket *skt) @@ -33,7 +32,8 @@ static int palmld_pcmcia_hw_init(struct soc_pcmcia_socket *skt) ret = gpio_request_array(palmld_pcmcia_gpios, ARRAY_SIZE(palmld_pcmcia_gpios)); - skt->socket.pci_irq = gpio_to_irq(GPIO_NR_PALMLD_PCMCIA_READY); + skt->stat[SOC_STAT_RDY].gpio = GPIO_NR_PALMLD_PCMCIA_READY; + skt->stat[SOC_STAT_RDY].name = "PCMCIA Ready"; return ret; } @@ -47,10 +47,6 @@ static void palmld_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { state->detect = 1; /* always inserted */ - state->ready = !!gpio_get_value(GPIO_NR_PALMLD_PCMCIA_READY); - state->bvd1 = 1; - state->bvd2 = 1; - state->wrprot = 0; state->vs_3v = 1; state->vs_Xv = 0; } diff --git a/drivers/pcmcia/pxa2xx_palmtc.c b/drivers/pcmcia/pxa2xx_palmtc.c index 9e38de769ba3..81225a7a8cbb 100644 --- a/drivers/pcmcia/pxa2xx_palmtc.c +++ b/drivers/pcmcia/pxa2xx_palmtc.c @@ -26,7 +26,6 @@ static struct gpio palmtc_pcmcia_gpios[] = { { GPIO_NR_PALMTC_PCMCIA_POWER2, GPIOF_INIT_LOW, "PCMCIA Power 2" }, { GPIO_NR_PALMTC_PCMCIA_POWER3, GPIOF_INIT_LOW, "PCMCIA Power 3" }, { GPIO_NR_PALMTC_PCMCIA_RESET, GPIOF_INIT_HIGH,"PCMCIA Reset" }, - { GPIO_NR_PALMTC_PCMCIA_READY, GPIOF_IN, "PCMCIA Ready" }, { GPIO_NR_PALMTC_PCMCIA_PWRREADY, GPIOF_IN, "PCMCIA Power Ready" }, }; @@ -37,7 +36,8 @@ static int palmtc_pcmcia_hw_init(struct soc_pcmcia_socket *skt) ret = gpio_request_array(palmtc_pcmcia_gpios, ARRAY_SIZE(palmtc_pcmcia_gpios)); - skt->socket.pci_irq = gpio_to_irq(GPIO_NR_PALMTC_PCMCIA_READY); + skt->stat[SOC_STAT_RDY].gpio = GPIO_NR_PALMTC_PCMCIA_READY; + skt->stat[SOC_STAT_RDY].name = "PCMCIA Ready"; return ret; } @@ -51,10 +51,6 @@ static void palmtc_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { state->detect = 1; /* always inserted */ - state->ready = !!gpio_get_value(GPIO_NR_PALMTC_PCMCIA_READY); - state->bvd1 = 1; - state->bvd2 = 1; - state->wrprot = 0; state->vs_3v = 1; state->vs_Xv = 0; } diff --git a/drivers/pcmcia/pxa2xx_palmtx.c b/drivers/pcmcia/pxa2xx_palmtx.c index 80645a688ee3..069b6bbcf319 100644 --- a/drivers/pcmcia/pxa2xx_palmtx.c +++ b/drivers/pcmcia/pxa2xx_palmtx.c @@ -23,7 +23,6 @@ static struct gpio palmtx_pcmcia_gpios[] = { { GPIO_NR_PALMTX_PCMCIA_POWER1, GPIOF_INIT_LOW, "PCMCIA Power 1" }, { GPIO_NR_PALMTX_PCMCIA_POWER2, GPIOF_INIT_LOW, "PCMCIA Power 2" }, { GPIO_NR_PALMTX_PCMCIA_RESET, GPIOF_INIT_HIGH,"PCMCIA Reset" }, - { GPIO_NR_PALMTX_PCMCIA_READY, GPIOF_IN, "PCMCIA Ready" }, }; static int palmtx_pcmcia_hw_init(struct soc_pcmcia_socket *skt) @@ -33,7 +32,8 @@ static int palmtx_pcmcia_hw_init(struct soc_pcmcia_socket *skt) ret = gpio_request_array(palmtx_pcmcia_gpios, ARRAY_SIZE(palmtx_pcmcia_gpios)); - skt->socket.pci_irq = gpio_to_irq(GPIO_NR_PALMTX_PCMCIA_READY); + skt->stat[SOC_STAT_RDY].gpio = GPIO_NR_PALMTX_PCMCIA_READY; + skt->stat[SOC_STAT_RDY].name = "PCMCIA Ready"; return ret; } @@ -47,10 +47,6 @@ static void palmtx_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { state->detect = 1; /* always inserted */ - state->ready = !!gpio_get_value(GPIO_NR_PALMTX_PCMCIA_READY); - state->bvd1 = 1; - state->bvd2 = 1; - state->wrprot = 0; state->vs_3v = 1; state->vs_Xv = 0; } diff --git a/drivers/pcmcia/pxa2xx_sharpsl.c b/drivers/pcmcia/pxa2xx_sharpsl.c index 69ae2fd22400..b066273b6b4f 100644 --- a/drivers/pcmcia/pxa2xx_sharpsl.c +++ b/drivers/pcmcia/pxa2xx_sharpsl.c @@ -46,21 +46,9 @@ static void sharpsl_pcmcia_init_reset(struct soc_pcmcia_socket *skt) static int sharpsl_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { - int ret; - - /* Register interrupts */ if (SCOOP_DEV[skt->nr].cd_irq >= 0) { - struct pcmcia_irqs cd_irq; - - cd_irq.sock = skt->nr; - cd_irq.irq = SCOOP_DEV[skt->nr].cd_irq; - cd_irq.str = SCOOP_DEV[skt->nr].cd_irq_str; - ret = soc_pcmcia_request_irqs(skt, &cd_irq, 1); - - if (ret) { - printk(KERN_ERR "Request for Compact Flash IRQ failed\n"); - return ret; - } + skt->stat[SOC_STAT_CD].irq = SCOOP_DEV[skt->nr].cd_irq; + skt->stat[SOC_STAT_CD].name = SCOOP_DEV[skt->nr].cd_irq_str; } skt->socket.pci_irq = SCOOP_DEV[skt->nr].irq; @@ -68,19 +56,6 @@ static int sharpsl_pcmcia_hw_init(struct soc_pcmcia_socket *skt) return 0; } -static void sharpsl_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) -{ - if (SCOOP_DEV[skt->nr].cd_irq >= 0) { - struct pcmcia_irqs cd_irq; - - cd_irq.sock = skt->nr; - cd_irq.irq = SCOOP_DEV[skt->nr].cd_irq; - cd_irq.str = SCOOP_DEV[skt->nr].cd_irq_str; - soc_pcmcia_free_irqs(skt, &cd_irq, 1); - } -} - - static void sharpsl_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { @@ -222,7 +197,6 @@ static void sharpsl_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) static struct pcmcia_low_level sharpsl_pcmcia_ops __initdata = { .owner = THIS_MODULE, .hw_init = sharpsl_pcmcia_hw_init, - .hw_shutdown = sharpsl_pcmcia_hw_shutdown, .socket_state = sharpsl_pcmcia_socket_state, .configure_socket = sharpsl_pcmcia_configure_socket, .socket_init = sharpsl_pcmcia_socket_init, diff --git a/drivers/pcmcia/pxa2xx_stargate2.c b/drivers/pcmcia/pxa2xx_stargate2.c index 6c2366b74a35..1d73c4401fdd 100644 --- a/drivers/pcmcia/pxa2xx_stargate2.c +++ b/drivers/pcmcia/pxa2xx_stargate2.c @@ -33,10 +33,6 @@ #define SG2_S0_GPIO_DETECT 53 #define SG2_S0_GPIO_READY 81 -static struct pcmcia_irqs irqs[] = { - {.sock = 0, .str = "PCMCIA0 CD" }, -}; - static struct gpio sg2_pcmcia_gpios[] = { { SG2_S0_GPIO_RESET, GPIOF_OUT_INIT_HIGH, "PCMCIA Reset" }, { SG2_S0_POWER_CTL, GPIOF_OUT_INIT_HIGH, "PCMCIA Power Ctrl" }, @@ -44,27 +40,20 @@ static struct gpio sg2_pcmcia_gpios[] = { static int sg2_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { - skt->socket.pci_irq = gpio_to_irq(SG2_S0_GPIO_READY); - irqs[0].irq = gpio_to_irq(SG2_S0_GPIO_DETECT); - - return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); -} - -static void sg2_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) -{ - soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); + skt->stat[SOC_STAT_CD].gpio = SG2_S0_GPIO_DETECT; + skt->stat[SOC_STAT_CD].name = "PCMCIA0 CD"; + skt->stat[SOC_STAT_RDY].gpio = SG2_S0_GPIO_READY; + skt->stat[SOC_STAT_RDY].name = "PCMCIA0 RDY"; + return 0; } static void sg2_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { - state->detect = !gpio_get_value(SG2_S0_GPIO_DETECT); - state->ready = !!gpio_get_value(SG2_S0_GPIO_READY); state->bvd1 = 0; /* not available - battery detect on card */ state->bvd2 = 0; /* not available */ state->vs_3v = 1; /* not available - voltage detect for card */ state->vs_Xv = 0; /* not available */ - state->wrprot = 0; /* not available - write protect */ } static int sg2_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, @@ -94,24 +83,11 @@ static int sg2_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, return 0; } -static void sg2_pcmcia_socket_init(struct soc_pcmcia_socket *skt) -{ - soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs)); -} - -static void sg2_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) -{ - soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs)); -} - static struct pcmcia_low_level sg2_pcmcia_ops __initdata = { .owner = THIS_MODULE, .hw_init = sg2_pcmcia_hw_init, - .hw_shutdown = sg2_pcmcia_hw_shutdown, .socket_state = sg2_pcmcia_socket_state, .configure_socket = sg2_pcmcia_configure_socket, - .socket_init = sg2_pcmcia_socket_init, - .socket_suspend = sg2_pcmcia_socket_suspend, .nr = 1, }; diff --git a/drivers/pcmcia/pxa2xx_trizeps4.c b/drivers/pcmcia/pxa2xx_trizeps4.c index 7c33f898135a..d326ba1fa1ce 100644 --- a/drivers/pcmcia/pxa2xx_trizeps4.c +++ b/drivers/pcmcia/pxa2xx_trizeps4.c @@ -29,32 +29,17 @@ extern void board_pcmcia_power(int power); -static struct pcmcia_irqs irqs[] = { - { .sock = 0, .str = "cs0_cd" } - /* on other baseboards we can have more inputs */ -}; - static int trizeps_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { - int ret, i; /* we dont have voltage/card/ready detection * so we dont need interrupts for it */ switch (skt->nr) { case 0: - if (gpio_request(GPIO_PRDY, "cf_irq") < 0) { - pr_err("%s: sock %d unable to request gpio %d\n", __func__, - skt->nr, GPIO_PRDY); - return -EBUSY; - } - if (gpio_direction_input(GPIO_PRDY) < 0) { - pr_err("%s: sock %d unable to set input gpio %d\n", __func__, - skt->nr, GPIO_PRDY); - gpio_free(GPIO_PRDY); - return -EINVAL; - } - skt->socket.pci_irq = gpio_to_irq(GPIO_PRDY); - irqs[0].irq = gpio_to_irq(GPIO_PCD); + skt->stat[SOC_STAT_CD].gpio = GPIO_PCD; + skt->stat[SOC_STAT_CD].name = "cs0_cd"; + skt->stat[SOC_STAT_RDY].gpio = GPIO_PRDY; + skt->stat[SOC_STAT_RDY].name = "cs0_rdy"; break; default: break; @@ -62,39 +47,7 @@ static int trizeps_pcmcia_hw_init(struct soc_pcmcia_socket *skt) /* release the reset of this card */ pr_debug("%s: sock %d irq %d\n", __func__, skt->nr, skt->socket.pci_irq); - /* supplementory irqs for the socket */ - for (i = 0; i < ARRAY_SIZE(irqs); i++) { - if (irqs[i].sock != skt->nr) - continue; - if (gpio_request(irq_to_gpio(irqs[i].irq), irqs[i].str) < 0) { - pr_err("%s: sock %d unable to request gpio %d\n", - __func__, skt->nr, irq_to_gpio(irqs[i].irq)); - ret = -EBUSY; - goto error; - } - if (gpio_direction_input(irq_to_gpio(irqs[i].irq)) < 0) { - pr_err("%s: sock %d unable to set input gpio %d\n", - __func__, skt->nr, irq_to_gpio(irqs[i].irq)); - ret = -EINVAL; - goto error; - } - } - return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); - -error: - for (; i >= 0; i--) { - gpio_free(irq_to_gpio(irqs[i].irq)); - } - return (ret); -} - -static void trizeps_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) -{ - int i; - /* free allocated gpio's */ - gpio_free(GPIO_PRDY); - for (i = 0; i < ARRAY_SIZE(irqs); i++) - gpio_free(irq_to_gpio(irqs[i].irq)); + return 0; } static unsigned long trizeps_pcmcia_status[2]; @@ -118,13 +71,10 @@ static void trizeps_pcmcia_socket_state(struct soc_pcmcia_socket *skt, switch (skt->nr) { case 0: /* just fill in fix states */ - state->detect = gpio_get_value(GPIO_PCD) ? 0 : 1; - state->ready = gpio_get_value(GPIO_PRDY) ? 1 : 0; state->bvd1 = (status & ConXS_CFSR_BVD1) ? 1 : 0; state->bvd2 = (status & ConXS_CFSR_BVD2) ? 1 : 0; state->vs_3v = (status & ConXS_CFSR_VS1) ? 0 : 1; state->vs_Xv = (status & ConXS_CFSR_VS2) ? 0 : 1; - state->wrprot = 0; /* not available */ break; #ifndef CONFIG_MACH_TRIZEPS_CONXS @@ -136,7 +86,6 @@ static void trizeps_pcmcia_socket_state(struct soc_pcmcia_socket *skt, state->bvd2 = 0; state->vs_3v = 0; state->vs_Xv = 0; - state->wrprot = 0; break; #endif @@ -204,7 +153,6 @@ static void trizeps_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) static struct pcmcia_low_level trizeps_pcmcia_ops = { .owner = THIS_MODULE, .hw_init = trizeps_pcmcia_hw_init, - .hw_shutdown = trizeps_pcmcia_hw_shutdown, .socket_state = trizeps_pcmcia_socket_state, .configure_socket = trizeps_pcmcia_configure_socket, .socket_init = trizeps_pcmcia_socket_init, diff --git a/drivers/pcmcia/pxa2xx_viper.c b/drivers/pcmcia/pxa2xx_viper.c index 1064b1c2869d..adfae4987a42 100644 --- a/drivers/pcmcia/pxa2xx_viper.c +++ b/drivers/pcmcia/pxa2xx_viper.c @@ -32,13 +32,6 @@ static struct platform_device *arcom_pcmcia_dev; -static struct pcmcia_irqs irqs[] = { - { - .sock = 0, - .str = "PCMCIA_CD", - }, -}; - static inline struct arcom_pcmcia_pdata *viper_get_pdata(void) { return arcom_pcmcia_dev->dev.platform_data; @@ -49,38 +42,28 @@ static int viper_pcmcia_hw_init(struct soc_pcmcia_socket *skt) struct arcom_pcmcia_pdata *pdata = viper_get_pdata(); unsigned long flags; - skt->socket.pci_irq = gpio_to_irq(pdata->rdy_gpio); - irqs[0].irq = gpio_to_irq(pdata->cd_gpio); - - if (gpio_request(pdata->cd_gpio, "CF detect")) - goto err_request_cd; - - if (gpio_request(pdata->rdy_gpio, "CF ready")) - goto err_request_rdy; + skt->stat[SOC_STAT_CD].gpio = pdata->cd_gpio; + skt->stat[SOC_STAT_CD].name = "PCMCIA_CD"; + skt->stat[SOC_STAT_RDY].gpio = pdata->rdy_gpio; + skt->stat[SOC_STAT_RDY].name = "CF ready"; if (gpio_request(pdata->pwr_gpio, "CF power")) goto err_request_pwr; local_irq_save(flags); - if (gpio_direction_output(pdata->pwr_gpio, 0) || - gpio_direction_input(pdata->cd_gpio) || - gpio_direction_input(pdata->rdy_gpio)) { + if (gpio_direction_output(pdata->pwr_gpio, 0)) { local_irq_restore(flags); goto err_dir; } local_irq_restore(flags); - return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); + return 0; err_dir: gpio_free(pdata->pwr_gpio); err_request_pwr: - gpio_free(pdata->rdy_gpio); -err_request_rdy: - gpio_free(pdata->cd_gpio); -err_request_cd: dev_err(&arcom_pcmcia_dev->dev, "Failed to setup PCMCIA GPIOs\n"); return -1; } @@ -92,22 +75,12 @@ static void viper_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) { struct arcom_pcmcia_pdata *pdata = viper_get_pdata(); - soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); gpio_free(pdata->pwr_gpio); - gpio_free(pdata->rdy_gpio); - gpio_free(pdata->cd_gpio); } static void viper_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { - struct arcom_pcmcia_pdata *pdata = viper_get_pdata(); - - state->detect = !gpio_get_value(pdata->cd_gpio); - state->ready = !!gpio_get_value(pdata->rdy_gpio); - state->bvd1 = 1; - state->bvd2 = 1; - state->wrprot = 0; state->vs_3v = 1; /* Can only apply 3.3V */ state->vs_Xv = 0; } diff --git a/drivers/pcmcia/pxa2xx_vpac270.c b/drivers/pcmcia/pxa2xx_vpac270.c index 61b17d235dbe..a47dcd24a26a 100644 --- a/drivers/pcmcia/pxa2xx_vpac270.c +++ b/drivers/pcmcia/pxa2xx_vpac270.c @@ -23,29 +23,14 @@ #include "soc_common.h" static struct gpio vpac270_pcmcia_gpios[] = { - { GPIO84_VPAC270_PCMCIA_CD, GPIOF_IN, "PCMCIA Card Detect" }, - { GPIO35_VPAC270_PCMCIA_RDY, GPIOF_IN, "PCMCIA Ready" }, { GPIO107_VPAC270_PCMCIA_PPEN, GPIOF_INIT_LOW, "PCMCIA PPEN" }, { GPIO11_VPAC270_PCMCIA_RESET, GPIOF_INIT_LOW, "PCMCIA Reset" }, }; static struct gpio vpac270_cf_gpios[] = { - { GPIO17_VPAC270_CF_CD, GPIOF_IN, "CF Card Detect" }, - { GPIO12_VPAC270_CF_RDY, GPIOF_IN, "CF Ready" }, { GPIO16_VPAC270_CF_RESET, GPIOF_INIT_LOW, "CF Reset" }, }; -static struct pcmcia_irqs cd_irqs[] = { - { - .sock = 0, - .str = "PCMCIA CD" - }, - { - .sock = 1, - .str = "CF CD" - }, -}; - static int vpac270_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { int ret; @@ -54,20 +39,18 @@ static int vpac270_pcmcia_hw_init(struct soc_pcmcia_socket *skt) ret = gpio_request_array(vpac270_pcmcia_gpios, ARRAY_SIZE(vpac270_pcmcia_gpios)); - skt->socket.pci_irq = gpio_to_irq(GPIO35_VPAC270_PCMCIA_RDY); - cd_irqs[0].irq = gpio_to_irq(GPIO84_VPAC270_PCMCIA_CD); - - if (!ret) - ret = soc_pcmcia_request_irqs(skt, &cd_irqs[0], 1); + skt->stat[SOC_STAT_CD].gpio = GPIO84_VPAC270_PCMCIA_CD; + skt->stat[SOC_STAT_CD].name = "PCMCIA CD"; + skt->stat[SOC_STAT_RDY].gpio = GPIO35_VPAC270_PCMCIA_RDY; + skt->stat[SOC_STAT_RDY].name = "PCMCIA Ready"; } else { ret = gpio_request_array(vpac270_cf_gpios, ARRAY_SIZE(vpac270_cf_gpios)); - skt->socket.pci_irq = gpio_to_irq(GPIO12_VPAC270_CF_RDY); - cd_irqs[1].irq = gpio_to_irq(GPIO17_VPAC270_CF_CD); - - if (!ret) - ret = soc_pcmcia_request_irqs(skt, &cd_irqs[1], 1); + skt->stat[SOC_STAT_CD].gpio = GPIO17_VPAC270_CF_CD; + skt->stat[SOC_STAT_CD].name = "CF CD"; + skt->stat[SOC_STAT_RDY].gpio = GPIO12_VPAC270_CF_RDY; + skt->stat[SOC_STAT_RDY].name = "CF Ready"; } return ret; @@ -86,16 +69,6 @@ static void vpac270_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) static void vpac270_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { - if (skt->nr == 0) { - state->detect = !gpio_get_value(GPIO84_VPAC270_PCMCIA_CD); - state->ready = !!gpio_get_value(GPIO35_VPAC270_PCMCIA_RDY); - } else { - state->detect = !gpio_get_value(GPIO17_VPAC270_CF_CD); - state->ready = !!gpio_get_value(GPIO12_VPAC270_CF_RDY); - } - state->bvd1 = 1; - state->bvd2 = 1; - state->wrprot = 0; state->vs_3v = 1; state->vs_Xv = 0; } @@ -117,14 +90,6 @@ vpac270_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, return 0; } -static void vpac270_pcmcia_socket_init(struct soc_pcmcia_socket *skt) -{ -} - -static void vpac270_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) -{ -} - static struct pcmcia_low_level vpac270_pcmcia_ops = { .owner = THIS_MODULE, @@ -136,9 +101,6 @@ static struct pcmcia_low_level vpac270_pcmcia_ops = { .socket_state = vpac270_pcmcia_socket_state, .configure_socket = vpac270_pcmcia_configure_socket, - - .socket_init = vpac270_pcmcia_socket_init, - .socket_suspend = vpac270_pcmcia_socket_suspend, }; static struct platform_device *vpac270_pcmcia_device; diff --git a/drivers/pcmcia/sa1100_assabet.c b/drivers/pcmcia/sa1100_assabet.c index f1e882272ab0..ba8557eea618 100644 --- a/drivers/pcmcia/sa1100_assabet.c +++ b/drivers/pcmcia/sa1100_assabet.c @@ -10,46 +10,30 @@ #include <linux/interrupt.h> #include <linux/device.h> #include <linux/init.h> +#include <linux/gpio.h> -#include <mach/hardware.h> #include <asm/mach-types.h> -#include <asm/irq.h> -#include <asm/signal.h> #include <mach/assabet.h> #include "sa1100_generic.h" -static struct pcmcia_irqs irqs[] = { - { 1, ASSABET_IRQ_GPIO_CF_CD, "CF CD" }, - { 1, ASSABET_IRQ_GPIO_CF_BVD2, "CF BVD2" }, - { 1, ASSABET_IRQ_GPIO_CF_BVD1, "CF BVD1" }, -}; - static int assabet_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { - skt->socket.pci_irq = ASSABET_IRQ_GPIO_CF_IRQ; - - return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); -} + skt->stat[SOC_STAT_CD].gpio = ASSABET_GPIO_CF_CD; + skt->stat[SOC_STAT_CD].name = "CF CD"; + skt->stat[SOC_STAT_BVD1].gpio = ASSABET_GPIO_CF_BVD1; + skt->stat[SOC_STAT_BVD1].name = "CF BVD1"; + skt->stat[SOC_STAT_BVD2].gpio = ASSABET_GPIO_CF_BVD2; + skt->stat[SOC_STAT_BVD2].name = "CF BVD2"; + skt->stat[SOC_STAT_RDY].gpio = ASSABET_GPIO_CF_IRQ; + skt->stat[SOC_STAT_RDY].name = "CF RDY"; -/* - * Release all resources. - */ -static void assabet_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) -{ - soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); + return 0; } static void assabet_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { - unsigned long levels = GPLR; - - state->detect = (levels & ASSABET_GPIO_CF_CD) ? 0 : 1; - state->ready = (levels & ASSABET_GPIO_CF_IRQ) ? 1 : 0; - state->bvd1 = (levels & ASSABET_GPIO_CF_BVD1) ? 1 : 0; - state->bvd2 = (levels & ASSABET_GPIO_CF_BVD2) ? 1 : 0; - state->wrprot = 0; /* Not available on Assabet. */ state->vs_3v = 1; /* Can only apply 3.3V on Assabet. */ state->vs_Xv = 0; } @@ -78,38 +62,24 @@ assabet_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_stat return -1; } - /* Silently ignore Vpp, output enable, speaker enable. */ + /* Silently ignore Vpp, speaker enable. */ if (state->flags & SS_RESET) mask |= ASSABET_BCR_CF_RST; + if (!(state->flags & SS_OUTPUT_ENA)) + mask |= ASSABET_BCR_CF_BUS_OFF; - ASSABET_BCR_frob(ASSABET_BCR_CF_RST | ASSABET_BCR_CF_PWR, mask); + ASSABET_BCR_frob(ASSABET_BCR_CF_RST | ASSABET_BCR_CF_PWR | + ASSABET_BCR_CF_BUS_OFF, mask); return 0; } /* - * Enable card status IRQs on (re-)initialisation. This can - * be called at initialisation, power management event, or - * pcmcia event. - */ -static void assabet_pcmcia_socket_init(struct soc_pcmcia_socket *skt) -{ - /* - * Enable CF bus - */ - ASSABET_BCR_clear(ASSABET_BCR_CF_BUS_OFF); - - soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs)); -} - -/* * Disable card status IRQs on suspend. */ static void assabet_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) { - soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs)); - /* * Tristate the CF bus signals. Also assert CF * reset as per user guide page 4-11. @@ -119,14 +89,9 @@ static void assabet_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) static struct pcmcia_low_level assabet_pcmcia_ops = { .owner = THIS_MODULE, - .hw_init = assabet_pcmcia_hw_init, - .hw_shutdown = assabet_pcmcia_hw_shutdown, - .socket_state = assabet_pcmcia_socket_state, .configure_socket = assabet_pcmcia_configure_socket, - - .socket_init = assabet_pcmcia_socket_init, .socket_suspend = assabet_pcmcia_socket_suspend, }; diff --git a/drivers/pcmcia/sa1100_cerf.c b/drivers/pcmcia/sa1100_cerf.c index 30560df8c76b..c59c44921a3a 100644 --- a/drivers/pcmcia/sa1100_cerf.c +++ b/drivers/pcmcia/sa1100_cerf.c @@ -10,6 +10,7 @@ #include <linux/device.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/gpio.h> #include <mach/hardware.h> #include <asm/mach-types.h> @@ -19,34 +20,34 @@ #define CERF_SOCKET 1 -static struct pcmcia_irqs irqs[] = { - { CERF_SOCKET, CERF_IRQ_GPIO_CF_CD, "CF_CD" }, - { CERF_SOCKET, CERF_IRQ_GPIO_CF_BVD2, "CF_BVD2" }, - { CERF_SOCKET, CERF_IRQ_GPIO_CF_BVD1, "CF_BVD1" } -}; - static int cerf_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { - skt->socket.pci_irq = CERF_IRQ_GPIO_CF_IRQ; + int ret; + + ret = gpio_request_one(CERF_GPIO_CF_RESET, GPIOF_OUT_INIT_LOW, "CF_RESET"); + if (ret) + return ret; + + skt->stat[SOC_STAT_CD].gpio = CERF_GPIO_CF_CD; + skt->stat[SOC_STAT_CD].name = "CF_CD"; + skt->stat[SOC_STAT_BVD1].gpio = CERF_GPIO_CF_BVD1; + skt->stat[SOC_STAT_BVD1].name = "CF_BVD1"; + skt->stat[SOC_STAT_BVD2].gpio = CERF_GPIO_CF_BVD2; + skt->stat[SOC_STAT_BVD2].name = "CF_BVD2"; + skt->stat[SOC_STAT_RDY].gpio = CERF_GPIO_CF_IRQ; + skt->stat[SOC_STAT_RDY].name = "CF_IRQ"; - return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); + return 0; } static void cerf_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) { - soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); + gpio_free(CERF_GPIO_CF_RESET); } static void cerf_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { - unsigned long levels = GPLR; - - state->detect = (levels & CERF_GPIO_CF_CD) ?0:1; - state->ready = (levels & CERF_GPIO_CF_IRQ) ?1:0; - state->bvd1 = (levels & CERF_GPIO_CF_BVD1)?1:0; - state->bvd2 = (levels & CERF_GPIO_CF_BVD2)?1:0; - state->wrprot = 0; state->vs_3v = 1; state->vs_Xv = 0; } @@ -67,34 +68,17 @@ cerf_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, return -1; } - if (state->flags & SS_RESET) { - GPSR = CERF_GPIO_CF_RESET; - } else { - GPCR = CERF_GPIO_CF_RESET; - } + gpio_set_value(CERF_GPIO_CF_RESET, !!(state->flags & SS_RESET)); return 0; } -static void cerf_pcmcia_socket_init(struct soc_pcmcia_socket *skt) -{ - soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs)); -} - -static void cerf_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) -{ - soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs)); -} - static struct pcmcia_low_level cerf_pcmcia_ops = { .owner = THIS_MODULE, .hw_init = cerf_pcmcia_hw_init, .hw_shutdown = cerf_pcmcia_hw_shutdown, .socket_state = cerf_pcmcia_socket_state, .configure_socket = cerf_pcmcia_configure_socket, - - .socket_init = cerf_pcmcia_socket_init, - .socket_suspend = cerf_pcmcia_socket_suspend, }; int __devinit pcmcia_cerf_init(struct device *dev) diff --git a/drivers/pcmcia/sa1100_h3600.c b/drivers/pcmcia/sa1100_h3600.c index edf8f0028898..d9c7337b909c 100644 --- a/drivers/pcmcia/sa1100_h3600.c +++ b/drivers/pcmcia/sa1100_h3600.c @@ -19,36 +19,20 @@ #include "sa1100_generic.h" -static struct pcmcia_irqs irqs[] = { - { .sock = 0, .str = "PCMCIA CD0" }, /* .irq will be filled later */ - { .sock = 1, .str = "PCMCIA CD1" } -}; - static int h3600_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { int err; switch (skt->nr) { case 0: - err = gpio_request(H3XXX_GPIO_PCMCIA_IRQ0, "PCMCIA IRQ0"); - if (err) - goto err00; - err = gpio_direction_input(H3XXX_GPIO_PCMCIA_IRQ0); - if (err) - goto err01; - skt->socket.pci_irq = gpio_to_irq(H3XXX_GPIO_PCMCIA_IRQ0); - - err = gpio_request(H3XXX_GPIO_PCMCIA_CD0, "PCMCIA CD0"); - if (err) - goto err01; - err = gpio_direction_input(H3XXX_GPIO_PCMCIA_CD0); - if (err) - goto err02; - irqs[0].irq = gpio_to_irq(H3XXX_GPIO_PCMCIA_CD0); + skt->stat[SOC_STAT_CD].gpio = H3XXX_GPIO_PCMCIA_CD0; + skt->stat[SOC_STAT_CD].name = "PCMCIA CD0"; + skt->stat[SOC_STAT_RDY].gpio = H3XXX_GPIO_PCMCIA_IRQ0; + skt->stat[SOC_STAT_RDY].name = "PCMCIA IRQ0"; err = gpio_request(H3XXX_EGPIO_OPT_NVRAM_ON, "OPT NVRAM ON"); if (err) - goto err02; + goto err01; err = gpio_direction_output(H3XXX_EGPIO_OPT_NVRAM_ON, 0); if (err) goto err03; @@ -70,30 +54,12 @@ static int h3600_pcmcia_hw_init(struct soc_pcmcia_socket *skt) err = gpio_direction_output(H3XXX_EGPIO_CARD_RESET, 0); if (err) goto err06; - err = soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); - if (err) - goto err06; break; case 1: - err = gpio_request(H3XXX_GPIO_PCMCIA_IRQ1, "PCMCIA IRQ1"); - if (err) - goto err10; - err = gpio_direction_input(H3XXX_GPIO_PCMCIA_IRQ1); - if (err) - goto err11; - skt->socket.pci_irq = gpio_to_irq(H3XXX_GPIO_PCMCIA_IRQ1); - - err = gpio_request(H3XXX_GPIO_PCMCIA_CD1, "PCMCIA CD1"); - if (err) - goto err11; - err = gpio_direction_input(H3XXX_GPIO_PCMCIA_CD1); - if (err) - goto err12; - irqs[1].irq = gpio_to_irq(H3XXX_GPIO_PCMCIA_CD1); - - err = soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); - if (err) - goto err12; + skt->stat[SOC_STAT_CD].gpio = H3XXX_GPIO_PCMCIA_CD1; + skt->stat[SOC_STAT_CD].name = "PCMCIA CD1"; + skt->stat[SOC_STAT_RDY].gpio = H3XXX_GPIO_PCMCIA_IRQ1; + skt->stat[SOC_STAT_RDY].name = "PCMCIA IRQ1"; break; } return 0; @@ -102,19 +68,12 @@ err06: gpio_free(H3XXX_EGPIO_CARD_RESET); err05: gpio_free(H3XXX_EGPIO_OPT_RESET); err04: gpio_free(H3XXX_EGPIO_OPT_ON); err03: gpio_free(H3XXX_EGPIO_OPT_NVRAM_ON); -err02: gpio_free(H3XXX_GPIO_PCMCIA_CD0); err01: gpio_free(H3XXX_GPIO_PCMCIA_IRQ0); -err00: return err; - -err12: gpio_free(H3XXX_GPIO_PCMCIA_CD0); -err11: gpio_free(H3XXX_GPIO_PCMCIA_IRQ0); -err10: return err; + return err; } static void h3600_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) { - soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); - switch (skt->nr) { case 0: /* Disable CF bus: */ @@ -126,12 +85,8 @@ static void h3600_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) gpio_free(H3XXX_EGPIO_OPT_RESET); gpio_free(H3XXX_EGPIO_OPT_ON); gpio_free(H3XXX_EGPIO_OPT_NVRAM_ON); - gpio_free(H3XXX_GPIO_PCMCIA_CD0); - gpio_free(H3XXX_GPIO_PCMCIA_IRQ0); break; case 1: - gpio_free(H3XXX_GPIO_PCMCIA_CD1); - gpio_free(H3XXX_GPIO_PCMCIA_IRQ1); break; } } @@ -139,27 +94,10 @@ static void h3600_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) static void h3600_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { - switch (skt->nr) { - case 0: - state->detect = !gpio_get_value(H3XXX_GPIO_PCMCIA_CD0); - state->ready = !!gpio_get_value(H3XXX_GPIO_PCMCIA_IRQ0); - state->bvd1 = 0; - state->bvd2 = 0; - state->wrprot = 0; /* Not available on H3600. */ - state->vs_3v = 0; - state->vs_Xv = 0; - break; - - case 1: - state->detect = !gpio_get_value(H3XXX_GPIO_PCMCIA_CD1); - state->ready = !!gpio_get_value(H3XXX_GPIO_PCMCIA_IRQ1); - state->bvd1 = 0; - state->bvd2 = 0; - state->wrprot = 0; /* Not available on H3600. */ - state->vs_3v = 0; - state->vs_Xv = 0; - break; - } + state->bvd1 = 0; + state->bvd2 = 0; + state->vs_3v = 0; + state->vs_Xv = 0; } static int @@ -186,14 +124,10 @@ static void h3600_pcmcia_socket_init(struct soc_pcmcia_socket *skt) gpio_set_value(H3XXX_EGPIO_OPT_RESET, 0); msleep(10); - - soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs)); } static void h3600_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) { - soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs)); - /* * FIXME: This doesn't fit well. We don't have the mechanism in * the generic PCMCIA layer to deal with the idea of two sockets diff --git a/drivers/pcmcia/sa1100_nanoengine.c b/drivers/pcmcia/sa1100_nanoengine.c index 93b9c9ba57c3..35c30ff41e81 100644 --- a/drivers/pcmcia/sa1100_nanoengine.c +++ b/drivers/pcmcia/sa1100_nanoengine.c @@ -19,6 +19,7 @@ */ #include <linux/device.h> #include <linux/errno.h> +#include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/init.h> @@ -34,43 +35,23 @@ #include "sa1100_generic.h" -static struct pcmcia_irqs irqs_skt0[] = { - /* socket, IRQ, name */ - { 0, NANOENGINE_IRQ_GPIO_PC_CD0, "PC CD0" }, -}; - -static struct pcmcia_irqs irqs_skt1[] = { - /* socket, IRQ, name */ - { 1, NANOENGINE_IRQ_GPIO_PC_CD1, "PC CD1" }, -}; - struct nanoengine_pins { - unsigned input_pins; unsigned output_pins; unsigned clear_outputs; - unsigned transition_pins; - unsigned pci_irq; - struct pcmcia_irqs *pcmcia_irqs; - unsigned pcmcia_irqs_size; + int gpio_rst; + int gpio_cd; + int gpio_rdy; }; static struct nanoengine_pins nano_skts[] = { { - .input_pins = GPIO_PC_READY0 | GPIO_PC_CD0, - .output_pins = GPIO_PC_RESET0, - .clear_outputs = GPIO_PC_RESET0, - .transition_pins = NANOENGINE_IRQ_GPIO_PC_CD0, - .pci_irq = NANOENGINE_IRQ_GPIO_PC_READY0, - .pcmcia_irqs = irqs_skt0, - .pcmcia_irqs_size = ARRAY_SIZE(irqs_skt0) + .gpio_rst = GPIO_PC_RESET0, + .gpio_cd = GPIO_PC_CD0, + .gpio_rdy = GPIO_PC_READY0, }, { - .input_pins = GPIO_PC_READY1 | GPIO_PC_CD1, - .output_pins = GPIO_PC_RESET1, - .clear_outputs = GPIO_PC_RESET1, - .transition_pins = NANOENGINE_IRQ_GPIO_PC_CD1, - .pci_irq = NANOENGINE_IRQ_GPIO_PC_READY1, - .pcmcia_irqs = irqs_skt1, - .pcmcia_irqs_size = ARRAY_SIZE(irqs_skt1) + .gpio_rst = GPIO_PC_RESET1, + .gpio_cd = GPIO_PC_CD1, + .gpio_rdy = GPIO_PC_READY1, } }; @@ -79,58 +60,38 @@ unsigned num_nano_pcmcia_sockets = ARRAY_SIZE(nano_skts); static int nanoengine_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { unsigned i = skt->nr; + int ret; if (i >= num_nano_pcmcia_sockets) return -ENXIO; - GPDR &= ~nano_skts[i].input_pins; - GPDR |= nano_skts[i].output_pins; - GPCR = nano_skts[i].clear_outputs; - irq_set_irq_type(nano_skts[i].transition_pins, IRQ_TYPE_EDGE_BOTH); - skt->socket.pci_irq = nano_skts[i].pci_irq; + ret = gpio_request_one(nano_skts[i].gpio_rst, GPIOF_OUT_INIT_LOW, + i ? "PC RST1" : "PC RST0"); + if (ret) + return ret; + + skt->stat[SOC_STAT_CD].gpio = nano_skts[i].gpio_cd; + skt->stat[SOC_STAT_CD].name = i ? "PC CD1" : "PC CD0"; + skt->stat[SOC_STAT_RDY].gpio = nano_skts[i].gpio_rdy; + skt->stat[SOC_STAT_RDY].name = i ? "PC RDY1" : "PC RDY0"; - return soc_pcmcia_request_irqs(skt, - nano_skts[i].pcmcia_irqs, nano_skts[i].pcmcia_irqs_size); + return 0; } -/* - * Release all resources. - */ static void nanoengine_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) { - unsigned i = skt->nr; - - if (i >= num_nano_pcmcia_sockets) - return; - - soc_pcmcia_free_irqs(skt, - nano_skts[i].pcmcia_irqs, nano_skts[i].pcmcia_irqs_size); + gpio_free(nano_skts[skt->nr].gpio_rst); } static int nanoengine_pcmcia_configure_socket( struct soc_pcmcia_socket *skt, const socket_state_t *state) { - unsigned reset; unsigned i = skt->nr; if (i >= num_nano_pcmcia_sockets) return -ENXIO; - switch (i) { - case 0: - reset = GPIO_PC_RESET0; - break; - case 1: - reset = GPIO_PC_RESET1; - break; - default: - return -ENXIO; - } - - if (state->flags & SS_RESET) - GPSR = reset; - else - GPCR = reset; + gpio_set_value(nano_skts[skt->nr].gpio_rst, !!(state->flags & SS_RESET)); return 0; } @@ -138,62 +99,17 @@ static int nanoengine_pcmcia_configure_socket( static void nanoengine_pcmcia_socket_state( struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { - unsigned long levels = GPLR; unsigned i = skt->nr; if (i >= num_nano_pcmcia_sockets) return; - memset(state, 0, sizeof(struct pcmcia_state)); - switch (i) { - case 0: - state->ready = (levels & GPIO_PC_READY0) ? 1 : 0; - state->detect = !(levels & GPIO_PC_CD0) ? 1 : 0; - break; - case 1: - state->ready = (levels & GPIO_PC_READY1) ? 1 : 0; - state->detect = !(levels & GPIO_PC_CD1) ? 1 : 0; - break; - default: - return; - } state->bvd1 = 1; state->bvd2 = 1; - state->wrprot = 0; /* Not available */ state->vs_3v = 1; /* Can only apply 3.3V */ state->vs_Xv = 0; } -/* - * Enable card status IRQs on (re-)initialisation. This can - * be called at initialisation, power management event, or - * pcmcia event. - */ -static void nanoengine_pcmcia_socket_init(struct soc_pcmcia_socket *skt) -{ - unsigned i = skt->nr; - - if (i >= num_nano_pcmcia_sockets) - return; - - soc_pcmcia_enable_irqs(skt, - nano_skts[i].pcmcia_irqs, nano_skts[i].pcmcia_irqs_size); -} - -/* - * Disable card status IRQs on suspend. - */ -static void nanoengine_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) -{ - unsigned i = skt->nr; - - if (i >= num_nano_pcmcia_sockets) - return; - - soc_pcmcia_disable_irqs(skt, - nano_skts[i].pcmcia_irqs, nano_skts[i].pcmcia_irqs_size); -} - static struct pcmcia_low_level nanoengine_pcmcia_ops = { .owner = THIS_MODULE, @@ -202,8 +118,6 @@ static struct pcmcia_low_level nanoengine_pcmcia_ops = { .configure_socket = nanoengine_pcmcia_configure_socket, .socket_state = nanoengine_pcmcia_socket_state, - .socket_init = nanoengine_pcmcia_socket_init, - .socket_suspend = nanoengine_pcmcia_socket_suspend, }; int pcmcia_nanoengine_init(struct device *dev) diff --git a/drivers/pcmcia/sa1100_shannon.c b/drivers/pcmcia/sa1100_shannon.c index 7ff1b43540b8..decb34730bcf 100644 --- a/drivers/pcmcia/sa1100_shannon.c +++ b/drivers/pcmcia/sa1100_shannon.c @@ -15,40 +15,35 @@ #include <asm/irq.h> #include "sa1100_generic.h" -static struct pcmcia_irqs irqs[] = { - { 0, SHANNON_IRQ_GPIO_EJECT_0, "PCMCIA_CD_0" }, - { 1, SHANNON_IRQ_GPIO_EJECT_1, "PCMCIA_CD_1" }, -}; - static int shannon_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { /* All those are inputs */ - GPDR &= ~(SHANNON_GPIO_EJECT_0 | SHANNON_GPIO_EJECT_1 | - SHANNON_GPIO_RDY_0 | SHANNON_GPIO_RDY_1); - GAFR &= ~(SHANNON_GPIO_EJECT_0 | SHANNON_GPIO_EJECT_1 | - SHANNON_GPIO_RDY_0 | SHANNON_GPIO_RDY_1); - - skt->socket.pci_irq = skt->nr ? SHANNON_IRQ_GPIO_RDY_1 : SHANNON_IRQ_GPIO_RDY_0; - - return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); -} + GAFR &= ~(GPIO_GPIO(SHANNON_GPIO_EJECT_0) | + GPIO_GPIO(SHANNON_GPIO_EJECT_1) | + GPIO_GPIO(SHANNON_GPIO_RDY_0) | + GPIO_GPIO(SHANNON_GPIO_RDY_1)); + + if (skt->nr == 0) { + skt->stat[SOC_STAT_CD].gpio = SHANNON_GPIO_EJECT_0; + skt->stat[SOC_STAT_CD].name = "PCMCIA_CD_0"; + skt->stat[SOC_STAT_RDY].gpio = SHANNON_GPIO_RDY_0; + skt->stat[SOC_STAT_RDY].name = "PCMCIA_RDY_0"; + } else { + skt->stat[SOC_STAT_CD].gpio = SHANNON_GPIO_EJECT_1; + skt->stat[SOC_STAT_CD].name = "PCMCIA_CD_1"; + skt->stat[SOC_STAT_RDY].gpio = SHANNON_GPIO_RDY_1; + skt->stat[SOC_STAT_RDY].name = "PCMCIA_RDY_1"; + } -static void shannon_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) -{ - soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); + return 0; } static void shannon_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { - unsigned long levels = GPLR; - switch (skt->nr) { case 0: - state->detect = (levels & SHANNON_GPIO_EJECT_0) ? 0 : 1; - state->ready = (levels & SHANNON_GPIO_RDY_0) ? 1 : 0; - state->wrprot = 0; /* Not available on Shannon. */ state->bvd1 = 1; state->bvd2 = 1; state->vs_3v = 1; /* FIXME Can only apply 3.3V on Shannon. */ @@ -56,9 +51,6 @@ shannon_pcmcia_socket_state(struct soc_pcmcia_socket *skt, break; case 1: - state->detect = (levels & SHANNON_GPIO_EJECT_1) ? 0 : 1; - state->ready = (levels & SHANNON_GPIO_RDY_1) ? 1 : 0; - state->wrprot = 0; /* Not available on Shannon. */ state->bvd1 = 1; state->bvd2 = 1; state->vs_3v = 1; /* FIXME Can only apply 3.3V on Shannon. */ @@ -92,25 +84,11 @@ shannon_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, return 0; } -static void shannon_pcmcia_socket_init(struct soc_pcmcia_socket *skt) -{ - soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs)); -} - -static void shannon_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) -{ - soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs)); -} - static struct pcmcia_low_level shannon_pcmcia_ops = { .owner = THIS_MODULE, .hw_init = shannon_pcmcia_hw_init, - .hw_shutdown = shannon_pcmcia_hw_shutdown, .socket_state = shannon_pcmcia_socket_state, .configure_socket = shannon_pcmcia_configure_socket, - - .socket_init = shannon_pcmcia_socket_init, - .socket_suspend = shannon_pcmcia_socket_suspend, }; int __devinit pcmcia_shannon_init(struct device *dev) diff --git a/drivers/pcmcia/sa1100_simpad.c b/drivers/pcmcia/sa1100_simpad.c index 0fac9658b020..8647b17c449e 100644 --- a/drivers/pcmcia/sa1100_simpad.c +++ b/drivers/pcmcia/sa1100_simpad.c @@ -15,24 +15,21 @@ #include <mach/simpad.h> #include "sa1100_generic.h" -static struct pcmcia_irqs irqs[] = { - { 1, IRQ_GPIO_CF_CD, "CF_CD" }, -}; - static int simpad_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { simpad_clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1); - skt->socket.pci_irq = IRQ_GPIO_CF_IRQ; + skt->stat[SOC_STAT_CD].gpio = GPIO_CF_CD; + skt->stat[SOC_STAT_CD].name = "CF_CD"; + skt->stat[SOC_STAT_RDY].gpio = GPIO_CF_IRQ; + skt->stat[SOC_STAT_RDY].name = "CF_RDY"; - return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); + return 0; } static void simpad_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) { - soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); - /* Disable CF bus: */ /*simpad_set_cs3_bit(PCMCIA_BUFF_DIS);*/ simpad_clear_cs3_bit(PCMCIA_RESET); @@ -42,14 +39,13 @@ static void simpad_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { - unsigned long levels = GPLR; long cs3reg = simpad_get_cs3_ro(); - state->detect=((levels & GPIO_CF_CD)==0)?1:0; - state->ready=(levels & GPIO_CF_IRQ)?1:0; + /* the detect signal is inverted - fix that up here */ + state->detect = !state->detect; + state->bvd1 = 1; /* Might be cs3reg & PCMCIA_BVD1 */ state->bvd2 = 1; /* Might be cs3reg & PCMCIA_BVD2 */ - state->wrprot=0; /* Not available on Simpad. */ if ((cs3reg & (PCMCIA_VS1|PCMCIA_VS2)) == (PCMCIA_VS1|PCMCIA_VS2)) { @@ -99,14 +95,8 @@ simpad_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, return 0; } -static void simpad_pcmcia_socket_init(struct soc_pcmcia_socket *skt) -{ - soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs)); -} - static void simpad_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) { - soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs)); simpad_set_cs3_bit(PCMCIA_RESET); } @@ -116,7 +106,6 @@ static struct pcmcia_low_level simpad_pcmcia_ops = { .hw_shutdown = simpad_pcmcia_hw_shutdown, .socket_state = simpad_pcmcia_socket_state, .configure_socket = simpad_pcmcia_configure_socket, - .socket_init = simpad_pcmcia_socket_init, .socket_suspend = simpad_pcmcia_socket_suspend, }; diff --git a/drivers/pcmcia/sa1100_badge4.c b/drivers/pcmcia/sa1111_badge4.c index 1ce53f493bef..4d206f4dd67b 100644 --- a/drivers/pcmcia/sa1100_badge4.c +++ b/drivers/pcmcia/sa1111_badge4.c @@ -122,13 +122,12 @@ badge4_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state local_irq_restore(flags); } - return 0; + return ret; } static struct pcmcia_low_level badge4_pcmcia_ops = { .owner = THIS_MODULE, .configure_socket = badge4_pcmcia_configure_socket, - .socket_init = sa1111_pcmcia_socket_init, .first = 0, .nr = 2, }; diff --git a/drivers/pcmcia/sa1111_generic.c b/drivers/pcmcia/sa1111_generic.c index 27f2fe3b7fb4..70f728ce1856 100644 --- a/drivers/pcmcia/sa1111_generic.c +++ b/drivers/pcmcia/sa1111_generic.c @@ -22,6 +22,40 @@ #include "sa1111_generic.h" +/* + * These are offsets from the above base. + */ +#define PCCR 0x0000 +#define PCSSR 0x0004 +#define PCSR 0x0008 + +#define PCSR_S0_READY (1<<0) +#define PCSR_S1_READY (1<<1) +#define PCSR_S0_DETECT (1<<2) +#define PCSR_S1_DETECT (1<<3) +#define PCSR_S0_VS1 (1<<4) +#define PCSR_S0_VS2 (1<<5) +#define PCSR_S1_VS1 (1<<6) +#define PCSR_S1_VS2 (1<<7) +#define PCSR_S0_WP (1<<8) +#define PCSR_S1_WP (1<<9) +#define PCSR_S0_BVD1 (1<<10) +#define PCSR_S0_BVD2 (1<<11) +#define PCSR_S1_BVD1 (1<<12) +#define PCSR_S1_BVD2 (1<<13) + +#define PCCR_S0_RST (1<<0) +#define PCCR_S1_RST (1<<1) +#define PCCR_S0_FLT (1<<2) +#define PCCR_S1_FLT (1<<3) +#define PCCR_S0_PWAITEN (1<<4) +#define PCCR_S1_PWAITEN (1<<5) +#define PCCR_S0_PSE (1<<6) +#define PCCR_S1_PSE (1<<7) + +#define PCSSR_S0_SLEEP (1<<0) +#define PCSSR_S1_SLEEP (1<<1) + #define IDX_IRQ_S0_READY_NINT (0) #define IDX_IRQ_S0_CD_VALID (1) #define IDX_IRQ_S0_BVD1_STSCHG (2) @@ -29,27 +63,10 @@ #define IDX_IRQ_S1_CD_VALID (4) #define IDX_IRQ_S1_BVD1_STSCHG (5) -static struct pcmcia_irqs irqs[] = { - { 0, NO_IRQ, "SA1111 PCMCIA card detect" }, - { 0, NO_IRQ, "SA1111 PCMCIA BVD1" }, - { 1, NO_IRQ, "SA1111 CF card detect" }, - { 1, NO_IRQ, "SA1111 CF BVD1" }, -}; - -static int sa1111_pcmcia_hw_init(struct soc_pcmcia_socket *skt) -{ - return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); -} - -static void sa1111_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) -{ - soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); -} - void sa1111_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { struct sa1111_pcmcia_socket *s = to_skt(skt); - unsigned long status = sa1111_readl(s->dev->mapbase + SA1111_PCSR); + unsigned long status = sa1111_readl(s->dev->mapbase + PCSR); switch (skt->nr) { case 0: @@ -105,35 +122,22 @@ int sa1111_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_s pccr_set_mask |= PCCR_S0_FLT|PCCR_S1_FLT; local_irq_save(flags); - val = sa1111_readl(s->dev->mapbase + SA1111_PCCR); + val = sa1111_readl(s->dev->mapbase + PCCR); val &= ~pccr_skt_mask; val |= pccr_set_mask & pccr_skt_mask; - sa1111_writel(val, s->dev->mapbase + SA1111_PCCR); + sa1111_writel(val, s->dev->mapbase + PCCR); local_irq_restore(flags); return 0; } -void sa1111_pcmcia_socket_init(struct soc_pcmcia_socket *skt) -{ - soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs)); -} - -static void sa1111_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) -{ - soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs)); -} - int sa1111_pcmcia_add(struct sa1111_dev *dev, struct pcmcia_low_level *ops, int (*add)(struct soc_pcmcia_socket *)) { struct sa1111_pcmcia_socket *s; int i, ret = 0; - ops->hw_init = sa1111_pcmcia_hw_init; - ops->hw_shutdown = sa1111_pcmcia_hw_shutdown; ops->socket_state = sa1111_pcmcia_socket_state; - ops->socket_suspend = sa1111_pcmcia_socket_suspend; for (i = 0; i < ops->nr; i++) { s = kzalloc(sizeof(*s), GFP_KERNEL); @@ -141,13 +145,21 @@ int sa1111_pcmcia_add(struct sa1111_dev *dev, struct pcmcia_low_level *ops, return -ENOMEM; s->soc.nr = ops->first + i; - s->soc.ops = ops; - s->soc.socket.owner = ops->owner; - s->soc.socket.dev.parent = &dev->dev; - s->soc.socket.pci_irq = s->soc.nr ? - dev->irq[IDX_IRQ_S0_READY_NINT] : - dev->irq[IDX_IRQ_S1_READY_NINT]; + soc_pcmcia_init_one(&s->soc, ops, &dev->dev); s->dev = dev; + if (s->soc.nr) { + s->soc.socket.pci_irq = dev->irq[IDX_IRQ_S1_READY_NINT]; + s->soc.stat[SOC_STAT_CD].irq = dev->irq[IDX_IRQ_S1_CD_VALID]; + s->soc.stat[SOC_STAT_CD].name = "SA1111 CF card detect"; + s->soc.stat[SOC_STAT_BVD1].irq = dev->irq[IDX_IRQ_S1_BVD1_STSCHG]; + s->soc.stat[SOC_STAT_BVD1].name = "SA1111 CF BVD1"; + } else { + s->soc.socket.pci_irq = dev->irq[IDX_IRQ_S0_READY_NINT]; + s->soc.stat[SOC_STAT_CD].irq = dev->irq[IDX_IRQ_S0_CD_VALID]; + s->soc.stat[SOC_STAT_CD].name = "SA1111 PCMCIA card detect"; + s->soc.stat[SOC_STAT_BVD1].irq = dev->irq[IDX_IRQ_S0_BVD1_STSCHG]; + s->soc.stat[SOC_STAT_BVD1].name = "SA1111 PCMCIA BVD1"; + } ret = add(&s->soc); if (ret == 0) { @@ -163,26 +175,26 @@ int sa1111_pcmcia_add(struct sa1111_dev *dev, struct pcmcia_low_level *ops, static int pcmcia_probe(struct sa1111_dev *dev) { void __iomem *base; + int ret; + + ret = sa1111_enable_device(dev); + if (ret) + return ret; dev_set_drvdata(&dev->dev, NULL); - if (!request_mem_region(dev->res.start, 512, - SA1111_DRIVER_NAME(dev))) + if (!request_mem_region(dev->res.start, 512, SA1111_DRIVER_NAME(dev))) { + sa1111_disable_device(dev); return -EBUSY; + } base = dev->mapbase; - /* Initialize PCMCIA IRQs */ - irqs[0].irq = dev->irq[IDX_IRQ_S0_CD_VALID]; - irqs[1].irq = dev->irq[IDX_IRQ_S0_BVD1_STSCHG]; - irqs[2].irq = dev->irq[IDX_IRQ_S1_CD_VALID]; - irqs[3].irq = dev->irq[IDX_IRQ_S1_BVD1_STSCHG]; - /* * Initialise the suspend state. */ - sa1111_writel(PCSSR_S0_SLEEP | PCSSR_S1_SLEEP, base + SA1111_PCSSR); - sa1111_writel(PCCR_S0_FLT | PCCR_S1_FLT, base + SA1111_PCCR); + sa1111_writel(PCSSR_S0_SLEEP | PCSSR_S1_SLEEP, base + PCSSR); + sa1111_writel(PCCR_S0_FLT | PCCR_S1_FLT, base + PCCR); #ifdef CONFIG_SA1100_BADGE4 pcmcia_badge4_init(&dev->dev); @@ -212,6 +224,7 @@ static int __devexit pcmcia_remove(struct sa1111_dev *dev) } release_mem_region(dev->res.start, 512); + sa1111_disable_device(dev); return 0; } diff --git a/drivers/pcmcia/sa1111_generic.h b/drivers/pcmcia/sa1111_generic.h index 02dc8577cdaf..f6376e34a7e4 100644 --- a/drivers/pcmcia/sa1111_generic.h +++ b/drivers/pcmcia/sa1111_generic.h @@ -17,7 +17,6 @@ int sa1111_pcmcia_add(struct sa1111_dev *dev, struct pcmcia_low_level *ops, extern void sa1111_pcmcia_socket_state(struct soc_pcmcia_socket *, struct pcmcia_state *); extern int sa1111_pcmcia_configure_socket(struct soc_pcmcia_socket *, const socket_state_t *); -extern void sa1111_pcmcia_socket_init(struct soc_pcmcia_socket *); extern int pcmcia_badge4_init(struct device *); extern int pcmcia_jornada720_init(struct device *); diff --git a/drivers/pcmcia/sa1100_jornada720.c b/drivers/pcmcia/sa1111_jornada720.c index 6bcabee6bde4..69428d1f5ae1 100644 --- a/drivers/pcmcia/sa1100_jornada720.c +++ b/drivers/pcmcia/sa1111_jornada720.c @@ -78,13 +78,8 @@ jornada720_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_s } ret = sa1111_pcmcia_configure_socket(skt, state); - if (ret == 0) { - unsigned long flags; - - local_irq_save(flags); + if (ret == 0) sa1111_set_io(s->dev, pa_dwr_mask, pa_dwr_set); - local_irq_restore(flags); - } return ret; } @@ -92,7 +87,6 @@ jornada720_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_s static struct pcmcia_low_level jornada720_pcmcia_ops = { .owner = THIS_MODULE, .configure_socket = jornada720_pcmcia_configure_socket, - .socket_init = sa1111_pcmcia_socket_init, .first = 0, .nr = 2, }; diff --git a/drivers/pcmcia/pxa2xx_lubbock.c b/drivers/pcmcia/sa1111_lubbock.c index c21888eebb58..c5caf5790451 100644 --- a/drivers/pcmcia/pxa2xx_lubbock.c +++ b/drivers/pcmcia/sa1111_lubbock.c @@ -202,7 +202,6 @@ lubbock_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, static struct pcmcia_low_level lubbock_pcmcia_ops = { .owner = THIS_MODULE, .configure_socket = lubbock_pcmcia_configure_socket, - .socket_init = sa1111_pcmcia_socket_init, .first = 0, .nr = 2, }; diff --git a/drivers/pcmcia/sa1100_neponset.c b/drivers/pcmcia/sa1111_neponset.c index c95639b5f2a0..1d78739c4c07 100644 --- a/drivers/pcmcia/sa1100_neponset.c +++ b/drivers/pcmcia/sa1111_neponset.c @@ -94,30 +94,16 @@ neponset_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_sta ret = sa1111_pcmcia_configure_socket(skt, state); if (ret == 0) { - unsigned long flags; - - local_irq_save(flags); - NCR_0 = (NCR_0 & ~ncr_mask) | ncr_set; - - local_irq_restore(flags); + neponset_ncr_frob(ncr_mask, ncr_set); sa1111_set_io(s->dev, pa_dwr_mask, pa_dwr_set); } - return 0; -} - -static void neponset_pcmcia_socket_init(struct soc_pcmcia_socket *skt) -{ - if (skt->nr == 0) - NCR_0 &= ~(NCR_A0VPP | NCR_A1VPP); - - sa1111_pcmcia_socket_init(skt); + return ret; } static struct pcmcia_low_level neponset_pcmcia_ops = { .owner = THIS_MODULE, .configure_socket = neponset_pcmcia_configure_socket, - .socket_init = neponset_pcmcia_socket_init, .first = 0, .nr = 2, }; diff --git a/drivers/pcmcia/sa11xx_base.c b/drivers/pcmcia/sa11xx_base.c index 0c62fe31a40e..a3ee89a6dd0e 100644 --- a/drivers/pcmcia/sa11xx_base.c +++ b/drivers/pcmcia/sa11xx_base.c @@ -236,10 +236,7 @@ int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, skt = &sinfo->skt[i]; skt->nr = first + i; - skt->ops = ops; - skt->socket.owner = ops->owner; - skt->socket.dev.parent = dev; - skt->socket.pci_irq = NO_IRQ; + soc_pcmcia_init_one(skt, ops, dev); ret = sa11xx_drv_pcmcia_add_one(skt); if (ret) diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c index a0a9c2aa8d78..e0433f571962 100644 --- a/drivers/pcmcia/soc_common.c +++ b/drivers/pcmcia/soc_common.c @@ -32,6 +32,7 @@ #include <linux/cpufreq.h> +#include <linux/gpio.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> @@ -49,6 +50,8 @@ #include "soc_common.h" +static irqreturn_t soc_common_pcmcia_interrupt(int irq, void *dev); + #ifdef CONFIG_PCMCIA_DEBUG static int pc_debug; @@ -104,6 +107,93 @@ void soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *skt, } EXPORT_SYMBOL(soc_common_pcmcia_get_timing); +static void __soc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt, + unsigned int nr) +{ + unsigned int i; + + for (i = 0; i < nr; i++) { + if (skt->stat[i].irq) + free_irq(skt->stat[i].irq, skt); + if (gpio_is_valid(skt->stat[i].gpio)) + gpio_free(skt->stat[i].gpio); + } + + if (skt->ops->hw_shutdown) + skt->ops->hw_shutdown(skt); +} + +static void soc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) +{ + __soc_pcmcia_hw_shutdown(skt, ARRAY_SIZE(skt->stat)); +} + +static int soc_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + int ret = 0, i; + + if (skt->ops->hw_init) { + ret = skt->ops->hw_init(skt); + if (ret) + return ret; + } + + for (i = 0; i < ARRAY_SIZE(skt->stat); i++) { + if (gpio_is_valid(skt->stat[i].gpio)) { + int irq; + + ret = gpio_request_one(skt->stat[i].gpio, GPIOF_IN, + skt->stat[i].name); + if (ret) { + __soc_pcmcia_hw_shutdown(skt, i); + return ret; + } + + irq = gpio_to_irq(skt->stat[i].gpio); + + if (i == SOC_STAT_RDY) + skt->socket.pci_irq = irq; + else + skt->stat[i].irq = irq; + } + + if (skt->stat[i].irq) { + ret = request_irq(skt->stat[i].irq, + soc_common_pcmcia_interrupt, + IRQF_TRIGGER_NONE, + skt->stat[i].name, skt); + if (ret) { + if (gpio_is_valid(skt->stat[i].gpio)) + gpio_free(skt->stat[i].gpio); + __soc_pcmcia_hw_shutdown(skt, i); + return ret; + } + } + } + + return ret; +} + +static void soc_pcmcia_hw_enable(struct soc_pcmcia_socket *skt) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(skt->stat); i++) + if (skt->stat[i].irq) { + irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_EDGE_RISING); + irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_EDGE_BOTH); + } +} + +static void soc_pcmcia_hw_disable(struct soc_pcmcia_socket *skt) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(skt->stat); i++) + if (skt->stat[i].irq) + irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_NONE); +} + static unsigned int soc_common_pcmcia_skt_state(struct soc_pcmcia_socket *skt) { struct pcmcia_state state; @@ -111,6 +201,22 @@ static unsigned int soc_common_pcmcia_skt_state(struct soc_pcmcia_socket *skt) memset(&state, 0, sizeof(struct pcmcia_state)); + /* Make battery voltage state report 'good' */ + state.bvd1 = 1; + state.bvd2 = 1; + + /* CD is active low by default */ + if (gpio_is_valid(skt->stat[SOC_STAT_CD].gpio)) + state.detect = !gpio_get_value(skt->stat[SOC_STAT_CD].gpio); + + /* RDY and BVD are active high by default */ + if (gpio_is_valid(skt->stat[SOC_STAT_RDY].gpio)) + state.ready = !!gpio_get_value(skt->stat[SOC_STAT_RDY].gpio); + if (gpio_is_valid(skt->stat[SOC_STAT_BVD1].gpio)) + state.bvd1 = !!gpio_get_value(skt->stat[SOC_STAT_BVD1].gpio); + if (gpio_is_valid(skt->stat[SOC_STAT_BVD2].gpio)) + state.bvd2 = !!gpio_get_value(skt->stat[SOC_STAT_BVD2].gpio); + skt->ops->socket_state(skt, &state); stat = state.detect ? SS_DETECT : 0; @@ -188,6 +294,7 @@ static int soc_common_pcmcia_sock_init(struct pcmcia_socket *sock) debug(skt, 2, "initializing socket\n"); if (skt->ops->socket_init) skt->ops->socket_init(skt); + soc_pcmcia_hw_enable(skt); return 0; } @@ -207,6 +314,7 @@ static int soc_common_pcmcia_suspend(struct pcmcia_socket *sock) debug(skt, 2, "suspending socket\n"); + soc_pcmcia_hw_disable(skt); if (skt->ops->socket_suspend) skt->ops->socket_suspend(skt); @@ -526,69 +634,6 @@ static struct pccard_operations soc_common_pcmcia_operations = { }; -int soc_pcmcia_request_irqs(struct soc_pcmcia_socket *skt, - struct pcmcia_irqs *irqs, int nr) -{ - int i, res = 0; - - for (i = 0; i < nr; i++) { - if (irqs[i].sock != skt->nr) - continue; - res = request_irq(irqs[i].irq, soc_common_pcmcia_interrupt, - IRQF_DISABLED, irqs[i].str, skt); - if (res) - break; - irq_set_irq_type(irqs[i].irq, IRQ_TYPE_NONE); - } - - if (res) { - printk(KERN_ERR "PCMCIA: request for IRQ%d failed (%d)\n", - irqs[i].irq, res); - - while (i--) - if (irqs[i].sock == skt->nr) - free_irq(irqs[i].irq, skt); - } - return res; -} -EXPORT_SYMBOL(soc_pcmcia_request_irqs); - -void soc_pcmcia_free_irqs(struct soc_pcmcia_socket *skt, - struct pcmcia_irqs *irqs, int nr) -{ - int i; - - for (i = 0; i < nr; i++) - if (irqs[i].sock == skt->nr) - free_irq(irqs[i].irq, skt); -} -EXPORT_SYMBOL(soc_pcmcia_free_irqs); - -void soc_pcmcia_disable_irqs(struct soc_pcmcia_socket *skt, - struct pcmcia_irqs *irqs, int nr) -{ - int i; - - for (i = 0; i < nr; i++) - if (irqs[i].sock == skt->nr) - irq_set_irq_type(irqs[i].irq, IRQ_TYPE_NONE); -} -EXPORT_SYMBOL(soc_pcmcia_disable_irqs); - -void soc_pcmcia_enable_irqs(struct soc_pcmcia_socket *skt, - struct pcmcia_irqs *irqs, int nr) -{ - int i; - - for (i = 0; i < nr; i++) - if (irqs[i].sock == skt->nr) { - irq_set_irq_type(irqs[i].irq, IRQ_TYPE_EDGE_RISING); - irq_set_irq_type(irqs[i].irq, IRQ_TYPE_EDGE_BOTH); - } -} -EXPORT_SYMBOL(soc_pcmcia_enable_irqs); - - static LIST_HEAD(soc_pcmcia_sockets); static DEFINE_MUTEX(soc_pcmcia_sockets_lock); @@ -635,6 +680,21 @@ module_exit(soc_pcmcia_cpufreq_unregister); #endif +void soc_pcmcia_init_one(struct soc_pcmcia_socket *skt, + struct pcmcia_low_level *ops, struct device *dev) +{ + int i; + + skt->ops = ops; + skt->socket.owner = ops->owner; + skt->socket.dev.parent = dev; + skt->socket.pci_irq = NO_IRQ; + + for (i = 0; i < ARRAY_SIZE(skt->stat); i++) + skt->stat[i].gpio = -EINVAL; +} +EXPORT_SYMBOL(soc_pcmcia_init_one); + void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt) { mutex_lock(&soc_pcmcia_sockets_lock); @@ -642,8 +702,9 @@ void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt) pcmcia_unregister_socket(&skt->socket); - skt->ops->hw_shutdown(skt); + soc_pcmcia_hw_shutdown(skt); + /* should not be required; violates some lowlevel drivers */ soc_common_pcmcia_config_skt(skt, &dead_socket); list_del(&skt->node); @@ -700,7 +761,7 @@ int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt) */ skt->ops->set_timing(skt); - ret = skt->ops->hw_init(skt); + ret = soc_pcmcia_hw_init(skt); if (ret) goto out_err_6; @@ -733,7 +794,7 @@ int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt) pcmcia_unregister_socket(&skt->socket); out_err_7: - skt->ops->hw_shutdown(skt); + soc_pcmcia_hw_shutdown(skt); out_err_6: list_del(&skt->node); mutex_unlock(&soc_pcmcia_sockets_lock); diff --git a/drivers/pcmcia/soc_common.h b/drivers/pcmcia/soc_common.h index 9daa73615c8b..e6fcbea5b682 100644 --- a/drivers/pcmcia/soc_common.h +++ b/drivers/pcmcia/soc_common.h @@ -50,6 +50,16 @@ struct soc_pcmcia_socket { struct resource res_attr; void __iomem *virt_io; + struct { + int gpio; + unsigned int irq; + const char *name; + } stat[4]; +#define SOC_STAT_CD 0 /* Card detect */ +#define SOC_STAT_BVD1 1 /* BATDEAD / IOSTSCHG */ +#define SOC_STAT_BVD2 2 /* BATWARN / IOSPKR */ +#define SOC_STAT_RDY 3 /* Ready / Interrupt */ + unsigned int irq_state; struct timer_list poll_timer; @@ -115,25 +125,16 @@ struct pcmcia_low_level { }; -struct pcmcia_irqs { - int sock; - int irq; - const char *str; -}; - struct soc_pcmcia_timing { unsigned short io; unsigned short mem; unsigned short attr; }; -extern int soc_pcmcia_request_irqs(struct soc_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr); -extern void soc_pcmcia_free_irqs(struct soc_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr); -extern void soc_pcmcia_disable_irqs(struct soc_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr); -extern void soc_pcmcia_enable_irqs(struct soc_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr); extern void soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *, struct soc_pcmcia_timing *); - +void soc_pcmcia_init_one(struct soc_pcmcia_socket *skt, + struct pcmcia_low_level *ops, struct device *dev); void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt); int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt); diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index afaf88558125..abfb96408779 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -17,21 +17,63 @@ config PINMUX config PINCONF bool "Support pin configuration controllers" +config GENERIC_PINCONF + bool + select PINCONF + config DEBUG_PINCTRL bool "Debug PINCTRL calls" depends on DEBUG_KERNEL help Say Y here to add some extra checks and diagnostics to PINCTRL calls. +config PINCTRL_PXA3xx + bool + select PINMUX + +config PINCTRL_MMP2 + bool "MMP2 pin controller driver" + depends on ARCH_MMP + select PINCTRL_PXA3xx + select PINCONF + +config PINCTRL_PXA168 + bool "PXA168 pin controller driver" + depends on ARCH_MMP + select PINCTRL_PXA3xx + select PINCONF + +config PINCTRL_PXA910 + bool "PXA910 pin controller driver" + depends on ARCH_MMP + select PINCTRL_PXA3xx + select PINCONF + config PINCTRL_SIRF bool "CSR SiRFprimaII pin controller driver" depends on ARCH_PRIMA2 select PINMUX +config PINCTRL_TEGRA + bool + +config PINCTRL_TEGRA20 + bool + select PINMUX + select PINCONF + select PINCTRL_TEGRA + +config PINCTRL_TEGRA30 + bool + select PINMUX + select PINCONF + select PINCTRL_TEGRA + config PINCTRL_U300 bool "U300 pin controller driver" depends on ARCH_U300 select PINMUX + select GENERIC_PINCONF config PINCTRL_COH901 bool "ST-Ericsson U300 COH 901 335/571 GPIO" diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 827601cc68f6..6d4150b4eced 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -5,6 +5,14 @@ ccflags-$(CONFIG_DEBUG_PINCTRL) += -DDEBUG obj-$(CONFIG_PINCTRL) += core.o obj-$(CONFIG_PINMUX) += pinmux.o obj-$(CONFIG_PINCONF) += pinconf.o +obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o +obj-$(CONFIG_PINCTRL_PXA3xx) += pinctrl-pxa3xx.o +obj-$(CONFIG_PINCTRL_MMP2) += pinctrl-mmp2.o +obj-$(CONFIG_PINCTRL_PXA168) += pinctrl-pxa168.o +obj-$(CONFIG_PINCTRL_PXA910) += pinctrl-pxa910.o obj-$(CONFIG_PINCTRL_SIRF) += pinctrl-sirf.o +obj-$(CONFIG_PINCTRL_TEGRA) += pinctrl-tegra.o +obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o +obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o obj-$(CONFIG_PINCTRL_U300) += pinctrl-u300.o obj-$(CONFIG_PINCTRL_COH901) += pinctrl-coh901.o diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 894cd5e103da..ec3b8cc188af 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -1,12 +1,14 @@ /* * Core driver for the pin control subsystem * - * Copyright (C) 2011 ST-Ericsson SA + * Copyright (C) 2011-2012 ST-Ericsson SA * Written on behalf of Linaro for ST-Ericsson * Based on bits of regulator core, gpio core and clk core * * Author: Linus Walleij <linus.walleij@linaro.org> * + * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. + * * License terms: GNU General Public License (GPL) version 2 */ #define pr_fmt(fmt) "pinctrl core: " fmt @@ -16,11 +18,8 @@ #include <linux/init.h> #include <linux/device.h> #include <linux/slab.h> -#include <linux/radix-tree.h> #include <linux/err.h> #include <linux/list.h> -#include <linux/mutex.h> -#include <linux/spinlock.h> #include <linux/sysfs.h> #include <linux/debugfs.h> #include <linux/seq_file.h> @@ -30,10 +29,36 @@ #include "pinmux.h" #include "pinconf.h" -/* Global list of pin control devices */ -static DEFINE_MUTEX(pinctrldev_list_mutex); +/** + * struct pinctrl_maps - a list item containing part of the mapping table + * @node: mapping table list node + * @maps: array of mapping table entries + * @num_maps: the number of entries in @maps + */ +struct pinctrl_maps { + struct list_head node; + struct pinctrl_map const *maps; + unsigned num_maps; +}; + +/* Mutex taken by all entry points */ +DEFINE_MUTEX(pinctrl_mutex); + +/* Global list of pin control devices (struct pinctrl_dev) */ static LIST_HEAD(pinctrldev_list); +/* List of pin controller handles (struct pinctrl) */ +static LIST_HEAD(pinctrl_list); + +/* List of pinctrl maps (struct pinctrl_maps) */ +static LIST_HEAD(pinctrl_maps); + +#define for_each_maps(_maps_node_, _i_, _map_) \ + list_for_each_entry(_maps_node_, &pinctrl_maps, node) \ + for (_i_ = 0, _map_ = &_maps_node_->maps[_i_]; \ + _i_ < _maps_node_->num_maps; \ + i++, _map_ = &_maps_node_->maps[_i_]) + const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev) { /* We're not allowed to register devices without name */ @@ -48,53 +73,31 @@ void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev) EXPORT_SYMBOL_GPL(pinctrl_dev_get_drvdata); /** - * get_pinctrl_dev_from_dev() - look up pin controller device - * @dev: a device pointer, this may be NULL but then devname needs to be - * defined instead - * @devname: the name of a device instance, as returned by dev_name(), this - * may be NULL but then dev needs to be defined instead + * get_pinctrl_dev_from_devname() - look up pin controller device + * @devname: the name of a device instance, as returned by dev_name() * * Looks up a pin control device matching a certain device name or pure device * pointer, the pure device pointer will take precedence. */ -struct pinctrl_dev *get_pinctrl_dev_from_dev(struct device *dev, - const char *devname) +struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *devname) { struct pinctrl_dev *pctldev = NULL; bool found = false; - mutex_lock(&pinctrldev_list_mutex); - list_for_each_entry(pctldev, &pinctrldev_list, node) { - if (dev && pctldev->dev == dev) { - /* Matched on device pointer */ - found = true; - break; - } + if (!devname) + return NULL; - if (devname && - !strcmp(dev_name(pctldev->dev), devname)) { + list_for_each_entry(pctldev, &pinctrldev_list, node) { + if (!strcmp(dev_name(pctldev->dev), devname)) { /* Matched on device name */ found = true; break; } } - mutex_unlock(&pinctrldev_list_mutex); return found ? pctldev : NULL; } -struct pin_desc *pin_desc_get(struct pinctrl_dev *pctldev, unsigned int pin) -{ - struct pin_desc *pindesc; - unsigned long flags; - - spin_lock_irqsave(&pctldev->pin_desc_tree_lock, flags); - pindesc = radix_tree_lookup(&pctldev->pin_desc_tree, pin); - spin_unlock_irqrestore(&pctldev->pin_desc_tree_lock, flags); - - return pindesc; -} - /** * pin_get_from_name() - look up a pin number from a name * @pctldev: the pin control device to lookup the pin on @@ -135,11 +138,11 @@ bool pin_is_valid(struct pinctrl_dev *pctldev, int pin) if (pin < 0) return false; + mutex_lock(&pinctrl_mutex); pindesc = pin_desc_get(pctldev, pin); - if (pindesc == NULL) - return false; + mutex_unlock(&pinctrl_mutex); - return true; + return pindesc != NULL; } EXPORT_SYMBOL_GPL(pin_is_valid); @@ -150,7 +153,6 @@ static void pinctrl_free_pindescs(struct pinctrl_dev *pctldev, { int i; - spin_lock(&pctldev->pin_desc_tree_lock); for (i = 0; i < num_pins; i++) { struct pin_desc *pindesc; @@ -164,7 +166,6 @@ static void pinctrl_free_pindescs(struct pinctrl_dev *pctldev, } kfree(pindesc); } - spin_unlock(&pctldev->pin_desc_tree_lock); } static int pinctrl_register_one_pin(struct pinctrl_dev *pctldev, @@ -180,10 +181,10 @@ static int pinctrl_register_one_pin(struct pinctrl_dev *pctldev, } pindesc = kzalloc(sizeof(*pindesc), GFP_KERNEL); - if (pindesc == NULL) + if (pindesc == NULL) { + dev_err(pctldev->dev, "failed to alloc struct pin_desc\n"); return -ENOMEM; - - spin_lock_init(&pindesc->lock); + } /* Set owner */ pindesc->pctldev = pctldev; @@ -198,9 +199,7 @@ static int pinctrl_register_one_pin(struct pinctrl_dev *pctldev, pindesc->dynamic_name = true; } - spin_lock(&pctldev->pin_desc_tree_lock); radix_tree_insert(&pctldev->pin_desc_tree, number, pindesc); - spin_unlock(&pctldev->pin_desc_tree_lock); pr_debug("registered pin %d (%s) on %s\n", number, pindesc->name, pctldev->desc->name); return 0; @@ -237,16 +236,13 @@ pinctrl_match_gpio_range(struct pinctrl_dev *pctldev, unsigned gpio) struct pinctrl_gpio_range *range = NULL; /* Loop over the ranges */ - mutex_lock(&pctldev->gpio_ranges_lock); list_for_each_entry(range, &pctldev->gpio_ranges, node) { /* Check if we're in the valid range */ if (gpio >= range->base && gpio < range->base + range->npins) { - mutex_unlock(&pctldev->gpio_ranges_lock); return range; } } - mutex_unlock(&pctldev->gpio_ranges_lock); return NULL; } @@ -261,14 +257,13 @@ pinctrl_match_gpio_range(struct pinctrl_dev *pctldev, unsigned gpio) * the GPIO subsystem, return the device and the matching GPIO range. Returns * negative if the GPIO range could not be found in any device. */ -int pinctrl_get_device_gpio_range(unsigned gpio, - struct pinctrl_dev **outdev, - struct pinctrl_gpio_range **outrange) +static int pinctrl_get_device_gpio_range(unsigned gpio, + struct pinctrl_dev **outdev, + struct pinctrl_gpio_range **outrange) { struct pinctrl_dev *pctldev = NULL; /* Loop over the pin controllers */ - mutex_lock(&pinctrldev_list_mutex); list_for_each_entry(pctldev, &pinctrldev_list, node) { struct pinctrl_gpio_range *range; @@ -276,11 +271,9 @@ int pinctrl_get_device_gpio_range(unsigned gpio, if (range != NULL) { *outdev = pctldev; *outrange = range; - mutex_unlock(&pinctrldev_list_mutex); return 0; } } - mutex_unlock(&pinctrldev_list_mutex); return -EINVAL; } @@ -296,10 +289,11 @@ int pinctrl_get_device_gpio_range(unsigned gpio, void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range) { - mutex_lock(&pctldev->gpio_ranges_lock); - list_add(&range->node, &pctldev->gpio_ranges); - mutex_unlock(&pctldev->gpio_ranges_lock); + mutex_lock(&pinctrl_mutex); + list_add_tail(&range->node, &pctldev->gpio_ranges); + mutex_unlock(&pinctrl_mutex); } +EXPORT_SYMBOL_GPL(pinctrl_add_gpio_range); /** * pinctrl_remove_gpio_range() - remove a range of GPIOs fro a pin controller @@ -309,10 +303,11 @@ void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev, void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range) { - mutex_lock(&pctldev->gpio_ranges_lock); + mutex_lock(&pinctrl_mutex); list_del(&range->node); - mutex_unlock(&pctldev->gpio_ranges_lock); + mutex_unlock(&pinctrl_mutex); } +EXPORT_SYMBOL_GPL(pinctrl_remove_gpio_range); /** * pinctrl_get_group_selector() - returns the group selector for a group @@ -345,6 +340,531 @@ int pinctrl_get_group_selector(struct pinctrl_dev *pctldev, return -EINVAL; } +/** + * pinctrl_request_gpio() - request a single pin to be used in as GPIO + * @gpio: the GPIO pin number from the GPIO subsystem number space + * + * This function should *ONLY* be used from gpiolib-based GPIO drivers, + * as part of their gpio_request() semantics, platforms and individual drivers + * shall *NOT* request GPIO pins to be muxed in. + */ +int pinctrl_request_gpio(unsigned gpio) +{ + struct pinctrl_dev *pctldev; + struct pinctrl_gpio_range *range; + int ret; + int pin; + + mutex_lock(&pinctrl_mutex); + + ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); + if (ret) { + mutex_unlock(&pinctrl_mutex); + return -EINVAL; + } + + /* Convert to the pin controllers number space */ + pin = gpio - range->base + range->pin_base; + + ret = pinmux_request_gpio(pctldev, range, pin, gpio); + + mutex_unlock(&pinctrl_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(pinctrl_request_gpio); + +/** + * pinctrl_free_gpio() - free control on a single pin, currently used as GPIO + * @gpio: the GPIO pin number from the GPIO subsystem number space + * + * This function should *ONLY* be used from gpiolib-based GPIO drivers, + * as part of their gpio_free() semantics, platforms and individual drivers + * shall *NOT* request GPIO pins to be muxed out. + */ +void pinctrl_free_gpio(unsigned gpio) +{ + struct pinctrl_dev *pctldev; + struct pinctrl_gpio_range *range; + int ret; + int pin; + + mutex_lock(&pinctrl_mutex); + + ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); + if (ret) { + mutex_unlock(&pinctrl_mutex); + return; + } + + /* Convert to the pin controllers number space */ + pin = gpio - range->base + range->pin_base; + + pinmux_free_gpio(pctldev, pin, range); + + mutex_unlock(&pinctrl_mutex); +} +EXPORT_SYMBOL_GPL(pinctrl_free_gpio); + +static int pinctrl_gpio_direction(unsigned gpio, bool input) +{ + struct pinctrl_dev *pctldev; + struct pinctrl_gpio_range *range; + int ret; + int pin; + + ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); + if (ret) + return ret; + + /* Convert to the pin controllers number space */ + pin = gpio - range->base + range->pin_base; + + return pinmux_gpio_direction(pctldev, range, pin, input); +} + +/** + * pinctrl_gpio_direction_input() - request a GPIO pin to go into input mode + * @gpio: the GPIO pin number from the GPIO subsystem number space + * + * This function should *ONLY* be used from gpiolib-based GPIO drivers, + * as part of their gpio_direction_input() semantics, platforms and individual + * drivers shall *NOT* touch pin control GPIO calls. + */ +int pinctrl_gpio_direction_input(unsigned gpio) +{ + int ret; + mutex_lock(&pinctrl_mutex); + ret = pinctrl_gpio_direction(gpio, true); + mutex_unlock(&pinctrl_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_input); + +/** + * pinctrl_gpio_direction_output() - request a GPIO pin to go into output mode + * @gpio: the GPIO pin number from the GPIO subsystem number space + * + * This function should *ONLY* be used from gpiolib-based GPIO drivers, + * as part of their gpio_direction_output() semantics, platforms and individual + * drivers shall *NOT* touch pin control GPIO calls. + */ +int pinctrl_gpio_direction_output(unsigned gpio) +{ + int ret; + mutex_lock(&pinctrl_mutex); + ret = pinctrl_gpio_direction(gpio, false); + mutex_unlock(&pinctrl_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_output); + +static struct pinctrl_state *find_state(struct pinctrl *p, + const char *name) +{ + struct pinctrl_state *state; + + list_for_each_entry(state, &p->states, node) + if (!strcmp(state->name, name)) + return state; + + return NULL; +} + +static struct pinctrl_state *create_state(struct pinctrl *p, + const char *name) +{ + struct pinctrl_state *state; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state == NULL) { + dev_err(p->dev, + "failed to alloc struct pinctrl_state\n"); + return ERR_PTR(-ENOMEM); + } + + state->name = name; + INIT_LIST_HEAD(&state->settings); + + list_add_tail(&state->node, &p->states); + + return state; +} + +static int add_setting(struct pinctrl *p, struct pinctrl_map const *map) +{ + struct pinctrl_state *state; + struct pinctrl_setting *setting; + int ret; + + state = find_state(p, map->name); + if (!state) + state = create_state(p, map->name); + if (IS_ERR(state)) + return PTR_ERR(state); + + if (map->type == PIN_MAP_TYPE_DUMMY_STATE) + return 0; + + setting = kzalloc(sizeof(*setting), GFP_KERNEL); + if (setting == NULL) { + dev_err(p->dev, + "failed to alloc struct pinctrl_setting\n"); + return -ENOMEM; + } + + setting->type = map->type; + + setting->pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name); + if (setting->pctldev == NULL) { + dev_err(p->dev, "unknown pinctrl device %s in map entry", + map->ctrl_dev_name); + kfree(setting); + /* Eventually, this should trigger deferred probe */ + return -ENODEV; + } + + switch (map->type) { + case PIN_MAP_TYPE_MUX_GROUP: + ret = pinmux_map_to_setting(map, setting); + break; + case PIN_MAP_TYPE_CONFIGS_PIN: + case PIN_MAP_TYPE_CONFIGS_GROUP: + ret = pinconf_map_to_setting(map, setting); + break; + default: + ret = -EINVAL; + break; + } + if (ret < 0) { + kfree(setting); + return ret; + } + + list_add_tail(&setting->node, &state->settings); + + return 0; +} + +static struct pinctrl *find_pinctrl(struct device *dev) +{ + struct pinctrl *p; + + list_for_each_entry(p, &pinctrl_list, node) + if (p->dev == dev) + return p; + + return NULL; +} + +static void pinctrl_put_locked(struct pinctrl *p, bool inlist); + +static struct pinctrl *create_pinctrl(struct device *dev) +{ + struct pinctrl *p; + const char *devname; + struct pinctrl_maps *maps_node; + int i; + struct pinctrl_map const *map; + int ret; + + /* + * create the state cookie holder struct pinctrl for each + * mapping, this is what consumers will get when requesting + * a pin control handle with pinctrl_get() + */ + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (p == NULL) { + dev_err(dev, "failed to alloc struct pinctrl\n"); + return ERR_PTR(-ENOMEM); + } + p->dev = dev; + INIT_LIST_HEAD(&p->states); + + devname = dev_name(dev); + + /* Iterate over the pin control maps to locate the right ones */ + for_each_maps(maps_node, i, map) { + /* Map must be for this device */ + if (strcmp(map->dev_name, devname)) + continue; + + ret = add_setting(p, map); + if (ret < 0) { + pinctrl_put_locked(p, false); + return ERR_PTR(ret); + } + } + + /* Add the pinmux to the global list */ + list_add_tail(&p->node, &pinctrl_list); + + return p; +} + +static struct pinctrl *pinctrl_get_locked(struct device *dev) +{ + struct pinctrl *p; + + if (WARN_ON(!dev)) + return ERR_PTR(-EINVAL); + + p = find_pinctrl(dev); + if (p != NULL) + return ERR_PTR(-EBUSY); + + p = create_pinctrl(dev); + if (IS_ERR(p)) + return p; + + return p; +} + +/** + * pinctrl_get() - retrieves the pinctrl handle for a device + * @dev: the device to obtain the handle for + */ +struct pinctrl *pinctrl_get(struct device *dev) +{ + struct pinctrl *p; + + mutex_lock(&pinctrl_mutex); + p = pinctrl_get_locked(dev); + mutex_unlock(&pinctrl_mutex); + + return p; +} +EXPORT_SYMBOL_GPL(pinctrl_get); + +static void pinctrl_put_locked(struct pinctrl *p, bool inlist) +{ + struct pinctrl_state *state, *n1; + struct pinctrl_setting *setting, *n2; + + list_for_each_entry_safe(state, n1, &p->states, node) { + list_for_each_entry_safe(setting, n2, &state->settings, node) { + switch (setting->type) { + case PIN_MAP_TYPE_MUX_GROUP: + if (state == p->state) + pinmux_disable_setting(setting); + pinmux_free_setting(setting); + break; + case PIN_MAP_TYPE_CONFIGS_PIN: + case PIN_MAP_TYPE_CONFIGS_GROUP: + pinconf_free_setting(setting); + break; + default: + break; + } + list_del(&setting->node); + kfree(setting); + } + list_del(&state->node); + kfree(state); + } + + if (inlist) + list_del(&p->node); + kfree(p); +} + +/** + * pinctrl_put() - release a previously claimed pinctrl handle + * @p: the pinctrl handle to release + */ +void pinctrl_put(struct pinctrl *p) +{ + mutex_lock(&pinctrl_mutex); + pinctrl_put_locked(p, true); + mutex_unlock(&pinctrl_mutex); +} +EXPORT_SYMBOL_GPL(pinctrl_put); + +static struct pinctrl_state *pinctrl_lookup_state_locked(struct pinctrl *p, + const char *name) +{ + struct pinctrl_state *state; + + state = find_state(p, name); + if (!state) + return ERR_PTR(-ENODEV); + + return state; +} + +/** + * pinctrl_lookup_state() - retrieves a state handle from a pinctrl handle + * @p: the pinctrl handle to retrieve the state from + * @name: the state name to retrieve + */ +struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char *name) +{ + struct pinctrl_state *s; + + mutex_lock(&pinctrl_mutex); + s = pinctrl_lookup_state_locked(p, name); + mutex_unlock(&pinctrl_mutex); + + return s; +} +EXPORT_SYMBOL_GPL(pinctrl_lookup_state); + +static int pinctrl_select_state_locked(struct pinctrl *p, + struct pinctrl_state *state) +{ + struct pinctrl_setting *setting, *setting2; + int ret; + + if (p->state == state) + return 0; + + if (p->state) { + /* + * The set of groups with a mux configuration in the old state + * may not be identical to the set of groups with a mux setting + * in the new state. While this might be unusual, it's entirely + * possible for the "user"-supplied mapping table to be written + * that way. For each group that was configured in the old state + * but not in the new state, this code puts that group into a + * safe/disabled state. + */ + list_for_each_entry(setting, &p->state->settings, node) { + bool found = false; + if (setting->type != PIN_MAP_TYPE_MUX_GROUP) + continue; + list_for_each_entry(setting2, &state->settings, node) { + if (setting2->type != PIN_MAP_TYPE_MUX_GROUP) + continue; + if (setting2->data.mux.group == + setting->data.mux.group) { + found = true; + break; + } + } + if (!found) + pinmux_disable_setting(setting); + } + } + + p->state = state; + + /* Apply all the settings for the new state */ + list_for_each_entry(setting, &state->settings, node) { + switch (setting->type) { + case PIN_MAP_TYPE_MUX_GROUP: + ret = pinmux_enable_setting(setting); + break; + case PIN_MAP_TYPE_CONFIGS_PIN: + case PIN_MAP_TYPE_CONFIGS_GROUP: + ret = pinconf_apply_setting(setting); + break; + default: + ret = -EINVAL; + break; + } + if (ret < 0) { + /* FIXME: Difficult to return to prev state */ + return ret; + } + } + + return 0; +} + +/** + * pinctrl_select() - select/activate/program a pinctrl state to HW + * @p: the pinctrl handle for the device that requests configuratio + * @state: the state handle to select/activate/program + */ +int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state) +{ + int ret; + + mutex_lock(&pinctrl_mutex); + ret = pinctrl_select_state_locked(p, state); + mutex_unlock(&pinctrl_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(pinctrl_select_state); + +/** + * pinctrl_register_mappings() - register a set of pin controller mappings + * @maps: the pincontrol mappings table to register. This should probably be + * marked with __initdata so it can be discarded after boot. This + * function will perform a shallow copy for the mapping entries. + * @num_maps: the number of maps in the mapping table + */ +int pinctrl_register_mappings(struct pinctrl_map const *maps, + unsigned num_maps) +{ + int i, ret; + struct pinctrl_maps *maps_node; + + pr_debug("add %d pinmux maps\n", num_maps); + + /* First sanity check the new mapping */ + for (i = 0; i < num_maps; i++) { + if (!maps[i].dev_name) { + pr_err("failed to register map %s (%d): no device given\n", + maps[i].name, i); + return -EINVAL; + } + + if (!maps[i].name) { + pr_err("failed to register map %d: no map name given\n", + i); + return -EINVAL; + } + + if (maps[i].type != PIN_MAP_TYPE_DUMMY_STATE && + !maps[i].ctrl_dev_name) { + pr_err("failed to register map %s (%d): no pin control device given\n", + maps[i].name, i); + return -EINVAL; + } + + switch (maps[i].type) { + case PIN_MAP_TYPE_DUMMY_STATE: + break; + case PIN_MAP_TYPE_MUX_GROUP: + ret = pinmux_validate_map(&maps[i], i); + if (ret < 0) + return 0; + break; + case PIN_MAP_TYPE_CONFIGS_PIN: + case PIN_MAP_TYPE_CONFIGS_GROUP: + ret = pinconf_validate_map(&maps[i], i); + if (ret < 0) + return 0; + break; + default: + pr_err("failed to register map %s (%d): invalid type given\n", + maps[i].name, i); + return -EINVAL; + } + } + + maps_node = kzalloc(sizeof(*maps_node), GFP_KERNEL); + if (!maps_node) { + pr_err("failed to alloc struct pinctrl_maps\n"); + return -ENOMEM; + } + + maps_node->num_maps = num_maps; + maps_node->maps = kmemdup(maps, sizeof(*maps) * num_maps, GFP_KERNEL); + if (!maps_node->maps) { + pr_err("failed to duplicate mapping table\n"); + kfree(maps_node); + return -ENOMEM; + } + + mutex_lock(&pinctrl_mutex); + list_add_tail(&maps_node->node, &pinctrl_maps); + mutex_unlock(&pinctrl_mutex); + + return 0; +} + #ifdef CONFIG_DEBUG_FS static int pinctrl_pins_show(struct seq_file *s, void *what) @@ -355,6 +875,8 @@ static int pinctrl_pins_show(struct seq_file *s, void *what) seq_printf(s, "registered pins: %d\n", pctldev->desc->npins); + mutex_lock(&pinctrl_mutex); + /* The pin number can be retrived from the pin controller descriptor */ for (i = 0; i < pctldev->desc->npins; i++) { struct pin_desc *desc; @@ -375,6 +897,8 @@ static int pinctrl_pins_show(struct seq_file *s, void *what) seq_puts(s, "\n"); } + mutex_unlock(&pinctrl_mutex); + return 0; } @@ -388,6 +912,8 @@ static int pinctrl_groups_show(struct seq_file *s, void *what) if (!ops) return 0; + mutex_lock(&pinctrl_mutex); + seq_puts(s, "registered pin groups:\n"); while (ops->list_groups(pctldev, selector) >= 0) { const unsigned *pins; @@ -410,6 +936,7 @@ static int pinctrl_groups_show(struct seq_file *s, void *what) selector++; } + mutex_unlock(&pinctrl_mutex); return 0; } @@ -421,8 +948,9 @@ static int pinctrl_gpioranges_show(struct seq_file *s, void *what) seq_puts(s, "GPIO ranges handled:\n"); + mutex_lock(&pinctrl_mutex); + /* Loop over the ranges */ - mutex_lock(&pctldev->gpio_ranges_lock); list_for_each_entry(range, &pctldev->gpio_ranges, node) { seq_printf(s, "%u: %s GPIOS [%u - %u] PINS [%u - %u]\n", range->id, range->name, @@ -430,7 +958,8 @@ static int pinctrl_gpioranges_show(struct seq_file *s, void *what) range->pin_base, (range->pin_base + range->npins - 1)); } - mutex_unlock(&pctldev->gpio_ranges_lock); + + mutex_unlock(&pinctrl_mutex); return 0; } @@ -440,7 +969,9 @@ static int pinctrl_devices_show(struct seq_file *s, void *what) struct pinctrl_dev *pctldev; seq_puts(s, "name [pinmux] [pinconf]\n"); - mutex_lock(&pinctrldev_list_mutex); + + mutex_lock(&pinctrl_mutex); + list_for_each_entry(pctldev, &pinctrldev_list, node) { seq_printf(s, "%s ", pctldev->desc->name); if (pctldev->desc->pmxops) @@ -453,7 +984,108 @@ static int pinctrl_devices_show(struct seq_file *s, void *what) seq_puts(s, "no"); seq_puts(s, "\n"); } - mutex_unlock(&pinctrldev_list_mutex); + + mutex_unlock(&pinctrl_mutex); + + return 0; +} + +static inline const char *map_type(enum pinctrl_map_type type) +{ + static const char * const names[] = { + "INVALID", + "DUMMY_STATE", + "MUX_GROUP", + "CONFIGS_PIN", + "CONFIGS_GROUP", + }; + + if (type >= ARRAY_SIZE(names)) + return "UNKNOWN"; + + return names[type]; +} + +static int pinctrl_maps_show(struct seq_file *s, void *what) +{ + struct pinctrl_maps *maps_node; + int i; + struct pinctrl_map const *map; + + seq_puts(s, "Pinctrl maps:\n"); + + mutex_lock(&pinctrl_mutex); + + for_each_maps(maps_node, i, map) { + seq_printf(s, "device %s\nstate %s\ntype %s (%d)\n", + map->dev_name, map->name, map_type(map->type), + map->type); + + if (map->type != PIN_MAP_TYPE_DUMMY_STATE) + seq_printf(s, "controlling device %s\n", + map->ctrl_dev_name); + + switch (map->type) { + case PIN_MAP_TYPE_MUX_GROUP: + pinmux_show_map(s, map); + break; + case PIN_MAP_TYPE_CONFIGS_PIN: + case PIN_MAP_TYPE_CONFIGS_GROUP: + pinconf_show_map(s, map); + break; + default: + break; + } + + seq_printf(s, "\n"); + } + + mutex_unlock(&pinctrl_mutex); + + return 0; +} + +static int pinctrl_show(struct seq_file *s, void *what) +{ + struct pinctrl *p; + struct pinctrl_state *state; + struct pinctrl_setting *setting; + + seq_puts(s, "Requested pin control handlers their pinmux maps:\n"); + + mutex_lock(&pinctrl_mutex); + + list_for_each_entry(p, &pinctrl_list, node) { + seq_printf(s, "device: %s current state: %s\n", + dev_name(p->dev), + p->state ? p->state->name : "none"); + + list_for_each_entry(state, &p->states, node) { + seq_printf(s, " state: %s\n", state->name); + + list_for_each_entry(setting, &state->settings, node) { + struct pinctrl_dev *pctldev = setting->pctldev; + + seq_printf(s, " type: %s controller %s ", + map_type(setting->type), + pinctrl_dev_get_name(pctldev)); + + switch (setting->type) { + case PIN_MAP_TYPE_MUX_GROUP: + pinmux_show_setting(s, setting); + break; + case PIN_MAP_TYPE_CONFIGS_PIN: + case PIN_MAP_TYPE_CONFIGS_GROUP: + pinconf_show_setting(s, setting); + break; + default: + break; + } + } + } + } + + mutex_unlock(&pinctrl_mutex); return 0; } @@ -478,6 +1110,16 @@ static int pinctrl_devices_open(struct inode *inode, struct file *file) return single_open(file, pinctrl_devices_show, NULL); } +static int pinctrl_maps_open(struct inode *inode, struct file *file) +{ + return single_open(file, pinctrl_maps_show, NULL); +} + +static int pinctrl_open(struct inode *inode, struct file *file) +{ + return single_open(file, pinctrl_show, NULL); +} + static const struct file_operations pinctrl_pins_ops = { .open = pinctrl_pins_open, .read = seq_read, @@ -506,6 +1148,20 @@ static const struct file_operations pinctrl_devices_ops = { .release = single_release, }; +static const struct file_operations pinctrl_maps_ops = { + .open = pinctrl_maps_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations pinctrl_ops = { + .open = pinctrl_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static struct dentry *debugfs_root; static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev) @@ -547,7 +1203,10 @@ static void pinctrl_init_debugfs(void) debugfs_create_file("pinctrl-devices", S_IFREG | S_IRUGO, debugfs_root, NULL, &pinctrl_devices_ops); - pinmux_init_debugfs(debugfs_root); + debugfs_create_file("pinctrl-maps", S_IFREG | S_IRUGO, + debugfs_root, NULL, &pinctrl_maps_ops); + debugfs_create_file("pinctrl-handles", S_IFREG | S_IRUGO, + debugfs_root, NULL, &pinctrl_ops); } #else /* CONFIG_DEBUG_FS */ @@ -583,18 +1242,18 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, if (pctldesc->name == NULL) return NULL; - pctldev = kzalloc(sizeof(struct pinctrl_dev), GFP_KERNEL); - if (pctldev == NULL) + pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL); + if (pctldev == NULL) { + dev_err(dev, "failed to alloc struct pinctrl_dev\n"); return NULL; + } /* Initialize pin control device struct */ pctldev->owner = pctldesc->owner; pctldev->desc = pctldesc; pctldev->driver_data = driver_data; INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL); - spin_lock_init(&pctldev->pin_desc_tree_lock); INIT_LIST_HEAD(&pctldev->gpio_ranges); - mutex_init(&pctldev->gpio_ranges_lock); pctldev->dev = dev; /* If we're implementing pinmuxing, check the ops for sanity */ @@ -628,11 +1287,23 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, goto out_err; } + mutex_lock(&pinctrl_mutex); + + list_add_tail(&pctldev->node, &pinctrldev_list); + + pctldev->p = pinctrl_get_locked(pctldev->dev); + if (!IS_ERR(pctldev->p)) { + struct pinctrl_state *s = + pinctrl_lookup_state_locked(pctldev->p, + PINCTRL_STATE_DEFAULT); + if (!IS_ERR(s)) + pinctrl_select_state_locked(pctldev->p, s); + } + + mutex_unlock(&pinctrl_mutex); + pinctrl_init_device_debugfs(pctldev); - mutex_lock(&pinctrldev_list_mutex); - list_add(&pctldev->node, &pinctrldev_list); - mutex_unlock(&pinctrldev_list_mutex); - pinmux_hog_maps(pctldev); + return pctldev; out_err: @@ -653,15 +1324,20 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) return; pinctrl_remove_device_debugfs(pctldev); - pinmux_unhog_maps(pctldev); + + mutex_lock(&pinctrl_mutex); + + if (!IS_ERR(pctldev->p)) + pinctrl_put_locked(pctldev->p, true); + /* TODO: check that no pinmuxes are still active? */ - mutex_lock(&pinctrldev_list_mutex); list_del(&pctldev->node); - mutex_unlock(&pinctrldev_list_mutex); /* Destroy descriptor tree */ pinctrl_free_pindescs(pctldev, pctldev->desc->pins, pctldev->desc->npins); kfree(pctldev); + + mutex_unlock(&pinctrl_mutex); } EXPORT_SYMBOL_GPL(pinctrl_unregister); diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h index cfa86da6b4b1..17ecf651b123 100644 --- a/drivers/pinctrl/core.h +++ b/drivers/pinctrl/core.h @@ -9,7 +9,10 @@ * License terms: GNU General Public License (GPL) version 2 */ +#include <linux/mutex.h> +#include <linux/radix-tree.h> #include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/machine.h> struct pinctrl_gpio_range; @@ -20,34 +23,94 @@ struct pinctrl_gpio_range; * controller * @pin_desc_tree: each pin descriptor for this pin controller is stored in * this radix tree - * @pin_desc_tree_lock: lock for the descriptor tree * @gpio_ranges: a list of GPIO ranges that is handled by this pin controller, * ranges are added to this list at runtime - * @gpio_ranges_lock: lock for the GPIO ranges list * @dev: the device entry for this pin controller * @owner: module providing the pin controller, used for refcounting * @driver_data: driver data for drivers registering to the pin controller * subsystem - * @pinmux_hogs_lock: lock for the pinmux hog list - * @pinmux_hogs: list of pinmux maps hogged by this device + * @p: result of pinctrl_get() for this device + * @device_root: debugfs root for this device */ struct pinctrl_dev { struct list_head node; struct pinctrl_desc *desc; struct radix_tree_root pin_desc_tree; - spinlock_t pin_desc_tree_lock; struct list_head gpio_ranges; - struct mutex gpio_ranges_lock; struct device *dev; struct module *owner; void *driver_data; + struct pinctrl *p; #ifdef CONFIG_DEBUG_FS struct dentry *device_root; #endif -#ifdef CONFIG_PINMUX - struct mutex pinmux_hogs_lock; - struct list_head pinmux_hogs; -#endif +}; + +/** + * struct pinctrl - per-device pin control state holder + * @node: global list node + * @dev: the device using this pin control handle + * @states: a list of states for this device + * @state: the current state + */ +struct pinctrl { + struct list_head node; + struct device *dev; + struct list_head states; + struct pinctrl_state *state; +}; + +/** + * struct pinctrl_state - a pinctrl state for a device + * @node: list not for struct pinctrl's @states field + * @name: the name of this state + * @settings: a list of settings for this state + */ +struct pinctrl_state { + struct list_head node; + const char *name; + struct list_head settings; +}; + +/** + * struct pinctrl_setting_mux - setting data for MAP_TYPE_MUX_GROUP + * @group: the group selector to program + * @func: the function selector to program + */ +struct pinctrl_setting_mux { + unsigned group; + unsigned func; +}; + +/** + * struct pinctrl_setting_configs - setting data for MAP_TYPE_CONFIGS_* + * @group_or_pin: the group selector or pin ID to program + * @configs: a pointer to an array of config parameters/values to program into + * hardware. Each individual pin controller defines the format and meaning + * of config parameters. + * @num_configs: the number of entries in array @configs + */ +struct pinctrl_setting_configs { + unsigned group_or_pin; + unsigned long *configs; + unsigned num_configs; +}; + +/** + * struct pinctrl_setting - an individual mux or config setting + * @node: list node for struct pinctrl_settings's @settings field + * @type: the type of setting + * @pctldev: pin control device handling to be programmed + * @data: Data specific to the setting type + */ +struct pinctrl_setting { + struct list_head node; + enum pinctrl_map_type type; + struct pinctrl_dev *pctldev; + union { + struct pinctrl_setting_mux mux; + struct pinctrl_setting_configs configs; + } data; }; /** @@ -56,28 +119,38 @@ struct pinctrl_dev { * @name: a name for the pin, e.g. the name of the pin/pad/finger on a * datasheet or such * @dynamic_name: if the name of this pin was dynamically allocated - * @lock: a lock to protect the descriptor structure - * @mux_requested: whether the pin is already requested by pinmux or not - * @mux_function: a named muxing function for the pin that will be passed to - * subdrivers and shown in debugfs etc + * @mux_usecount: If zero, the pin is not claimed, and @owner should be NULL. + * If non-zero, this pin is claimed by @owner. This field is an integer + * rather than a boolean, since pinctrl_get() might process multiple + * mapping table entries that refer to, and hence claim, the same group + * or pin, and each of these will increment the @usecount. + * @mux_owner: The name of device that called pinctrl_get(). + * @mux_setting: The most recent selected mux setting for this pin, if any. + * @gpio_owner: If pinctrl_request_gpio() was called for this pin, this is + * the name of the GPIO that "owns" this pin. */ struct pin_desc { struct pinctrl_dev *pctldev; const char *name; bool dynamic_name; - spinlock_t lock; /* These fields only added when supporting pinmux drivers */ #ifdef CONFIG_PINMUX - const char *mux_function; + unsigned mux_usecount; + const char *mux_owner; + const struct pinctrl_setting_mux *mux_setting; + const char *gpio_owner; #endif }; -struct pinctrl_dev *get_pinctrl_dev_from_dev(struct device *dev, - const char *dev_name); -struct pin_desc *pin_desc_get(struct pinctrl_dev *pctldev, unsigned int pin); +struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *dev_name); int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name); -int pinctrl_get_device_gpio_range(unsigned gpio, - struct pinctrl_dev **outdev, - struct pinctrl_gpio_range **outrange); int pinctrl_get_group_selector(struct pinctrl_dev *pctldev, const char *pin_group); + +static inline struct pin_desc *pin_desc_get(struct pinctrl_dev *pctldev, + unsigned int pin) +{ + return radix_tree_lookup(&pctldev->pin_desc_tree, pin); +} + +extern struct mutex pinctrl_mutex; diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c new file mode 100644 index 000000000000..33fbaeaa65dd --- /dev/null +++ b/drivers/pinctrl/pinconf-generic.c @@ -0,0 +1,120 @@ +/* + * Core driver for the generic pin config portions of the pin control subsystem + * + * Copyright (C) 2011 ST-Ericsson SA + * Written on behalf of Linaro for ST-Ericsson + * + * Author: Linus Walleij <linus.walleij@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#define pr_fmt(fmt) "generic pinconfig core: " fmt + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> +#include "core.h" +#include "pinconf.h" + +#ifdef CONFIG_DEBUG_FS + +struct pin_config_item { + const enum pin_config_param param; + const char * const display; + const char * const format; +}; + +#define PCONFDUMP(a, b, c) { .param = a, .display = b, .format = c } + +struct pin_config_item conf_items[] = { + PCONFDUMP(PIN_CONFIG_BIAS_DISABLE, "input bias disabled", NULL), + PCONFDUMP(PIN_CONFIG_BIAS_HIGH_IMPEDANCE, "input bias high impedance", NULL), + PCONFDUMP(PIN_CONFIG_BIAS_PULL_UP, "input bias pull up", NULL), + PCONFDUMP(PIN_CONFIG_BIAS_PULL_DOWN, "input bias pull down", NULL), + PCONFDUMP(PIN_CONFIG_DRIVE_PUSH_PULL, "output drive push pull", NULL), + PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_DRAIN, "output drive open drain", NULL), + PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_SOURCE, "output drive open source", NULL), + PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT, "input schmitt trigger", NULL), + PCONFDUMP(PIN_CONFIG_INPUT_DEBOUNCE, "input debounce", "time units"), + PCONFDUMP(PIN_CONFIG_POWER_SOURCE, "pin power source", "selector"), + PCONFDUMP(PIN_CONFIG_LOW_POWER_MODE, "pin low power", "mode"), +}; + +void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned pin) +{ + const struct pinconf_ops *ops = pctldev->desc->confops; + int i; + + if (!ops->is_generic) + return; + + for(i = 0; i < ARRAY_SIZE(conf_items); i++) { + unsigned long config; + int ret; + + /* We want to check out this parameter */ + config = pinconf_to_config_packed(conf_items[i].param, 0); + ret = pin_config_get_for_pin(pctldev, pin, &config); + /* These are legal errors */ + if (ret == -EINVAL || ret == -ENOTSUPP) + continue; + if (ret) { + seq_printf(s, "ERROR READING CONFIG SETTING %d ", i); + continue; + } + /* Space between multiple configs */ + seq_puts(s, " "); + seq_puts(s, conf_items[i].display); + /* Print unit if available */ + if (conf_items[i].format && + pinconf_to_config_argument(config) != 0) + seq_printf(s, " (%u %s)", + pinconf_to_config_argument(config), + conf_items[i].format); + } +} + +void pinconf_generic_dump_group(struct pinctrl_dev *pctldev, + struct seq_file *s, const char *gname) +{ + const struct pinconf_ops *ops = pctldev->desc->confops; + int i; + + if (!ops->is_generic) + return; + + for(i = 0; i < ARRAY_SIZE(conf_items); i++) { + unsigned long config; + int ret; + + /* We want to check out this parameter */ + config = pinconf_to_config_packed(conf_items[i].param, 0); + ret = pin_config_group_get(dev_name(pctldev->dev), gname, + &config); + /* These are legal errors */ + if (ret == -EINVAL || ret == -ENOTSUPP) + continue; + if (ret) { + seq_printf(s, "ERROR READING CONFIG SETTING %d ", i); + continue; + } + /* Space between multiple configs */ + seq_puts(s, " "); + seq_puts(s, conf_items[i].display); + /* Print unit if available */ + if (conf_items[i].format && config != 0) + seq_printf(s, " (%u %s)", + pinconf_to_config_argument(config), + conf_items[i].format); + } +} + +#endif diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index 9fb75456824c..7321e8601294 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -23,6 +23,37 @@ #include "core.h" #include "pinconf.h" +int pinconf_check_ops(struct pinctrl_dev *pctldev) +{ + const struct pinconf_ops *ops = pctldev->desc->confops; + + /* We must be able to read out pin status */ + if (!ops->pin_config_get && !ops->pin_config_group_get) + return -EINVAL; + /* We have to be able to config the pins in SOME way */ + if (!ops->pin_config_set && !ops->pin_config_group_set) + return -EINVAL; + return 0; +} + +int pinconf_validate_map(struct pinctrl_map const *map, int i) +{ + if (!map->data.configs.group_or_pin) { + pr_err("failed to register map %s (%d): no group/pin given\n", + map->name, i); + return -EINVAL; + } + + if (map->data.configs.num_configs && + !map->data.configs.configs) { + pr_err("failed to register map %s (%d): no configs ptr given\n", + map->name, i); + return -EINVAL; + } + + return 0; +} + int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config) { @@ -51,19 +82,27 @@ int pin_config_get(const char *dev_name, const char *name, struct pinctrl_dev *pctldev; int pin; - pctldev = get_pinctrl_dev_from_dev(NULL, dev_name); - if (!pctldev) - return -EINVAL; + mutex_lock(&pinctrl_mutex); + + pctldev = get_pinctrl_dev_from_devname(dev_name); + if (!pctldev) { + pin = -EINVAL; + goto unlock; + } pin = pin_get_from_name(pctldev, name); if (pin < 0) - return pin; + goto unlock; + + pin = pin_config_get_for_pin(pctldev, pin, config); - return pin_config_get_for_pin(pctldev, pin, config); +unlock: + mutex_unlock(&pinctrl_mutex); + return pin; } EXPORT_SYMBOL(pin_config_get); -int pin_config_set_for_pin(struct pinctrl_dev *pctldev, unsigned pin, +static int pin_config_set_for_pin(struct pinctrl_dev *pctldev, unsigned pin, unsigned long config) { const struct pinconf_ops *ops = pctldev->desc->confops; @@ -97,17 +136,27 @@ int pin_config_set(const char *dev_name, const char *name, unsigned long config) { struct pinctrl_dev *pctldev; - int pin; + int pin, ret; - pctldev = get_pinctrl_dev_from_dev(NULL, dev_name); - if (!pctldev) - return -EINVAL; + mutex_lock(&pinctrl_mutex); + + pctldev = get_pinctrl_dev_from_devname(dev_name); + if (!pctldev) { + ret = -EINVAL; + goto unlock; + } pin = pin_get_from_name(pctldev, name); - if (pin < 0) - return pin; + if (pin < 0) { + ret = pin; + goto unlock; + } + + ret = pin_config_set_for_pin(pctldev, pin, config); - return pin_config_set_for_pin(pctldev, pin, config); +unlock: + mutex_unlock(&pinctrl_mutex); + return ret; } EXPORT_SYMBOL(pin_config_set); @@ -116,29 +165,39 @@ int pin_config_group_get(const char *dev_name, const char *pin_group, { struct pinctrl_dev *pctldev; const struct pinconf_ops *ops; - int selector; + int selector, ret; - pctldev = get_pinctrl_dev_from_dev(NULL, dev_name); - if (!pctldev) - return -EINVAL; + mutex_lock(&pinctrl_mutex); + + pctldev = get_pinctrl_dev_from_devname(dev_name); + if (!pctldev) { + ret = -EINVAL; + goto unlock; + } ops = pctldev->desc->confops; if (!ops || !ops->pin_config_group_get) { dev_err(pctldev->dev, "cannot get configuration for pin " "group, missing group config get function in " "driver\n"); - return -EINVAL; + ret = -EINVAL; + goto unlock; } selector = pinctrl_get_group_selector(pctldev, pin_group); - if (selector < 0) - return selector; + if (selector < 0) { + ret = selector; + goto unlock; + } + + ret = ops->pin_config_group_get(pctldev, selector, config); - return ops->pin_config_group_get(pctldev, selector, config); +unlock: + mutex_unlock(&pinctrl_mutex); + return ret; } EXPORT_SYMBOL(pin_config_group_get); - int pin_config_group_set(const char *dev_name, const char *pin_group, unsigned long config) { @@ -151,27 +210,34 @@ int pin_config_group_set(const char *dev_name, const char *pin_group, int ret; int i; - pctldev = get_pinctrl_dev_from_dev(NULL, dev_name); - if (!pctldev) - return -EINVAL; + mutex_lock(&pinctrl_mutex); + + pctldev = get_pinctrl_dev_from_devname(dev_name); + if (!pctldev) { + ret = -EINVAL; + goto unlock; + } ops = pctldev->desc->confops; pctlops = pctldev->desc->pctlops; if (!ops || (!ops->pin_config_group_set && !ops->pin_config_set)) { dev_err(pctldev->dev, "cannot configure pin group, missing " "config function in driver\n"); - return -EINVAL; + ret = -EINVAL; + goto unlock; } selector = pinctrl_get_group_selector(pctldev, pin_group); - if (selector < 0) - return selector; + if (selector < 0) { + ret = selector; + goto unlock; + } ret = pctlops->get_group_pins(pctldev, selector, &pins, &num_pins); if (ret) { dev_err(pctldev->dev, "cannot configure pin group, error " "getting pins\n"); - return ret; + goto unlock; } /* @@ -185,46 +251,196 @@ int pin_config_group_set(const char *dev_name, const char *pin_group, * pin-by-pin as well, it returns -EAGAIN. */ if (ret != -EAGAIN) - return ret; + goto unlock; } /* * If the controller cannot handle entire groups, we configure each pin * individually. */ - if (!ops->pin_config_set) - return 0; + if (!ops->pin_config_set) { + ret = 0; + goto unlock; + } for (i = 0; i < num_pins; i++) { ret = ops->pin_config_set(pctldev, pins[i], config); if (ret < 0) - return ret; + goto unlock; } - return 0; + ret = 0; + +unlock: + mutex_unlock(&pinctrl_mutex); + + return ret; } EXPORT_SYMBOL(pin_config_group_set); -int pinconf_check_ops(struct pinctrl_dev *pctldev) +int pinconf_map_to_setting(struct pinctrl_map const *map, + struct pinctrl_setting *setting) { + struct pinctrl_dev *pctldev = setting->pctldev; + int pin; + + switch (setting->type) { + case PIN_MAP_TYPE_CONFIGS_PIN: + pin = pin_get_from_name(pctldev, + map->data.configs.group_or_pin); + if (pin < 0) { + dev_err(pctldev->dev, "could not map pin config for \"%s\"", + map->data.configs.group_or_pin); + return pin; + } + setting->data.configs.group_or_pin = pin; + break; + case PIN_MAP_TYPE_CONFIGS_GROUP: + pin = pinctrl_get_group_selector(pctldev, + map->data.configs.group_or_pin); + if (pin < 0) { + dev_err(pctldev->dev, "could not map group config for \"%s\"", + map->data.configs.group_or_pin); + return pin; + } + setting->data.configs.group_or_pin = pin; + break; + default: + return -EINVAL; + } + + setting->data.configs.num_configs = map->data.configs.num_configs; + setting->data.configs.configs = map->data.configs.configs; + + return 0; +} + +void pinconf_free_setting(struct pinctrl_setting const *setting) +{ +} + +int pinconf_apply_setting(struct pinctrl_setting const *setting) +{ + struct pinctrl_dev *pctldev = setting->pctldev; const struct pinconf_ops *ops = pctldev->desc->confops; + int i, ret; - /* We must be able to read out pin status */ - if (!ops->pin_config_get && !ops->pin_config_group_get) + if (!ops) { + dev_err(pctldev->dev, "missing confops\n"); return -EINVAL; - /* We have to be able to config the pins in SOME way */ - if (!ops->pin_config_set && !ops->pin_config_group_set) + } + + switch (setting->type) { + case PIN_MAP_TYPE_CONFIGS_PIN: + if (!ops->pin_config_set) { + dev_err(pctldev->dev, "missing pin_config_set op\n"); + return -EINVAL; + } + for (i = 0; i < setting->data.configs.num_configs; i++) { + ret = ops->pin_config_set(pctldev, + setting->data.configs.group_or_pin, + setting->data.configs.configs[i]); + if (ret < 0) { + dev_err(pctldev->dev, + "pin_config_set op failed for pin %d config %08lx\n", + setting->data.configs.group_or_pin, + setting->data.configs.configs[i]); + return ret; + } + } + break; + case PIN_MAP_TYPE_CONFIGS_GROUP: + if (!ops->pin_config_group_set) { + dev_err(pctldev->dev, + "missing pin_config_group_set op\n"); + return -EINVAL; + } + for (i = 0; i < setting->data.configs.num_configs; i++) { + ret = ops->pin_config_group_set(pctldev, + setting->data.configs.group_or_pin, + setting->data.configs.configs[i]); + if (ret < 0) { + dev_err(pctldev->dev, + "pin_config_group_set op failed for group %d config %08lx\n", + setting->data.configs.group_or_pin, + setting->data.configs.configs[i]); + return ret; + } + } + break; + default: return -EINVAL; + } + return 0; } #ifdef CONFIG_DEBUG_FS +void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map) +{ + int i; + + switch (map->type) { + case PIN_MAP_TYPE_CONFIGS_PIN: + seq_printf(s, "pin "); + break; + case PIN_MAP_TYPE_CONFIGS_GROUP: + seq_printf(s, "group "); + break; + default: + break; + } + + seq_printf(s, "%s\n", map->data.configs.group_or_pin); + + for (i = 0; i < map->data.configs.num_configs; i++) + seq_printf(s, "config %08lx\n", map->data.configs.configs[i]); +} + +void pinconf_show_setting(struct seq_file *s, + struct pinctrl_setting const *setting) +{ + struct pinctrl_dev *pctldev = setting->pctldev; + const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; + struct pin_desc *desc; + int i; + + switch (setting->type) { + case PIN_MAP_TYPE_CONFIGS_PIN: + desc = pin_desc_get(setting->pctldev, + setting->data.configs.group_or_pin); + seq_printf(s, "pin %s (%d)", + desc->name ? desc->name : "unnamed", + setting->data.configs.group_or_pin); + break; + case PIN_MAP_TYPE_CONFIGS_GROUP: + seq_printf(s, "group %s (%d)", + pctlops->get_group_name(pctldev, + setting->data.configs.group_or_pin), + setting->data.configs.group_or_pin); + break; + default: + break; + } + + /* + * FIXME: We should really get the pin controler to dump the config + * values, so they can be decoded to something meaningful. + */ + for (i = 0; i < setting->data.configs.num_configs; i++) + seq_printf(s, " %08lx", setting->data.configs.configs[i]); + + seq_printf(s, "\n"); +} + static void pinconf_dump_pin(struct pinctrl_dev *pctldev, struct seq_file *s, int pin) { const struct pinconf_ops *ops = pctldev->desc->confops; + /* no-op when not using generic pin config */ + pinconf_generic_dump_pin(pctldev, s, pin); if (ops && ops->pin_config_dbg_show) ops->pin_config_dbg_show(pctldev, s, pin); } @@ -237,6 +453,8 @@ static int pinconf_pins_show(struct seq_file *s, void *what) seq_puts(s, "Pin config settings per pin\n"); seq_puts(s, "Format: pin (name): pinmux setting array\n"); + mutex_lock(&pinctrl_mutex); + /* The pin number can be retrived from the pin controller descriptor */ for (i = 0; i < pctldev->desc->npins; i++) { struct pin_desc *desc; @@ -255,6 +473,8 @@ static int pinconf_pins_show(struct seq_file *s, void *what) seq_printf(s, "\n"); } + mutex_unlock(&pinctrl_mutex); + return 0; } @@ -264,6 +484,8 @@ static void pinconf_dump_group(struct pinctrl_dev *pctldev, { const struct pinconf_ops *ops = pctldev->desc->confops; + /* no-op when not using generic pin config */ + pinconf_generic_dump_group(pctldev, s, gname); if (ops && ops->pin_config_group_dbg_show) ops->pin_config_group_dbg_show(pctldev, s, selector); } @@ -281,14 +503,20 @@ static int pinconf_groups_show(struct seq_file *s, void *what) seq_puts(s, "Pin config settings per pin group\n"); seq_puts(s, "Format: group (name): pinmux setting array\n"); + mutex_lock(&pinctrl_mutex); + while (pctlops->list_groups(pctldev, selector) >= 0) { const char *gname = pctlops->get_group_name(pctldev, selector); seq_printf(s, "%u (%s):", selector, gname); pinconf_dump_group(pctldev, s, selector, gname); + seq_printf(s, "\n"); + selector++; } + mutex_unlock(&pinctrl_mutex); + return 0; } diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h index 006b77fa737e..54510de5e8c6 100644 --- a/drivers/pinctrl/pinconf.h +++ b/drivers/pinctrl/pinconf.h @@ -14,12 +14,25 @@ #ifdef CONFIG_PINCONF int pinconf_check_ops(struct pinctrl_dev *pctldev); +int pinconf_validate_map(struct pinctrl_map const *map, int i); +int pinconf_map_to_setting(struct pinctrl_map const *map, + struct pinctrl_setting *setting); +void pinconf_free_setting(struct pinctrl_setting const *setting); +int pinconf_apply_setting(struct pinctrl_setting const *setting); +void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map); +void pinconf_show_setting(struct seq_file *s, + struct pinctrl_setting const *setting); void pinconf_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev); + +/* + * You will only be interested in these if you're using PINCONF + * so don't supply any stubs for these. + */ int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config); -int pin_config_set_for_pin(struct pinctrl_dev *pctldev, unsigned pin, - unsigned long config); +int pin_config_group_get(const char *dev_name, const char *pin_group, + unsigned long *config); #else @@ -28,9 +41,70 @@ static inline int pinconf_check_ops(struct pinctrl_dev *pctldev) return 0; } +static inline int pinconf_validate_map(struct pinctrl_map const *map, int i) +{ + return 0; +} + +static inline int pinconf_map_to_setting(struct pinctrl_map const *map, + struct pinctrl_setting *setting) +{ + return 0; +} + +static inline void pinconf_free_setting(struct pinctrl_setting const *setting) +{ +} + +static inline int pinconf_apply_setting(struct pinctrl_setting const *setting) +{ + return 0; +} + +static inline void pinconf_show_map(struct seq_file *s, + struct pinctrl_map const *map) +{ +} + +static inline void pinconf_show_setting(struct seq_file *s, + struct pinctrl_setting const *setting) +{ +} + static inline void pinconf_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev) { } #endif + +/* + * The following functions are available if the driver uses the generic + * pin config. + */ + +#ifdef CONFIG_GENERIC_PINCONF + +void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned pin); + +void pinconf_generic_dump_group(struct pinctrl_dev *pctldev, + struct seq_file *s, const char *gname); + +#else + +static inline void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned pin) +{ + return; +} + +static inline void pinconf_generic_dump_group(struct pinctrl_dev *pctldev, + struct seq_file *s, + const char *gname) +{ + return; +} + +#endif diff --git a/drivers/pinctrl/pinctrl-coh901.c b/drivers/pinctrl/pinctrl-coh901.c index 69fb7072a23e..0797eba3e33a 100644 --- a/drivers/pinctrl/pinctrl-coh901.c +++ b/drivers/pinctrl/pinctrl-coh901.c @@ -22,38 +22,10 @@ #include <linux/gpio.h> #include <linux/list.h> #include <linux/slab.h> -#include <linux/pinctrl/pinmux.h> +#include <linux/pinctrl/consumer.h> +#include <linux/pinctrl/pinconf-generic.h> #include <mach/gpio-u300.h> - -/* - * Bias modes for U300 GPIOs - * - * GPIO_U300_CONFIG_BIAS_UNKNOWN: this bias mode is not known to us - * GPIO_U300_CONFIG_BIAS_FLOAT: no specific bias, the GPIO will float or state - * is not controlled by software - * GPIO_U300_CONFIG_BIAS_PULL_UP: the GPIO will be pulled up (usually with high - * impedance to VDD) - */ -#define GPIO_U300_CONFIG_BIAS_UNKNOWN 0x1000 -#define GPIO_U300_CONFIG_BIAS_FLOAT 0x1001 -#define GPIO_U300_CONFIG_BIAS_PULL_UP 0x1002 - -/* - * Drive modes for U300 GPIOs (output) - * - * GPIO_U300_CONFIG_DRIVE_PUSH_PULL: the GPIO will be driven actively high and - * low, this is the most typical case and is typically achieved with two - * active transistors on the output - * GPIO_U300_CONFIG_DRIVE_OPEN_DRAIN: the GPIO will be driven with open drain - * (open collector) which means it is usually wired with other output - * ports which are then pulled up with an external resistor - * GPIO_U300_CONFIG_DRIVE_OPEN_SOURCE: the GPIO will be driven with open drain - * (open emitter) which is the same as open drain mutatis mutandis but - * pulled to ground - */ -#define GPIO_U300_CONFIG_DRIVE_PUSH_PULL 0x2000 -#define GPIO_U300_CONFIG_DRIVE_OPEN_DRAIN 0x2001 -#define GPIO_U300_CONFIG_DRIVE_OPEN_SOURCE 0x2002 +#include "pinctrl-coh901.h" /* * Register definitions for COH 901 335 variant @@ -181,12 +153,12 @@ struct u300_gpio_confdata { #define BS365_GPIO_NUM_PORTS 5 #define U300_FLOATING_INPUT { \ - .bias_mode = GPIO_U300_CONFIG_BIAS_FLOAT, \ + .bias_mode = PIN_CONFIG_BIAS_HIGH_IMPEDANCE, \ .output = false, \ } #define U300_PULL_UP_INPUT { \ - .bias_mode = GPIO_U300_CONFIG_BIAS_PULL_UP, \ + .bias_mode = PIN_CONFIG_BIAS_PULL_UP, \ .output = false, \ } @@ -360,14 +332,14 @@ static int u300_gpio_request(struct gpio_chip *chip, unsigned offset) */ int gpio = chip->base + offset; - return pinmux_request_gpio(gpio); + return pinctrl_request_gpio(gpio); } static void u300_gpio_free(struct gpio_chip *chip, unsigned offset) { int gpio = chip->base + offset; - pinmux_free_gpio(gpio); + pinctrl_free_gpio(gpio); } static int u300_gpio_get(struct gpio_chip *chip, unsigned offset) @@ -448,8 +420,68 @@ static int u300_gpio_to_irq(struct gpio_chip *chip, unsigned offset) return retirq; } -static int u300_gpio_config(struct gpio_chip *chip, unsigned offset, - u16 param, unsigned long *data) +/* Returning -EINVAL means "supported but not available" */ +int u300_gpio_config_get(struct gpio_chip *chip, + unsigned offset, + unsigned long *config) +{ + struct u300_gpio *gpio = to_u300_gpio(chip); + enum pin_config_param param = (enum pin_config_param) *config; + bool biasmode; + u32 drmode; + + /* One bit per pin, clamp to bool range */ + biasmode = !!(readl(U300_PIN_REG(offset, per)) & U300_PIN_BIT(offset)); + + /* Mask out the two bits for this pin and shift to bits 0,1 */ + drmode = readl(U300_PIN_REG(offset, pcr)); + drmode &= (U300_GPIO_PXPCR_PIN_MODE_MASK << ((offset & 0x07) << 1)); + drmode >>= ((offset & 0x07) << 1); + + switch(param) { + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + *config = 0; + if (biasmode) + return 0; + else + return -EINVAL; + break; + case PIN_CONFIG_BIAS_PULL_UP: + *config = 0; + if (!biasmode) + return 0; + else + return -EINVAL; + break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + *config = 0; + if (drmode == U300_GPIO_PXPCR_PIN_MODE_OUTPUT_PUSH_PULL) + return 0; + else + return -EINVAL; + break; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + *config = 0; + if (drmode == U300_GPIO_PXPCR_PIN_MODE_OUTPUT_OPEN_DRAIN) + return 0; + else + return -EINVAL; + break; + case PIN_CONFIG_DRIVE_OPEN_SOURCE: + *config = 0; + if (drmode == U300_GPIO_PXPCR_PIN_MODE_OUTPUT_OPEN_SOURCE) + return 0; + else + return -EINVAL; + break; + default: + break; + } + return -ENOTSUPP; +} + +int u300_gpio_config_set(struct gpio_chip *chip, unsigned offset, + enum pin_config_param param) { struct u300_gpio *gpio = to_u300_gpio(chip); unsigned long flags; @@ -457,16 +489,16 @@ static int u300_gpio_config(struct gpio_chip *chip, unsigned offset, local_irq_save(flags); switch (param) { - case GPIO_U300_CONFIG_BIAS_UNKNOWN: - case GPIO_U300_CONFIG_BIAS_FLOAT: + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: val = readl(U300_PIN_REG(offset, per)); writel(val | U300_PIN_BIT(offset), U300_PIN_REG(offset, per)); break; - case GPIO_U300_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_UP: val = readl(U300_PIN_REG(offset, per)); writel(val & ~U300_PIN_BIT(offset), U300_PIN_REG(offset, per)); break; - case GPIO_U300_CONFIG_DRIVE_PUSH_PULL: + case PIN_CONFIG_DRIVE_PUSH_PULL: val = readl(U300_PIN_REG(offset, pcr)); val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK << ((offset & 0x07) << 1)); @@ -474,7 +506,7 @@ static int u300_gpio_config(struct gpio_chip *chip, unsigned offset, << ((offset & 0x07) << 1)); writel(val, U300_PIN_REG(offset, pcr)); break; - case GPIO_U300_CONFIG_DRIVE_OPEN_DRAIN: + case PIN_CONFIG_DRIVE_OPEN_DRAIN: val = readl(U300_PIN_REG(offset, pcr)); val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK << ((offset & 0x07) << 1)); @@ -482,7 +514,7 @@ static int u300_gpio_config(struct gpio_chip *chip, unsigned offset, << ((offset & 0x07) << 1)); writel(val, U300_PIN_REG(offset, pcr)); break; - case GPIO_U300_CONFIG_DRIVE_OPEN_SOURCE: + case PIN_CONFIG_DRIVE_OPEN_SOURCE: val = readl(U300_PIN_REG(offset, pcr)); val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK << ((offset & 0x07) << 1)); @@ -650,13 +682,12 @@ static void __init u300_gpio_init_pin(struct u300_gpio *gpio, u300_gpio_direction_output(&gpio->chip, offset, conf->outval); /* Deactivate bias mode for output */ - u300_gpio_config(&gpio->chip, offset, - GPIO_U300_CONFIG_BIAS_FLOAT, - NULL); + u300_gpio_config_set(&gpio->chip, offset, + PIN_CONFIG_BIAS_HIGH_IMPEDANCE); /* Set drive mode for output */ - u300_gpio_config(&gpio->chip, offset, - GPIO_U300_CONFIG_DRIVE_PUSH_PULL, NULL); + u300_gpio_config_set(&gpio->chip, offset, + PIN_CONFIG_DRIVE_PUSH_PULL); dev_dbg(gpio->dev, "set up pin %d as output, value: %d\n", offset, conf->outval); @@ -667,7 +698,7 @@ static void __init u300_gpio_init_pin(struct u300_gpio *gpio, u300_gpio_set(&gpio->chip, offset, 0); /* Set bias mode for input */ - u300_gpio_config(&gpio->chip, offset, conf->bias_mode, NULL); + u300_gpio_config_set(&gpio->chip, offset, conf->bias_mode); dev_dbg(gpio->dev, "set up pin %d as input, bias: %04x\n", offset, conf->bias_mode); @@ -705,7 +736,6 @@ static inline void u300_gpio_free_ports(struct u300_gpio *gpio) list_for_each_safe(p, n, &gpio->port_list) { port = list_entry(p, struct u300_gpio_port, node); list_del(&port->node); - free_irq(port->irq, port); kfree(port); } } @@ -861,10 +891,18 @@ static int __init u300_gpio_probe(struct platform_device *pdev) goto err_no_chip; } + /* Spawn pin controller device as child of the GPIO, pass gpio chip */ + plat->pinctrl_device->dev.platform_data = &gpio->chip; + err = platform_device_register(plat->pinctrl_device); + if (err) + goto err_no_pinctrl; + platform_set_drvdata(pdev, gpio); return 0; +err_no_pinctrl: + err = gpiochip_remove(&gpio->chip); err_no_chip: err_no_port: u300_gpio_free_ports(gpio); @@ -919,7 +957,6 @@ static struct platform_driver u300_gpio_driver = { .remove = __exit_p(u300_gpio_remove), }; - static int __init u300_gpio_init(void) { return platform_driver_probe(&u300_gpio_driver, u300_gpio_probe); diff --git a/drivers/pinctrl/pinctrl-coh901.h b/drivers/pinctrl/pinctrl-coh901.h new file mode 100644 index 000000000000..87294222583e --- /dev/null +++ b/drivers/pinctrl/pinctrl-coh901.h @@ -0,0 +1,5 @@ +int u300_gpio_config_get(struct gpio_chip *chip, + unsigned offset, + unsigned long *config); +int u300_gpio_config_set(struct gpio_chip *chip, unsigned offset, + enum pin_config_param param); diff --git a/drivers/pinctrl/pinctrl-mmp2.c b/drivers/pinctrl/pinctrl-mmp2.c new file mode 100644 index 000000000000..2cfed552bbe4 --- /dev/null +++ b/drivers/pinctrl/pinctrl-mmp2.c @@ -0,0 +1,722 @@ +/* + * linux/drivers/pinctrl/pinmux-mmp2.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * publishhed by the Free Software Foundation. + * + * Copyright (C) 2011, Marvell Technology Group Ltd. + * + * Author: Haojian Zhuang <haojian.zhuang@marvell.com> + * + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include "pinctrl-pxa3xx.h" + +#define MMP2_DS_MASK 0x1800 +#define MMP2_DS_SHIFT 11 +#define MMP2_SLEEP_MASK 0x38 +#define MMP2_SLEEP_SELECT (1 << 9) +#define MMP2_SLEEP_DATA (1 << 8) +#define MMP2_SLEEP_DIR (1 << 7) + +#define MFPR_MMP2(a, r, f0, f1, f2, f3, f4, f5, f6, f7) \ + { \ + .name = #a, \ + .pin = a, \ + .mfpr = r, \ + .func = { \ + MMP2_MUX_##f0, \ + MMP2_MUX_##f1, \ + MMP2_MUX_##f2, \ + MMP2_MUX_##f3, \ + MMP2_MUX_##f4, \ + MMP2_MUX_##f5, \ + MMP2_MUX_##f6, \ + MMP2_MUX_##f7, \ + }, \ + } + +#define GRP_MMP2(a, m, p) \ + { .name = a, .mux = MMP2_MUX_##m, .pins = p, .npins = ARRAY_SIZE(p), } + +/* 174 pins */ +enum mmp2_pin_list { + /* 0~168: GPIO0~GPIO168 */ + TWSI4_SCL = 169, + TWSI4_SDA, /* 170 */ + G_CLKREQ, + VCXO_REQ, + VCXO_OUT, +}; + +enum mmp2_mux { + /* PXA3xx_MUX_GPIO = 0 (predefined in pinctrl-pxa3xx.h) */ + MMP2_MUX_GPIO = 0, + MMP2_MUX_G_CLKREQ, + MMP2_MUX_VCXO_REQ, + MMP2_MUX_VCXO_OUT, + MMP2_MUX_KP_MK, + MMP2_MUX_KP_DK, + MMP2_MUX_CCIC1, + MMP2_MUX_CCIC2, + MMP2_MUX_SPI, + MMP2_MUX_SSPA2, + MMP2_MUX_ROT, + MMP2_MUX_I2S, + MMP2_MUX_TB, + MMP2_MUX_CAM2, + MMP2_MUX_HDMI, + MMP2_MUX_TWSI2, + MMP2_MUX_TWSI3, + MMP2_MUX_TWSI4, + MMP2_MUX_TWSI5, + MMP2_MUX_TWSI6, + MMP2_MUX_UART1, + MMP2_MUX_UART2, + MMP2_MUX_UART3, + MMP2_MUX_UART4, + MMP2_MUX_SSP1_RX, + MMP2_MUX_SSP1_FRM, + MMP2_MUX_SSP1_TXRX, + MMP2_MUX_SSP2_RX, + MMP2_MUX_SSP2_FRM, + MMP2_MUX_SSP1, + MMP2_MUX_SSP2, + MMP2_MUX_SSP3, + MMP2_MUX_SSP4, + MMP2_MUX_MMC1, + MMP2_MUX_MMC2, + MMP2_MUX_MMC3, + MMP2_MUX_MMC4, + MMP2_MUX_ULPI, + MMP2_MUX_AC, + MMP2_MUX_CA, + MMP2_MUX_PWM, + MMP2_MUX_USIM, + MMP2_MUX_TIPU, + MMP2_MUX_PLL, + MMP2_MUX_NAND, + MMP2_MUX_FSIC, + MMP2_MUX_SLEEP_IND, + MMP2_MUX_EXT_DMA, + MMP2_MUX_ONE_WIRE, + MMP2_MUX_LCD, + MMP2_MUX_SMC, + MMP2_MUX_SMC_INT, + MMP2_MUX_MSP, + MMP2_MUX_G_CLKOUT, + MMP2_MUX_32K_CLKOUT, + MMP2_MUX_PRI_JTAG, + MMP2_MUX_AAS_JTAG, + MMP2_MUX_AAS_GPIO, + MMP2_MUX_AAS_SPI, + MMP2_MUX_AAS_TWSI, + MMP2_MUX_AAS_DEU_EX, + MMP2_MUX_NONE = 0xffff, +}; + +static struct pinctrl_pin_desc mmp2_pads[] = { + /* + * The name indicates function 0 of this pin. + * After reset, function 0 is the default function of pin. + */ + PINCTRL_PIN(GPIO0, "GPIO0"), + PINCTRL_PIN(GPIO1, "GPIO1"), + PINCTRL_PIN(GPIO2, "GPIO2"), + PINCTRL_PIN(GPIO3, "GPIO3"), + PINCTRL_PIN(GPIO4, "GPIO4"), + PINCTRL_PIN(GPIO5, "GPIO5"), + PINCTRL_PIN(GPIO6, "GPIO6"), + PINCTRL_PIN(GPIO7, "GPIO7"), + PINCTRL_PIN(GPIO8, "GPIO8"), + PINCTRL_PIN(GPIO9, "GPIO9"), + PINCTRL_PIN(GPIO10, "GPIO10"), + PINCTRL_PIN(GPIO11, "GPIO11"), + PINCTRL_PIN(GPIO12, "GPIO12"), + PINCTRL_PIN(GPIO13, "GPIO13"), + PINCTRL_PIN(GPIO14, "GPIO14"), + PINCTRL_PIN(GPIO15, "GPIO15"), + PINCTRL_PIN(GPIO16, "GPIO16"), + PINCTRL_PIN(GPIO17, "GPIO17"), + PINCTRL_PIN(GPIO18, "GPIO18"), + PINCTRL_PIN(GPIO19, "GPIO19"), + PINCTRL_PIN(GPIO20, "GPIO20"), + PINCTRL_PIN(GPIO21, "GPIO21"), + PINCTRL_PIN(GPIO22, "GPIO22"), + PINCTRL_PIN(GPIO23, "GPIO23"), + PINCTRL_PIN(GPIO24, "GPIO24"), + PINCTRL_PIN(GPIO25, "GPIO25"), + PINCTRL_PIN(GPIO26, "GPIO26"), + PINCTRL_PIN(GPIO27, "GPIO27"), + PINCTRL_PIN(GPIO28, "GPIO28"), + PINCTRL_PIN(GPIO29, "GPIO29"), + PINCTRL_PIN(GPIO30, "GPIO30"), + PINCTRL_PIN(GPIO31, "GPIO31"), + PINCTRL_PIN(GPIO32, "GPIO32"), + PINCTRL_PIN(GPIO33, "GPIO33"), + PINCTRL_PIN(GPIO34, "GPIO34"), + PINCTRL_PIN(GPIO35, "GPIO35"), + PINCTRL_PIN(GPIO36, "GPIO36"), + PINCTRL_PIN(GPIO37, "GPIO37"), + PINCTRL_PIN(GPIO38, "GPIO38"), + PINCTRL_PIN(GPIO39, "GPIO39"), + PINCTRL_PIN(GPIO40, "GPIO40"), + PINCTRL_PIN(GPIO41, "GPIO41"), + PINCTRL_PIN(GPIO42, "GPIO42"), + PINCTRL_PIN(GPIO43, "GPIO43"), + PINCTRL_PIN(GPIO44, "GPIO44"), + PINCTRL_PIN(GPIO45, "GPIO45"), + PINCTRL_PIN(GPIO46, "GPIO46"), + PINCTRL_PIN(GPIO47, "GPIO47"), + PINCTRL_PIN(GPIO48, "GPIO48"), + PINCTRL_PIN(GPIO49, "GPIO49"), + PINCTRL_PIN(GPIO50, "GPIO50"), + PINCTRL_PIN(GPIO51, "GPIO51"), + PINCTRL_PIN(GPIO52, "GPIO52"), + PINCTRL_PIN(GPIO53, "GPIO53"), + PINCTRL_PIN(GPIO54, "GPIO54"), + PINCTRL_PIN(GPIO55, "GPIO55"), + PINCTRL_PIN(GPIO56, "GPIO56"), + PINCTRL_PIN(GPIO57, "GPIO57"), + PINCTRL_PIN(GPIO58, "GPIO58"), + PINCTRL_PIN(GPIO59, "GPIO59"), + PINCTRL_PIN(GPIO60, "GPIO60"), + PINCTRL_PIN(GPIO61, "GPIO61"), + PINCTRL_PIN(GPIO62, "GPIO62"), + PINCTRL_PIN(GPIO63, "GPIO63"), + PINCTRL_PIN(GPIO64, "GPIO64"), + PINCTRL_PIN(GPIO65, "GPIO65"), + PINCTRL_PIN(GPIO66, "GPIO66"), + PINCTRL_PIN(GPIO67, "GPIO67"), + PINCTRL_PIN(GPIO68, "GPIO68"), + PINCTRL_PIN(GPIO69, "GPIO69"), + PINCTRL_PIN(GPIO70, "GPIO70"), + PINCTRL_PIN(GPIO71, "GPIO71"), + PINCTRL_PIN(GPIO72, "GPIO72"), + PINCTRL_PIN(GPIO73, "GPIO73"), + PINCTRL_PIN(GPIO74, "GPIO74"), + PINCTRL_PIN(GPIO75, "GPIO75"), + PINCTRL_PIN(GPIO76, "GPIO76"), + PINCTRL_PIN(GPIO77, "GPIO77"), + PINCTRL_PIN(GPIO78, "GPIO78"), + PINCTRL_PIN(GPIO79, "GPIO79"), + PINCTRL_PIN(GPIO80, "GPIO80"), + PINCTRL_PIN(GPIO81, "GPIO81"), + PINCTRL_PIN(GPIO82, "GPIO82"), + PINCTRL_PIN(GPIO83, "GPIO83"), + PINCTRL_PIN(GPIO84, "GPIO84"), + PINCTRL_PIN(GPIO85, "GPIO85"), + PINCTRL_PIN(GPIO86, "GPIO86"), + PINCTRL_PIN(GPIO87, "GPIO87"), + PINCTRL_PIN(GPIO88, "GPIO88"), + PINCTRL_PIN(GPIO89, "GPIO89"), + PINCTRL_PIN(GPIO90, "GPIO90"), + PINCTRL_PIN(GPIO91, "GPIO91"), + PINCTRL_PIN(GPIO92, "GPIO92"), + PINCTRL_PIN(GPIO93, "GPIO93"), + PINCTRL_PIN(GPIO94, "GPIO94"), + PINCTRL_PIN(GPIO95, "GPIO95"), + PINCTRL_PIN(GPIO96, "GPIO96"), + PINCTRL_PIN(GPIO97, "GPIO97"), + PINCTRL_PIN(GPIO98, "GPIO98"), + PINCTRL_PIN(GPIO99, "GPIO99"), + PINCTRL_PIN(GPIO100, "GPIO100"), + PINCTRL_PIN(GPIO101, "GPIO101"), + PINCTRL_PIN(GPIO102, "GPIO102"), + PINCTRL_PIN(GPIO103, "GPIO103"), + PINCTRL_PIN(GPIO104, "GPIO104"), + PINCTRL_PIN(GPIO105, "GPIO105"), + PINCTRL_PIN(GPIO106, "GPIO106"), + PINCTRL_PIN(GPIO107, "GPIO107"), + PINCTRL_PIN(GPIO108, "GPIO108"), + PINCTRL_PIN(GPIO109, "GPIO109"), + PINCTRL_PIN(GPIO110, "GPIO110"), + PINCTRL_PIN(GPIO111, "GPIO111"), + PINCTRL_PIN(GPIO112, "GPIO112"), + PINCTRL_PIN(GPIO113, "GPIO113"), + PINCTRL_PIN(GPIO114, "GPIO114"), + PINCTRL_PIN(GPIO115, "GPIO115"), + PINCTRL_PIN(GPIO116, "GPIO116"), + PINCTRL_PIN(GPIO117, "GPIO117"), + PINCTRL_PIN(GPIO118, "GPIO118"), + PINCTRL_PIN(GPIO119, "GPIO119"), + PINCTRL_PIN(GPIO120, "GPIO120"), + PINCTRL_PIN(GPIO121, "GPIO121"), + PINCTRL_PIN(GPIO122, "GPIO122"), + PINCTRL_PIN(GPIO123, "GPIO123"), + PINCTRL_PIN(GPIO124, "GPIO124"), + PINCTRL_PIN(GPIO125, "GPIO125"), + PINCTRL_PIN(GPIO126, "GPIO126"), + PINCTRL_PIN(GPIO127, "GPIO127"), + PINCTRL_PIN(GPIO128, "GPIO128"), + PINCTRL_PIN(GPIO129, "GPIO129"), + PINCTRL_PIN(GPIO130, "GPIO130"), + PINCTRL_PIN(GPIO131, "GPIO131"), + PINCTRL_PIN(GPIO132, "GPIO132"), + PINCTRL_PIN(GPIO133, "GPIO133"), + PINCTRL_PIN(GPIO134, "GPIO134"), + PINCTRL_PIN(GPIO135, "GPIO135"), + PINCTRL_PIN(GPIO136, "GPIO136"), + PINCTRL_PIN(GPIO137, "GPIO137"), + PINCTRL_PIN(GPIO138, "GPIO138"), + PINCTRL_PIN(GPIO139, "GPIO139"), + PINCTRL_PIN(GPIO140, "GPIO140"), + PINCTRL_PIN(GPIO141, "GPIO141"), + PINCTRL_PIN(GPIO142, "GPIO142"), + PINCTRL_PIN(GPIO143, "GPIO143"), + PINCTRL_PIN(GPIO144, "GPIO144"), + PINCTRL_PIN(GPIO145, "GPIO145"), + PINCTRL_PIN(GPIO146, "GPIO146"), + PINCTRL_PIN(GPIO147, "GPIO147"), + PINCTRL_PIN(GPIO148, "GPIO148"), + PINCTRL_PIN(GPIO149, "GPIO149"), + PINCTRL_PIN(GPIO150, "GPIO150"), + PINCTRL_PIN(GPIO151, "GPIO151"), + PINCTRL_PIN(GPIO152, "GPIO152"), + PINCTRL_PIN(GPIO153, "GPIO153"), + PINCTRL_PIN(GPIO154, "GPIO154"), + PINCTRL_PIN(GPIO155, "GPIO155"), + PINCTRL_PIN(GPIO156, "GPIO156"), + PINCTRL_PIN(GPIO157, "GPIO157"), + PINCTRL_PIN(GPIO158, "GPIO158"), + PINCTRL_PIN(GPIO159, "GPIO159"), + PINCTRL_PIN(GPIO160, "GPIO160"), + PINCTRL_PIN(GPIO161, "GPIO161"), + PINCTRL_PIN(GPIO162, "GPIO162"), + PINCTRL_PIN(GPIO163, "GPIO163"), + PINCTRL_PIN(GPIO164, "GPIO164"), + PINCTRL_PIN(GPIO165, "GPIO165"), + PINCTRL_PIN(GPIO166, "GPIO166"), + PINCTRL_PIN(GPIO167, "GPIO167"), + PINCTRL_PIN(GPIO168, "GPIO168"), + PINCTRL_PIN(TWSI4_SCL, "TWSI4_SCL"), + PINCTRL_PIN(TWSI4_SDA, "TWSI4_SDA"), + PINCTRL_PIN(G_CLKREQ, "G_CLKREQ"), + PINCTRL_PIN(VCXO_REQ, "VCXO_REQ"), + PINCTRL_PIN(VCXO_OUT, "VCXO_OUT"), +}; + +struct pxa3xx_mfp_pin mmp2_mfp[] = { + /* pin offs f0 f1 f2 f3 f4 f5 f6 f7 */ + MFPR_MMP2(GPIO0, 0x054, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO1, 0x058, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO2, 0x05C, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO3, 0x060, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO4, 0x064, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO5, 0x068, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO6, 0x06C, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO7, 0x070, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO8, 0x074, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO9, 0x078, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO10, 0x07C, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO11, 0x080, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO12, 0x084, GPIO, KP_MK, NONE, CCIC1, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO13, 0x088, GPIO, KP_MK, NONE, CCIC1, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO14, 0x08C, GPIO, KP_MK, NONE, CCIC1, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO15, 0x090, GPIO, KP_MK, KP_DK, CCIC1, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO16, 0x094, GPIO, KP_DK, ROT, CCIC1, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO17, 0x098, GPIO, KP_DK, ROT, CCIC1, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO18, 0x09C, GPIO, KP_DK, ROT, CCIC1, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO19, 0x0A0, GPIO, KP_DK, ROT, CCIC1, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO20, 0x0A4, GPIO, KP_DK, TB, CCIC1, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO21, 0x0A8, GPIO, KP_DK, TB, CCIC1, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO22, 0x0AC, GPIO, KP_DK, TB, CCIC1, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO23, 0x0B0, GPIO, KP_DK, TB, CCIC1, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO24, 0x0B4, GPIO, I2S, VCXO_OUT, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO25, 0x0B8, GPIO, I2S, HDMI, SSPA2, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO26, 0x0BC, GPIO, I2S, HDMI, SSPA2, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO27, 0x0C0, GPIO, I2S, HDMI, SSPA2, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO28, 0x0C4, GPIO, I2S, NONE, SSPA2, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO29, 0x0C8, GPIO, UART1, KP_MK, NONE, NONE, NONE, AAS_SPI, NONE), + MFPR_MMP2(GPIO30, 0x0CC, GPIO, UART1, KP_MK, NONE, NONE, NONE, AAS_SPI, NONE), + MFPR_MMP2(GPIO31, 0x0D0, GPIO, UART1, KP_MK, NONE, NONE, NONE, AAS_SPI, NONE), + MFPR_MMP2(GPIO32, 0x0D4, GPIO, UART1, KP_MK, NONE, NONE, NONE, AAS_SPI, NONE), + MFPR_MMP2(GPIO33, 0x0D8, GPIO, SSPA2, I2S, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO34, 0x0DC, GPIO, SSPA2, I2S, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO35, 0x0E0, GPIO, SSPA2, I2S, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO36, 0x0E4, GPIO, SSPA2, I2S, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO37, 0x0E8, GPIO, MMC2, SSP1, TWSI2, UART2, UART3, AAS_SPI, AAS_TWSI), + MFPR_MMP2(GPIO38, 0x0EC, GPIO, MMC2, SSP1, TWSI2, UART2, UART3, AAS_SPI, AAS_TWSI), + MFPR_MMP2(GPIO39, 0x0F0, GPIO, MMC2, SSP1, TWSI2, UART2, UART3, AAS_SPI, AAS_TWSI), + MFPR_MMP2(GPIO40, 0x0F4, GPIO, MMC2, SSP1, TWSI2, UART2, UART3, AAS_SPI, AAS_TWSI), + MFPR_MMP2(GPIO41, 0x0F8, GPIO, MMC2, TWSI5, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO42, 0x0FC, GPIO, MMC2, TWSI5, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO43, 0x100, GPIO, TWSI2, UART4, SSP1, UART2, UART3, NONE, AAS_TWSI), + MFPR_MMP2(GPIO44, 0x104, GPIO, TWSI2, UART4, SSP1, UART2, UART3, NONE, AAS_TWSI), + MFPR_MMP2(GPIO45, 0x108, GPIO, UART1, UART4, SSP1, UART2, UART3, NONE, NONE), + MFPR_MMP2(GPIO46, 0x10C, GPIO, UART1, UART4, SSP1, UART2, UART3, NONE, NONE), + MFPR_MMP2(GPIO47, 0x110, GPIO, UART2, SSP2, TWSI6, CAM2, AAS_SPI, AAS_GPIO, NONE), + MFPR_MMP2(GPIO48, 0x114, GPIO, UART2, SSP2, TWSI6, CAM2, AAS_SPI, AAS_GPIO, NONE), + MFPR_MMP2(GPIO49, 0x118, GPIO, UART2, SSP2, PWM, CCIC2, AAS_SPI, NONE, NONE), + MFPR_MMP2(GPIO50, 0x11C, GPIO, UART2, SSP2, PWM, CCIC2, AAS_SPI, NONE, NONE), + MFPR_MMP2(GPIO51, 0x120, GPIO, UART3, ROT, AAS_GPIO, PWM, NONE, NONE, NONE), + MFPR_MMP2(GPIO52, 0x124, GPIO, UART3, ROT, AAS_GPIO, PWM, NONE, NONE, NONE), + MFPR_MMP2(GPIO53, 0x128, GPIO, UART3, TWSI2, VCXO_REQ, NONE, PWM, NONE, AAS_TWSI), + MFPR_MMP2(GPIO54, 0x12C, GPIO, UART3, TWSI2, VCXO_OUT, HDMI, PWM, NONE, AAS_TWSI), + MFPR_MMP2(GPIO55, 0x130, GPIO, SSP2, SSP1, UART2, ROT, TWSI2, SSP3, AAS_TWSI), + MFPR_MMP2(GPIO56, 0x134, GPIO, SSP2, SSP1, UART2, ROT, TWSI2, KP_DK, AAS_TWSI), + MFPR_MMP2(GPIO57, 0x138, GPIO, SSP2_RX, SSP1_TXRX, SSP2_FRM, SSP1_RX, VCXO_REQ, KP_DK, NONE), + MFPR_MMP2(GPIO58, 0x13C, GPIO, SSP2, SSP1_RX, SSP1_FRM, SSP1_TXRX, VCXO_REQ, KP_DK, NONE), + MFPR_MMP2(GPIO59, 0x280, GPIO, CCIC1, ULPI, MMC3, CCIC2, UART3, UART4, NONE), + MFPR_MMP2(GPIO60, 0x284, GPIO, CCIC1, ULPI, MMC3, CCIC2, UART3, UART4, NONE), + MFPR_MMP2(GPIO61, 0x288, GPIO, CCIC1, ULPI, MMC3, CCIC2, UART3, HDMI, NONE), + MFPR_MMP2(GPIO62, 0x28C, GPIO, CCIC1, ULPI, MMC3, CCIC2, UART3, NONE, NONE), + MFPR_MMP2(GPIO63, 0x290, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, UART4, NONE), + MFPR_MMP2(GPIO64, 0x294, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, UART4, NONE), + MFPR_MMP2(GPIO65, 0x298, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, UART4, NONE), + MFPR_MMP2(GPIO66, 0x29C, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, UART4, NONE), + MFPR_MMP2(GPIO67, 0x2A0, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, NONE, NONE), + MFPR_MMP2(GPIO68, 0x2A4, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, LCD, NONE), + MFPR_MMP2(GPIO69, 0x2A8, GPIO, CCIC1, ULPI, MMC3, CCIC2, NONE, LCD, NONE), + MFPR_MMP2(GPIO70, 0x2AC, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, LCD, NONE), + MFPR_MMP2(GPIO71, 0x2B0, GPIO, TWSI3, NONE, PWM, NONE, NONE, LCD, AAS_TWSI), + MFPR_MMP2(GPIO72, 0x2B4, GPIO, TWSI3, HDMI, PWM, NONE, NONE, LCD, AAS_TWSI), + MFPR_MMP2(GPIO73, 0x2B8, GPIO, VCXO_REQ, 32K_CLKOUT, PWM, VCXO_OUT, NONE, LCD, NONE), + MFPR_MMP2(GPIO74, 0x170, GPIO, LCD, SMC, MMC4, SSP3, UART2, UART4, TIPU), + MFPR_MMP2(GPIO75, 0x174, GPIO, LCD, SMC, MMC4, SSP3, UART2, UART4, TIPU), + MFPR_MMP2(GPIO76, 0x178, GPIO, LCD, SMC, MMC4, SSP3, UART2, UART4, TIPU), + MFPR_MMP2(GPIO77, 0x17C, GPIO, LCD, SMC, MMC4, SSP3, UART2, UART4, TIPU), + MFPR_MMP2(GPIO78, 0x180, GPIO, LCD, HDMI, MMC4, NONE, SSP4, AAS_SPI, TIPU), + MFPR_MMP2(GPIO79, 0x184, GPIO, LCD, AAS_GPIO, MMC4, NONE, SSP4, AAS_SPI, TIPU), + MFPR_MMP2(GPIO80, 0x188, GPIO, LCD, AAS_GPIO, MMC4, NONE, SSP4, AAS_SPI, TIPU), + MFPR_MMP2(GPIO81, 0x18C, GPIO, LCD, AAS_GPIO, MMC4, NONE, SSP4, AAS_SPI, TIPU), + MFPR_MMP2(GPIO82, 0x190, GPIO, LCD, NONE, MMC4, NONE, NONE, CCIC2, TIPU), + MFPR_MMP2(GPIO83, 0x194, GPIO, LCD, NONE, MMC4, NONE, NONE, CCIC2, TIPU), + MFPR_MMP2(GPIO84, 0x198, GPIO, LCD, SMC, MMC2, NONE, TWSI5, AAS_TWSI, TIPU), + MFPR_MMP2(GPIO85, 0x19C, GPIO, LCD, SMC, MMC2, NONE, TWSI5, AAS_TWSI, TIPU), + MFPR_MMP2(GPIO86, 0x1A0, GPIO, LCD, SMC, MMC2, NONE, TWSI6, CCIC2, TIPU), + MFPR_MMP2(GPIO87, 0x1A4, GPIO, LCD, SMC, MMC2, NONE, TWSI6, CCIC2, TIPU), + MFPR_MMP2(GPIO88, 0x1A8, GPIO, LCD, AAS_GPIO, MMC2, NONE, NONE, CCIC2, TIPU), + MFPR_MMP2(GPIO89, 0x1AC, GPIO, LCD, AAS_GPIO, MMC2, NONE, NONE, CCIC2, TIPU), + MFPR_MMP2(GPIO90, 0x1B0, GPIO, LCD, AAS_GPIO, MMC2, NONE, NONE, CCIC2, TIPU), + MFPR_MMP2(GPIO91, 0x1B4, GPIO, LCD, AAS_GPIO, MMC2, NONE, NONE, CCIC2, TIPU), + MFPR_MMP2(GPIO92, 0x1B8, GPIO, LCD, AAS_GPIO, MMC2, NONE, NONE, CCIC2, TIPU), + MFPR_MMP2(GPIO93, 0x1BC, GPIO, LCD, AAS_GPIO, MMC2, NONE, NONE, CCIC2, TIPU), + MFPR_MMP2(GPIO94, 0x1C0, GPIO, LCD, AAS_GPIO, SPI, NONE, AAS_SPI, CCIC2, TIPU), + MFPR_MMP2(GPIO95, 0x1C4, GPIO, LCD, TWSI3, SPI, AAS_DEU_EX, AAS_SPI, CCIC2, TIPU), + MFPR_MMP2(GPIO96, 0x1C8, GPIO, LCD, TWSI3, SPI, AAS_DEU_EX, AAS_SPI, NONE, TIPU), + MFPR_MMP2(GPIO97, 0x1CC, GPIO, LCD, TWSI6, SPI, AAS_DEU_EX, AAS_SPI, NONE, TIPU), + MFPR_MMP2(GPIO98, 0x1D0, GPIO, LCD, TWSI6, SPI, ONE_WIRE, NONE, NONE, TIPU), + MFPR_MMP2(GPIO99, 0x1D4, GPIO, LCD, SMC, SPI, TWSI5, NONE, NONE, TIPU), + MFPR_MMP2(GPIO100, 0x1D8, GPIO, LCD, SMC, SPI, TWSI5, NONE, NONE, TIPU), + MFPR_MMP2(GPIO101, 0x1DC, GPIO, LCD, SMC, SPI, NONE, NONE, NONE, TIPU), + MFPR_MMP2(GPIO102, 0x000, USIM, GPIO, FSIC, KP_DK, LCD, NONE, NONE, NONE), + MFPR_MMP2(GPIO103, 0x004, USIM, GPIO, FSIC, KP_DK, LCD, NONE, NONE, NONE), + MFPR_MMP2(GPIO104, 0x1FC, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO105, 0x1F8, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO106, 0x1F4, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO107, 0x1F0, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO108, 0x21C, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO109, 0x218, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO110, 0x214, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO111, 0x200, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO112, 0x244, NAND, GPIO, MMC3, SMC, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO113, 0x25C, SMC, GPIO, EXT_DMA, MMC3, SMC, HDMI, NONE, NONE), + MFPR_MMP2(GPIO114, 0x164, G_CLKOUT, 32K_CLKOUT, HDMI, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO115, 0x260, GPIO, NONE, AC, UART4, UART3, SSP1, NONE, NONE), + MFPR_MMP2(GPIO116, 0x264, GPIO, NONE, AC, UART4, UART3, SSP1, NONE, NONE), + MFPR_MMP2(GPIO117, 0x268, GPIO, NONE, AC, UART4, UART3, SSP1, NONE, NONE), + MFPR_MMP2(GPIO118, 0x26C, GPIO, NONE, AC, UART4, UART3, SSP1, NONE, NONE), + MFPR_MMP2(GPIO119, 0x270, GPIO, NONE, CA, SSP3, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO120, 0x274, GPIO, NONE, CA, SSP3, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO121, 0x278, GPIO, NONE, CA, SSP3, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO122, 0x27C, GPIO, NONE, CA, SSP3, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO123, 0x148, GPIO, SLEEP_IND, ONE_WIRE, 32K_CLKOUT, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO124, 0x00C, GPIO, MMC1, LCD, MMC3, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO125, 0x010, GPIO, MMC1, LCD, MMC3, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO126, 0x014, GPIO, MMC1, LCD, MMC3, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO127, 0x018, GPIO, NONE, LCD, MMC3, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO128, 0x01C, GPIO, NONE, LCD, MMC3, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO129, 0x020, GPIO, MMC1, LCD, MMC3, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO130, 0x024, GPIO, MMC1, LCD, MMC3, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO131, 0x028, GPIO, MMC1, NONE, MSP, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO132, 0x02C, GPIO, MMC1, PRI_JTAG, MSP, SSP3, AAS_JTAG, NONE, NONE), + MFPR_MMP2(GPIO133, 0x030, GPIO, MMC1, PRI_JTAG, MSP, SSP3, AAS_JTAG, NONE, NONE), + MFPR_MMP2(GPIO134, 0x034, GPIO, MMC1, PRI_JTAG, MSP, SSP3, AAS_JTAG, NONE, NONE), + MFPR_MMP2(GPIO135, 0x038, GPIO, NONE, LCD, MMC3, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO136, 0x03C, GPIO, MMC1, PRI_JTAG, MSP, SSP3, AAS_JTAG, NONE, NONE), + MFPR_MMP2(GPIO137, 0x040, GPIO, HDMI, LCD, MSP, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO138, 0x044, GPIO, NONE, LCD, MMC3, SMC, NONE, NONE, NONE), + MFPR_MMP2(GPIO139, 0x048, GPIO, MMC1, PRI_JTAG, MSP, NONE, AAS_JTAG, NONE, NONE), + MFPR_MMP2(GPIO140, 0x04C, GPIO, MMC1, LCD, NONE, NONE, UART2, UART1, NONE), + MFPR_MMP2(GPIO141, 0x050, GPIO, MMC1, LCD, NONE, NONE, UART2, UART1, NONE), + MFPR_MMP2(GPIO142, 0x008, USIM, GPIO, FSIC, KP_DK, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO143, 0x220, NAND, GPIO, SMC, NONE, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO144, 0x224, NAND, GPIO, SMC_INT, SMC, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO145, 0x228, SMC, GPIO, NONE, NONE, SMC, NONE, NONE, NONE), + MFPR_MMP2(GPIO146, 0x22C, SMC, GPIO, NONE, NONE, SMC, NONE, NONE, NONE), + MFPR_MMP2(GPIO147, 0x230, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO148, 0x234, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO149, 0x238, NAND, GPIO, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO150, 0x23C, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO151, 0x240, SMC, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO152, 0x248, SMC, GPIO, NONE, NONE, SMC, NONE, NONE, NONE), + MFPR_MMP2(GPIO153, 0x24C, SMC, GPIO, NONE, NONE, SMC, NONE, NONE, NONE), + MFPR_MMP2(GPIO154, 0x254, SMC_INT, GPIO, SMC, NONE, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO155, 0x258, EXT_DMA, GPIO, SMC, NONE, EXT_DMA, NONE, NONE, NONE), + MFPR_MMP2(GPIO156, 0x14C, PRI_JTAG, GPIO, PWM, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO157, 0x150, PRI_JTAG, GPIO, PWM, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO158, 0x154, PRI_JTAG, GPIO, PWM, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO159, 0x158, PRI_JTAG, GPIO, PWM, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO160, 0x250, NAND, GPIO, SMC, NONE, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO161, 0x210, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE), + MFPR_MMP2(GPIO162, 0x20C, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO163, 0x208, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO164, 0x204, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO165, 0x1EC, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO166, 0x1E8, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO167, 0x1E4, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(GPIO168, 0x1E0, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(TWSI4_SCL, 0x2BC, TWSI4, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(TWSI4_SDA, 0x2C0, TWSI4, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(G_CLKREQ, 0x160, G_CLKREQ, ONE_WIRE, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(VCXO_REQ, 0x168, VCXO_REQ, ONE_WIRE, PLL, NONE, NONE, NONE, NONE, NONE), + MFPR_MMP2(VCXO_OUT, 0x16C, VCXO_OUT, 32K_CLKOUT, NONE, NONE, NONE, NONE, NONE, NONE), +}; + +static const unsigned mmp2_uart1_pin1[] = {GPIO29, GPIO30, GPIO31, GPIO32}; +static const unsigned mmp2_uart1_pin2[] = {GPIO45, GPIO46}; +static const unsigned mmp2_uart1_pin3[] = {GPIO140, GPIO141}; +static const unsigned mmp2_uart2_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40}; +static const unsigned mmp2_uart2_pin2[] = {GPIO43, GPIO44, GPIO45, GPIO46}; +static const unsigned mmp2_uart2_pin3[] = {GPIO47, GPIO48, GPIO49, GPIO50}; +static const unsigned mmp2_uart2_pin4[] = {GPIO74, GPIO75, GPIO76, GPIO77}; +static const unsigned mmp2_uart2_pin5[] = {GPIO55, GPIO56}; +static const unsigned mmp2_uart2_pin6[] = {GPIO140, GPIO141}; +static const unsigned mmp2_uart3_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40}; +static const unsigned mmp2_uart3_pin2[] = {GPIO43, GPIO44, GPIO45, GPIO46}; +static const unsigned mmp2_uart3_pin3[] = {GPIO51, GPIO52, GPIO53, GPIO54}; +static const unsigned mmp2_uart3_pin4[] = {GPIO59, GPIO60, GPIO61, GPIO62}; +static const unsigned mmp2_uart3_pin5[] = {GPIO115, GPIO116, GPIO117, GPIO118}; +static const unsigned mmp2_uart3_pin6[] = {GPIO51, GPIO52}; +static const unsigned mmp2_uart4_pin1[] = {GPIO43, GPIO44, GPIO45, GPIO46}; +static const unsigned mmp2_uart4_pin2[] = {GPIO63, GPIO64, GPIO65, GPIO66}; +static const unsigned mmp2_uart4_pin3[] = {GPIO74, GPIO75, GPIO76, GPIO77}; +static const unsigned mmp2_uart4_pin4[] = {GPIO115, GPIO116, GPIO117, GPIO118}; +static const unsigned mmp2_uart4_pin5[] = {GPIO59, GPIO60}; +static const unsigned mmp2_kpdk_pin1[] = {GPIO16, GPIO17, GPIO18, GPIO19}; +static const unsigned mmp2_kpdk_pin2[] = {GPIO16, GPIO17}; +static const unsigned mmp2_twsi2_pin1[] = {GPIO37, GPIO38}; +static const unsigned mmp2_twsi2_pin2[] = {GPIO39, GPIO40}; +static const unsigned mmp2_twsi2_pin3[] = {GPIO43, GPIO44}; +static const unsigned mmp2_twsi2_pin4[] = {GPIO53, GPIO54}; +static const unsigned mmp2_twsi2_pin5[] = {GPIO55, GPIO56}; +static const unsigned mmp2_twsi3_pin1[] = {GPIO71, GPIO72}; +static const unsigned mmp2_twsi3_pin2[] = {GPIO95, GPIO96}; +static const unsigned mmp2_twsi4_pin1[] = {TWSI4_SCL, TWSI4_SDA}; +static const unsigned mmp2_twsi5_pin1[] = {GPIO41, GPIO42}; +static const unsigned mmp2_twsi5_pin2[] = {GPIO84, GPIO85}; +static const unsigned mmp2_twsi5_pin3[] = {GPIO99, GPIO100}; +static const unsigned mmp2_twsi6_pin1[] = {GPIO47, GPIO48}; +static const unsigned mmp2_twsi6_pin2[] = {GPIO86, GPIO87}; +static const unsigned mmp2_twsi6_pin3[] = {GPIO97, GPIO98}; +static const unsigned mmp2_ccic1_pin1[] = {GPIO12, GPIO13, GPIO14, GPIO15, + GPIO16, GPIO17, GPIO18, GPIO19, GPIO20, GPIO21, GPIO22, GPIO23}; +static const unsigned mmp2_ccic1_pin2[] = {GPIO59, GPIO60, GPIO61, GPIO62, + GPIO63, GPIO64, GPIO65, GPIO66, GPIO67, GPIO68, GPIO69, GPIO70}; +static const unsigned mmp2_ccic2_pin1[] = {GPIO59, GPIO60, GPIO61, GPIO62, + GPIO63, GPIO64, GPIO65, GPIO66, GPIO67, GPIO68, GPIO69, GPIO70}; +static const unsigned mmp2_ccic2_pin2[] = {GPIO82, GPIO83, GPIO86, GPIO87, + GPIO88, GPIO89, GPIO90, GPIO91, GPIO92, GPIO93, GPIO94, GPIO95}; +static const unsigned mmp2_ulpi_pin1[] = {GPIO59, GPIO60, GPIO61, GPIO62, + GPIO63, GPIO64, GPIO65, GPIO66, GPIO67, GPIO68, GPIO69, GPIO70}; +static const unsigned mmp2_ro_pin1[] = {GPIO16, GPIO17}; +static const unsigned mmp2_ro_pin2[] = {GPIO18, GPIO19}; +static const unsigned mmp2_ro_pin3[] = {GPIO51, GPIO52}; +static const unsigned mmp2_ro_pin4[] = {GPIO55, GPIO56}; +static const unsigned mmp2_i2s_pin1[] = {GPIO24, GPIO25, GPIO26, GPIO27, + GPIO28}; +static const unsigned mmp2_i2s_pin2[] = {GPIO33, GPIO34, GPIO35, GPIO36}; +static const unsigned mmp2_ssp1_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40}; +static const unsigned mmp2_ssp1_pin2[] = {GPIO43, GPIO44, GPIO45, GPIO46}; +static const unsigned mmp2_ssp1_pin3[] = {GPIO115, GPIO116, GPIO117, GPIO118}; +static const unsigned mmp2_ssp2_pin1[] = {GPIO47, GPIO48, GPIO49, GPIO50}; +static const unsigned mmp2_ssp3_pin1[] = {GPIO119, GPIO120, GPIO121, GPIO122}; +static const unsigned mmp2_ssp3_pin2[] = {GPIO132, GPIO133, GPIO133, GPIO136}; +static const unsigned mmp2_sspa2_pin1[] = {GPIO25, GPIO26, GPIO27, GPIO28}; +static const unsigned mmp2_sspa2_pin2[] = {GPIO33, GPIO34, GPIO35, GPIO36}; +static const unsigned mmp2_mmc1_pin1[] = {GPIO131, GPIO132, GPIO133, GPIO134, + GPIO136, GPIO139, GPIO140, GPIO141}; +static const unsigned mmp2_mmc2_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40, + GPIO41, GPIO42}; +static const unsigned mmp2_mmc3_pin1[] = {GPIO111, GPIO112, GPIO151, GPIO162, + GPIO163, GPIO164, GPIO165, GPIO166, GPIO167, GPIO168}; + +static struct pxa3xx_pin_group mmp2_grps[] = { + GRP_MMP2("uart1 4p1", UART1, mmp2_uart1_pin1), + GRP_MMP2("uart1 2p2", UART1, mmp2_uart1_pin2), + GRP_MMP2("uart1 2p3", UART1, mmp2_uart1_pin3), + GRP_MMP2("uart2 4p1", UART2, mmp2_uart2_pin1), + GRP_MMP2("uart2 4p2", UART2, mmp2_uart2_pin2), + GRP_MMP2("uart2 4p3", UART2, mmp2_uart2_pin3), + GRP_MMP2("uart2 4p4", UART2, mmp2_uart2_pin4), + GRP_MMP2("uart2 2p5", UART2, mmp2_uart2_pin5), + GRP_MMP2("uart2 2p6", UART2, mmp2_uart2_pin6), + GRP_MMP2("uart3 4p1", UART3, mmp2_uart3_pin1), + GRP_MMP2("uart3 4p2", UART3, mmp2_uart3_pin2), + GRP_MMP2("uart3 4p3", UART3, mmp2_uart3_pin3), + GRP_MMP2("uart3 4p4", UART3, mmp2_uart3_pin4), + GRP_MMP2("uart3 4p5", UART3, mmp2_uart3_pin5), + GRP_MMP2("uart3 2p6", UART3, mmp2_uart3_pin6), + GRP_MMP2("uart4 4p1", UART4, mmp2_uart4_pin1), + GRP_MMP2("uart4 4p2", UART4, mmp2_uart4_pin2), + GRP_MMP2("uart4 4p3", UART4, mmp2_uart4_pin3), + GRP_MMP2("uart4 4p4", UART4, mmp2_uart4_pin4), + GRP_MMP2("uart4 2p5", UART4, mmp2_uart4_pin5), + GRP_MMP2("kpdk 4p1", KP_DK, mmp2_kpdk_pin1), + GRP_MMP2("kpdk 4p2", KP_DK, mmp2_kpdk_pin2), + GRP_MMP2("twsi2-1", TWSI2, mmp2_twsi2_pin1), + GRP_MMP2("twsi2-2", TWSI2, mmp2_twsi2_pin2), + GRP_MMP2("twsi2-3", TWSI2, mmp2_twsi2_pin3), + GRP_MMP2("twsi2-4", TWSI2, mmp2_twsi2_pin4), + GRP_MMP2("twsi2-5", TWSI2, mmp2_twsi2_pin5), + GRP_MMP2("twsi3-1", TWSI3, mmp2_twsi3_pin1), + GRP_MMP2("twsi3-2", TWSI3, mmp2_twsi3_pin2), + GRP_MMP2("twsi4", TWSI4, mmp2_twsi4_pin1), + GRP_MMP2("twsi5-1", TWSI5, mmp2_twsi5_pin1), + GRP_MMP2("twsi5-2", TWSI5, mmp2_twsi5_pin2), + GRP_MMP2("twsi5-3", TWSI5, mmp2_twsi5_pin3), + GRP_MMP2("twsi6-1", TWSI6, mmp2_twsi6_pin1), + GRP_MMP2("twsi6-2", TWSI6, mmp2_twsi6_pin2), + GRP_MMP2("twsi6-3", TWSI6, mmp2_twsi6_pin3), + GRP_MMP2("ccic1-1", CCIC1, mmp2_ccic1_pin1), + GRP_MMP2("ccic1-2", CCIC1, mmp2_ccic1_pin2), + GRP_MMP2("ccic2-1", CCIC2, mmp2_ccic2_pin1), + GRP_MMP2("ccic2-1", CCIC2, mmp2_ccic2_pin2), + GRP_MMP2("ulpi", ULPI, mmp2_ulpi_pin1), + GRP_MMP2("ro-1", ROT, mmp2_ro_pin1), + GRP_MMP2("ro-2", ROT, mmp2_ro_pin2), + GRP_MMP2("ro-3", ROT, mmp2_ro_pin3), + GRP_MMP2("ro-4", ROT, mmp2_ro_pin4), + GRP_MMP2("i2s 5p1", I2S, mmp2_i2s_pin1), + GRP_MMP2("i2s 4p2", I2S, mmp2_i2s_pin2), + GRP_MMP2("ssp1 4p1", SSP1, mmp2_ssp1_pin1), + GRP_MMP2("ssp1 4p2", SSP1, mmp2_ssp1_pin2), + GRP_MMP2("ssp1 4p3", SSP1, mmp2_ssp1_pin3), + GRP_MMP2("ssp2 4p1", SSP2, mmp2_ssp2_pin1), + GRP_MMP2("ssp3 4p1", SSP3, mmp2_ssp3_pin1), + GRP_MMP2("ssp3 4p2", SSP3, mmp2_ssp3_pin2), + GRP_MMP2("sspa2 4p1", SSPA2, mmp2_sspa2_pin1), + GRP_MMP2("sspa2 4p2", SSPA2, mmp2_sspa2_pin2), + GRP_MMP2("mmc1 8p1", MMC1, mmp2_mmc1_pin1), + GRP_MMP2("mmc2 6p1", MMC2, mmp2_mmc2_pin1), + GRP_MMP2("mmc3 10p1", MMC3, mmp2_mmc3_pin1), +}; + +static const char * const mmp2_uart1_grps[] = {"uart1 4p1", "uart1 2p2", + "uart1 2p3"}; +static const char * const mmp2_uart2_grps[] = {"uart2 4p1", "uart2 4p2", + "uart2 4p3", "uart2 4p4", "uart2 4p5", "uart2 4p6"}; +static const char * const mmp2_uart3_grps[] = {"uart3 4p1", "uart3 4p2", + "uart3 4p3", "uart3 4p4", "uart3 4p5", "uart3 2p6"}; +static const char * const mmp2_uart4_grps[] = {"uart4 4p1", "uart4 4p2", + "uart4 4p3", "uart4 4p4", "uart4 2p5"}; +static const char * const mmp2_kpdk_grps[] = {"kpdk 4p1", "kpdk 4p2"}; +static const char * const mmp2_twsi2_grps[] = {"twsi2-1", "twsi2-2", + "twsi2-3", "twsi2-4", "twsi2-5"}; +static const char * const mmp2_twsi3_grps[] = {"twsi3-1", "twsi3-2"}; +static const char * const mmp2_twsi4_grps[] = {"twsi4"}; +static const char * const mmp2_twsi5_grps[] = {"twsi5-1", "twsi5-2", + "twsi5-3"}; +static const char * const mmp2_twsi6_grps[] = {"twsi6-1", "twsi6-2", + "twsi6-3"}; +static const char * const mmp2_ccic1_grps[] = {"ccic1-1", "ccic1-2"}; +static const char * const mmp2_ccic2_grps[] = {"ccic2-1", "ccic2-2"}; +static const char * const mmp2_ulpi_grps[] = {"ulpi"}; +static const char * const mmp2_ro_grps[] = {"ro-1", "ro-2", "ro-3", "ro-4"}; +static const char * const mmp2_i2s_grps[] = {"i2s 5p1", "i2s 4p2"}; +static const char * const mmp2_ssp1_grps[] = {"ssp1 4p1", "ssp1 4p2", + "ssp1 4p3"}; +static const char * const mmp2_ssp2_grps[] = {"ssp2 4p1"}; +static const char * const mmp2_ssp3_grps[] = {"ssp3 4p1", "ssp3 4p2"}; +static const char * const mmp2_sspa2_grps[] = {"sspa2 4p1", "sspa2 4p2"}; +static const char * const mmp2_mmc1_grps[] = {"mmc1 8p1"}; +static const char * const mmp2_mmc2_grps[] = {"mmc2 6p1"}; +static const char * const mmp2_mmc3_grps[] = {"mmc3 10p1"}; + +static struct pxa3xx_pmx_func mmp2_funcs[] = { + {"uart1", ARRAY_AND_SIZE(mmp2_uart1_grps)}, + {"uart2", ARRAY_AND_SIZE(mmp2_uart2_grps)}, + {"uart3", ARRAY_AND_SIZE(mmp2_uart3_grps)}, + {"uart4", ARRAY_AND_SIZE(mmp2_uart4_grps)}, + {"kpdk", ARRAY_AND_SIZE(mmp2_kpdk_grps)}, + {"twsi2", ARRAY_AND_SIZE(mmp2_twsi2_grps)}, + {"twsi3", ARRAY_AND_SIZE(mmp2_twsi3_grps)}, + {"twsi4", ARRAY_AND_SIZE(mmp2_twsi4_grps)}, + {"twsi5", ARRAY_AND_SIZE(mmp2_twsi5_grps)}, + {"twsi6", ARRAY_AND_SIZE(mmp2_twsi6_grps)}, + {"ccic1", ARRAY_AND_SIZE(mmp2_ccic1_grps)}, + {"ccic2", ARRAY_AND_SIZE(mmp2_ccic2_grps)}, + {"ulpi", ARRAY_AND_SIZE(mmp2_ulpi_grps)}, + {"ro", ARRAY_AND_SIZE(mmp2_ro_grps)}, + {"i2s", ARRAY_AND_SIZE(mmp2_i2s_grps)}, + {"ssp1", ARRAY_AND_SIZE(mmp2_ssp1_grps)}, + {"ssp2", ARRAY_AND_SIZE(mmp2_ssp2_grps)}, + {"ssp3", ARRAY_AND_SIZE(mmp2_ssp3_grps)}, + {"sspa2", ARRAY_AND_SIZE(mmp2_sspa2_grps)}, + {"mmc1", ARRAY_AND_SIZE(mmp2_mmc1_grps)}, + {"mmc2", ARRAY_AND_SIZE(mmp2_mmc2_grps)}, + {"mmc3", ARRAY_AND_SIZE(mmp2_mmc3_grps)}, +}; + +static struct pinctrl_desc mmp2_pctrl_desc = { + .name = "mmp2-pinctrl", + .owner = THIS_MODULE, +}; + +static struct pxa3xx_pinmux_info mmp2_info = { + .mfp = mmp2_mfp, + .num_mfp = ARRAY_SIZE(mmp2_mfp), + .grps = mmp2_grps, + .num_grps = ARRAY_SIZE(mmp2_grps), + .funcs = mmp2_funcs, + .num_funcs = ARRAY_SIZE(mmp2_funcs), + .num_gpio = 169, + .desc = &mmp2_pctrl_desc, + .pads = mmp2_pads, + .num_pads = ARRAY_SIZE(mmp2_pads), + + .cputype = PINCTRL_MMP2, + .ds_mask = MMP2_DS_MASK, + .ds_shift = MMP2_DS_SHIFT, +}; + +static int __devinit mmp2_pinmux_probe(struct platform_device *pdev) +{ + return pxa3xx_pinctrl_register(pdev, &mmp2_info); +} + +static int __devexit mmp2_pinmux_remove(struct platform_device *pdev) +{ + return pxa3xx_pinctrl_unregister(pdev); +} + +static struct platform_driver mmp2_pinmux_driver = { + .driver = { + .name = "mmp2-pinmux", + .owner = THIS_MODULE, + }, + .probe = mmp2_pinmux_probe, + .remove = __devexit_p(mmp2_pinmux_remove), +}; + +static int __init mmp2_pinmux_init(void) +{ + return platform_driver_register(&mmp2_pinmux_driver); +} +core_initcall_sync(mmp2_pinmux_init); + +static void __exit mmp2_pinmux_exit(void) +{ + platform_driver_unregister(&mmp2_pinmux_driver); +} +module_exit(mmp2_pinmux_exit); + +MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); +MODULE_DESCRIPTION("PXA3xx pin control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/pinctrl-pxa168.c b/drivers/pinctrl/pinctrl-pxa168.c new file mode 100644 index 000000000000..c1997fa7f28c --- /dev/null +++ b/drivers/pinctrl/pinctrl-pxa168.c @@ -0,0 +1,651 @@ +/* + * linux/drivers/pinctrl/pinmux-pxa168.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * publishhed by the Free Software Foundation. + * + * Copyright (C) 2011, Marvell Technology Group Ltd. + * + * Author: Haojian Zhuang <haojian.zhuang@marvell.com> + * + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include "pinctrl-pxa3xx.h" + +#define PXA168_DS_MASK 0x1800 +#define PXA168_DS_SHIFT 11 +#define PXA168_SLEEP_MASK 0x38 +#define PXA168_SLEEP_SELECT (1 << 9) +#define PXA168_SLEEP_DATA (1 << 8) +#define PXA168_SLEEP_DIR (1 << 7) + +#define MFPR_168(a, r, f0, f1, f2, f3, f4, f5, f6, f7) \ + { \ + .name = #a, \ + .pin = a, \ + .mfpr = r, \ + .func = { \ + PXA168_MUX_##f0, \ + PXA168_MUX_##f1, \ + PXA168_MUX_##f2, \ + PXA168_MUX_##f3, \ + PXA168_MUX_##f4, \ + PXA168_MUX_##f5, \ + PXA168_MUX_##f6, \ + PXA168_MUX_##f7, \ + }, \ + } + +#define GRP_168(a, m, p) \ + { .name = a, .mux = PXA168_MUX_##m, .pins = p, .npins = ARRAY_SIZE(p), } + +/* 131 pins */ +enum pxa168_pin_list { + /* 0~122: GPIO0~GPIO122 */ + PWR_SCL = 123, + PWR_SDA, + TDI, + TMS, + TCK, + TDO, + TRST, + WAKEUP = 130, +}; + +enum pxa168_mux { + /* PXA3xx_MUX_GPIO = 0 (predefined in pinctrl-pxa3xx.h) */ + PXA168_MUX_GPIO = 0, + PXA168_MUX_DFIO, + PXA168_MUX_NAND, + PXA168_MUX_SMC, + PXA168_MUX_SMC_CS0, + PXA168_MUX_SMC_CS1, + PXA168_MUX_SMC_INT, + PXA168_MUX_SMC_RDY, + PXA168_MUX_MMC1, + PXA168_MUX_MMC2, + PXA168_MUX_MMC2_CMD, + PXA168_MUX_MMC2_CLK, + PXA168_MUX_MMC3, + PXA168_MUX_MMC3_CMD, + PXA168_MUX_MMC3_CLK, + PXA168_MUX_MMC4, + PXA168_MUX_MSP, + PXA168_MUX_MSP_DAT3, + PXA168_MUX_MSP_INS, + PXA168_MUX_I2C, + PXA168_MUX_PWRI2C, + PXA168_MUX_AC97, + PXA168_MUX_AC97_SYSCLK, + PXA168_MUX_PWM, + PXA168_MUX_PWM1, + PXA168_MUX_XD, + PXA168_MUX_XP, + PXA168_MUX_LCD, + PXA168_MUX_CCIC, + PXA168_MUX_CF, + PXA168_MUX_CF_RDY, + PXA168_MUX_CF_nINPACK, + PXA168_MUX_CF_nWAIT, + PXA168_MUX_KP_MKOUT, + PXA168_MUX_KP_MKIN, + PXA168_MUX_KP_DK, + PXA168_MUX_ETH, + PXA168_MUX_ETH_TX, + PXA168_MUX_ETH_RX, + PXA168_MUX_ONE_WIRE, + PXA168_MUX_UART1, + PXA168_MUX_UART1_TX, + PXA168_MUX_UART1_CTS, + PXA168_MUX_UART1_nRI, + PXA168_MUX_UART1_DTR, + PXA168_MUX_UART2, + PXA168_MUX_UART2_TX, + PXA168_MUX_UART3, + PXA168_MUX_UART3_TX, + PXA168_MUX_UART3_CTS, + PXA168_MUX_SSP1, + PXA168_MUX_SSP1_TX, + PXA168_MUX_SSP2, + PXA168_MUX_SSP2_TX, + PXA168_MUX_SSP3, + PXA168_MUX_SSP3_TX, + PXA168_MUX_SSP4, + PXA168_MUX_SSP4_TX, + PXA168_MUX_SSP5, + PXA168_MUX_SSP5_TX, + PXA168_MUX_USB, + PXA168_MUX_JTAG, + PXA168_MUX_RESET, + PXA168_MUX_WAKEUP, + PXA168_MUX_EXT_32K_IN, + PXA168_MUX_NONE = 0xffff, +}; + +static struct pinctrl_pin_desc pxa168_pads[] = { + PINCTRL_PIN(GPIO0, "GPIO0"), + PINCTRL_PIN(GPIO1, "GPIO1"), + PINCTRL_PIN(GPIO2, "GPIO2"), + PINCTRL_PIN(GPIO3, "GPIO3"), + PINCTRL_PIN(GPIO4, "GPIO4"), + PINCTRL_PIN(GPIO5, "GPIO5"), + PINCTRL_PIN(GPIO6, "GPIO6"), + PINCTRL_PIN(GPIO7, "GPIO7"), + PINCTRL_PIN(GPIO8, "GPIO8"), + PINCTRL_PIN(GPIO9, "GPIO9"), + PINCTRL_PIN(GPIO10, "GPIO10"), + PINCTRL_PIN(GPIO11, "GPIO11"), + PINCTRL_PIN(GPIO12, "GPIO12"), + PINCTRL_PIN(GPIO13, "GPIO13"), + PINCTRL_PIN(GPIO14, "GPIO14"), + PINCTRL_PIN(GPIO15, "GPIO15"), + PINCTRL_PIN(GPIO16, "GPIO16"), + PINCTRL_PIN(GPIO17, "GPIO17"), + PINCTRL_PIN(GPIO18, "GPIO18"), + PINCTRL_PIN(GPIO19, "GPIO19"), + PINCTRL_PIN(GPIO20, "GPIO20"), + PINCTRL_PIN(GPIO21, "GPIO21"), + PINCTRL_PIN(GPIO22, "GPIO22"), + PINCTRL_PIN(GPIO23, "GPIO23"), + PINCTRL_PIN(GPIO24, "GPIO24"), + PINCTRL_PIN(GPIO25, "GPIO25"), + PINCTRL_PIN(GPIO26, "GPIO26"), + PINCTRL_PIN(GPIO27, "GPIO27"), + PINCTRL_PIN(GPIO28, "GPIO28"), + PINCTRL_PIN(GPIO29, "GPIO29"), + PINCTRL_PIN(GPIO30, "GPIO30"), + PINCTRL_PIN(GPIO31, "GPIO31"), + PINCTRL_PIN(GPIO32, "GPIO32"), + PINCTRL_PIN(GPIO33, "GPIO33"), + PINCTRL_PIN(GPIO34, "GPIO34"), + PINCTRL_PIN(GPIO35, "GPIO35"), + PINCTRL_PIN(GPIO36, "GPIO36"), + PINCTRL_PIN(GPIO37, "GPIO37"), + PINCTRL_PIN(GPIO38, "GPIO38"), + PINCTRL_PIN(GPIO39, "GPIO39"), + PINCTRL_PIN(GPIO40, "GPIO40"), + PINCTRL_PIN(GPIO41, "GPIO41"), + PINCTRL_PIN(GPIO42, "GPIO42"), + PINCTRL_PIN(GPIO43, "GPIO43"), + PINCTRL_PIN(GPIO44, "GPIO44"), + PINCTRL_PIN(GPIO45, "GPIO45"), + PINCTRL_PIN(GPIO46, "GPIO46"), + PINCTRL_PIN(GPIO47, "GPIO47"), + PINCTRL_PIN(GPIO48, "GPIO48"), + PINCTRL_PIN(GPIO49, "GPIO49"), + PINCTRL_PIN(GPIO50, "GPIO50"), + PINCTRL_PIN(GPIO51, "GPIO51"), + PINCTRL_PIN(GPIO52, "GPIO52"), + PINCTRL_PIN(GPIO53, "GPIO53"), + PINCTRL_PIN(GPIO54, "GPIO54"), + PINCTRL_PIN(GPIO55, "GPIO55"), + PINCTRL_PIN(GPIO56, "GPIO56"), + PINCTRL_PIN(GPIO57, "GPIO57"), + PINCTRL_PIN(GPIO58, "GPIO58"), + PINCTRL_PIN(GPIO59, "GPIO59"), + PINCTRL_PIN(GPIO60, "GPIO60"), + PINCTRL_PIN(GPIO61, "GPIO61"), + PINCTRL_PIN(GPIO62, "GPIO62"), + PINCTRL_PIN(GPIO63, "GPIO63"), + PINCTRL_PIN(GPIO64, "GPIO64"), + PINCTRL_PIN(GPIO65, "GPIO65"), + PINCTRL_PIN(GPIO66, "GPIO66"), + PINCTRL_PIN(GPIO67, "GPIO67"), + PINCTRL_PIN(GPIO68, "GPIO68"), + PINCTRL_PIN(GPIO69, "GPIO69"), + PINCTRL_PIN(GPIO70, "GPIO70"), + PINCTRL_PIN(GPIO71, "GPIO71"), + PINCTRL_PIN(GPIO72, "GPIO72"), + PINCTRL_PIN(GPIO73, "GPIO73"), + PINCTRL_PIN(GPIO74, "GPIO74"), + PINCTRL_PIN(GPIO75, "GPIO75"), + PINCTRL_PIN(GPIO76, "GPIO76"), + PINCTRL_PIN(GPIO77, "GPIO77"), + PINCTRL_PIN(GPIO78, "GPIO78"), + PINCTRL_PIN(GPIO79, "GPIO79"), + PINCTRL_PIN(GPIO80, "GPIO80"), + PINCTRL_PIN(GPIO81, "GPIO81"), + PINCTRL_PIN(GPIO82, "GPIO82"), + PINCTRL_PIN(GPIO83, "GPIO83"), + PINCTRL_PIN(GPIO84, "GPIO84"), + PINCTRL_PIN(GPIO85, "GPIO85"), + PINCTRL_PIN(GPIO86, "GPIO86"), + PINCTRL_PIN(GPIO87, "GPIO87"), + PINCTRL_PIN(GPIO88, "GPIO88"), + PINCTRL_PIN(GPIO89, "GPIO89"), + PINCTRL_PIN(GPIO90, "GPIO90"), + PINCTRL_PIN(GPIO91, "GPIO91"), + PINCTRL_PIN(GPIO92, "GPIO92"), + PINCTRL_PIN(GPIO93, "GPIO93"), + PINCTRL_PIN(GPIO94, "GPIO94"), + PINCTRL_PIN(GPIO95, "GPIO95"), + PINCTRL_PIN(GPIO96, "GPIO96"), + PINCTRL_PIN(GPIO97, "GPIO97"), + PINCTRL_PIN(GPIO98, "GPIO98"), + PINCTRL_PIN(GPIO99, "GPIO99"), + PINCTRL_PIN(GPIO100, "GPIO100"), + PINCTRL_PIN(GPIO101, "GPIO101"), + PINCTRL_PIN(GPIO102, "GPIO102"), + PINCTRL_PIN(GPIO103, "GPIO103"), + PINCTRL_PIN(GPIO104, "GPIO104"), + PINCTRL_PIN(GPIO105, "GPIO105"), + PINCTRL_PIN(GPIO106, "GPIO106"), + PINCTRL_PIN(GPIO107, "GPIO107"), + PINCTRL_PIN(GPIO108, "GPIO108"), + PINCTRL_PIN(GPIO109, "GPIO109"), + PINCTRL_PIN(GPIO110, "GPIO110"), + PINCTRL_PIN(GPIO111, "GPIO111"), + PINCTRL_PIN(GPIO112, "GPIO112"), + PINCTRL_PIN(GPIO113, "GPIO113"), + PINCTRL_PIN(GPIO114, "GPIO114"), + PINCTRL_PIN(GPIO115, "GPIO115"), + PINCTRL_PIN(GPIO116, "GPIO116"), + PINCTRL_PIN(GPIO117, "GPIO117"), + PINCTRL_PIN(GPIO118, "GPIO118"), + PINCTRL_PIN(GPIO119, "GPIO119"), + PINCTRL_PIN(GPIO120, "GPIO120"), + PINCTRL_PIN(GPIO121, "GPIO121"), + PINCTRL_PIN(GPIO122, "GPIO122"), + PINCTRL_PIN(PWR_SCL, "PWR_SCL"), + PINCTRL_PIN(PWR_SDA, "PWR_SDA"), + PINCTRL_PIN(TDI, "TDI"), + PINCTRL_PIN(TMS, "TMS"), + PINCTRL_PIN(TCK, "TCK"), + PINCTRL_PIN(TDO, "TDO"), + PINCTRL_PIN(TRST, "TRST"), + PINCTRL_PIN(WAKEUP, "WAKEUP"), +}; + +struct pxa3xx_mfp_pin pxa168_mfp[] = { + /* pin offs f0 f1 f2 f3 f4 f5 f6 f7 */ + MFPR_168(GPIO0, 0x04C, DFIO, NONE, NONE, MSP, MMC3_CMD, GPIO, MMC3, NONE), + MFPR_168(GPIO1, 0x050, DFIO, NONE, NONE, MSP, MMC3_CLK, GPIO, MMC3, NONE), + MFPR_168(GPIO2, 0x054, DFIO, NONE, NONE, MSP, NONE, GPIO, MMC3, NONE), + MFPR_168(GPIO3, 0x058, DFIO, NONE, NONE, NONE, NONE, GPIO, MMC3, NONE), + MFPR_168(GPIO4, 0x05C, DFIO, NONE, NONE, MSP_DAT3, NONE, GPIO, MMC3, NONE), + MFPR_168(GPIO5, 0x060, DFIO, NONE, NONE, MSP, NONE, GPIO, MMC3, NONE), + MFPR_168(GPIO6, 0x064, DFIO, NONE, NONE, MSP, NONE, GPIO, MMC3, NONE), + MFPR_168(GPIO7, 0x068, DFIO, NONE, NONE, MSP, NONE, GPIO, MMC3, NONE), + MFPR_168(GPIO8, 0x06C, DFIO, MMC2, UART3_TX, NONE, MMC2_CMD, GPIO, MMC3_CLK, NONE), + MFPR_168(GPIO9, 0x070, DFIO, MMC2, UART3, NONE, MMC2_CLK, GPIO, MMC3_CMD, NONE), + MFPR_168(GPIO10, 0x074, DFIO, MMC2, UART3, NONE, NONE, GPIO, MSP_DAT3, NONE), + MFPR_168(GPIO11, 0x078, DFIO, MMC2, UART3, NONE, NONE, GPIO, MSP, NONE), + MFPR_168(GPIO12, 0x07C, DFIO, MMC2, UART3, NONE, NONE, GPIO, MSP, NONE), + MFPR_168(GPIO13, 0x080, DFIO, MMC2, UART3, NONE, NONE, GPIO, MSP, NONE), + MFPR_168(GPIO14, 0x084, DFIO, MMC2, NONE, NONE, NONE, GPIO, MSP, NONE), + MFPR_168(GPIO15, 0x088, DFIO, MMC2, NONE, NONE, NONE, GPIO, MSP, NONE), + MFPR_168(GPIO16, 0x08C, GPIO, NAND, SMC_CS0, SMC_CS1, NONE, NONE, MMC3, NONE), + MFPR_168(GPIO17, 0x090, NAND, NONE, NONE, NONE, NONE, GPIO, MSP, NONE), + MFPR_168(GPIO18, 0x094, GPIO, NAND, SMC_CS1, SMC_CS0, NONE, NONE, NONE, NONE), + MFPR_168(GPIO19, 0x098, SMC_CS0, NONE, NONE, CF, NONE, GPIO, NONE, NONE), + MFPR_168(GPIO20, 0x09C, GPIO, NONE, SMC_CS1, CF, CF_RDY, NONE, NONE, NONE), + MFPR_168(GPIO21, 0x0A0, NAND, MMC2_CLK, NONE, NONE, NONE, GPIO, NONE, NONE), + MFPR_168(GPIO22, 0x0A4, NAND, MMC2_CMD, NONE, NONE, NONE, GPIO, NONE, NONE), + MFPR_168(GPIO23, 0x0A8, SMC, NAND, NONE, CF, NONE, GPIO, NONE, NONE), + MFPR_168(GPIO24, 0x0AC, NAND, NONE, NONE, NONE, NONE, GPIO, NONE, NONE), + MFPR_168(GPIO25, 0x0B0, SMC, NAND, NONE, CF, NONE, GPIO, NONE, NONE), + MFPR_168(GPIO26, 0x0B4, GPIO, NAND, NONE, NONE, CF, NONE, NONE, NONE), + MFPR_168(GPIO27, 0x0B8, SMC_INT, NAND, SMC, NONE, SMC_RDY, GPIO, NONE, NONE), + MFPR_168(GPIO28, 0x0BC, SMC_RDY, MMC4, SMC, CF_RDY, NONE, GPIO, MMC2_CMD, NONE), + MFPR_168(GPIO29, 0x0C0, SMC, MMC4, NONE, CF, NONE, GPIO, MMC2_CLK, KP_DK), + MFPR_168(GPIO30, 0x0C4, SMC, MMC4, UART3_TX, CF, NONE, GPIO, MMC2, KP_DK), + MFPR_168(GPIO31, 0x0C8, SMC, MMC4, UART3, CF, NONE, GPIO, MMC2, KP_DK), + MFPR_168(GPIO32, 0x0CC, SMC, MMC4, UART3, CF, NONE, GPIO, MMC2, KP_DK), + MFPR_168(GPIO33, 0x0D0, SMC, MMC4, UART3, CF, CF_nINPACK, GPIO, MMC2, KP_DK), + MFPR_168(GPIO34, 0x0D4, GPIO, NONE, SMC_CS1, CF, CF_nWAIT, NONE, MMC3, KP_DK), + MFPR_168(GPIO35, 0x0D8, GPIO, NONE, SMC, CF_nINPACK, NONE, NONE, MMC3_CMD, KP_DK), + MFPR_168(GPIO36, 0x0DC, GPIO, NONE, SMC, CF_nWAIT, NONE, NONE, MMC3_CLK, KP_DK), + MFPR_168(GPIO37, 0x000, GPIO, MMC1, NONE, KP_MKOUT, CCIC, XP, KP_MKIN, KP_DK), + MFPR_168(GPIO38, 0x004, GPIO, MMC1, NONE, KP_MKOUT, CCIC, XP, KP_MKIN, KP_DK), + MFPR_168(GPIO39, 0x008, GPIO, NONE, NONE, KP_MKOUT, CCIC, XP, KP_MKIN, KP_DK), + MFPR_168(GPIO40, 0x00C, GPIO, MMC1, MSP, KP_MKOUT, CCIC, XP, KP_MKIN, KP_DK), + MFPR_168(GPIO41, 0x010, GPIO, MMC1, MSP, NONE, CCIC, XP, KP_MKIN, KP_DK), + MFPR_168(GPIO42, 0x014, GPIO, I2C, NONE, MSP, CCIC, XP, KP_MKIN, KP_DK), + MFPR_168(GPIO43, 0x018, GPIO, MMC1, MSP, MSP_INS, NONE, NONE, KP_MKIN, KP_DK), + MFPR_168(GPIO44, 0x01C, GPIO, MMC1, MSP_DAT3, MSP, CCIC, XP, KP_MKIN, KP_DK), + MFPR_168(GPIO45, 0x020, GPIO, NONE, NONE, MSP, CCIC, XP, NONE, KP_DK), + MFPR_168(GPIO46, 0x024, GPIO, MMC1, MSP_INS, MSP, CCIC, NONE, KP_MKOUT, KP_DK), + MFPR_168(GPIO47, 0x028, GPIO, NONE, NONE, MSP_INS, NONE, XP, NONE, KP_DK), + MFPR_168(GPIO48, 0x02C, GPIO, MMC1, NONE, MSP_DAT3, CCIC, NONE, NONE, KP_DK), + MFPR_168(GPIO49, 0x030, GPIO, MMC1, NONE, MSP, NONE, XD, KP_MKOUT, NONE), + MFPR_168(GPIO50, 0x034, GPIO, I2C, NONE, MSP, CCIC, XD, KP_MKOUT, NONE), + MFPR_168(GPIO51, 0x038, GPIO, MMC1, NONE, MSP, NONE, XD, KP_MKOUT, NONE), + MFPR_168(GPIO52, 0x03C, GPIO, MMC1, NONE, MSP, NONE, XD, KP_MKOUT, NONE), + MFPR_168(GPIO53, 0x040, GPIO, MMC1, NONE, NONE, NONE, XD, KP_MKOUT, NONE), + MFPR_168(GPIO54, 0x044, GPIO, MMC1, NONE, NONE, CCIC, XD, KP_MKOUT, NONE), + MFPR_168(GPIO55, 0x048, GPIO, NONE, NONE, MSP, CCIC, XD, KP_MKOUT, NONE), + MFPR_168(GPIO56, 0x0E0, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_168(GPIO57, 0x0E4, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_168(GPIO58, 0x0E8, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_168(GPIO59, 0x0EC, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_168(GPIO60, 0x0F0, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_168(GPIO61, 0x0F4, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_168(GPIO62, 0x0F8, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_168(GPIO63, 0x0FC, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_168(GPIO64, 0x100, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_168(GPIO65, 0x104, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_168(GPIO66, 0x108, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_168(GPIO67, 0x10C, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_168(GPIO68, 0x110, GPIO, LCD, NONE, XD, NONE, NONE, NONE, NONE), + MFPR_168(GPIO69, 0x114, GPIO, LCD, NONE, XD, NONE, NONE, NONE, NONE), + MFPR_168(GPIO70, 0x118, GPIO, LCD, NONE, XD, NONE, NONE, NONE, NONE), + MFPR_168(GPIO71, 0x11C, GPIO, LCD, NONE, XD, NONE, NONE, NONE, NONE), + MFPR_168(GPIO72, 0x120, GPIO, LCD, NONE, XD, NONE, NONE, NONE, NONE), + MFPR_168(GPIO73, 0x124, GPIO, LCD, NONE, XD, NONE, NONE, NONE, NONE), + MFPR_168(GPIO74, 0x128, GPIO, LCD, PWM, XD, NONE, NONE, NONE, NONE), + MFPR_168(GPIO75, 0x12C, GPIO, LCD, PWM, XD, ONE_WIRE, NONE, NONE, NONE), + MFPR_168(GPIO76, 0x130, GPIO, LCD, PWM, I2C, NONE, NONE, MSP_INS, NONE), + MFPR_168(GPIO77, 0x134, GPIO, LCD, PWM1, I2C, ONE_WIRE, NONE, XD, NONE), + MFPR_168(GPIO78, 0x138, GPIO, LCD, NONE, NONE, NONE, MMC4, NONE, NONE), + MFPR_168(GPIO79, 0x13C, GPIO, LCD, NONE, NONE, ONE_WIRE, MMC4, NONE, NONE), + MFPR_168(GPIO80, 0x140, GPIO, LCD, NONE, I2C, NONE, MMC4, NONE, NONE), + MFPR_168(GPIO81, 0x144, GPIO, LCD, NONE, I2C, ONE_WIRE, MMC4, NONE, NONE), + MFPR_168(GPIO82, 0x148, GPIO, LCD, PWM, NONE, NONE, MMC4, NONE, NONE), + MFPR_168(GPIO83, 0x14C, GPIO, LCD, PWM, NONE, RESET, MMC4, NONE, NONE), + MFPR_168(GPIO84, 0x150, GPIO, NONE, PWM, ONE_WIRE, PWM1, NONE, NONE, EXT_32K_IN), + MFPR_168(GPIO85, 0x154, GPIO, NONE, PWM1, NONE, NONE, NONE, NONE, USB), + MFPR_168(GPIO86, 0x158, GPIO, MMC2, UART2, NONE, JTAG, ETH_TX, SSP5_TX, SSP5), + MFPR_168(GPIO87, 0x15C, GPIO, MMC2, UART2, NONE, JTAG, ETH_TX, SSP5, SSP5_TX), + MFPR_168(GPIO88, 0x160, GPIO, MMC2, UART2, UART2_TX, JTAG, ETH_TX, ETH_RX, SSP5), + MFPR_168(GPIO89, 0x164, GPIO, MMC2, UART2_TX, UART2, JTAG, ETH_TX, ETH_RX, SSP5), + MFPR_168(GPIO90, 0x168, GPIO, MMC2, NONE, SSP3, JTAG, ETH_TX, ETH_RX, NONE), + MFPR_168(GPIO91, 0x16C, GPIO, MMC2, NONE, SSP3, SSP4, ETH_TX, ETH_RX, NONE), + MFPR_168(GPIO92, 0x170, GPIO, MMC2, NONE, SSP3, SSP3_TX, ETH, NONE, NONE), + MFPR_168(GPIO93, 0x174, GPIO, MMC2, NONE, SSP3_TX, SSP3, ETH, NONE, NONE), + MFPR_168(GPIO94, 0x178, GPIO, MMC2_CMD, SSP3, AC97_SYSCLK, AC97, ETH, NONE, NONE), + MFPR_168(GPIO95, 0x17C, GPIO, MMC2_CLK, NONE, NONE, AC97, ETH, NONE, NONE), + MFPR_168(GPIO96, 0x180, GPIO, PWM, NONE, MMC2, NONE, ETH_RX, ETH_TX, NONE), + MFPR_168(GPIO97, 0x184, GPIO, PWM, ONE_WIRE, NONE, NONE, ETH_RX, ETH_TX, NONE), + MFPR_168(GPIO98, 0x188, GPIO, PWM1, UART3_TX, UART3, NONE, ETH_RX, ETH_TX, NONE), + MFPR_168(GPIO99, 0x18C, GPIO, ONE_WIRE, UART3, UART3_TX, NONE, ETH_RX, ETH_TX, NONE), + MFPR_168(GPIO100, 0x190, GPIO, NONE, UART3_CTS, UART3, NONE, ETH, NONE, NONE), + MFPR_168(GPIO101, 0x194, GPIO, NONE, UART3, UART3_CTS, NONE, ETH, NONE, NONE), + MFPR_168(GPIO102, 0x198, GPIO, I2C, UART3, SSP4, NONE, NONE, NONE, NONE), + MFPR_168(GPIO103, 0x19C, GPIO, I2C, UART3, SSP4, SSP2, ETH, NONE, NONE), + MFPR_168(GPIO104, 0x1A0, GPIO, PWM, UART1, SSP4, SSP4_TX, AC97, KP_MKOUT, NONE), + MFPR_168(GPIO105, 0x1A4, GPIO, I2C, UART1, SSP4_TX, SSP4, AC97, KP_MKOUT, NONE), + MFPR_168(GPIO106, 0x1A8, GPIO, I2C, PWM1, AC97_SYSCLK, MMC2, NONE, KP_MKOUT, NONE), + MFPR_168(GPIO107, 0x1AC, GPIO, UART1_TX, UART1, NONE, SSP2, MSP_DAT3, NONE, KP_MKIN), + MFPR_168(GPIO108, 0x1B0, GPIO, UART1, UART1_TX, NONE, SSP2_TX, MSP, NONE, KP_MKIN), + MFPR_168(GPIO109, 0x1B4, GPIO, UART1_CTS, UART1, NONE, AC97_SYSCLK, MSP, NONE, KP_MKIN), + MFPR_168(GPIO110, 0x1B8, GPIO, UART1, UART1_CTS, NONE, SMC_RDY, MSP, NONE, KP_MKIN), + MFPR_168(GPIO111, 0x1BC, GPIO, UART1_nRI, UART1, SSP3, SSP2, MSP, XD, KP_MKOUT), + MFPR_168(GPIO112, 0x1C0, GPIO, UART1_DTR, UART1, ONE_WIRE, SSP2, MSP, XD, KP_MKOUT), + MFPR_168(GPIO113, 0x1C4, GPIO, NONE, NONE, NONE, NONE, NONE, AC97_SYSCLK, NONE), + MFPR_168(GPIO114, 0x1C8, GPIO, SSP1, NONE, NONE, NONE, NONE, AC97, NONE), + MFPR_168(GPIO115, 0x1CC, GPIO, SSP1, NONE, NONE, NONE, NONE, AC97, NONE), + MFPR_168(GPIO116, 0x1D0, GPIO, SSP1_TX, SSP1, NONE, NONE, NONE, AC97, NONE), + MFPR_168(GPIO117, 0x1D4, GPIO, SSP1, SSP1_TX, NONE, MMC2_CMD, NONE, AC97, NONE), + MFPR_168(GPIO118, 0x1D8, GPIO, SSP2, NONE, NONE, MMC2_CLK, NONE, AC97, KP_MKIN), + MFPR_168(GPIO119, 0x1DC, GPIO, SSP2, NONE, NONE, MMC2, NONE, AC97, KP_MKIN), + MFPR_168(GPIO120, 0x1E0, GPIO, SSP2, SSP2_TX, NONE, MMC2, NONE, NONE, KP_MKIN), + MFPR_168(GPIO121, 0x1E4, GPIO, SSP2_TX, SSP2, NONE, MMC2, NONE, NONE, KP_MKIN), + MFPR_168(GPIO122, 0x1E8, GPIO, AC97_SYSCLK, SSP2, PWM, MMC2, NONE, NONE, NONE), + MFPR_168(PWR_SCL, 0x1EC, PWRI2C, NONE, NONE, NONE, NONE, NONE, GPIO, MMC4), + MFPR_168(PWR_SDA, 0x1F0, PWRI2C, NONE, NONE, NONE, NONE, NONE, GPIO, NONE), + MFPR_168(TDI, 0x1F4, JTAG, PWM1, UART2, MMC4, SSP5, NONE, XD, MMC4), + MFPR_168(TMS, 0x1F8, JTAG, PWM, UART2, NONE, SSP5, NONE, XD, MMC4), + MFPR_168(TCK, 0x1FC, JTAG, PWM, UART2, UART2_TX, SSP5, NONE, XD, MMC4), + MFPR_168(TDO, 0x200, JTAG, PWM, UART2_TX, UART2, SSP5_TX, NONE, XD, MMC4), + MFPR_168(TRST, 0x204, JTAG, ONE_WIRE, SSP2, SSP3, AC97_SYSCLK, NONE, XD, MMC4), + MFPR_168(WAKEUP, 0x208, WAKEUP, ONE_WIRE, PWM1, PWM, SSP2, NONE, GPIO, MMC4), +}; + +static const unsigned p168_jtag_pin1[] = {TDI, TMS, TCK, TDO, TRST}; +static const unsigned p168_wakeup_pin1[] = {WAKEUP}; +static const unsigned p168_ssp1rx_pin1[] = {GPIO114, GPIO115, GPIO116}; +static const unsigned p168_ssp1tx_pin1[] = {GPIO117}; +static const unsigned p168_ssp4rx_pin1[] = {GPIO102, GPIO103, GPIO104}; +static const unsigned p168_ssp4tx_pin1[] = {GPIO105}; +static const unsigned p168_ssp5rx_pin1[] = {GPIO86, GPIO88, GPIO89}; +static const unsigned p168_ssp5tx_pin1[] = {GPIO87}; +static const unsigned p168_i2c_pin1[] = {GPIO105, GPIO106}; +static const unsigned p168_pwri2c_pin1[] = {PWR_SCL, PWR_SDA}; +static const unsigned p168_mmc1_pin1[] = {GPIO40, GPIO41, GPIO43, GPIO46, + GPIO49, GPIO51, GPIO52, GPIO53}; +static const unsigned p168_mmc2_data_pin1[] = {GPIO90, GPIO91, GPIO92, GPIO93}; +static const unsigned p168_mmc2_cmd_pin1[] = {GPIO94}; +static const unsigned p168_mmc2_clk_pin1[] = {GPIO95}; +static const unsigned p168_mmc3_data_pin1[] = {GPIO0, GPIO1, GPIO2, GPIO3, + GPIO4, GPIO5, GPIO6, GPIO7}; +static const unsigned p168_mmc3_cmd_pin1[] = {GPIO9}; +static const unsigned p168_mmc3_clk_pin1[] = {GPIO8}; +static const unsigned p168_eth_pin1[] = {GPIO92, GPIO93, GPIO100, GPIO101, + GPIO103}; +static const unsigned p168_ethtx_pin1[] = {GPIO86, GPIO87, GPIO88, GPIO89, + GPIO90, GPIO91}; +static const unsigned p168_ethrx_pin1[] = {GPIO94, GPIO95, GPIO96, GPIO97, + GPIO98, GPIO99}; +static const unsigned p168_uart1rx_pin1[] = {GPIO107}; +static const unsigned p168_uart1tx_pin1[] = {GPIO108}; +static const unsigned p168_uart3rx_pin1[] = {GPIO98, GPIO100, GPIO101}; +static const unsigned p168_uart3tx_pin1[] = {GPIO99}; +static const unsigned p168_msp_pin1[] = {GPIO40, GPIO41, GPIO42, GPIO43, + GPIO44, GPIO50}; +static const unsigned p168_ccic_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40, + GPIO41, GPIO42, GPIO44, GPIO45, GPIO46, GPIO48, GPIO54, GPIO55}; +static const unsigned p168_xd_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40, + GPIO41, GPIO42, GPIO44, GPIO45, GPIO47, GPIO48, GPIO49, GPIO50, + GPIO51, GPIO52}; +static const unsigned p168_lcd_pin1[] = {GPIO56, GPIO57, GPIO58, GPIO59, + GPIO60, GPIO61, GPIO62, GPIO63, GPIO64, GPIO65, GPIO66, GPIO67, + GPIO68, GPIO69, GPIO70, GPIO71, GPIO72, GPIO73, GPIO74, GPIO75, + GPIO76, GPIO77, GPIO78, GPIO79, GPIO80, GPIO81, GPIO82, GPIO83}; +static const unsigned p168_dfio_pin1[] = {GPIO0, GPIO1, GPIO2, GPIO3, + GPIO4, GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO10, GPIO11, GPIO12, + GPIO13, GPIO14, GPIO15}; +static const unsigned p168_nand_pin1[] = {GPIO16, GPIO17, GPIO21, GPIO22, + GPIO24, GPIO26}; +static const unsigned p168_smc_pin1[] = {GPIO23, GPIO25, GPIO29, GPIO35, + GPIO36}; +static const unsigned p168_smccs0_pin1[] = {GPIO18}; +static const unsigned p168_smccs1_pin1[] = {GPIO34}; +static const unsigned p168_smcrdy_pin1[] = {GPIO28}; +static const unsigned p168_ac97sysclk_pin1[] = {GPIO113}; +static const unsigned p168_ac97_pin1[] = {GPIO114, GPIO115, GPIO117, GPIO118, + GPIO119}; +static const unsigned p168_cf_pin1[] = {GPIO19, GPIO20, GPIO23, GPIO25, + GPIO28, GPIO29, GPIO30, GPIO31, GPIO32, GPIO33, GPIO34, GPIO35, + GPIO36}; +static const unsigned p168_kpmkin_pin1[] = {GPIO109, GPIO110, GPIO121}; +static const unsigned p168_kpmkout_pin1[] = {GPIO111, GPIO112}; +static const unsigned p168_gpio86_pin1[] = {WAKEUP}; +static const unsigned p168_gpio86_pin2[] = {GPIO86}; +static const unsigned p168_gpio87_pin1[] = {GPIO87}; +static const unsigned p168_gpio87_pin2[] = {PWR_SDA}; +static const unsigned p168_gpio88_pin1[] = {GPIO88}; +static const unsigned p168_gpio88_pin2[] = {PWR_SCL}; + +static struct pxa3xx_pin_group pxa168_grps[] = { + GRP_168("uart1rx-1", UART1, p168_uart1rx_pin1), + GRP_168("uart1tx-1", UART1_TX, p168_uart1tx_pin1), + GRP_168("uart3rx-1", UART3, p168_uart3rx_pin1), + GRP_168("uart3tx-1", UART3_TX, p168_uart3tx_pin1), + GRP_168("ssp1rx-1", SSP1, p168_ssp1rx_pin1), + GRP_168("ssp1tx-1", SSP1_TX, p168_ssp1tx_pin1), + GRP_168("ssp4rx-1", SSP4, p168_ssp4rx_pin1), + GRP_168("ssp4tx-1", SSP4_TX, p168_ssp4tx_pin1), + GRP_168("ssp5rx-1", SSP5, p168_ssp5rx_pin1), + GRP_168("ssp5tx-1", SSP5_TX, p168_ssp5tx_pin1), + GRP_168("jtag", JTAG, p168_jtag_pin1), + GRP_168("wakeup", WAKEUP, p168_wakeup_pin1), + GRP_168("i2c", I2C, p168_i2c_pin1), + GRP_168("pwri2c", PWRI2C, p168_pwri2c_pin1), + GRP_168("mmc1 8p1", MMC1, p168_mmc1_pin1), + GRP_168("mmc2 4p1", MMC2, p168_mmc2_data_pin1), + GRP_168("mmc2 cmd1", MMC2_CMD, p168_mmc2_cmd_pin1), + GRP_168("mmc2 clk1", MMC2_CLK, p168_mmc2_clk_pin1), + GRP_168("mmc3 8p1", MMC3, p168_mmc3_data_pin1), + GRP_168("mmc3 cmd1", MMC3_CMD, p168_mmc3_cmd_pin1), + GRP_168("mmc3 clk1", MMC3_CLK, p168_mmc3_clk_pin1), + GRP_168("eth", ETH, p168_eth_pin1), + GRP_168("eth rx", ETH_RX, p168_ethrx_pin1), + GRP_168("eth tx", ETH_TX, p168_ethtx_pin1), + GRP_168("msp", MSP, p168_msp_pin1), + GRP_168("ccic", CCIC, p168_ccic_pin1), + GRP_168("xd", XD, p168_xd_pin1), + GRP_168("lcd", LCD, p168_lcd_pin1), + GRP_168("dfio", DFIO, p168_dfio_pin1), + GRP_168("nand", NAND, p168_nand_pin1), + GRP_168("smc", SMC, p168_smc_pin1), + GRP_168("smc cs0", SMC_CS0, p168_smccs0_pin1), + GRP_168("smc cs1", SMC_CS1, p168_smccs1_pin1), + GRP_168("smc rdy", SMC_RDY, p168_smcrdy_pin1), + GRP_168("ac97 sysclk", AC97_SYSCLK, p168_ac97sysclk_pin1), + GRP_168("ac97", AC97, p168_ac97_pin1), + GRP_168("cf", CF, p168_cf_pin1), + GRP_168("kp mkin 3p1", KP_MKIN, p168_kpmkin_pin1), + GRP_168("kp mkout 2p1", KP_MKOUT, p168_kpmkout_pin1), + GRP_168("gpio86-1", GPIO, p168_gpio86_pin1), + GRP_168("gpio86-2", GPIO, p168_gpio86_pin2), + GRP_168("gpio87-1", GPIO, p168_gpio87_pin1), + GRP_168("gpio87-2", GPIO, p168_gpio87_pin2), + GRP_168("gpio88-1", GPIO, p168_gpio88_pin1), + GRP_168("gpio88-2", GPIO, p168_gpio88_pin2), +}; + +static const char * const p168_uart1rx_grps[] = {"uart1rx-1"}; +static const char * const p168_uart1tx_grps[] = {"uart1tx-1"}; +static const char * const p168_uart3rx_grps[] = {"uart3rx-1"}; +static const char * const p168_uart3tx_grps[] = {"uart3tx-1"}; +static const char * const p168_ssp1rx_grps[] = {"ssp1rx-1"}; +static const char * const p168_ssp1tx_grps[] = {"ssp1tx-1"}; +static const char * const p168_ssp4rx_grps[] = {"ssp4rx-1"}; +static const char * const p168_ssp4tx_grps[] = {"ssp4tx-1"}; +static const char * const p168_ssp5rx_grps[] = {"ssp5rx-1"}; +static const char * const p168_ssp5tx_grps[] = {"ssp5tx-1"}; +static const char * const p168_i2c_grps[] = {"i2c"}; +static const char * const p168_pwri2c_grps[] = {"pwri2c"}; +static const char * const p168_mmc1_grps[] = {"mmc1 8p1"}; +static const char * const p168_mmc2_data_grps[] = {"mmc2 4p1"}; +static const char * const p168_mmc2_cmd_grps[] = {"mmc2 cmd1"}; +static const char * const p168_mmc2_clk_grps[] = {"mmc2 clk1"}; +static const char * const p168_mmc3_data_grps[] = {"mmc3 8p1"}; +static const char * const p168_mmc3_cmd_grps[] = {"mmc3 cmd1"}; +static const char * const p168_mmc3_clk_grps[] = {"mmc3 clk1"}; +static const char * const p168_eth_grps[] = {"eth"}; +static const char * const p168_ethrx_grps[] = {"eth rx"}; +static const char * const p168_ethtx_grps[] = {"eth tx"}; +static const char * const p168_msp_grps[] = {"msp"}; +static const char * const p168_ccic_grps[] = {"ccic"}; +static const char * const p168_xd_grps[] = {"xd"}; +static const char * const p168_lcd_grps[] = {"lcd"}; +static const char * const p168_dfio_grps[] = {"dfio"}; +static const char * const p168_nand_grps[] = {"nand"}; +static const char * const p168_smc_grps[] = {"smc"}; +static const char * const p168_smccs0_grps[] = {"smc cs0"}; +static const char * const p168_smccs1_grps[] = {"smc cs1"}; +static const char * const p168_smcrdy_grps[] = {"smc rdy"}; +static const char * const p168_ac97sysclk_grps[] = {"ac97 sysclk"}; +static const char * const p168_ac97_grps[] = {"ac97"}; +static const char * const p168_cf_grps[] = {"cf"}; +static const char * const p168_kpmkin_grps[] = {"kp mkin 3p1"}; +static const char * const p168_kpmkout_grps[] = {"kp mkout 2p1"}; +static const char * const p168_gpio86_grps[] = {"gpio86-1", "gpio86-2"}; +static const char * const p168_gpio87_grps[] = {"gpio87-1", "gpio87-2"}; +static const char * const p168_gpio88_grps[] = {"gpio88-1", "gpio88-2"}; + +static struct pxa3xx_pmx_func pxa168_funcs[] = { + {"uart1 rx", ARRAY_AND_SIZE(p168_uart1rx_grps)}, + {"uart1 tx", ARRAY_AND_SIZE(p168_uart1tx_grps)}, + {"uart3 rx", ARRAY_AND_SIZE(p168_uart3rx_grps)}, + {"uart3 tx", ARRAY_AND_SIZE(p168_uart3tx_grps)}, + {"ssp1 rx", ARRAY_AND_SIZE(p168_ssp1rx_grps)}, + {"ssp1 tx", ARRAY_AND_SIZE(p168_ssp1tx_grps)}, + {"ssp4 rx", ARRAY_AND_SIZE(p168_ssp4rx_grps)}, + {"ssp4 tx", ARRAY_AND_SIZE(p168_ssp4tx_grps)}, + {"ssp5 rx", ARRAY_AND_SIZE(p168_ssp5rx_grps)}, + {"ssp5 tx", ARRAY_AND_SIZE(p168_ssp5tx_grps)}, + {"i2c", ARRAY_AND_SIZE(p168_i2c_grps)}, + {"pwri2c", ARRAY_AND_SIZE(p168_pwri2c_grps)}, + {"mmc1", ARRAY_AND_SIZE(p168_mmc1_grps)}, + {"mmc2", ARRAY_AND_SIZE(p168_mmc2_data_grps)}, + {"mmc2 cmd", ARRAY_AND_SIZE(p168_mmc2_cmd_grps)}, + {"mmc2 clk", ARRAY_AND_SIZE(p168_mmc2_clk_grps)}, + {"mmc3", ARRAY_AND_SIZE(p168_mmc3_data_grps)}, + {"mmc3 cmd", ARRAY_AND_SIZE(p168_mmc3_cmd_grps)}, + {"mmc3 clk", ARRAY_AND_SIZE(p168_mmc3_clk_grps)}, + {"eth", ARRAY_AND_SIZE(p168_eth_grps)}, + {"eth rx", ARRAY_AND_SIZE(p168_ethrx_grps)}, + {"eth tx", ARRAY_AND_SIZE(p168_ethtx_grps)}, + {"msp", ARRAY_AND_SIZE(p168_msp_grps)}, + {"ccic", ARRAY_AND_SIZE(p168_ccic_grps)}, + {"xd", ARRAY_AND_SIZE(p168_xd_grps)}, + {"lcd", ARRAY_AND_SIZE(p168_lcd_grps)}, + {"dfio", ARRAY_AND_SIZE(p168_dfio_grps)}, + {"nand", ARRAY_AND_SIZE(p168_nand_grps)}, + {"smc", ARRAY_AND_SIZE(p168_smc_grps)}, + {"smc cs0", ARRAY_AND_SIZE(p168_smccs0_grps)}, + {"smc cs1", ARRAY_AND_SIZE(p168_smccs1_grps)}, + {"smc rdy", ARRAY_AND_SIZE(p168_smcrdy_grps)}, + {"ac97", ARRAY_AND_SIZE(p168_ac97_grps)}, + {"ac97 sysclk", ARRAY_AND_SIZE(p168_ac97sysclk_grps)}, + {"cf", ARRAY_AND_SIZE(p168_cf_grps)}, + {"kpmkin", ARRAY_AND_SIZE(p168_kpmkin_grps)}, + {"kpmkout", ARRAY_AND_SIZE(p168_kpmkout_grps)}, + {"gpio86", ARRAY_AND_SIZE(p168_gpio86_grps)}, + {"gpio87", ARRAY_AND_SIZE(p168_gpio87_grps)}, + {"gpio88", ARRAY_AND_SIZE(p168_gpio88_grps)}, +}; + +static struct pinctrl_desc pxa168_pctrl_desc = { + .name = "pxa168-pinctrl", + .owner = THIS_MODULE, +}; + +static struct pxa3xx_pinmux_info pxa168_info = { + .mfp = pxa168_mfp, + .num_mfp = ARRAY_SIZE(pxa168_mfp), + .grps = pxa168_grps, + .num_grps = ARRAY_SIZE(pxa168_grps), + .funcs = pxa168_funcs, + .num_funcs = ARRAY_SIZE(pxa168_funcs), + .num_gpio = 128, + .desc = &pxa168_pctrl_desc, + .pads = pxa168_pads, + .num_pads = ARRAY_SIZE(pxa168_pads), + + .cputype = PINCTRL_PXA168, + .ds_mask = PXA168_DS_MASK, + .ds_shift = PXA168_DS_SHIFT, +}; + +static int __devinit pxa168_pinmux_probe(struct platform_device *pdev) +{ + return pxa3xx_pinctrl_register(pdev, &pxa168_info); +} + +static int __devexit pxa168_pinmux_remove(struct platform_device *pdev) +{ + return pxa3xx_pinctrl_unregister(pdev); +} + +static struct platform_driver pxa168_pinmux_driver = { + .driver = { + .name = "pxa168-pinmux", + .owner = THIS_MODULE, + }, + .probe = pxa168_pinmux_probe, + .remove = __devexit_p(pxa168_pinmux_remove), +}; + +static int __init pxa168_pinmux_init(void) +{ + return platform_driver_register(&pxa168_pinmux_driver); +} +core_initcall_sync(pxa168_pinmux_init); + +static void __exit pxa168_pinmux_exit(void) +{ + platform_driver_unregister(&pxa168_pinmux_driver); +} +module_exit(pxa168_pinmux_exit); + +MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); +MODULE_DESCRIPTION("PXA3xx pin control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/pinctrl-pxa3xx.c b/drivers/pinctrl/pinctrl-pxa3xx.c new file mode 100644 index 000000000000..079dce0e93e9 --- /dev/null +++ b/drivers/pinctrl/pinctrl-pxa3xx.c @@ -0,0 +1,244 @@ +/* + * linux/drivers/pinctrl/pinctrl-pxa3xx.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * publishhed by the Free Software Foundation. + * + * Copyright (C) 2011, Marvell Technology Group Ltd. + * + * Author: Haojian Zhuang <haojian.zhuang@marvell.com> + * + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include "pinctrl-pxa3xx.h" + +static struct pinctrl_gpio_range pxa3xx_pinctrl_gpio_range = { + .name = "PXA3xx GPIO", + .id = 0, + .base = 0, + .pin_base = 0, +}; + +static int pxa3xx_list_groups(struct pinctrl_dev *pctrldev, unsigned selector) +{ + struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); + if (selector >= info->num_grps) + return -EINVAL; + return 0; +} + +static const char *pxa3xx_get_group_name(struct pinctrl_dev *pctrldev, + unsigned selector) +{ + struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); + if (selector >= info->num_grps) + return NULL; + return info->grps[selector].name; +} + +static int pxa3xx_get_group_pins(struct pinctrl_dev *pctrldev, + unsigned selector, + const unsigned **pins, + unsigned *num_pins) +{ + struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); + if (selector >= info->num_grps) + return -EINVAL; + *pins = info->grps[selector].pins; + *num_pins = info->grps[selector].npins; + return 0; +} + +static struct pinctrl_ops pxa3xx_pctrl_ops = { + .list_groups = pxa3xx_list_groups, + .get_group_name = pxa3xx_get_group_name, + .get_group_pins = pxa3xx_get_group_pins, +}; + +static int pxa3xx_pmx_list_func(struct pinctrl_dev *pctrldev, unsigned func) +{ + struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); + if (func >= info->num_funcs) + return -EINVAL; + return 0; +} + +static const char *pxa3xx_pmx_get_func_name(struct pinctrl_dev *pctrldev, + unsigned func) +{ + struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); + return info->funcs[func].name; +} + +static int pxa3xx_pmx_get_groups(struct pinctrl_dev *pctrldev, unsigned func, + const char * const **groups, + unsigned * const num_groups) +{ + struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); + *groups = info->funcs[func].groups; + *num_groups = info->funcs[func].num_groups; + return 0; +} + +/* Return function number. If failure, return negative value. */ +static int match_mux(struct pxa3xx_mfp_pin *mfp, unsigned mux) +{ + int i; + for (i = 0; i < PXA3xx_MAX_MUX; i++) { + if (mfp->func[i] == mux) + break; + } + if (i >= PXA3xx_MAX_MUX) + return -EINVAL; + return i; +} + +/* check whether current pin configuration is valid. Negative for failure */ +static int match_group_mux(struct pxa3xx_pin_group *grp, + struct pxa3xx_pinmux_info *info, + unsigned mux) +{ + int i, pin, ret = 0; + for (i = 0; i < grp->npins; i++) { + pin = grp->pins[i]; + ret = match_mux(&info->mfp[pin], mux); + if (ret < 0) { + dev_err(info->dev, "Can't find mux %d on pin%d\n", + mux, pin); + break; + } + } + return ret; +} + +static int pxa3xx_pmx_enable(struct pinctrl_dev *pctrldev, unsigned func, + unsigned group) +{ + struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); + struct pxa3xx_pin_group *pin_grp = &info->grps[group]; + unsigned int data; + int i, mfpr, pin, pin_func; + + if (!pin_grp->npins || + (match_group_mux(pin_grp, info, pin_grp->mux) < 0)) { + dev_err(info->dev, "Failed to set the pin group: %d\n", group); + return -EINVAL; + } + for (i = 0; i < pin_grp->npins; i++) { + pin = pin_grp->pins[i]; + pin_func = match_mux(&info->mfp[pin], pin_grp->mux); + mfpr = info->mfp[pin].mfpr; + data = readl_relaxed(info->virt_base + mfpr); + data &= ~MFPR_FUNC_MASK; + data |= pin_func; + writel_relaxed(data, info->virt_base + mfpr); + } + return 0; +} + +static void pxa3xx_pmx_disable(struct pinctrl_dev *pctrldev, unsigned func, + unsigned group) +{ +} + +static int pxa3xx_pmx_request_gpio(struct pinctrl_dev *pctrldev, + struct pinctrl_gpio_range *range, + unsigned pin) +{ + struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); + unsigned int data; + int pin_func, mfpr; + + pin_func = match_mux(&info->mfp[pin], PXA3xx_MUX_GPIO); + if (pin_func < 0) { + dev_err(info->dev, "No GPIO function on pin%d (%s)\n", + pin, info->pads[pin].name); + return -EINVAL; + } + mfpr = info->mfp[pin].mfpr; + /* write gpio function into mfpr register */ + data = readl_relaxed(info->virt_base + mfpr) & ~MFPR_FUNC_MASK; + data |= pin_func; + writel_relaxed(data, info->virt_base + mfpr); + return 0; +} + +static struct pinmux_ops pxa3xx_pmx_ops = { + .list_functions = pxa3xx_pmx_list_func, + .get_function_name = pxa3xx_pmx_get_func_name, + .get_function_groups = pxa3xx_pmx_get_groups, + .enable = pxa3xx_pmx_enable, + .disable = pxa3xx_pmx_disable, + .gpio_request_enable = pxa3xx_pmx_request_gpio, +}; + +int pxa3xx_pinctrl_register(struct platform_device *pdev, + struct pxa3xx_pinmux_info *info) +{ + struct pinctrl_desc *desc; + struct resource *res; + int ret = 0; + + if (!info || !info->cputype) + return -EINVAL; + desc = info->desc; + desc->pins = info->pads; + desc->npins = info->num_pads; + desc->pctlops = &pxa3xx_pctrl_ops; + desc->pmxops = &pxa3xx_pmx_ops; + info->dev = &pdev->dev; + pxa3xx_pinctrl_gpio_range.npins = info->num_gpio; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + info->phy_base = res->start; + info->phy_size = resource_size(res); + info->virt_base = ioremap(info->phy_base, info->phy_size); + if (!info->virt_base) + return -ENOMEM; + info->pctrl = pinctrl_register(desc, &pdev->dev, info); + if (!info->pctrl) { + dev_err(&pdev->dev, "failed to register PXA pinmux driver\n"); + ret = -EINVAL; + goto err; + } + pinctrl_add_gpio_range(info->pctrl, &pxa3xx_pinctrl_gpio_range); + platform_set_drvdata(pdev, info); + return 0; +err: + iounmap(info->virt_base); + return ret; +} + +int pxa3xx_pinctrl_unregister(struct platform_device *pdev) +{ + struct pxa3xx_pinmux_info *info = platform_get_drvdata(pdev); + + pinctrl_unregister(info->pctrl); + iounmap(info->virt_base); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static int __init pxa3xx_pinctrl_init(void) +{ + pr_info("pxa3xx-pinctrl: PXA3xx pinctrl driver initializing\n"); + return 0; +} +core_initcall_sync(pxa3xx_pinctrl_init); + +static void __exit pxa3xx_pinctrl_exit(void) +{ +} +module_exit(pxa3xx_pinctrl_exit); + +MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); +MODULE_DESCRIPTION("PXA3xx pin control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/pinctrl-pxa3xx.h b/drivers/pinctrl/pinctrl-pxa3xx.h new file mode 100644 index 000000000000..8135744d6599 --- /dev/null +++ b/drivers/pinctrl/pinctrl-pxa3xx.h @@ -0,0 +1,264 @@ +/* + * linux/drivers/pinctrl/pinctrl-pxa3xx.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * publishhed by the Free Software Foundation. + * + * Copyright (C) 2011, Marvell Technology Group Ltd. + * + * Author: Haojian Zhuang <haojian.zhuang@marvell.com> + * + */ + +#ifndef __PINCTRL_PXA3XX_H + +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> + +#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x) + +#define PXA3xx_MUX_GPIO 0 + +#define PXA3xx_MAX_MUX 8 +#define MFPR_FUNC_MASK 0x7 + +enum pxa_cpu_type { + PINCTRL_INVALID = 0, + PINCTRL_PXA300, + PINCTRL_PXA310, + PINCTRL_PXA320, + PINCTRL_PXA168, + PINCTRL_PXA910, + PINCTRL_PXA930, + PINCTRL_PXA955, + PINCTRL_MMP2, + PINCTRL_MAX, +}; + +struct pxa3xx_mfp_pin { + const char *name; + const unsigned int pin; + const unsigned int mfpr; /* register offset */ + const unsigned short func[8]; +}; + +struct pxa3xx_pin_group { + const char *name; + const unsigned mux; + const unsigned *pins; + const unsigned npins; +}; + +struct pxa3xx_pmx_func { + const char *name; + const char * const * groups; + const unsigned num_groups; +}; + +struct pxa3xx_pinmux_info { + struct device *dev; + struct pinctrl_dev *pctrl; + enum pxa_cpu_type cputype; + unsigned int phy_base; + unsigned int phy_size; + void __iomem *virt_base; + + struct pxa3xx_mfp_pin *mfp; + unsigned int num_mfp; + struct pxa3xx_pin_group *grps; + unsigned int num_grps; + struct pxa3xx_pmx_func *funcs; + unsigned int num_funcs; + unsigned int num_gpio; + struct pinctrl_desc *desc; + struct pinctrl_pin_desc *pads; + unsigned int num_pads; + + unsigned ds_mask; /* drive strength mask */ + unsigned ds_shift; /* drive strength shift */ + unsigned slp_mask; /* sleep mask */ + unsigned slp_input_low; + unsigned slp_input_high; + unsigned slp_output_low; + unsigned slp_output_high; + unsigned slp_float; +}; + +enum pxa3xx_pin_list { + GPIO0 = 0, + GPIO1, + GPIO2, + GPIO3, + GPIO4, + GPIO5, + GPIO6, + GPIO7, + GPIO8, + GPIO9, + GPIO10, /* 10 */ + GPIO11, + GPIO12, + GPIO13, + GPIO14, + GPIO15, + GPIO16, + GPIO17, + GPIO18, + GPIO19, + GPIO20, /* 20 */ + GPIO21, + GPIO22, + GPIO23, + GPIO24, + GPIO25, + GPIO26, + GPIO27, + GPIO28, + GPIO29, + GPIO30, /* 30 */ + GPIO31, + GPIO32, + GPIO33, + GPIO34, + GPIO35, + GPIO36, + GPIO37, + GPIO38, + GPIO39, + GPIO40, /* 40 */ + GPIO41, + GPIO42, + GPIO43, + GPIO44, + GPIO45, + GPIO46, + GPIO47, + GPIO48, + GPIO49, + GPIO50, /* 50 */ + GPIO51, + GPIO52, + GPIO53, + GPIO54, + GPIO55, + GPIO56, + GPIO57, + GPIO58, + GPIO59, + GPIO60, /* 60 */ + GPIO61, + GPIO62, + GPIO63, + GPIO64, + GPIO65, + GPIO66, + GPIO67, + GPIO68, + GPIO69, + GPIO70, /* 70 */ + GPIO71, + GPIO72, + GPIO73, + GPIO74, + GPIO75, + GPIO76, + GPIO77, + GPIO78, + GPIO79, + GPIO80, /* 80 */ + GPIO81, + GPIO82, + GPIO83, + GPIO84, + GPIO85, + GPIO86, + GPIO87, + GPIO88, + GPIO89, + GPIO90, /* 90 */ + GPIO91, + GPIO92, + GPIO93, + GPIO94, + GPIO95, + GPIO96, + GPIO97, + GPIO98, + GPIO99, + GPIO100, /* 100 */ + GPIO101, + GPIO102, + GPIO103, + GPIO104, + GPIO105, + GPIO106, + GPIO107, + GPIO108, + GPIO109, + GPIO110, /* 110 */ + GPIO111, + GPIO112, + GPIO113, + GPIO114, + GPIO115, + GPIO116, + GPIO117, + GPIO118, + GPIO119, + GPIO120, /* 120 */ + GPIO121, + GPIO122, + GPIO123, + GPIO124, + GPIO125, + GPIO126, + GPIO127, + GPIO128, + GPIO129, + GPIO130, /* 130 */ + GPIO131, + GPIO132, + GPIO133, + GPIO134, + GPIO135, + GPIO136, + GPIO137, + GPIO138, + GPIO139, + GPIO140, /* 140 */ + GPIO141, + GPIO142, + GPIO143, + GPIO144, + GPIO145, + GPIO146, + GPIO147, + GPIO148, + GPIO149, + GPIO150, /* 150 */ + GPIO151, + GPIO152, + GPIO153, + GPIO154, + GPIO155, + GPIO156, + GPIO157, + GPIO158, + GPIO159, + GPIO160, /* 160 */ + GPIO161, + GPIO162, + GPIO163, + GPIO164, + GPIO165, + GPIO166, + GPIO167, + GPIO168, + GPIO169, +}; + +extern int pxa3xx_pinctrl_register(struct platform_device *pdev, + struct pxa3xx_pinmux_info *info); +extern int pxa3xx_pinctrl_unregister(struct platform_device *pdev); +#endif /* __PINCTRL_PXA3XX_H */ diff --git a/drivers/pinctrl/pinctrl-pxa910.c b/drivers/pinctrl/pinctrl-pxa910.c new file mode 100644 index 000000000000..c72ab4b9cc8c --- /dev/null +++ b/drivers/pinctrl/pinctrl-pxa910.c @@ -0,0 +1,1007 @@ +/* + * linux/drivers/pinctrl/pinmux-pxa910.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * publishhed by the Free Software Foundation. + * + * Copyright (C) 2011, Marvell Technology Group Ltd. + * + * Author: Haojian Zhuang <haojian.zhuang@marvell.com> + * + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include "pinctrl-pxa3xx.h" + +#define PXA910_DS_MASK 0x1800 +#define PXA910_DS_SHIFT 11 +#define PXA910_SLEEP_MASK 0x38 +#define PXA910_SLEEP_SELECT (1 << 9) +#define PXA910_SLEEP_DATA (1 << 8) +#define PXA910_SLEEP_DIR (1 << 7) + +#define MFPR_910(a, r, f0, f1, f2, f3, f4, f5, f6, f7) \ + { \ + .name = #a, \ + .pin = a, \ + .mfpr = r, \ + .func = { \ + PXA910_MUX_##f0, \ + PXA910_MUX_##f1, \ + PXA910_MUX_##f2, \ + PXA910_MUX_##f3, \ + PXA910_MUX_##f4, \ + PXA910_MUX_##f5, \ + PXA910_MUX_##f6, \ + PXA910_MUX_##f7, \ + }, \ + } + +#define GRP_910(a, m, p) \ + { .name = a, .mux = PXA910_MUX_##m, .pins = p, .npins = ARRAY_SIZE(p), } + +/* 170 pins */ +enum pxa910_pin_list { + /* 0~127: GPIO0~GPIO127 */ + ND_IO15 = 128, + ND_IO14, + ND_IO13, /* 130 */ + ND_IO12, + ND_IO11, + ND_IO10, + ND_IO9, + ND_IO8, + ND_IO7, + ND_IO6, + ND_IO5, + ND_IO4, + ND_IO3, /* 140 */ + ND_IO2, + ND_IO1, + ND_IO0, + ND_NCS0, + ND_NCS1, + SM_NCS0, + SM_NCS1, + ND_NWE, + ND_NRE, + ND_CLE, /* 150 */ + ND_ALE, + SM_SCLK, + ND_RDY0, + SM_ADV, + ND_RDY1, + SM_ADVMUX, + SM_RDY, + MMC1_DAT7, + MMC1_DAT6, + MMC1_DAT5, /* 160 */ + MMC1_DAT4, + MMC1_DAT3, + MMC1_DAT2, + MMC1_DAT1, + MMC1_DAT0, + MMC1_CMD, + MMC1_CLK, + MMC1_CD, + VCXO_OUT, +}; + +enum pxa910_mux { + /* PXA3xx_MUX_GPIO = 0 (predefined in pinctrl-pxa3xx.h) */ + PXA910_MUX_GPIO = 0, + PXA910_MUX_NAND, + PXA910_MUX_USIM2, + PXA910_MUX_EXT_DMA, + PXA910_MUX_EXT_INT, + PXA910_MUX_MMC1, + PXA910_MUX_MMC2, + PXA910_MUX_MMC3, + PXA910_MUX_SM_INT, + PXA910_MUX_PRI_JTAG, + PXA910_MUX_SEC1_JTAG, + PXA910_MUX_SEC2_JTAG, + PXA910_MUX_RESET, /* SLAVE RESET OUT */ + PXA910_MUX_CLK_REQ, + PXA910_MUX_VCXO_REQ, + PXA910_MUX_VCXO_OUT, + PXA910_MUX_VCXO_REQ2, + PXA910_MUX_VCXO_OUT2, + PXA910_MUX_SPI, + PXA910_MUX_SPI2, + PXA910_MUX_GSSP, + PXA910_MUX_SSP0, + PXA910_MUX_SSP1, + PXA910_MUX_SSP2, + PXA910_MUX_DSSP2, + PXA910_MUX_DSSP3, + PXA910_MUX_UART0, + PXA910_MUX_UART1, + PXA910_MUX_UART2, + PXA910_MUX_TWSI, + PXA910_MUX_CCIC, + PXA910_MUX_PWM0, + PXA910_MUX_PWM1, + PXA910_MUX_PWM2, + PXA910_MUX_PWM3, + PXA910_MUX_HSL, + PXA910_MUX_ONE_WIRE, + PXA910_MUX_LCD, + PXA910_MUX_DAC_ST23, + PXA910_MUX_ULPI, + PXA910_MUX_TB, + PXA910_MUX_KP_MK, + PXA910_MUX_KP_DK, + PXA910_MUX_TCU_GPOA, + PXA910_MUX_TCU_GPOB, + PXA910_MUX_ROT, + PXA910_MUX_TDS, + PXA910_MUX_32K_CLK, /* 32KHz CLK OUT */ + PXA910_MUX_MN_CLK, /* MN CLK OUT */ + PXA910_MUX_SMC, + PXA910_MUX_SM_ADDR18, + PXA910_MUX_SM_ADDR19, + PXA910_MUX_SM_ADDR20, + PXA910_MUX_NONE = 0xffff, +}; + + +static struct pinctrl_pin_desc pxa910_pads[] = { + PINCTRL_PIN(GPIO0, "GPIO0"), + PINCTRL_PIN(GPIO1, "GPIO1"), + PINCTRL_PIN(GPIO2, "GPIO2"), + PINCTRL_PIN(GPIO3, "GPIO3"), + PINCTRL_PIN(GPIO4, "GPIO4"), + PINCTRL_PIN(GPIO5, "GPIO5"), + PINCTRL_PIN(GPIO6, "GPIO6"), + PINCTRL_PIN(GPIO7, "GPIO7"), + PINCTRL_PIN(GPIO8, "GPIO8"), + PINCTRL_PIN(GPIO9, "GPIO9"), + PINCTRL_PIN(GPIO10, "GPIO10"), + PINCTRL_PIN(GPIO11, "GPIO11"), + PINCTRL_PIN(GPIO12, "GPIO12"), + PINCTRL_PIN(GPIO13, "GPIO13"), + PINCTRL_PIN(GPIO14, "GPIO14"), + PINCTRL_PIN(GPIO15, "GPIO15"), + PINCTRL_PIN(GPIO16, "GPIO16"), + PINCTRL_PIN(GPIO17, "GPIO17"), + PINCTRL_PIN(GPIO18, "GPIO18"), + PINCTRL_PIN(GPIO19, "GPIO19"), + PINCTRL_PIN(GPIO20, "GPIO20"), + PINCTRL_PIN(GPIO21, "GPIO21"), + PINCTRL_PIN(GPIO22, "GPIO22"), + PINCTRL_PIN(GPIO23, "GPIO23"), + PINCTRL_PIN(GPIO24, "GPIO24"), + PINCTRL_PIN(GPIO25, "GPIO25"), + PINCTRL_PIN(GPIO26, "GPIO26"), + PINCTRL_PIN(GPIO27, "GPIO27"), + PINCTRL_PIN(GPIO28, "GPIO28"), + PINCTRL_PIN(GPIO29, "GPIO29"), + PINCTRL_PIN(GPIO30, "GPIO30"), + PINCTRL_PIN(GPIO31, "GPIO31"), + PINCTRL_PIN(GPIO32, "GPIO32"), + PINCTRL_PIN(GPIO33, "GPIO33"), + PINCTRL_PIN(GPIO34, "GPIO34"), + PINCTRL_PIN(GPIO35, "GPIO35"), + PINCTRL_PIN(GPIO36, "GPIO36"), + PINCTRL_PIN(GPIO37, "GPIO37"), + PINCTRL_PIN(GPIO38, "GPIO38"), + PINCTRL_PIN(GPIO39, "GPIO39"), + PINCTRL_PIN(GPIO40, "GPIO40"), + PINCTRL_PIN(GPIO41, "GPIO41"), + PINCTRL_PIN(GPIO42, "GPIO42"), + PINCTRL_PIN(GPIO43, "GPIO43"), + PINCTRL_PIN(GPIO44, "GPIO44"), + PINCTRL_PIN(GPIO45, "GPIO45"), + PINCTRL_PIN(GPIO46, "GPIO46"), + PINCTRL_PIN(GPIO47, "GPIO47"), + PINCTRL_PIN(GPIO48, "GPIO48"), + PINCTRL_PIN(GPIO49, "GPIO49"), + PINCTRL_PIN(GPIO50, "GPIO50"), + PINCTRL_PIN(GPIO51, "GPIO51"), + PINCTRL_PIN(GPIO52, "GPIO52"), + PINCTRL_PIN(GPIO53, "GPIO53"), + PINCTRL_PIN(GPIO54, "GPIO54"), + PINCTRL_PIN(GPIO55, "GPIO55"), + PINCTRL_PIN(GPIO56, "GPIO56"), + PINCTRL_PIN(GPIO57, "GPIO57"), + PINCTRL_PIN(GPIO58, "GPIO58"), + PINCTRL_PIN(GPIO59, "GPIO59"), + PINCTRL_PIN(GPIO60, "GPIO60"), + PINCTRL_PIN(GPIO61, "GPIO61"), + PINCTRL_PIN(GPIO62, "GPIO62"), + PINCTRL_PIN(GPIO63, "GPIO63"), + PINCTRL_PIN(GPIO64, "GPIO64"), + PINCTRL_PIN(GPIO65, "GPIO65"), + PINCTRL_PIN(GPIO66, "GPIO66"), + PINCTRL_PIN(GPIO67, "GPIO67"), + PINCTRL_PIN(GPIO68, "GPIO68"), + PINCTRL_PIN(GPIO69, "GPIO69"), + PINCTRL_PIN(GPIO70, "GPIO70"), + PINCTRL_PIN(GPIO71, "GPIO71"), + PINCTRL_PIN(GPIO72, "GPIO72"), + PINCTRL_PIN(GPIO73, "GPIO73"), + PINCTRL_PIN(GPIO74, "GPIO74"), + PINCTRL_PIN(GPIO75, "GPIO75"), + PINCTRL_PIN(GPIO76, "GPIO76"), + PINCTRL_PIN(GPIO77, "GPIO77"), + PINCTRL_PIN(GPIO78, "GPIO78"), + PINCTRL_PIN(GPIO79, "GPIO79"), + PINCTRL_PIN(GPIO80, "GPIO80"), + PINCTRL_PIN(GPIO81, "GPIO81"), + PINCTRL_PIN(GPIO82, "GPIO82"), + PINCTRL_PIN(GPIO83, "GPIO83"), + PINCTRL_PIN(GPIO84, "GPIO84"), + PINCTRL_PIN(GPIO85, "GPIO85"), + PINCTRL_PIN(GPIO86, "GPIO86"), + PINCTRL_PIN(GPIO87, "GPIO87"), + PINCTRL_PIN(GPIO88, "GPIO88"), + PINCTRL_PIN(GPIO89, "GPIO89"), + PINCTRL_PIN(GPIO90, "GPIO90"), + PINCTRL_PIN(GPIO91, "GPIO91"), + PINCTRL_PIN(GPIO92, "GPIO92"), + PINCTRL_PIN(GPIO93, "GPIO93"), + PINCTRL_PIN(GPIO94, "GPIO94"), + PINCTRL_PIN(GPIO95, "GPIO95"), + PINCTRL_PIN(GPIO96, "GPIO96"), + PINCTRL_PIN(GPIO97, "GPIO97"), + PINCTRL_PIN(GPIO98, "GPIO98"), + PINCTRL_PIN(GPIO99, "GPIO99"), + PINCTRL_PIN(GPIO100, "GPIO100"), + PINCTRL_PIN(GPIO101, "GPIO101"), + PINCTRL_PIN(GPIO102, "GPIO102"), + PINCTRL_PIN(GPIO103, "GPIO103"), + PINCTRL_PIN(GPIO104, "GPIO104"), + PINCTRL_PIN(GPIO105, "GPIO105"), + PINCTRL_PIN(GPIO106, "GPIO106"), + PINCTRL_PIN(GPIO107, "GPIO107"), + PINCTRL_PIN(GPIO108, "GPIO108"), + PINCTRL_PIN(GPIO109, "GPIO109"), + PINCTRL_PIN(GPIO110, "GPIO110"), + PINCTRL_PIN(GPIO111, "GPIO111"), + PINCTRL_PIN(GPIO112, "GPIO112"), + PINCTRL_PIN(GPIO113, "GPIO113"), + PINCTRL_PIN(GPIO114, "GPIO114"), + PINCTRL_PIN(GPIO115, "GPIO115"), + PINCTRL_PIN(GPIO116, "GPIO116"), + PINCTRL_PIN(GPIO117, "GPIO117"), + PINCTRL_PIN(GPIO118, "GPIO118"), + PINCTRL_PIN(GPIO119, "GPIO119"), + PINCTRL_PIN(GPIO120, "GPIO120"), + PINCTRL_PIN(GPIO121, "GPIO121"), + PINCTRL_PIN(GPIO122, "GPIO122"), + PINCTRL_PIN(GPIO123, "GPIO123"), + PINCTRL_PIN(GPIO124, "GPIO124"), + PINCTRL_PIN(GPIO125, "GPIO125"), + PINCTRL_PIN(GPIO126, "GPIO126"), + PINCTRL_PIN(GPIO127, "GPIO127"), + PINCTRL_PIN(ND_IO15, "ND_IO15"), + PINCTRL_PIN(ND_IO14, "ND_IO14"), + PINCTRL_PIN(ND_IO13, "ND_IO13"), + PINCTRL_PIN(ND_IO12, "ND_IO12"), + PINCTRL_PIN(ND_IO11, "ND_IO11"), + PINCTRL_PIN(ND_IO10, "ND_IO10"), + PINCTRL_PIN(ND_IO9, "ND_IO9"), + PINCTRL_PIN(ND_IO8, "ND_IO8"), + PINCTRL_PIN(ND_IO7, "ND_IO7"), + PINCTRL_PIN(ND_IO6, "ND_IO6"), + PINCTRL_PIN(ND_IO5, "ND_IO5"), + PINCTRL_PIN(ND_IO4, "ND_IO4"), + PINCTRL_PIN(ND_IO3, "ND_IO3"), + PINCTRL_PIN(ND_IO2, "ND_IO2"), + PINCTRL_PIN(ND_IO1, "ND_IO1"), + PINCTRL_PIN(ND_IO0, "ND_IO0"), + PINCTRL_PIN(ND_NCS0, "ND_NCS0_SM_NCS2"), + PINCTRL_PIN(ND_NCS1, "ND_NCS1_SM_NCS3"), + PINCTRL_PIN(SM_NCS0, "SM_NCS0"), + PINCTRL_PIN(SM_NCS1, "SM_NCS1"), + PINCTRL_PIN(ND_NWE, "ND_NWE"), + PINCTRL_PIN(ND_NRE, "ND_NRE"), + PINCTRL_PIN(ND_CLE, "ND_CLE_SM_NOE"), + PINCTRL_PIN(ND_ALE, "ND_ALE_SM_NWE"), + PINCTRL_PIN(SM_SCLK, "SM_SCLK"), + PINCTRL_PIN(ND_RDY0, "ND_RDY0"), + PINCTRL_PIN(SM_ADV, "SM_ADV"), + PINCTRL_PIN(ND_RDY1, "ND_RDY1"), + PINCTRL_PIN(SM_RDY, "SM_RDY"), + PINCTRL_PIN(MMC1_DAT7, "MMC1_DAT7"), + PINCTRL_PIN(MMC1_DAT6, "MMC1_DAT6"), + PINCTRL_PIN(MMC1_DAT5, "MMC1_DAT5"), + PINCTRL_PIN(MMC1_DAT4, "MMC1_DAT4"), + PINCTRL_PIN(MMC1_DAT3, "MMC1_DAT3"), + PINCTRL_PIN(MMC1_DAT2, "MMC1_DAT2"), + PINCTRL_PIN(MMC1_DAT1, "MMC1_DAT1"), + PINCTRL_PIN(MMC1_DAT0, "MMC1_DAT0"), + PINCTRL_PIN(MMC1_CMD, "MMC1 CMD"), + PINCTRL_PIN(MMC1_CLK, "MMC1 CLK"), + PINCTRL_PIN(MMC1_CD, "MMC1 CD"), + PINCTRL_PIN(VCXO_OUT, "VCXO_OUT"), +}; + +struct pxa3xx_mfp_pin pxa910_mfp[] = { + /* pin offs f0 f1 f2 f3 f4 f5 f6 f7 */ + MFPR_910(GPIO0, 0x0DC, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO1, 0x0E0, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO2, 0x0E4, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO3, 0x0E8, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO4, 0x0EC, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO5, 0x0F0, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO6, 0x0F4, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO7, 0x0F8, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO8, 0x0FC, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO9, 0x100, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO10, 0x104, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO11, 0x108, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO12, 0x10C, GPIO, KP_MK, NONE, NONE, KP_DK, NONE, NONE, NONE), + MFPR_910(GPIO13, 0x110, GPIO, KP_MK, NONE, NONE, KP_DK, NONE, NONE, NONE), + MFPR_910(GPIO14, 0x114, GPIO, KP_MK, NONE, NONE, KP_DK, TB, NONE, NONE), + MFPR_910(GPIO15, 0x118, GPIO, KP_MK, NONE, NONE, KP_DK, TB, NONE, NONE), + MFPR_910(GPIO16, 0x11C, GPIO, KP_DK, NONE, NONE, NONE, TB, NONE, NONE), + MFPR_910(GPIO17, 0x120, GPIO, KP_DK, NONE, NONE, NONE, TB, NONE, NONE), + MFPR_910(GPIO18, 0x124, GPIO, KP_DK, NONE, NONE, ROT, NONE, NONE, NONE), + MFPR_910(GPIO19, 0x128, GPIO, KP_DK, NONE, NONE, ROT, NONE, NONE, NONE), + MFPR_910(GPIO20, 0x12C, GPIO, SSP1, NONE, NONE, VCXO_OUT, NONE, NONE, NONE), + MFPR_910(GPIO21, 0x130, GPIO, SSP1, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO22, 0x134, GPIO, SSP1, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO23, 0x138, GPIO, SSP1, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO24, 0x13C, GPIO, SSP1, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO25, 0x140, GPIO, GSSP, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO26, 0x144, GPIO, GSSP, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO27, 0x148, GPIO, GSSP, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO28, 0x14C, GPIO, GSSP, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO29, 0x150, GPIO, UART0, NONE, NONE, UART1, NONE, NONE, NONE), + MFPR_910(GPIO30, 0x154, GPIO, UART0, NONE, NONE, UART1, NONE, NONE, NONE), + MFPR_910(GPIO31, 0x158, GPIO, UART0, NONE, NONE, UART1, NONE, NONE, NONE), + MFPR_910(GPIO32, 0x15C, GPIO, UART0, DAC_ST23, NONE, UART1, NONE, NONE, NONE), + MFPR_910(GPIO33, 0x160, GPIO, MMC2, SSP0, SSP2, NONE, SPI, NONE, MMC3), + MFPR_910(GPIO34, 0x164, GPIO, MMC2, SSP0, SSP2, NONE, SPI, NONE, MMC3), + MFPR_910(GPIO35, 0x168, GPIO, MMC2, SSP0, SSP2, NONE, SPI, NONE, MMC3), + MFPR_910(GPIO36, 0x16C, GPIO, MMC2, SSP0, SSP2, NONE, SPI, NONE, MMC3), + MFPR_910(GPIO37, 0x170, GPIO, MMC2, NONE, NONE, NONE, SPI, HSL, NONE), + MFPR_910(GPIO38, 0x174, GPIO, MMC2, NONE, NONE, NONE, NONE, HSL, NONE), + MFPR_910(GPIO39, 0x178, GPIO, MMC2, NONE, NONE, NONE, NONE, HSL, NONE), + MFPR_910(GPIO40, 0x17C, GPIO, MMC2, NONE, NONE, NONE, NONE, HSL, NONE), + MFPR_910(GPIO41, 0x180, GPIO, MMC2, NONE, NONE, NONE, NONE, HSL, NONE), + MFPR_910(GPIO42, 0x184, GPIO, MMC2, NONE, NONE, NONE, NONE, HSL, NONE), + MFPR_910(GPIO43, 0x188, GPIO, UART1, NONE, DAC_ST23, NONE, DSSP2, SPI, UART2), + MFPR_910(GPIO44, 0x18C, GPIO, UART1, NONE, EXT_INT, NONE, DSSP2, SPI, UART2), + MFPR_910(GPIO45, 0x190, GPIO, UART1, NONE, EXT_INT, NONE, DSSP2, SPI, UART2), + MFPR_910(GPIO46, 0x194, GPIO, UART1, NONE, EXT_INT, NONE, DSSP2, SPI, UART2), + MFPR_910(GPIO47, 0x198, GPIO, SSP0, NONE, NONE, NONE, SSP2, UART1, NONE), + MFPR_910(GPIO48, 0x19C, GPIO, SSP0, NONE, NONE, NONE, SSP2, UART1, NONE), + MFPR_910(GPIO49, 0x1A0, GPIO, SSP0, UART0, VCXO_REQ, NONE, SSP2, NONE, MMC3), + MFPR_910(GPIO50, 0x1A4, GPIO, SSP0, UART0, VCXO_OUT, NONE, SSP2, NONE, MMC3), + MFPR_910(GPIO51, 0x1A8, GPIO, UART2, PWM1, TWSI, SSP0, NONE, DSSP3, NONE), + MFPR_910(GPIO52, 0x1AC, GPIO, UART2, DAC_ST23, TWSI, SSP0, NONE, DSSP3, NONE), + MFPR_910(GPIO53, 0x1B0, GPIO, UART2, TWSI, NONE, SSP0, NONE, DSSP3, NONE), + MFPR_910(GPIO54, 0x1B4, GPIO, UART2, TWSI, SSP0, NONE, NONE, DSSP3, NONE), + MFPR_910(GPIO55, 0x2F0, TDS, GPIO, TB, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO56, 0x2F4, TDS, GPIO, TB, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO57, 0x2F8, TDS, GPIO, TB, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO58, 0x2FC, TDS, GPIO, TB, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO59, 0x300, TDS, GPIO, TCU_GPOA, TCU_GPOB, ONE_WIRE, NONE, NONE, NONE), + MFPR_910(GPIO60, 0x304, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO61, 0x308, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, HSL), + MFPR_910(GPIO62, 0x30C, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, HSL), + MFPR_910(GPIO63, 0x310, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, HSL), + MFPR_910(GPIO64, 0x314, GPIO, SPI2, NONE, NONE, NONE, NONE, NONE, HSL), + MFPR_910(GPIO65, 0x318, GPIO, SPI2, NONE, NONE, NONE, NONE, ONE_WIRE, HSL), + MFPR_910(GPIO66, 0x31C, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, HSL), + MFPR_910(GPIO67, 0x1B8, GPIO, CCIC, SPI, NONE, NONE, ULPI, NONE, USIM2), + MFPR_910(GPIO68, 0x1BC, GPIO, CCIC, SPI, NONE, NONE, ULPI, NONE, USIM2), + MFPR_910(GPIO69, 0x1C0, GPIO, CCIC, SPI, NONE, NONE, ULPI, NONE, USIM2), + MFPR_910(GPIO70, 0x1C4, GPIO, CCIC, SPI, NONE, NONE, ULPI, NONE, NONE), + MFPR_910(GPIO71, 0x1C8, GPIO, CCIC, SPI, NONE, NONE, ULPI, NONE, NONE), + MFPR_910(GPIO72, 0x1CC, GPIO, CCIC, EXT_DMA, NONE, NONE, ULPI, NONE, NONE), + MFPR_910(GPIO73, 0x1D0, GPIO, CCIC, EXT_DMA, NONE, NONE, ULPI, NONE, NONE), + MFPR_910(GPIO74, 0x1D4, GPIO, CCIC, EXT_DMA, NONE, NONE, ULPI, NONE, NONE), + MFPR_910(GPIO75, 0x1D8, GPIO, CCIC, NONE, NONE, NONE, ULPI, NONE, NONE), + MFPR_910(GPIO76, 0x1DC, GPIO, CCIC, NONE, NONE, NONE, ULPI, NONE, NONE), + MFPR_910(GPIO77, 0x1E0, GPIO, CCIC, NONE, NONE, NONE, ULPI, NONE, NONE), + MFPR_910(GPIO78, 0x1E4, GPIO, CCIC, NONE, NONE, NONE, ULPI, NONE, NONE), + MFPR_910(GPIO79, 0x1E8, GPIO, TWSI, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO80, 0x1EC, GPIO, TWSI, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO81, 0x1F0, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO82, 0x1F4, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO83, 0x1F8, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO84, 0x1FC, GPIO, LCD, VCXO_REQ2, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO85, 0x200, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO86, 0x204, GPIO, LCD, VCXO_OUT2, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO87, 0x208, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO88, 0x20C, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO89, 0x210, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO90, 0x214, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO91, 0x218, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO92, 0x21C, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO93, 0x220, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO94, 0x224, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO95, 0x228, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO96, 0x22C, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO97, 0x230, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO98, 0x234, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO99, 0x0B0, MMC1, GPIO, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO100, 0x238, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO101, 0x23C, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO102, 0x240, GPIO, LCD, DSSP2, SPI, NONE, NONE, NONE, SPI2), + MFPR_910(GPIO103, 0x244, GPIO, LCD, DSSP2, SPI, NONE, NONE, NONE, SPI2), + MFPR_910(GPIO104, 0x248, GPIO, LCD, DSSP2, SPI, NONE, NONE, NONE, NONE), + MFPR_910(GPIO105, 0x24C, GPIO, LCD, DSSP2, SPI, NONE, NONE, NONE, NONE), + MFPR_910(GPIO106, 0x250, GPIO, LCD, DSSP3, ONE_WIRE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO107, 0x254, GPIO, LCD, DSSP3, SPI, NONE, NONE, NONE, NONE), + MFPR_910(GPIO108, 0x258, GPIO, LCD, DSSP3, SPI, NONE, NONE, NONE, NONE), + MFPR_910(GPIO109, 0x25C, GPIO, LCD, DSSP3, SPI, NONE, NONE, NONE, NONE), + MFPR_910(GPIO110, 0x298, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO111, 0x29C, GPIO, NONE, DSSP2, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO112, 0x2A0, GPIO, NONE, DSSP2, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO113, 0x2A4, GPIO, NONE, DSSP2, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO114, 0x2A8, GPIO, NONE, DSSP3, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO115, 0x2AC, GPIO, NONE, DSSP3, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO116, 0x2B0, GPIO, NONE, DSSP3, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO117, 0x0B4, PRI_JTAG, GPIO, PWM0, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO118, 0x0B8, PRI_JTAG, GPIO, PWM1, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO119, 0x0BC, PRI_JTAG, GPIO, PWM2, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO120, 0x0C0, PRI_JTAG, GPIO, PWM3, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO121, 0x32C, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO122, 0x0C8, RESET, GPIO, 32K_CLK, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO123, 0x0CC, CLK_REQ, GPIO, ONE_WIRE, EXT_DMA, NONE, NONE, NONE, NONE), + MFPR_910(GPIO124, 0x0D0, GPIO, MN_CLK, DAC_ST23, NONE, NONE, NONE, NONE, NONE), + MFPR_910(GPIO125, 0x0D4, VCXO_REQ, GPIO, NONE, EXT_INT, NONE, NONE, NONE, NONE), + MFPR_910(GPIO126, 0x06C, GPIO, SMC, NONE, SM_ADDR18, NONE, EXT_DMA, NONE, NONE), + MFPR_910(GPIO127, 0x070, GPIO, SMC, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(ND_IO15, 0x004, NAND, GPIO, USIM2, EXT_DMA, NONE, NONE, NONE, NONE), + MFPR_910(ND_IO14, 0x008, NAND, GPIO, USIM2, NONE, NONE, NONE, NONE, NONE), + MFPR_910(ND_IO13, 0x00C, NAND, GPIO, USIM2, EXT_INT, NONE, NONE, NONE, NONE), + MFPR_910(ND_IO12, 0x010, NAND, GPIO, SSP2, EXT_INT, NONE, NONE, NONE, NONE), + MFPR_910(ND_IO11, 0x014, NAND, GPIO, SSP2, NONE, NONE, NONE, NONE, NONE), + MFPR_910(ND_IO10, 0x018, NAND, GPIO, SSP2, NONE, NONE, NONE, NONE, NONE), + MFPR_910(ND_IO9, 0x01C, NAND, GPIO, SSP2, NONE, VCXO_OUT2, NONE, NONE, NONE), + MFPR_910(ND_IO8, 0x020, NAND, GPIO, NONE, NONE, PWM3, NONE, NONE, NONE), + MFPR_910(ND_IO7, 0x024, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(ND_IO6, 0x028, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(ND_IO5, 0x02C, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(ND_IO4, 0x030, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(ND_IO3, 0x034, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(ND_IO2, 0x038, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(ND_IO1, 0x03C, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(ND_IO0, 0x040, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(ND_NCS0, 0x044, NAND, GPIO, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(ND_NCS1, 0x048, NAND, GPIO, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(SM_NCS0, 0x04C, SMC, GPIO, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(SM_NCS1, 0x050, SMC, GPIO, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(ND_NWE, 0x054, GPIO, NAND, NONE, SM_ADDR20, NONE, SMC, NONE, NONE), + MFPR_910(ND_NRE, 0x058, GPIO, NAND, NONE, SMC, NONE, EXT_DMA, NONE, NONE), + MFPR_910(ND_CLE, 0x05C, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(ND_ALE, 0x060, GPIO, NAND, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(SM_SCLK, 0x064, MMC3, NONE, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(ND_RDY0, 0x068, NAND, GPIO, NONE, SMC, NONE, NONE, NONE, NONE), + MFPR_910(SM_ADV, 0x074, SMC, GPIO, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(ND_RDY1, 0x078, NAND, GPIO, NONE, SMC, NONE, NONE, NONE, NONE), + MFPR_910(SM_ADVMUX, 0x07C, SMC, GPIO, NONE, SM_ADDR19, NONE, NONE, NONE, NONE), + MFPR_910(SM_RDY, 0x080, SMC, GPIO, NONE, NONE, NONE, NONE, NONE, NONE), + MFPR_910(MMC1_DAT7, 0x084, MMC1, GPIO, SEC1_JTAG, TB, NONE, NONE, NONE, NONE), + MFPR_910(MMC1_DAT6, 0x088, MMC1, GPIO, SEC1_JTAG, TB, NONE, NONE, NONE, NONE), + MFPR_910(MMC1_DAT5, 0x08C, MMC1, GPIO, SEC1_JTAG, TB, NONE, NONE, NONE, NONE), + MFPR_910(MMC1_DAT4, 0x090, MMC1, GPIO, NONE, TB, NONE, NONE, NONE, NONE), + MFPR_910(MMC1_DAT3, 0x094, MMC1, HSL, SEC2_JTAG, SSP0, NONE, NONE, NONE, NONE), + MFPR_910(MMC1_DAT2, 0x098, MMC1, HSL, SEC2_JTAG, SSP2, SSP0, NONE, NONE, NONE), + MFPR_910(MMC1_DAT1, 0x09C, MMC1, HSL, SEC2_JTAG, SSP2, SSP0, NONE, NONE, NONE), + MFPR_910(MMC1_DAT0, 0x0A0, MMC1, HSL, SEC2_JTAG, SSP2, NONE, NONE, NONE, NONE), + MFPR_910(MMC1_CMD, 0x0A4, MMC1, HSL, SEC1_JTAG, SSP2, NONE, NONE, NONE, NONE), + MFPR_910(MMC1_CLK, 0x0A8, MMC1, HSL, SEC2_JTAG, SSP0, NONE, NONE, NONE, NONE), + MFPR_910(MMC1_CD, 0x0AC, MMC1, GPIO, SEC1_JTAG, NONE, NONE, NONE, NONE, NONE), + MFPR_910(VCXO_OUT, 0x0D8, VCXO_OUT, PWM3, NONE, NONE, NONE, NONE, NONE, NONE), +}; + + +static const unsigned p910_usim2_pin1[] = {GPIO67, GPIO68, GPIO69}; +static const unsigned p910_usim2_pin2[] = {ND_IO15, ND_IO14, ND_IO13}; +static const unsigned p910_mmc1_pin1[] = {MMC1_DAT7, MMC1_DAT6, MMC1_DAT5, + MMC1_DAT4, MMC1_DAT3, MMC1_DAT2, MMC1_DAT1, MMC1_DAT0, MMC1_CMD, + MMC1_CLK, MMC1_CD, GPIO99}; +static const unsigned p910_mmc2_pin1[] = {GPIO33, GPIO34, GPIO35, GPIO36, + GPIO37, GPIO38, GPIO39, GPIO40, GPIO41, GPIO42}; +static const unsigned p910_mmc3_pin1[] = {GPIO33, GPIO34, GPIO35, GPIO36, + GPIO49, GPIO50}; +static const unsigned p910_mmc3_pin2[] = {ND_IO7, ND_IO6, ND_IO5, ND_IO4, + ND_IO3, ND_IO2, ND_IO1, ND_IO0, ND_CLE, SM_SCLK}; +static const unsigned p910_uart0_pin1[] = {GPIO29, GPIO30, GPIO31, GPIO32}; +static const unsigned p910_uart1_pin1[] = {GPIO47, GPIO48}; +static const unsigned p910_uart1_pin2[] = {GPIO31, GPIO32}; +static const unsigned p910_uart1_pin3[] = {GPIO45, GPIO46}; +static const unsigned p910_uart1_pin4[] = {GPIO29, GPIO30, GPIO31, GPIO32}; +static const unsigned p910_uart1_pin5[] = {GPIO43, GPIO44, GPIO45, GPIO46}; +static const unsigned p910_uart2_pin1[] = {GPIO43, GPIO44}; +static const unsigned p910_uart2_pin2[] = {GPIO51, GPIO52}; +static const unsigned p910_uart2_pin3[] = {GPIO43, GPIO44, GPIO45, GPIO46}; +static const unsigned p910_uart2_pin4[] = {GPIO51, GPIO52, GPIO53, GPIO54}; +static const unsigned p910_twsi_pin1[] = {GPIO51, GPIO52}; +static const unsigned p910_twsi_pin2[] = {GPIO53, GPIO54}; +static const unsigned p910_twsi_pin3[] = {GPIO79, GPIO80}; +static const unsigned p910_ccic_pin1[] = {GPIO67, GPIO68, GPIO69, GPIO70, + GPIO71, GPIO72, GPIO73, GPIO74, GPIO75, GPIO76, GPIO77, GPIO78}; +static const unsigned p910_lcd_pin1[] = {GPIO81, GPIO82, GPIO83, GPIO84, + GPIO85, GPIO86, GPIO87, GPIO88, GPIO89, GPIO90, GPIO91, GPIO92, + GPIO93, GPIO94, GPIO95, GPIO96, GPIO97, GPIO98, GPIO100, GPIO101, + GPIO102, GPIO103}; +static const unsigned p910_spi_pin1[] = {GPIO104, GPIO105, GPIO107, GPIO108}; +static const unsigned p910_spi_pin2[] = {GPIO43, GPIO44, GPIO45, GPIO46}; +static const unsigned p910_spi_pin3[] = {GPIO33, GPIO34, GPIO35, GPIO36, + GPIO37}; +static const unsigned p910_spi_pin4[] = {GPIO67, GPIO68, GPIO69, GPIO70, + GPIO71}; +static const unsigned p910_spi2_pin1[] = {GPIO64, GPIO65}; +static const unsigned p910_spi2_pin2[] = {GPIO102, GPIO103}; +static const unsigned p910_dssp2_pin1[] = {GPIO102, GPIO103, GPIO104, GPIO105}; +static const unsigned p910_dssp2_pin2[] = {GPIO43, GPIO44, GPIO45, GPIO46}; +static const unsigned p910_dssp2_pin3[] = {GPIO111, GPIO112, GPIO113}; +static const unsigned p910_dssp3_pin1[] = {GPIO106, GPIO107, GPIO108, GPIO109}; +static const unsigned p910_dssp3_pin2[] = {GPIO51, GPIO52, GPIO53, GPIO54}; +static const unsigned p910_dssp3_pin3[] = {GPIO114, GPIO115, GPIO116}; +static const unsigned p910_ssp0_pin1[] = {MMC1_DAT3, MMC1_DAT2, MMC1_DAT1, + MMC1_CLK}; +static const unsigned p910_ssp0_pin2[] = {GPIO33, GPIO34, GPIO35, GPIO36}; +static const unsigned p910_ssp0_pin3[] = {GPIO47, GPIO48, GPIO49, GPIO50}; +static const unsigned p910_ssp0_pin4[] = {GPIO51, GPIO52, GPIO53, GPIO54}; +static const unsigned p910_ssp1_pin1[] = {GPIO21, GPIO22, GPIO23, GPIO24}; +static const unsigned p910_ssp1_pin2[] = {GPIO20, GPIO21, GPIO22, GPIO23, + GPIO24}; +static const unsigned p910_ssp2_pin1[] = {MMC1_DAT2, MMC1_DAT1, MMC1_DAT0, + MMC1_CMD}; +static const unsigned p910_ssp2_pin2[] = {GPIO33, GPIO34, GPIO35, GPIO36}; +static const unsigned p910_ssp2_pin3[] = {GPIO47, GPIO48, GPIO49, GPIO50}; +static const unsigned p910_ssp2_pin4[] = {ND_IO12, ND_IO11, ND_IO10, ND_IO9}; +static const unsigned p910_gssp_pin1[] = {GPIO25, GPIO26, GPIO27, GPIO28}; +static const unsigned p910_pwm0_pin1[] = {GPIO117}; +static const unsigned p910_pwm1_pin1[] = {GPIO118}; +static const unsigned p910_pwm1_pin2[] = {GPIO51}; +static const unsigned p910_pwm2_pin1[] = {GPIO119}; +static const unsigned p910_pwm3_pin1[] = {GPIO120}; +static const unsigned p910_pwm3_pin2[] = {ND_IO8}; +static const unsigned p910_pwm3_pin3[] = {VCXO_OUT}; +static const unsigned p910_pri_jtag_pin1[] = {GPIO117, GPIO118, GPIO119, + GPIO120}; +static const unsigned p910_sec1_jtag_pin1[] = {MMC1_DAT7, MMC1_DAT6, MMC1_DAT5, + MMC1_CMD, MMC1_CD}; +static const unsigned p910_sec2_jtag_pin1[] = {MMC1_DAT3, MMC1_DAT2, MMC1_DAT1, + MMC1_DAT0, MMC1_CLK}; +static const unsigned p910_hsl_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40, + GPIO41, GPIO42}; +static const unsigned p910_hsl_pin2[] = {GPIO61, GPIO62, GPIO63, GPIO64, + GPIO65, GPIO66}; +static const unsigned p910_hsl_pin3[] = {MMC1_DAT3, MMC1_DAT2, MMC1_DAT1, + MMC1_DAT0, MMC1_CMD, MMC1_CLK}; +static const unsigned p910_w1_pin1[] = {GPIO59}; +static const unsigned p910_w1_pin2[] = {GPIO65}; +static const unsigned p910_w1_pin3[] = {GPIO106}; +static const unsigned p910_w1_pin4[] = {GPIO123}; +static const unsigned p910_kpmk_pin1[] = {GPIO0, GPIO1, GPIO2, GPIO3, GPIO4, + GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO10, GPIO11, GPIO12, GPIO13, + GPIO14, GPIO15}; +static const unsigned p910_kpmk_pin2[] = {GPIO0, GPIO1, GPIO2, GPIO3, GPIO4, + GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO12}; +static const unsigned p910_kpdk_pin1[] = {GPIO12, GPIO13, GPIO14, GPIO15, + GPIO16, GPIO17, GPIO18, GPIO19}; +static const unsigned p910_tds_pin1[] = {GPIO55, GPIO56, GPIO57, GPIO58, + GPIO59}; +static const unsigned p910_tds_pin2[] = {GPIO55, GPIO57, GPIO58, GPIO59}; +static const unsigned p910_tb_pin1[] = {GPIO14, GPIO15, GPIO16, GPIO17}; +static const unsigned p910_tb_pin2[] = {GPIO55, GPIO56, GPIO57, GPIO58}; +static const unsigned p910_tb_pin3[] = {MMC1_DAT7, MMC1_DAT6, MMC1_DAT5, + MMC1_DAT4}; +static const unsigned p910_ext_dma0_pin1[] = {GPIO72}; +static const unsigned p910_ext_dma0_pin2[] = {ND_IO15}; +static const unsigned p910_ext_dma0_pin3[] = {ND_NRE}; +static const unsigned p910_ext_dma1_pin1[] = {GPIO73}; +static const unsigned p910_ext_dma1_pin2[] = {GPIO123}; +static const unsigned p910_ext_dma1_pin3[] = {GPIO126}; +static const unsigned p910_ext_dma2_pin1[] = {GPIO74}; +static const unsigned p910_ext0_int_pin1[] = {GPIO44}; +static const unsigned p910_ext0_int_pin2[] = {ND_IO13}; +static const unsigned p910_ext1_int_pin1[] = {GPIO45}; +static const unsigned p910_ext1_int_pin2[] = {ND_IO12}; +static const unsigned p910_ext2_int_pin1[] = {GPIO46}; +static const unsigned p910_ext2_int_pin2[] = {GPIO125}; +static const unsigned p910_dac_st23_pin1[] = {GPIO32}; +static const unsigned p910_dac_st23_pin2[] = {GPIO43}; +static const unsigned p910_dac_st23_pin3[] = {GPIO52}; +static const unsigned p910_dac_st23_pin4[] = {GPIO124}; +static const unsigned p910_vcxo_out_pin1[] = {GPIO50}; +static const unsigned p910_vcxo_out_pin2[] = {VCXO_OUT}; +static const unsigned p910_vcxo_out_pin3[] = {GPIO20}; +static const unsigned p910_vcxo_req_pin1[] = {GPIO49}; +static const unsigned p910_vcxo_req_pin2[] = {GPIO125}; +static const unsigned p910_vcxo_out2_pin1[] = {GPIO86}; +static const unsigned p910_vcxo_out2_pin2[] = {ND_IO9}; +static const unsigned p910_vcxo_req2_pin1[] = {GPIO84}; +static const unsigned p910_ulpi_pin1[] = {GPIO67, GPIO68, GPIO69, GPIO70, + GPIO71, GPIO72, GPIO73, GPIO74, GPIO75, GPIO76, GPIO77, GPIO78}; +static const unsigned p910_nand_pin1[] = {ND_IO15, ND_IO14, ND_IO13, ND_IO12, + ND_IO11, ND_IO10, ND_IO9, ND_IO8, ND_IO7, ND_IO6, ND_IO5, ND_IO4, + ND_IO3, ND_IO2, ND_IO1, ND_IO0, ND_NCS0, ND_NWE, ND_NRE, ND_CLE, + ND_ALE, ND_RDY0}; +static const unsigned p910_gpio0_pin1[] = {GPIO0}; +static const unsigned p910_gpio0_pin2[] = {SM_ADV}; +static const unsigned p910_gpio1_pin1[] = {GPIO1}; +static const unsigned p910_gpio1_pin2[] = {ND_RDY1}; +static const unsigned p910_gpio2_pin1[] = {GPIO2}; +static const unsigned p910_gpio2_pin2[] = {SM_ADVMUX}; +static const unsigned p910_gpio3_pin1[] = {GPIO3}; +static const unsigned p910_gpio3_pin2[] = {SM_RDY}; +static const unsigned p910_gpio20_pin1[] = {GPIO20}; +static const unsigned p910_gpio20_pin2[] = {ND_IO15}; +static const unsigned p910_gpio20_pin3[] = {MMC1_DAT6}; +static const unsigned p910_gpio21_pin1[] = {GPIO21}; +static const unsigned p910_gpio21_pin2[] = {ND_IO14}; +static const unsigned p910_gpio21_pin3[] = {MMC1_DAT5}; +static const unsigned p910_gpio22_pin1[] = {GPIO22}; +static const unsigned p910_gpio22_pin2[] = {ND_IO13}; +static const unsigned p910_gpio22_pin3[] = {MMC1_DAT4}; +static const unsigned p910_gpio23_pin1[] = {GPIO23}; +static const unsigned p910_gpio23_pin2[] = {ND_IO12}; +static const unsigned p910_gpio23_pin3[] = {MMC1_CD}; +static const unsigned p910_gpio24_pin1[] = {GPIO24}; +static const unsigned p910_gpio24_pin2[] = {ND_IO11}; +static const unsigned p910_gpio24_pin3[] = {MMC1_DAT7}; +static const unsigned p910_gpio25_pin1[] = {GPIO25}; +static const unsigned p910_gpio25_pin2[] = {ND_IO10}; +static const unsigned p910_gpio26_pin1[] = {GPIO26}; +static const unsigned p910_gpio26_pin2[] = {ND_IO9}; +static const unsigned p910_gpio27_pin1[] = {GPIO27}; +static const unsigned p910_gpio27_pin2[] = {ND_IO8}; +static const unsigned p910_gpio85_pin1[] = {GPIO85}; +static const unsigned p910_gpio85_pin2[] = {ND_NCS0}; +static const unsigned p910_gpio86_pin1[] = {GPIO86}; +static const unsigned p910_gpio86_pin2[] = {ND_NCS1}; +static const unsigned p910_gpio87_pin1[] = {GPIO87}; +static const unsigned p910_gpio87_pin2[] = {SM_NCS0}; +static const unsigned p910_gpio88_pin1[] = {GPIO88}; +static const unsigned p910_gpio88_pin2[] = {SM_NCS1}; +static const unsigned p910_gpio89_pin1[] = {GPIO89}; +static const unsigned p910_gpio89_pin2[] = {ND_NWE}; +static const unsigned p910_gpio90_pin1[] = {GPIO90}; +static const unsigned p910_gpio90_pin2[] = {ND_NRE}; +static const unsigned p910_gpio91_pin1[] = {GPIO91}; +static const unsigned p910_gpio91_pin2[] = {ND_ALE}; +static const unsigned p910_gpio92_pin1[] = {GPIO92}; +static const unsigned p910_gpio92_pin2[] = {ND_RDY0}; + +static struct pxa3xx_pin_group pxa910_grps[] = { + GRP_910("usim2 3p1", USIM2, p910_usim2_pin1), + GRP_910("usim2 3p2", USIM2, p910_usim2_pin2), + GRP_910("mmc1 12p", MMC1, p910_mmc1_pin1), + GRP_910("mmc2 10p", MMC2, p910_mmc2_pin1), + GRP_910("mmc3 6p", MMC3, p910_mmc3_pin1), + GRP_910("mmc3 10p", MMC3, p910_mmc3_pin2), + GRP_910("uart0 4p", UART0, p910_uart0_pin1), + GRP_910("uart1 2p1", UART1, p910_uart1_pin1), + GRP_910("uart1 2p2", UART1, p910_uart1_pin2), + GRP_910("uart1 2p3", UART1, p910_uart1_pin3), + GRP_910("uart1 4p4", UART1, p910_uart1_pin4), + GRP_910("uart1 4p5", UART1, p910_uart1_pin5), + GRP_910("uart2 2p1", UART2, p910_uart2_pin1), + GRP_910("uart2 2p2", UART2, p910_uart2_pin2), + GRP_910("uart2 4p3", UART2, p910_uart2_pin3), + GRP_910("uart2 4p4", UART2, p910_uart2_pin4), + GRP_910("twsi 2p1", TWSI, p910_twsi_pin1), + GRP_910("twsi 2p2", TWSI, p910_twsi_pin2), + GRP_910("twsi 2p3", TWSI, p910_twsi_pin3), + GRP_910("ccic", CCIC, p910_ccic_pin1), + GRP_910("lcd", LCD, p910_lcd_pin1), + GRP_910("spi 4p1", SPI, p910_spi_pin1), + GRP_910("spi 4p2", SPI, p910_spi_pin2), + GRP_910("spi 5p3", SPI, p910_spi_pin3), + GRP_910("spi 5p4", SPI, p910_spi_pin4), + GRP_910("dssp2 4p1", DSSP2, p910_dssp2_pin1), + GRP_910("dssp2 4p2", DSSP2, p910_dssp2_pin2), + GRP_910("dssp2 3p3", DSSP2, p910_dssp2_pin3), + GRP_910("dssp3 4p1", DSSP3, p910_dssp3_pin1), + GRP_910("dssp3 4p2", DSSP3, p910_dssp3_pin2), + GRP_910("dssp3 3p3", DSSP3, p910_dssp3_pin3), + GRP_910("ssp0 4p1", SSP0, p910_ssp0_pin1), + GRP_910("ssp0 4p2", SSP0, p910_ssp0_pin2), + GRP_910("ssp0 4p3", SSP0, p910_ssp0_pin3), + GRP_910("ssp0 4p4", SSP0, p910_ssp0_pin4), + GRP_910("ssp1 4p1", SSP1, p910_ssp1_pin1), + GRP_910("ssp1 5p2", SSP1, p910_ssp1_pin2), + GRP_910("ssp2 4p1", SSP2, p910_ssp2_pin1), + GRP_910("ssp2 4p2", SSP2, p910_ssp2_pin2), + GRP_910("ssp2 4p3", SSP2, p910_ssp2_pin3), + GRP_910("ssp2 4p4", SSP2, p910_ssp2_pin4), + GRP_910("gssp", GSSP, p910_gssp_pin1), + GRP_910("pwm0", PWM0, p910_pwm0_pin1), + GRP_910("pwm1-1", PWM1, p910_pwm1_pin1), + GRP_910("pwm1-2", PWM1, p910_pwm1_pin2), + GRP_910("pwm2", PWM2, p910_pwm2_pin1), + GRP_910("pwm3-1", PWM3, p910_pwm3_pin1), + GRP_910("pwm3-2", PWM3, p910_pwm3_pin2), + GRP_910("pwm3-3", PWM3, p910_pwm3_pin3), + GRP_910("pri jtag", PRI_JTAG, p910_pri_jtag_pin1), + GRP_910("sec1 jtag", SEC1_JTAG, p910_sec1_jtag_pin1), + GRP_910("sec2 jtag", SEC2_JTAG, p910_sec2_jtag_pin1), + GRP_910("hsl 6p1", HSL, p910_hsl_pin1), + GRP_910("hsl 6p2", HSL, p910_hsl_pin2), + GRP_910("hsl 6p3", HSL, p910_hsl_pin3), + GRP_910("w1-1", ONE_WIRE, p910_w1_pin1), + GRP_910("w1-2", ONE_WIRE, p910_w1_pin2), + GRP_910("w1-3", ONE_WIRE, p910_w1_pin3), + GRP_910("w1-4", ONE_WIRE, p910_w1_pin4), + GRP_910("kpmk 16p1", KP_MK, p910_kpmk_pin1), + GRP_910("kpmk 11p2", KP_MK, p910_kpmk_pin2), + GRP_910("kpdk 8p1", KP_DK, p910_kpdk_pin1), + GRP_910("tds 5p1", TDS, p910_tds_pin1), + GRP_910("tds 4p2", TDS, p910_tds_pin2), + GRP_910("tb 4p1", TB, p910_tb_pin1), + GRP_910("tb 4p2", TB, p910_tb_pin2), + GRP_910("tb 4p3", TB, p910_tb_pin3), + GRP_910("ext dma0-1", EXT_DMA, p910_ext_dma0_pin1), + GRP_910("ext dma0-2", EXT_DMA, p910_ext_dma0_pin2), + GRP_910("ext dma0-3", EXT_DMA, p910_ext_dma0_pin3), + GRP_910("ext dma1-1", EXT_DMA, p910_ext_dma1_pin1), + GRP_910("ext dma1-2", EXT_DMA, p910_ext_dma1_pin2), + GRP_910("ext dma1-3", EXT_DMA, p910_ext_dma1_pin3), + GRP_910("ext dma2", EXT_DMA, p910_ext_dma2_pin1), + GRP_910("ext0 int-1", EXT_INT, p910_ext0_int_pin1), + GRP_910("ext0 int-2", EXT_INT, p910_ext0_int_pin2), + GRP_910("ext1 int-1", EXT_INT, p910_ext1_int_pin1), + GRP_910("ext1 int-2", EXT_INT, p910_ext1_int_pin2), + GRP_910("ext2 int-1", EXT_INT, p910_ext2_int_pin1), + GRP_910("ext2 int-2", EXT_INT, p910_ext2_int_pin2), + GRP_910("dac st23-1", DAC_ST23, p910_dac_st23_pin1), + GRP_910("dac st23-2", DAC_ST23, p910_dac_st23_pin2), + GRP_910("dac st23-3", DAC_ST23, p910_dac_st23_pin3), + GRP_910("dac st23-4", DAC_ST23, p910_dac_st23_pin4), + GRP_910("vcxo out-1", VCXO_OUT, p910_vcxo_out_pin1), + GRP_910("vcxo out-2", VCXO_OUT, p910_vcxo_out_pin2), + GRP_910("vcxo out-3", VCXO_OUT, p910_vcxo_out_pin3), + GRP_910("vcxo req-1", VCXO_REQ, p910_vcxo_req_pin1), + GRP_910("vcxo req-2", VCXO_REQ, p910_vcxo_req_pin2), + GRP_910("vcxo out2-1", VCXO_OUT2, p910_vcxo_out2_pin1), + GRP_910("vcxo out2-2", VCXO_OUT2, p910_vcxo_out2_pin2), + GRP_910("vcxo req2", VCXO_REQ2, p910_vcxo_req2_pin1), + GRP_910("ulpi", ULPI, p910_ulpi_pin1), + GRP_910("nand", NAND, p910_nand_pin1), + GRP_910("gpio0-1", GPIO, p910_gpio0_pin1), + GRP_910("gpio0-2", GPIO, p910_gpio0_pin2), + GRP_910("gpio1-1", GPIO, p910_gpio1_pin1), + GRP_910("gpio1-2", GPIO, p910_gpio1_pin2), + GRP_910("gpio2-1", GPIO, p910_gpio2_pin1), + GRP_910("gpio2-2", GPIO, p910_gpio2_pin2), + GRP_910("gpio3-1", GPIO, p910_gpio3_pin1), + GRP_910("gpio3-2", GPIO, p910_gpio3_pin2), + GRP_910("gpio20-1", GPIO, p910_gpio20_pin1), + GRP_910("gpio20-2", GPIO, p910_gpio20_pin2), + GRP_910("gpio21-1", GPIO, p910_gpio21_pin1), + GRP_910("gpio21-2", GPIO, p910_gpio21_pin2), + GRP_910("gpio22-1", GPIO, p910_gpio22_pin1), + GRP_910("gpio22-2", GPIO, p910_gpio22_pin2), + GRP_910("gpio23-1", GPIO, p910_gpio23_pin1), + GRP_910("gpio23-2", GPIO, p910_gpio23_pin2), + GRP_910("gpio24-1", GPIO, p910_gpio24_pin1), + GRP_910("gpio24-2", GPIO, p910_gpio24_pin2), + GRP_910("gpio25-1", GPIO, p910_gpio25_pin1), + GRP_910("gpio25-2", GPIO, p910_gpio25_pin2), + GRP_910("gpio26-1", GPIO, p910_gpio26_pin1), + GRP_910("gpio26-2", GPIO, p910_gpio26_pin2), + GRP_910("gpio27-1", GPIO, p910_gpio27_pin1), + GRP_910("gpio27-2", GPIO, p910_gpio27_pin2), + GRP_910("gpio85-1", GPIO, p910_gpio85_pin1), + GRP_910("gpio85-2", GPIO, p910_gpio85_pin2), + GRP_910("gpio86-1", GPIO, p910_gpio86_pin1), + GRP_910("gpio86-2", GPIO, p910_gpio86_pin2), + GRP_910("gpio87-1", GPIO, p910_gpio87_pin1), + GRP_910("gpio87-2", GPIO, p910_gpio87_pin2), + GRP_910("gpio88-1", GPIO, p910_gpio88_pin1), + GRP_910("gpio88-2", GPIO, p910_gpio88_pin2), + GRP_910("gpio89-1", GPIO, p910_gpio89_pin1), + GRP_910("gpio89-2", GPIO, p910_gpio89_pin2), + GRP_910("gpio90-1", GPIO, p910_gpio90_pin1), + GRP_910("gpio90-2", GPIO, p910_gpio90_pin2), + GRP_910("gpio91-1", GPIO, p910_gpio91_pin1), + GRP_910("gpio91-2", GPIO, p910_gpio91_pin2), + GRP_910("gpio92-1", GPIO, p910_gpio92_pin1), + GRP_910("gpio92-2", GPIO, p910_gpio92_pin2), +}; + +static const char * const p910_usim2_grps[] = {"usim2 3p1", "usim2 3p2"}; +static const char * const p910_mmc1_grps[] = {"mmc1 12p"}; +static const char * const p910_mmc2_grps[] = {"mmc2 10p"}; +static const char * const p910_mmc3_grps[] = {"mmc3 6p", "mmc3 10p"}; +static const char * const p910_uart0_grps[] = {"uart0 4p"}; +static const char * const p910_uart1_grps[] = {"uart1 2p1", "uart1 2p2", + "uart1 2p3", "uart1 4p4", "uart1 4p5"}; +static const char * const p910_uart2_grps[] = {"uart2 2p1", "uart2 2p2", + "uart2 4p3", "uart2 4p4"}; +static const char * const p910_twsi_grps[] = {"twsi 2p1", "twsi 2p2", + "twsi 2p3"}; +static const char * const p910_ccic_grps[] = {"ccic"}; +static const char * const p910_lcd_grps[] = {"lcd"}; +static const char * const p910_spi_grps[] = {"spi 4p1", "spi 4p2", "spi 5p3", + "spi 5p4"}; +static const char * const p910_dssp2_grps[] = {"dssp2 4p1", "dssp2 4p2", + "dssp2 3p3"}; +static const char * const p910_dssp3_grps[] = {"dssp3 4p1", "dssp3 4p2", + "dssp3 3p3"}; +static const char * const p910_ssp0_grps[] = {"ssp0 4p1", "ssp0 4p2", + "ssp0 4p3", "ssp0 4p4"}; +static const char * const p910_ssp1_grps[] = {"ssp1 4p1", "ssp1 5p2"}; +static const char * const p910_ssp2_grps[] = {"ssp2 4p1", "ssp2 4p2", + "ssp2 4p3", "ssp2 4p4"}; +static const char * const p910_gssp_grps[] = {"gssp"}; +static const char * const p910_pwm0_grps[] = {"pwm0"}; +static const char * const p910_pwm1_grps[] = {"pwm1-1", "pwm1-2"}; +static const char * const p910_pwm2_grps[] = {"pwm2"}; +static const char * const p910_pwm3_grps[] = {"pwm3-1", "pwm3-2", "pwm3-3"}; +static const char * const p910_pri_jtag_grps[] = {"pri jtag"}; +static const char * const p910_sec1_jtag_grps[] = {"sec1 jtag"}; +static const char * const p910_sec2_jtag_grps[] = {"sec2 jtag"}; +static const char * const p910_hsl_grps[] = {"hsl 6p1", "hsl 6p2", "hsl 6p3"}; +static const char * const p910_w1_grps[] = {"w1-1", "w1-2", "w1-3", "w1-4"}; +static const char * const p910_kpmk_grps[] = {"kpmk 16p1", "kpmk 11p2"}; +static const char * const p910_kpdk_grps[] = {"kpdk 8p1"}; +static const char * const p910_tds_grps[] = {"tds 5p1", "tds 4p2"}; +static const char * const p910_tb_grps[] = {"tb 4p1", "tb 4p2", "tb 4p3"}; +static const char * const p910_dma0_grps[] = {"ext dma0-1", "ext dma0-2", + "ext dma0-3"}; +static const char * const p910_dma1_grps[] = {"ext dma1-1", "ext dma1-2", + "ext dma1-3"}; +static const char * const p910_dma2_grps[] = {"ext dma2"}; +static const char * const p910_int0_grps[] = {"ext0 int-1", "ext0 int-2"}; +static const char * const p910_int1_grps[] = {"ext1 int-1", "ext1 int-2"}; +static const char * const p910_int2_grps[] = {"ext2 int-1", "ext2 int-2"}; +static const char * const p910_dac_st23_grps[] = {"dac st23-1", "dac st23-2", + "dac st23-3", "dac st23-4"}; +static const char * const p910_vcxo_out_grps[] = {"vcxo out-1", "vcxo out-2", + "vcxo out-3"}; +static const char * const p910_vcxo_req_grps[] = {"vcxo req-1", "vcxo req-2"}; +static const char * const p910_vcxo_out2_grps[] = {"vcxo out2-1", + "vcxo out2-2"}; +static const char * const p910_vcxo_req2_grps[] = {"vcxo req2"}; +static const char * const p910_ulpi_grps[] = {"ulpi"}; +static const char * const p910_nand_grps[] = {"nand"}; +static const char * const p910_gpio0_grps[] = {"gpio0-1", "gpio0-2"}; +static const char * const p910_gpio1_grps[] = {"gpio1-1", "gpio1-2"}; +static const char * const p910_gpio2_grps[] = {"gpio2-1", "gpio2-2"}; +static const char * const p910_gpio3_grps[] = {"gpio3-1", "gpio3-2"}; +static const char * const p910_gpio20_grps[] = {"gpio20-1", "gpio20-2"}; +static const char * const p910_gpio21_grps[] = {"gpio21-1", "gpio21-2"}; +static const char * const p910_gpio22_grps[] = {"gpio22-1", "gpio22-2"}; +static const char * const p910_gpio23_grps[] = {"gpio23-1", "gpio23-2"}; +static const char * const p910_gpio24_grps[] = {"gpio24-1", "gpio24-2"}; +static const char * const p910_gpio25_grps[] = {"gpio25-1", "gpio25-2"}; +static const char * const p910_gpio26_grps[] = {"gpio26-1", "gpio26-2"}; +static const char * const p910_gpio27_grps[] = {"gpio27-1", "gpio27-2"}; +static const char * const p910_gpio85_grps[] = {"gpio85-1", "gpio85-2"}; +static const char * const p910_gpio86_grps[] = {"gpio86-1", "gpio86-2"}; +static const char * const p910_gpio87_grps[] = {"gpio87-1", "gpio87-2"}; +static const char * const p910_gpio88_grps[] = {"gpio88-1", "gpio88-2"}; +static const char * const p910_gpio89_grps[] = {"gpio89-1", "gpio89-2"}; +static const char * const p910_gpio90_grps[] = {"gpio90-1", "gpio90-2"}; +static const char * const p910_gpio91_grps[] = {"gpio91-1", "gpio91-2"}; +static const char * const p910_gpio92_grps[] = {"gpio92-1", "gpio92-2"}; + +static struct pxa3xx_pmx_func pxa910_funcs[] = { + {"usim2", ARRAY_AND_SIZE(p910_usim2_grps)}, + {"mmc1", ARRAY_AND_SIZE(p910_mmc1_grps)}, + {"mmc2", ARRAY_AND_SIZE(p910_mmc2_grps)}, + {"mmc3", ARRAY_AND_SIZE(p910_mmc3_grps)}, + {"uart0", ARRAY_AND_SIZE(p910_uart0_grps)}, + {"uart1", ARRAY_AND_SIZE(p910_uart1_grps)}, + {"uart2", ARRAY_AND_SIZE(p910_uart2_grps)}, + {"twsi", ARRAY_AND_SIZE(p910_twsi_grps)}, + {"ccic", ARRAY_AND_SIZE(p910_ccic_grps)}, + {"lcd", ARRAY_AND_SIZE(p910_lcd_grps)}, + {"spi", ARRAY_AND_SIZE(p910_spi_grps)}, + {"dssp2", ARRAY_AND_SIZE(p910_dssp2_grps)}, + {"dssp3", ARRAY_AND_SIZE(p910_dssp3_grps)}, + {"ssp0", ARRAY_AND_SIZE(p910_ssp0_grps)}, + {"ssp1", ARRAY_AND_SIZE(p910_ssp1_grps)}, + {"ssp2", ARRAY_AND_SIZE(p910_ssp2_grps)}, + {"gssp", ARRAY_AND_SIZE(p910_gssp_grps)}, + {"pwm0", ARRAY_AND_SIZE(p910_pwm0_grps)}, + {"pwm1", ARRAY_AND_SIZE(p910_pwm1_grps)}, + {"pwm2", ARRAY_AND_SIZE(p910_pwm2_grps)}, + {"pwm3", ARRAY_AND_SIZE(p910_pwm3_grps)}, + {"pri_jtag", ARRAY_AND_SIZE(p910_pri_jtag_grps)}, + {"sec1_jtag", ARRAY_AND_SIZE(p910_sec1_jtag_grps)}, + {"sec2_jtag", ARRAY_AND_SIZE(p910_sec2_jtag_grps)}, + {"hsl", ARRAY_AND_SIZE(p910_hsl_grps)}, + {"w1", ARRAY_AND_SIZE(p910_w1_grps)}, + {"kpmk", ARRAY_AND_SIZE(p910_kpmk_grps)}, + {"kpdk", ARRAY_AND_SIZE(p910_kpdk_grps)}, + {"tds", ARRAY_AND_SIZE(p910_tds_grps)}, + {"tb", ARRAY_AND_SIZE(p910_tb_grps)}, + {"dma0", ARRAY_AND_SIZE(p910_dma0_grps)}, + {"dma1", ARRAY_AND_SIZE(p910_dma1_grps)}, + {"dma2", ARRAY_AND_SIZE(p910_dma2_grps)}, + {"int0", ARRAY_AND_SIZE(p910_int0_grps)}, + {"int1", ARRAY_AND_SIZE(p910_int1_grps)}, + {"int2", ARRAY_AND_SIZE(p910_int2_grps)}, + {"dac_st23", ARRAY_AND_SIZE(p910_dac_st23_grps)}, + {"vcxo_out", ARRAY_AND_SIZE(p910_vcxo_out_grps)}, + {"vcxo_req", ARRAY_AND_SIZE(p910_vcxo_req_grps)}, + {"vcxo_out2", ARRAY_AND_SIZE(p910_vcxo_out2_grps)}, + {"vcxo_req2", ARRAY_AND_SIZE(p910_vcxo_req2_grps)}, + {"ulpi", ARRAY_AND_SIZE(p910_ulpi_grps)}, + {"nand", ARRAY_AND_SIZE(p910_nand_grps)}, + {"gpio0", ARRAY_AND_SIZE(p910_gpio0_grps)}, + {"gpio1", ARRAY_AND_SIZE(p910_gpio1_grps)}, + {"gpio2", ARRAY_AND_SIZE(p910_gpio2_grps)}, + {"gpio3", ARRAY_AND_SIZE(p910_gpio3_grps)}, + {"gpio20", ARRAY_AND_SIZE(p910_gpio20_grps)}, + {"gpio21", ARRAY_AND_SIZE(p910_gpio21_grps)}, + {"gpio22", ARRAY_AND_SIZE(p910_gpio22_grps)}, + {"gpio23", ARRAY_AND_SIZE(p910_gpio23_grps)}, + {"gpio24", ARRAY_AND_SIZE(p910_gpio24_grps)}, + {"gpio25", ARRAY_AND_SIZE(p910_gpio25_grps)}, + {"gpio26", ARRAY_AND_SIZE(p910_gpio26_grps)}, + {"gpio27", ARRAY_AND_SIZE(p910_gpio27_grps)}, + {"gpio85", ARRAY_AND_SIZE(p910_gpio85_grps)}, + {"gpio86", ARRAY_AND_SIZE(p910_gpio86_grps)}, + {"gpio87", ARRAY_AND_SIZE(p910_gpio87_grps)}, + {"gpio88", ARRAY_AND_SIZE(p910_gpio88_grps)}, + {"gpio89", ARRAY_AND_SIZE(p910_gpio89_grps)}, + {"gpio90", ARRAY_AND_SIZE(p910_gpio90_grps)}, + {"gpio91", ARRAY_AND_SIZE(p910_gpio91_grps)}, + {"gpio92", ARRAY_AND_SIZE(p910_gpio92_grps)}, +}; + +static struct pinctrl_desc pxa910_pctrl_desc = { + .name = "pxa910-pinctrl", + .owner = THIS_MODULE, +}; + +static struct pxa3xx_pinmux_info pxa910_info = { + .mfp = pxa910_mfp, + .num_mfp = ARRAY_SIZE(pxa910_mfp), + .grps = pxa910_grps, + .num_grps = ARRAY_SIZE(pxa910_grps), + .funcs = pxa910_funcs, + .num_funcs = ARRAY_SIZE(pxa910_funcs), + .num_gpio = 128, + .desc = &pxa910_pctrl_desc, + .pads = pxa910_pads, + .num_pads = ARRAY_SIZE(pxa910_pads), + + .cputype = PINCTRL_PXA910, + .ds_mask = PXA910_DS_MASK, + .ds_shift = PXA910_DS_SHIFT, +}; + +static int __devinit pxa910_pinmux_probe(struct platform_device *pdev) +{ + return pxa3xx_pinctrl_register(pdev, &pxa910_info); +} + +static int __devexit pxa910_pinmux_remove(struct platform_device *pdev) +{ + return pxa3xx_pinctrl_unregister(pdev); +} + +static struct platform_driver pxa910_pinmux_driver = { + .driver = { + .name = "pxa910-pinmux", + .owner = THIS_MODULE, + }, + .probe = pxa910_pinmux_probe, + .remove = __devexit_p(pxa910_pinmux_remove), +}; + +static int __init pxa910_pinmux_init(void) +{ + return platform_driver_register(&pxa910_pinmux_driver); +} +core_initcall_sync(pxa910_pinmux_init); + +static void __exit pxa910_pinmux_exit(void) +{ + platform_driver_unregister(&pxa910_pinmux_driver); +} +module_exit(pxa910_pinmux_exit); + +MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); +MODULE_DESCRIPTION("PXA3xx pin control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c new file mode 100644 index 000000000000..9b329688120c --- /dev/null +++ b/drivers/pinctrl/pinctrl-tegra.c @@ -0,0 +1,559 @@ +/* + * Driver for the NVIDIA Tegra pinmux + * + * Copyright (c) 2011, NVIDIA CORPORATION. All rights reserved. + * + * Derived from code: + * Copyright (C) 2010 Google, Inc. + * Copyright (C) 2010 NVIDIA Corporation + * Copyright (C) 2009-2011 ST-Ericsson AB + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/pinctrl/pinconf.h> + +#include <mach/pinconf-tegra.h> + +#include "pinctrl-tegra.h" + +#define DRIVER_NAME "tegra-pinmux-disabled" + +struct tegra_pmx { + struct device *dev; + struct pinctrl_dev *pctl; + + const struct tegra_pinctrl_soc_data *soc; + + int nbanks; + void __iomem **regs; +}; + +static inline u32 pmx_readl(struct tegra_pmx *pmx, u32 bank, u32 reg) +{ + return readl(pmx->regs[bank] + reg); +} + +static inline void pmx_writel(struct tegra_pmx *pmx, u32 val, u32 bank, u32 reg) +{ + writel(val, pmx->regs[bank] + reg); +} + +static int tegra_pinctrl_list_groups(struct pinctrl_dev *pctldev, + unsigned group) +{ + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + + if (group >= pmx->soc->ngroups) + return -EINVAL; + + return 0; +} + +static const char *tegra_pinctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned group) +{ + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + + if (group >= pmx->soc->ngroups) + return NULL; + + return pmx->soc->groups[group].name; +} + +static int tegra_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned group, + const unsigned **pins, + unsigned *num_pins) +{ + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + + if (group >= pmx->soc->ngroups) + return -EINVAL; + + *pins = pmx->soc->groups[group].pins; + *num_pins = pmx->soc->groups[group].npins; + + return 0; +} + +static void tegra_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned offset) +{ + seq_printf(s, " " DRIVER_NAME); +} + +static struct pinctrl_ops tegra_pinctrl_ops = { + .list_groups = tegra_pinctrl_list_groups, + .get_group_name = tegra_pinctrl_get_group_name, + .get_group_pins = tegra_pinctrl_get_group_pins, + .pin_dbg_show = tegra_pinctrl_pin_dbg_show, +}; + +static int tegra_pinctrl_list_funcs(struct pinctrl_dev *pctldev, + unsigned function) +{ + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + + if (function >= pmx->soc->nfunctions) + return -EINVAL; + + return 0; +} + +static const char *tegra_pinctrl_get_func_name(struct pinctrl_dev *pctldev, + unsigned function) +{ + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + + if (function >= pmx->soc->nfunctions) + return NULL; + + return pmx->soc->functions[function].name; +} + +static int tegra_pinctrl_get_func_groups(struct pinctrl_dev *pctldev, + unsigned function, + const char * const **groups, + unsigned * const num_groups) +{ + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + + if (function >= pmx->soc->nfunctions) + return -EINVAL; + + *groups = pmx->soc->functions[function].groups; + *num_groups = pmx->soc->functions[function].ngroups; + + return 0; +} + +static int tegra_pinctrl_enable(struct pinctrl_dev *pctldev, unsigned function, + unsigned group) +{ + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tegra_pingroup *g; + int i; + u32 val; + + if (group >= pmx->soc->ngroups) + return -EINVAL; + g = &pmx->soc->groups[group]; + + if (g->mux_reg < 0) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(g->funcs); i++) { + if (g->funcs[i] == function) + break; + } + if (i == ARRAY_SIZE(g->funcs)) + return -EINVAL; + + val = pmx_readl(pmx, g->mux_bank, g->mux_reg); + val &= ~(0x3 << g->mux_bit); + val |= i << g->mux_bit; + pmx_writel(pmx, val, g->mux_bank, g->mux_reg); + + return 0; +} + +static void tegra_pinctrl_disable(struct pinctrl_dev *pctldev, + unsigned function, unsigned group) +{ + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tegra_pingroup *g; + u32 val; + + if (group >= pmx->soc->ngroups) + return; + g = &pmx->soc->groups[group]; + + if (g->mux_reg < 0) + return; + + val = pmx_readl(pmx, g->mux_bank, g->mux_reg); + val &= ~(0x3 << g->mux_bit); + val |= g->func_safe << g->mux_bit; + pmx_writel(pmx, val, g->mux_bank, g->mux_reg); +} + +static struct pinmux_ops tegra_pinmux_ops = { + .list_functions = tegra_pinctrl_list_funcs, + .get_function_name = tegra_pinctrl_get_func_name, + .get_function_groups = tegra_pinctrl_get_func_groups, + .enable = tegra_pinctrl_enable, + .disable = tegra_pinctrl_disable, +}; + +static int tegra_pinconf_reg(struct tegra_pmx *pmx, + const struct tegra_pingroup *g, + enum tegra_pinconf_param param, + s8 *bank, s16 *reg, s8 *bit, s8 *width) +{ + switch (param) { + case TEGRA_PINCONF_PARAM_PULL: + *bank = g->pupd_bank; + *reg = g->pupd_reg; + *bit = g->pupd_bit; + *width = 2; + break; + case TEGRA_PINCONF_PARAM_TRISTATE: + *bank = g->tri_bank; + *reg = g->tri_reg; + *bit = g->tri_bit; + *width = 1; + break; + case TEGRA_PINCONF_PARAM_ENABLE_INPUT: + *bank = g->einput_bank; + *reg = g->einput_reg; + *bit = g->einput_bit; + *width = 1; + break; + case TEGRA_PINCONF_PARAM_OPEN_DRAIN: + *bank = g->odrain_bank; + *reg = g->odrain_reg; + *bit = g->odrain_bit; + *width = 1; + break; + case TEGRA_PINCONF_PARAM_LOCK: + *bank = g->lock_bank; + *reg = g->lock_reg; + *bit = g->lock_bit; + *width = 1; + break; + case TEGRA_PINCONF_PARAM_IORESET: + *bank = g->ioreset_bank; + *reg = g->ioreset_reg; + *bit = g->ioreset_bit; + *width = 1; + break; + case TEGRA_PINCONF_PARAM_HIGH_SPEED_MODE: + *bank = g->drv_bank; + *reg = g->drv_reg; + *bit = g->hsm_bit; + *width = 1; + break; + case TEGRA_PINCONF_PARAM_SCHMITT: + *bank = g->drv_bank; + *reg = g->drv_reg; + *bit = g->schmitt_bit; + *width = 1; + break; + case TEGRA_PINCONF_PARAM_LOW_POWER_MODE: + *bank = g->drv_bank; + *reg = g->drv_reg; + *bit = g->lpmd_bit; + *width = 1; + break; + case TEGRA_PINCONF_PARAM_DRIVE_DOWN_STRENGTH: + *bank = g->drv_bank; + *reg = g->drv_reg; + *bit = g->drvdn_bit; + *width = g->drvdn_width; + break; + case TEGRA_PINCONF_PARAM_DRIVE_UP_STRENGTH: + *bank = g->drv_bank; + *reg = g->drv_reg; + *bit = g->drvup_bit; + *width = g->drvup_width; + break; + case TEGRA_PINCONF_PARAM_SLEW_RATE_FALLING: + *bank = g->drv_bank; + *reg = g->drv_reg; + *bit = g->slwf_bit; + *width = g->slwf_width; + break; + case TEGRA_PINCONF_PARAM_SLEW_RATE_RISING: + *bank = g->drv_bank; + *reg = g->drv_reg; + *bit = g->slwr_bit; + *width = g->slwr_width; + break; + default: + dev_err(pmx->dev, "Invalid config param %04x\n", param); + return -ENOTSUPP; + } + + if (*reg < 0) { + dev_err(pmx->dev, + "Config param %04x not supported on group %s\n", + param, g->name); + return -ENOTSUPP; + } + + return 0; +} + +static int tegra_pinconf_get(struct pinctrl_dev *pctldev, + unsigned pin, unsigned long *config) +{ + return -ENOTSUPP; +} + +static int tegra_pinconf_set(struct pinctrl_dev *pctldev, + unsigned pin, unsigned long config) +{ + return -ENOTSUPP; +} + +static int tegra_pinconf_group_get(struct pinctrl_dev *pctldev, + unsigned group, unsigned long *config) +{ + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + enum tegra_pinconf_param param = TEGRA_PINCONF_UNPACK_PARAM(*config); + u16 arg; + const struct tegra_pingroup *g; + int ret; + s8 bank, bit, width; + s16 reg; + u32 val, mask; + + if (group >= pmx->soc->ngroups) + return -EINVAL; + g = &pmx->soc->groups[group]; + + ret = tegra_pinconf_reg(pmx, g, param, &bank, ®, &bit, &width); + if (ret < 0) + return ret; + + val = pmx_readl(pmx, bank, reg); + mask = (1 << width) - 1; + arg = (val >> bit) & mask; + + *config = TEGRA_PINCONF_PACK(param, arg); + + return 0; +} + +static int tegra_pinconf_group_set(struct pinctrl_dev *pctldev, + unsigned group, unsigned long config) +{ + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + enum tegra_pinconf_param param = TEGRA_PINCONF_UNPACK_PARAM(config); + u16 arg = TEGRA_PINCONF_UNPACK_ARG(config); + const struct tegra_pingroup *g; + int ret; + s8 bank, bit, width; + s16 reg; + u32 val, mask; + + if (group >= pmx->soc->ngroups) + return -EINVAL; + g = &pmx->soc->groups[group]; + + ret = tegra_pinconf_reg(pmx, g, param, &bank, ®, &bit, &width); + if (ret < 0) + return ret; + + val = pmx_readl(pmx, bank, reg); + + /* LOCK can't be cleared */ + if (param == TEGRA_PINCONF_PARAM_LOCK) { + if ((val & BIT(bit)) && !arg) + return -EINVAL; + } + + /* Special-case Boolean values; allow any non-zero as true */ + if (width == 1) + arg = !!arg; + + /* Range-check user-supplied value */ + mask = (1 << width) - 1; + if (arg & ~mask) + return -EINVAL; + + /* Update register */ + val &= ~(mask << bit); + val |= arg << bit; + pmx_writel(pmx, val, bank, reg); + + return 0; +} + +static void tegra_pinconf_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned offset) +{ +} + +static void tegra_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned selector) +{ +} + +struct pinconf_ops tegra_pinconf_ops = { + .pin_config_get = tegra_pinconf_get, + .pin_config_set = tegra_pinconf_set, + .pin_config_group_get = tegra_pinconf_group_get, + .pin_config_group_set = tegra_pinconf_group_set, + .pin_config_dbg_show = tegra_pinconf_dbg_show, + .pin_config_group_dbg_show = tegra_pinconf_group_dbg_show, +}; + +static struct pinctrl_gpio_range tegra_pinctrl_gpio_range = { + .name = "Tegra GPIOs", + .id = 0, + .base = 0, +}; + +static struct pinctrl_desc tegra_pinctrl_desc = { + .name = DRIVER_NAME, + .pctlops = &tegra_pinctrl_ops, + .pmxops = &tegra_pinmux_ops, + .confops = &tegra_pinconf_ops, + .owner = THIS_MODULE, +}; + +static struct of_device_id tegra_pinctrl_of_match[] __devinitdata = { +#ifdef CONFIG_PINCTRL_TEGRA20 + { + .compatible = "nvidia,tegra20-pinmux-disabled", + .data = tegra20_pinctrl_init, + }, +#endif +#ifdef CONFIG_PINCTRL_TEGRA30 + { + .compatible = "nvidia,tegra30-pinmux-disabled", + .data = tegra30_pinctrl_init, + }, +#endif + {}, +}; + +static int __devinit tegra_pinctrl_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + tegra_pinctrl_soc_initf initf = NULL; + struct tegra_pmx *pmx; + struct resource *res; + int i; + + match = of_match_device(tegra_pinctrl_of_match, &pdev->dev); + if (match) + initf = (tegra_pinctrl_soc_initf)match->data; +#ifdef CONFIG_PINCTRL_TEGRA20 + if (!initf) + initf = tegra20_pinctrl_init; +#endif + if (!initf) { + dev_err(&pdev->dev, + "Could not determine SoC-specific init func\n"); + return -EINVAL; + } + + pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL); + if (!pmx) { + dev_err(&pdev->dev, "Can't alloc tegra_pmx\n"); + return -ENOMEM; + } + pmx->dev = &pdev->dev; + + (*initf)(&pmx->soc); + + tegra_pinctrl_gpio_range.npins = pmx->soc->ngpios; + tegra_pinctrl_desc.pins = pmx->soc->pins; + tegra_pinctrl_desc.npins = pmx->soc->npins; + + for (i = 0; ; i++) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) + break; + } + pmx->nbanks = i; + + pmx->regs = devm_kzalloc(&pdev->dev, pmx->nbanks * sizeof(*pmx->regs), + GFP_KERNEL); + if (!pmx->regs) { + dev_err(&pdev->dev, "Can't alloc regs pointer\n"); + return -ENODEV; + } + + for (i = 0; i < pmx->nbanks; i++) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) { + dev_err(&pdev->dev, "Missing MEM resource\n"); + return -ENODEV; + } + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), + dev_name(&pdev->dev))) { + dev_err(&pdev->dev, + "Couldn't request MEM resource %d\n", i); + return -ENODEV; + } + + pmx->regs[i] = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!pmx->regs[i]) { + dev_err(&pdev->dev, "Couldn't ioremap regs %d\n", i); + return -ENODEV; + } + } + + pmx->pctl = pinctrl_register(&tegra_pinctrl_desc, &pdev->dev, pmx); + if (IS_ERR(pmx->pctl)) { + dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); + return PTR_ERR(pmx->pctl); + } + + pinctrl_add_gpio_range(pmx->pctl, &tegra_pinctrl_gpio_range); + + platform_set_drvdata(pdev, pmx); + + dev_dbg(&pdev->dev, "Probed Tegra pinctrl driver\n"); + + return 0; +} + +static int __devexit tegra_pinctrl_remove(struct platform_device *pdev) +{ + struct tegra_pmx *pmx = platform_get_drvdata(pdev); + + pinctrl_remove_gpio_range(pmx->pctl, &tegra_pinctrl_gpio_range); + pinctrl_unregister(pmx->pctl); + + return 0; +} + +static struct platform_driver tegra_pinctrl_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = tegra_pinctrl_of_match, + }, + .probe = tegra_pinctrl_probe, + .remove = __devexit_p(tegra_pinctrl_remove), +}; + +static int __init tegra_pinctrl_init(void) +{ + return platform_driver_register(&tegra_pinctrl_driver); +} +arch_initcall(tegra_pinctrl_init); + +static void __exit tegra_pinctrl_exit(void) +{ + platform_driver_unregister(&tegra_pinctrl_driver); +} +module_exit(tegra_pinctrl_exit); + +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_DESCRIPTION("NVIDIA Tegra pinctrl driver"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, tegra_pinctrl_of_match); diff --git a/drivers/pinctrl/pinctrl-tegra.h b/drivers/pinctrl/pinctrl-tegra.h new file mode 100644 index 000000000000..782c795326ef --- /dev/null +++ b/drivers/pinctrl/pinctrl-tegra.h @@ -0,0 +1,163 @@ +/* + * Driver for the NVIDIA Tegra pinmux + * + * Copyright (c) 2011, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __PINMUX_TEGRA_H__ +#define __PINMUX_TEGRA_H__ + +/** + * struct tegra_function - Tegra pinctrl mux function + * @name: The name of the function, exported to pinctrl core. + * @groups: An array of pin groups that may select this function. + * @ngroups: The number of entries in @groups. + */ +struct tegra_function { + const char *name; + const char * const *groups; + unsigned ngroups; +}; + +/** + * struct tegra_pingroup - Tegra pin group + * @mux_reg: Mux register offset. -1 if unsupported. + * @mux_bank: Mux register bank. 0 if unsupported. + * @mux_bit: Mux register bit. 0 if unsupported. + * @pupd_reg: Pull-up/down register offset. -1 if unsupported. + * @pupd_bank: Pull-up/down register bank. 0 if unsupported. + * @pupd_bit: Pull-up/down register bit. 0 if unsupported. + * @tri_reg: Tri-state register offset. -1 if unsupported. + * @tri_bank: Tri-state register bank. 0 if unsupported. + * @tri_bit: Tri-state register bit. 0 if unsupported. + * @einput_reg: Enable-input register offset. -1 if unsupported. + * @einput_bank: Enable-input register bank. 0 if unsupported. + * @einput_bit: Enable-input register bit. 0 if unsupported. + * @odrain_reg: Open-drain register offset. -1 if unsupported. + * @odrain_bank: Open-drain register bank. 0 if unsupported. + * @odrain_bit: Open-drain register bit. 0 if unsupported. + * @lock_reg: Lock register offset. -1 if unsupported. + * @lock_bank: Lock register bank. 0 if unsupported. + * @lock_bit: Lock register bit. 0 if unsupported. + * @ioreset_reg: IO reset register offset. -1 if unsupported. + * @ioreset_bank: IO reset register bank. 0 if unsupported. + * @ioreset_bit: IO reset register bit. 0 if unsupported. + * @drv_reg: Drive fields register offset. -1 if unsupported. + * This register contains the hsm, schmitt, lpmd, drvdn, + * drvup, slwr, and slwf parameters. + * @drv_bank: Drive fields register bank. 0 if unsupported. + * @hsm_bit: High Speed Mode register bit. 0 if unsupported. + * @schmitt_bit: Scmitt register bit. 0 if unsupported. + * @lpmd_bit: Low Power Mode register bit. 0 if unsupported. + * @drvdn_bit: Drive Down register bit. 0 if unsupported. + * @drvdn_width: Drive Down field width. 0 if unsupported. + * @drvup_bit: Drive Up register bit. 0 if unsupported. + * @drvup_width: Drive Up field width. 0 if unsupported. + * @slwr_bit: Slew Rising register bit. 0 if unsupported. + * @slwr_width: Slew Rising field width. 0 if unsupported. + * @slwf_bit: Slew Falling register bit. 0 if unsupported. + * @slwf_width: Slew Falling field width. 0 if unsupported. + * + * A representation of a group of pins (possibly just one pin) in the Tegra + * pin controller. Each group allows some parameter or parameters to be + * configured. The most common is mux function selection. Many others exist + * such as pull-up/down, tri-state, etc. Tegra's pin controller is complex; + * certain groups may only support configuring certain parameters, hence + * each parameter is optional, represented by a -1 "reg" value. + */ +struct tegra_pingroup { + const char *name; + const unsigned *pins; + unsigned npins; + unsigned funcs[4]; + unsigned func_safe; + s16 mux_reg; + s16 pupd_reg; + s16 tri_reg; + s16 einput_reg; + s16 odrain_reg; + s16 lock_reg; + s16 ioreset_reg; + s16 drv_reg; + u32 mux_bank:2; + u32 pupd_bank:2; + u32 tri_bank:2; + u32 einput_bank:2; + u32 odrain_bank:2; + u32 ioreset_bank:2; + u32 lock_bank:2; + u32 drv_bank:2; + u32 mux_bit:5; + u32 pupd_bit:5; + u32 tri_bit:5; + u32 einput_bit:5; + u32 odrain_bit:5; + u32 lock_bit:5; + u32 ioreset_bit:5; + u32 hsm_bit:5; + u32 schmitt_bit:5; + u32 lpmd_bit:5; + u32 drvdn_bit:5; + u32 drvup_bit:5; + u32 slwr_bit:5; + u32 slwf_bit:5; + u32 drvdn_width:6; + u32 drvup_width:6; + u32 slwr_width:6; + u32 slwf_width:6; +}; + +/** + * struct tegra_pinctrl_soc_data - Tegra pin controller driver configuration + * @ngpios: The number of GPIO pins the pin controller HW affects. + * @pins: An array describing all pins the pin controller affects. + * All pins which are also GPIOs must be listed first within the + * array, and be numbered identically to the GPIO controller's + * numbering. + * @npins: The numbmer of entries in @pins. + * @functions: An array describing all mux functions the SoC supports. + * @nfunctions: The numbmer of entries in @functions. + * @groups: An array describing all pin groups the pin SoC supports. + * @ngroups: The numbmer of entries in @groups. + */ +struct tegra_pinctrl_soc_data { + unsigned ngpios; + const struct pinctrl_pin_desc *pins; + unsigned npins; + const struct tegra_function *functions; + unsigned nfunctions; + const struct tegra_pingroup *groups; + unsigned ngroups; +}; + +/** + * tegra_pinctrl_soc_initf() - Retrieve pin controller details for a SoC. + * @soc_data: This pointer must be updated to point at a struct containing + * details of the SoC. + */ +typedef void (*tegra_pinctrl_soc_initf)( + const struct tegra_pinctrl_soc_data **soc_data); + +/** + * tegra20_pinctrl_init() - Retrieve pin controller details for Tegra20 + * @soc_data: This pointer will be updated to point at a struct containing + * details of Tegra20's pin controller. + */ +void tegra20_pinctrl_init(const struct tegra_pinctrl_soc_data **soc_data); +/** + * tegra30_pinctrl_init() - Retrieve pin controller details for Tegra20 + * @soc_data: This pointer will be updated to point at a struct containing + * details of Tegra30's pin controller. + */ +void tegra30_pinctrl_init(const struct tegra_pinctrl_soc_data **soc_data); + +#endif diff --git a/drivers/pinctrl/pinctrl-tegra20.c b/drivers/pinctrl/pinctrl-tegra20.c new file mode 100644 index 000000000000..f69ff96aa292 --- /dev/null +++ b/drivers/pinctrl/pinctrl-tegra20.c @@ -0,0 +1,2860 @@ +/* + * Pinctrl data for the NVIDIA Tegra20 pinmux + * + * Copyright (c) 2011, NVIDIA CORPORATION. All rights reserved. + * + * Derived from code: + * Copyright (C) 2010 Google, Inc. + * Copyright (C) 2010 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/platform_device.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> + +#include "pinctrl-tegra.h" + +/* + * Most pins affected by the pinmux can also be GPIOs. Define these first. + * These must match how the GPIO driver names/numbers its pins. + */ +#define _GPIO(offset) (offset) + +#define TEGRA_PIN_VI_GP6_PA0 _GPIO(0) +#define TEGRA_PIN_UART3_CTS_N_PA1 _GPIO(1) +#define TEGRA_PIN_DAP2_FS_PA2 _GPIO(2) +#define TEGRA_PIN_DAP2_SCLK_PA3 _GPIO(3) +#define TEGRA_PIN_DAP2_DIN_PA4 _GPIO(4) +#define TEGRA_PIN_DAP2_DOUT_PA5 _GPIO(5) +#define TEGRA_PIN_SDIO3_CLK_PA6 _GPIO(6) +#define TEGRA_PIN_SDIO3_CMD_PA7 _GPIO(7) +#define TEGRA_PIN_GMI_AD17_PB0 _GPIO(8) +#define TEGRA_PIN_GMI_AD18_PB1 _GPIO(9) +#define TEGRA_PIN_LCD_PWR0_PB2 _GPIO(10) +#define TEGRA_PIN_LCD_PCLK_PB3 _GPIO(11) +#define TEGRA_PIN_SDIO3_DAT3_PB4 _GPIO(12) +#define TEGRA_PIN_SDIO3_DAT2_PB5 _GPIO(13) +#define TEGRA_PIN_SDIO3_DAT1_PB6 _GPIO(14) +#define TEGRA_PIN_SDIO3_DAT0_PB7 _GPIO(15) +#define TEGRA_PIN_UART3_RTS_N_PC0 _GPIO(16) +#define TEGRA_PIN_LCD_PWR1_PC1 _GPIO(17) +#define TEGRA_PIN_UART2_TXD_PC2 _GPIO(18) +#define TEGRA_PIN_UART2_RXD_PC3 _GPIO(19) +#define TEGRA_PIN_GEN1_I2C_SCL_PC4 _GPIO(20) +#define TEGRA_PIN_GEN1_I2C_SDA_PC5 _GPIO(21) +#define TEGRA_PIN_LCD_PWR2_PC6 _GPIO(22) +#define TEGRA_PIN_GMI_WP_N_PC7 _GPIO(23) +#define TEGRA_PIN_SDIO3_DAT5_PD0 _GPIO(24) +#define TEGRA_PIN_SDIO3_DAT4_PD1 _GPIO(25) +#define TEGRA_PIN_VI_GP5_PD2 _GPIO(26) +#define TEGRA_PIN_SDIO3_DAT6_PD3 _GPIO(27) +#define TEGRA_PIN_SDIO3_DAT7_PD4 _GPIO(28) +#define TEGRA_PIN_VI_D1_PD5 _GPIO(29) +#define TEGRA_PIN_VI_VSYNC_PD6 _GPIO(30) +#define TEGRA_PIN_VI_HSYNC_PD7 _GPIO(31) +#define TEGRA_PIN_LCD_D0_PE0 _GPIO(32) +#define TEGRA_PIN_LCD_D1_PE1 _GPIO(33) +#define TEGRA_PIN_LCD_D2_PE2 _GPIO(34) +#define TEGRA_PIN_LCD_D3_PE3 _GPIO(35) +#define TEGRA_PIN_LCD_D4_PE4 _GPIO(36) +#define TEGRA_PIN_LCD_D5_PE5 _GPIO(37) +#define TEGRA_PIN_LCD_D6_PE6 _GPIO(38) +#define TEGRA_PIN_LCD_D7_PE7 _GPIO(39) +#define TEGRA_PIN_LCD_D8_PF0 _GPIO(40) +#define TEGRA_PIN_LCD_D9_PF1 _GPIO(41) +#define TEGRA_PIN_LCD_D10_PF2 _GPIO(42) +#define TEGRA_PIN_LCD_D11_PF3 _GPIO(43) +#define TEGRA_PIN_LCD_D12_PF4 _GPIO(44) +#define TEGRA_PIN_LCD_D13_PF5 _GPIO(45) +#define TEGRA_PIN_LCD_D14_PF6 _GPIO(46) +#define TEGRA_PIN_LCD_D15_PF7 _GPIO(47) +#define TEGRA_PIN_GMI_AD0_PG0 _GPIO(48) +#define TEGRA_PIN_GMI_AD1_PG1 _GPIO(49) +#define TEGRA_PIN_GMI_AD2_PG2 _GPIO(50) +#define TEGRA_PIN_GMI_AD3_PG3 _GPIO(51) +#define TEGRA_PIN_GMI_AD4_PG4 _GPIO(52) +#define TEGRA_PIN_GMI_AD5_PG5 _GPIO(53) +#define TEGRA_PIN_GMI_AD6_PG6 _GPIO(54) +#define TEGRA_PIN_GMI_AD7_PG7 _GPIO(55) +#define TEGRA_PIN_GMI_AD8_PH0 _GPIO(56) +#define TEGRA_PIN_GMI_AD9_PH1 _GPIO(57) +#define TEGRA_PIN_GMI_AD10_PH2 _GPIO(58) +#define TEGRA_PIN_GMI_AD11_PH3 _GPIO(59) +#define TEGRA_PIN_GMI_AD12_PH4 _GPIO(60) +#define TEGRA_PIN_GMI_AD13_PH5 _GPIO(61) +#define TEGRA_PIN_GMI_AD14_PH6 _GPIO(62) +#define TEGRA_PIN_GMI_AD15_PH7 _GPIO(63) +#define TEGRA_PIN_GMI_HIOW_N_PI0 _GPIO(64) +#define TEGRA_PIN_GMI_HIOR_N_PI1 _GPIO(65) +#define TEGRA_PIN_GMI_CS5_N_PI2 _GPIO(66) +#define TEGRA_PIN_GMI_CS6_N_PI3 _GPIO(67) +#define TEGRA_PIN_GMI_RST_N_PI4 _GPIO(68) +#define TEGRA_PIN_GMI_IORDY_PI5 _GPIO(69) +#define TEGRA_PIN_GMI_CS7_N_PI6 _GPIO(70) +#define TEGRA_PIN_GMI_WAIT_PI7 _GPIO(71) +#define TEGRA_PIN_GMI_CS0_N_PJ0 _GPIO(72) +#define TEGRA_PIN_LCD_DE_PJ1 _GPIO(73) +#define TEGRA_PIN_GMI_CS1_N_PJ2 _GPIO(74) +#define TEGRA_PIN_LCD_HSYNC_PJ3 _GPIO(75) +#define TEGRA_PIN_LCD_VSYNC_PJ4 _GPIO(76) +#define TEGRA_PIN_UART2_CTS_N_PJ5 _GPIO(77) +#define TEGRA_PIN_UART2_RTS_N_PJ6 _GPIO(78) +#define TEGRA_PIN_GMI_AD16_PJ7 _GPIO(79) +#define TEGRA_PIN_GMI_ADV_N_PK0 _GPIO(80) +#define TEGRA_PIN_GMI_CLK_PK1 _GPIO(81) +#define TEGRA_PIN_GMI_CS4_N_PK2 _GPIO(82) +#define TEGRA_PIN_GMI_CS2_N_PK3 _GPIO(83) +#define TEGRA_PIN_GMI_CS3_N_PK4 _GPIO(84) +#define TEGRA_PIN_SPDIF_OUT_PK5 _GPIO(85) +#define TEGRA_PIN_SPDIF_IN_PK6 _GPIO(86) +#define TEGRA_PIN_GMI_AD19_PK7 _GPIO(87) +#define TEGRA_PIN_VI_D2_PL0 _GPIO(88) +#define TEGRA_PIN_VI_D3_PL1 _GPIO(89) +#define TEGRA_PIN_VI_D4_PL2 _GPIO(90) +#define TEGRA_PIN_VI_D5_PL3 _GPIO(91) +#define TEGRA_PIN_VI_D6_PL4 _GPIO(92) +#define TEGRA_PIN_VI_D7_PL5 _GPIO(93) +#define TEGRA_PIN_VI_D8_PL6 _GPIO(94) +#define TEGRA_PIN_VI_D9_PL7 _GPIO(95) +#define TEGRA_PIN_LCD_D16_PM0 _GPIO(96) +#define TEGRA_PIN_LCD_D17_PM1 _GPIO(97) +#define TEGRA_PIN_LCD_D18_PM2 _GPIO(98) +#define TEGRA_PIN_LCD_D19_PM3 _GPIO(99) +#define TEGRA_PIN_LCD_D20_PM4 _GPIO(100) +#define TEGRA_PIN_LCD_D21_PM5 _GPIO(101) +#define TEGRA_PIN_LCD_D22_PM6 _GPIO(102) +#define TEGRA_PIN_LCD_D23_PM7 _GPIO(103) +#define TEGRA_PIN_DAP1_FS_PN0 _GPIO(104) +#define TEGRA_PIN_DAP1_DIN_PN1 _GPIO(105) +#define TEGRA_PIN_DAP1_DOUT_PN2 _GPIO(106) +#define TEGRA_PIN_DAP1_SCLK_PN3 _GPIO(107) +#define TEGRA_PIN_LCD_CS0_N_PN4 _GPIO(108) +#define TEGRA_PIN_LCD_SDOUT_PN5 _GPIO(109) +#define TEGRA_PIN_LCD_DC0_PN6 _GPIO(110) +#define TEGRA_PIN_HDMI_INT_N_PN7 _GPIO(111) +#define TEGRA_PIN_ULPI_DATA7_PO0 _GPIO(112) +#define TEGRA_PIN_ULPI_DATA0_PO1 _GPIO(113) +#define TEGRA_PIN_ULPI_DATA1_PO2 _GPIO(114) +#define TEGRA_PIN_ULPI_DATA2_PO3 _GPIO(115) +#define TEGRA_PIN_ULPI_DATA3_PO4 _GPIO(116) +#define TEGRA_PIN_ULPI_DATA4_PO5 _GPIO(117) +#define TEGRA_PIN_ULPI_DATA5_PO6 _GPIO(118) +#define TEGRA_PIN_ULPI_DATA6_PO7 _GPIO(119) +#define TEGRA_PIN_DAP3_FS_PP0 _GPIO(120) +#define TEGRA_PIN_DAP3_DIN_PP1 _GPIO(121) +#define TEGRA_PIN_DAP3_DOUT_PP2 _GPIO(122) +#define TEGRA_PIN_DAP3_SCLK_PP3 _GPIO(123) +#define TEGRA_PIN_DAP4_FS_PP4 _GPIO(124) +#define TEGRA_PIN_DAP4_DIN_PP5 _GPIO(125) +#define TEGRA_PIN_DAP4_DOUT_PP6 _GPIO(126) +#define TEGRA_PIN_DAP4_SCLK_PP7 _GPIO(127) +#define TEGRA_PIN_KB_COL0_PQ0 _GPIO(128) +#define TEGRA_PIN_KB_COL1_PQ1 _GPIO(129) +#define TEGRA_PIN_KB_COL2_PQ2 _GPIO(130) +#define TEGRA_PIN_KB_COL3_PQ3 _GPIO(131) +#define TEGRA_PIN_KB_COL4_PQ4 _GPIO(132) +#define TEGRA_PIN_KB_COL5_PQ5 _GPIO(133) +#define TEGRA_PIN_KB_COL6_PQ6 _GPIO(134) +#define TEGRA_PIN_KB_COL7_PQ7 _GPIO(135) +#define TEGRA_PIN_KB_ROW0_PR0 _GPIO(136) +#define TEGRA_PIN_KB_ROW1_PR1 _GPIO(137) +#define TEGRA_PIN_KB_ROW2_PR2 _GPIO(138) +#define TEGRA_PIN_KB_ROW3_PR3 _GPIO(139) +#define TEGRA_PIN_KB_ROW4_PR4 _GPIO(140) +#define TEGRA_PIN_KB_ROW5_PR5 _GPIO(141) +#define TEGRA_PIN_KB_ROW6_PR6 _GPIO(142) +#define TEGRA_PIN_KB_ROW7_PR7 _GPIO(143) +#define TEGRA_PIN_KB_ROW8_PS0 _GPIO(144) +#define TEGRA_PIN_KB_ROW9_PS1 _GPIO(145) +#define TEGRA_PIN_KB_ROW10_PS2 _GPIO(146) +#define TEGRA_PIN_KB_ROW11_PS3 _GPIO(147) +#define TEGRA_PIN_KB_ROW12_PS4 _GPIO(148) +#define TEGRA_PIN_KB_ROW13_PS5 _GPIO(149) +#define TEGRA_PIN_KB_ROW14_PS6 _GPIO(150) +#define TEGRA_PIN_KB_ROW15_PS7 _GPIO(151) +#define TEGRA_PIN_VI_PCLK_PT0 _GPIO(152) +#define TEGRA_PIN_VI_MCLK_PT1 _GPIO(153) +#define TEGRA_PIN_VI_D10_PT2 _GPIO(154) +#define TEGRA_PIN_VI_D11_PT3 _GPIO(155) +#define TEGRA_PIN_VI_D0_PT4 _GPIO(156) +#define TEGRA_PIN_GEN2_I2C_SCL_PT5 _GPIO(157) +#define TEGRA_PIN_GEN2_I2C_SDA_PT6 _GPIO(158) +#define TEGRA_PIN_GMI_DPD_PT7 _GPIO(159) +#define TEGRA_PIN_PU0 _GPIO(160) +#define TEGRA_PIN_PU1 _GPIO(161) +#define TEGRA_PIN_PU2 _GPIO(162) +#define TEGRA_PIN_PU3 _GPIO(163) +#define TEGRA_PIN_PU4 _GPIO(164) +#define TEGRA_PIN_PU5 _GPIO(165) +#define TEGRA_PIN_PU6 _GPIO(166) +#define TEGRA_PIN_JTAG_RTCK_PU7 _GPIO(167) +#define TEGRA_PIN_PV0 _GPIO(168) +#define TEGRA_PIN_PV1 _GPIO(169) +#define TEGRA_PIN_PV2 _GPIO(170) +#define TEGRA_PIN_PV3 _GPIO(171) +#define TEGRA_PIN_PV4 _GPIO(172) +#define TEGRA_PIN_PV5 _GPIO(173) +#define TEGRA_PIN_PV6 _GPIO(174) +#define TEGRA_PIN_LCD_DC1_PV7 _GPIO(175) +#define TEGRA_PIN_LCD_CS1_N_PW0 _GPIO(176) +#define TEGRA_PIN_LCD_M1_PW1 _GPIO(177) +#define TEGRA_PIN_SPI2_CS1_N_PW2 _GPIO(178) +#define TEGRA_PIN_SPI2_CS2_N_PW3 _GPIO(179) +#define TEGRA_PIN_DAP_MCLK1_PW4 _GPIO(180) +#define TEGRA_PIN_DAP_MCLK2_PW5 _GPIO(181) +#define TEGRA_PIN_UART3_TXD_PW6 _GPIO(182) +#define TEGRA_PIN_UART3_RXD_PW7 _GPIO(183) +#define TEGRA_PIN_SPI2_MOSI_PX0 _GPIO(184) +#define TEGRA_PIN_SPI2_MISO_PX1 _GPIO(185) +#define TEGRA_PIN_SPI2_SCK_PX2 _GPIO(186) +#define TEGRA_PIN_SPI2_CS0_N_PX3 _GPIO(187) +#define TEGRA_PIN_SPI1_MOSI_PX4 _GPIO(188) +#define TEGRA_PIN_SPI1_SCK_PX5 _GPIO(189) +#define TEGRA_PIN_SPI1_CS0_N_PX6 _GPIO(190) +#define TEGRA_PIN_SPI1_MISO_PX7 _GPIO(191) +#define TEGRA_PIN_ULPI_CLK_PY0 _GPIO(192) +#define TEGRA_PIN_ULPI_DIR_PY1 _GPIO(193) +#define TEGRA_PIN_ULPI_NXT_PY2 _GPIO(194) +#define TEGRA_PIN_ULPI_STP_PY3 _GPIO(195) +#define TEGRA_PIN_SDIO1_DAT3_PY4 _GPIO(196) +#define TEGRA_PIN_SDIO1_DAT2_PY5 _GPIO(197) +#define TEGRA_PIN_SDIO1_DAT1_PY6 _GPIO(198) +#define TEGRA_PIN_SDIO1_DAT0_PY7 _GPIO(199) +#define TEGRA_PIN_SDIO1_CLK_PZ0 _GPIO(200) +#define TEGRA_PIN_SDIO1_CMD_PZ1 _GPIO(201) +#define TEGRA_PIN_LCD_SDIN_PZ2 _GPIO(202) +#define TEGRA_PIN_LCD_WR_N_PZ3 _GPIO(203) +#define TEGRA_PIN_LCD_SCK_PZ4 _GPIO(204) +#define TEGRA_PIN_SYS_CLK_REQ_PZ5 _GPIO(205) +#define TEGRA_PIN_PWR_I2C_SCL_PZ6 _GPIO(206) +#define TEGRA_PIN_PWR_I2C_SDA_PZ7 _GPIO(207) +#define TEGRA_PIN_GMI_AD20_PAA0 _GPIO(208) +#define TEGRA_PIN_GMI_AD21_PAA1 _GPIO(209) +#define TEGRA_PIN_GMI_AD22_PAA2 _GPIO(210) +#define TEGRA_PIN_GMI_AD23_PAA3 _GPIO(211) +#define TEGRA_PIN_GMI_AD24_PAA4 _GPIO(212) +#define TEGRA_PIN_GMI_AD25_PAA5 _GPIO(213) +#define TEGRA_PIN_GMI_AD26_PAA6 _GPIO(214) +#define TEGRA_PIN_GMI_AD27_PAA7 _GPIO(215) +#define TEGRA_PIN_LED_BLINK_PBB0 _GPIO(216) +#define TEGRA_PIN_VI_GP0_PBB1 _GPIO(217) +#define TEGRA_PIN_CAM_I2C_SCL_PBB2 _GPIO(218) +#define TEGRA_PIN_CAM_I2C_SDA_PBB3 _GPIO(219) +#define TEGRA_PIN_VI_GP3_PBB4 _GPIO(220) +#define TEGRA_PIN_VI_GP4_PBB5 _GPIO(221) +#define TEGRA_PIN_PBB6 _GPIO(222) +#define TEGRA_PIN_PBB7 _GPIO(223) + +/* All non-GPIO pins follow */ +#define NUM_GPIOS (TEGRA_PIN_PBB7 + 1) +#define _PIN(offset) (NUM_GPIOS + (offset)) + +#define TEGRA_PIN_CRT_HSYNC _PIN(30) +#define TEGRA_PIN_CRT_VSYNC _PIN(31) +#define TEGRA_PIN_DDC_SCL _PIN(32) +#define TEGRA_PIN_DDC_SDA _PIN(33) +#define TEGRA_PIN_OWC _PIN(34) +#define TEGRA_PIN_CORE_PWR_REQ _PIN(35) +#define TEGRA_PIN_CPU_PWR_REQ _PIN(36) +#define TEGRA_PIN_PWR_INT_N _PIN(37) +#define TEGRA_PIN_CLK_32_K_IN _PIN(38) +#define TEGRA_PIN_DDR_COMP_PD _PIN(39) +#define TEGRA_PIN_DDR_COMP_PU _PIN(40) +#define TEGRA_PIN_DDR_A0 _PIN(41) +#define TEGRA_PIN_DDR_A1 _PIN(42) +#define TEGRA_PIN_DDR_A2 _PIN(43) +#define TEGRA_PIN_DDR_A3 _PIN(44) +#define TEGRA_PIN_DDR_A4 _PIN(45) +#define TEGRA_PIN_DDR_A5 _PIN(46) +#define TEGRA_PIN_DDR_A6 _PIN(47) +#define TEGRA_PIN_DDR_A7 _PIN(48) +#define TEGRA_PIN_DDR_A8 _PIN(49) +#define TEGRA_PIN_DDR_A9 _PIN(50) +#define TEGRA_PIN_DDR_A10 _PIN(51) +#define TEGRA_PIN_DDR_A11 _PIN(52) +#define TEGRA_PIN_DDR_A12 _PIN(53) +#define TEGRA_PIN_DDR_A13 _PIN(54) +#define TEGRA_PIN_DDR_A14 _PIN(55) +#define TEGRA_PIN_DDR_CAS_N _PIN(56) +#define TEGRA_PIN_DDR_BA0 _PIN(57) +#define TEGRA_PIN_DDR_BA1 _PIN(58) +#define TEGRA_PIN_DDR_BA2 _PIN(59) +#define TEGRA_PIN_DDR_DQS0P _PIN(60) +#define TEGRA_PIN_DDR_DQS0N _PIN(61) +#define TEGRA_PIN_DDR_DQS1P _PIN(62) +#define TEGRA_PIN_DDR_DQS1N _PIN(63) +#define TEGRA_PIN_DDR_DQS2P _PIN(64) +#define TEGRA_PIN_DDR_DQS2N _PIN(65) +#define TEGRA_PIN_DDR_DQS3P _PIN(66) +#define TEGRA_PIN_DDR_DQS3N _PIN(67) +#define TEGRA_PIN_DDR_CKE0 _PIN(68) +#define TEGRA_PIN_DDR_CKE1 _PIN(69) +#define TEGRA_PIN_DDR_CLK _PIN(70) +#define TEGRA_PIN_DDR_CLK_N _PIN(71) +#define TEGRA_PIN_DDR_DM0 _PIN(72) +#define TEGRA_PIN_DDR_DM1 _PIN(73) +#define TEGRA_PIN_DDR_DM2 _PIN(74) +#define TEGRA_PIN_DDR_DM3 _PIN(75) +#define TEGRA_PIN_DDR_ODT _PIN(76) +#define TEGRA_PIN_DDR_QUSE0 _PIN(77) +#define TEGRA_PIN_DDR_QUSE1 _PIN(78) +#define TEGRA_PIN_DDR_QUSE2 _PIN(79) +#define TEGRA_PIN_DDR_QUSE3 _PIN(80) +#define TEGRA_PIN_DDR_RAS_N _PIN(81) +#define TEGRA_PIN_DDR_WE_N _PIN(82) +#define TEGRA_PIN_DDR_DQ0 _PIN(83) +#define TEGRA_PIN_DDR_DQ1 _PIN(84) +#define TEGRA_PIN_DDR_DQ2 _PIN(85) +#define TEGRA_PIN_DDR_DQ3 _PIN(86) +#define TEGRA_PIN_DDR_DQ4 _PIN(87) +#define TEGRA_PIN_DDR_DQ5 _PIN(88) +#define TEGRA_PIN_DDR_DQ6 _PIN(89) +#define TEGRA_PIN_DDR_DQ7 _PIN(90) +#define TEGRA_PIN_DDR_DQ8 _PIN(91) +#define TEGRA_PIN_DDR_DQ9 _PIN(92) +#define TEGRA_PIN_DDR_DQ10 _PIN(93) +#define TEGRA_PIN_DDR_DQ11 _PIN(94) +#define TEGRA_PIN_DDR_DQ12 _PIN(95) +#define TEGRA_PIN_DDR_DQ13 _PIN(96) +#define TEGRA_PIN_DDR_DQ14 _PIN(97) +#define TEGRA_PIN_DDR_DQ15 _PIN(98) +#define TEGRA_PIN_DDR_DQ16 _PIN(99) +#define TEGRA_PIN_DDR_DQ17 _PIN(100) +#define TEGRA_PIN_DDR_DQ18 _PIN(101) +#define TEGRA_PIN_DDR_DQ19 _PIN(102) +#define TEGRA_PIN_DDR_DQ20 _PIN(103) +#define TEGRA_PIN_DDR_DQ21 _PIN(104) +#define TEGRA_PIN_DDR_DQ22 _PIN(105) +#define TEGRA_PIN_DDR_DQ23 _PIN(106) +#define TEGRA_PIN_DDR_DQ24 _PIN(107) +#define TEGRA_PIN_DDR_DQ25 _PIN(108) +#define TEGRA_PIN_DDR_DQ26 _PIN(109) +#define TEGRA_PIN_DDR_DQ27 _PIN(110) +#define TEGRA_PIN_DDR_DQ28 _PIN(111) +#define TEGRA_PIN_DDR_DQ29 _PIN(112) +#define TEGRA_PIN_DDR_DQ30 _PIN(113) +#define TEGRA_PIN_DDR_DQ31 _PIN(114) +#define TEGRA_PIN_DDR_CS0_N _PIN(115) +#define TEGRA_PIN_DDR_CS1_N _PIN(116) +#define TEGRA_PIN_SYS_RESET _PIN(117) +#define TEGRA_PIN_JTAG_TRST_N _PIN(118) +#define TEGRA_PIN_JTAG_TDO _PIN(119) +#define TEGRA_PIN_JTAG_TMS _PIN(120) +#define TEGRA_PIN_JTAG_TCK _PIN(121) +#define TEGRA_PIN_JTAG_TDI _PIN(122) +#define TEGRA_PIN_TEST_MODE_EN _PIN(123) + +static const struct pinctrl_pin_desc tegra20_pins[] = { + PINCTRL_PIN(TEGRA_PIN_VI_GP6_PA0, "VI_GP6 PA0"), + PINCTRL_PIN(TEGRA_PIN_UART3_CTS_N_PA1, "UART3_CTS_N PA1"), + PINCTRL_PIN(TEGRA_PIN_DAP2_FS_PA2, "DAP2_FS PA2"), + PINCTRL_PIN(TEGRA_PIN_DAP2_SCLK_PA3, "DAP2_SCLK PA3"), + PINCTRL_PIN(TEGRA_PIN_DAP2_DIN_PA4, "DAP2_DIN PA4"), + PINCTRL_PIN(TEGRA_PIN_DAP2_DOUT_PA5, "DAP2_DOUT PA5"), + PINCTRL_PIN(TEGRA_PIN_SDIO3_CLK_PA6, "SDIO3_CLK PA6"), + PINCTRL_PIN(TEGRA_PIN_SDIO3_CMD_PA7, "SDIO3_CMD PA7"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD17_PB0, "GMI_AD17 PB0"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD18_PB1, "GMI_AD18 PB1"), + PINCTRL_PIN(TEGRA_PIN_LCD_PWR0_PB2, "LCD_PWR0 PB2"), + PINCTRL_PIN(TEGRA_PIN_LCD_PCLK_PB3, "LCD_PCLK PB3"), + PINCTRL_PIN(TEGRA_PIN_SDIO3_DAT3_PB4, "SDIO3_DAT3 PB4"), + PINCTRL_PIN(TEGRA_PIN_SDIO3_DAT2_PB5, "SDIO3_DAT2 PB5"), + PINCTRL_PIN(TEGRA_PIN_SDIO3_DAT1_PB6, "SDIO3_DAT1 PB6"), + PINCTRL_PIN(TEGRA_PIN_SDIO3_DAT0_PB7, "SDIO3_DAT0 PB7"), + PINCTRL_PIN(TEGRA_PIN_UART3_RTS_N_PC0, "UART3_RTS_N PC0"), + PINCTRL_PIN(TEGRA_PIN_LCD_PWR1_PC1, "LCD_PWR1 PC1"), + PINCTRL_PIN(TEGRA_PIN_UART2_TXD_PC2, "UART2_TXD PC2"), + PINCTRL_PIN(TEGRA_PIN_UART2_RXD_PC3, "UART2_RXD PC3"), + PINCTRL_PIN(TEGRA_PIN_GEN1_I2C_SCL_PC4, "GEN1_I2C_SCL PC4"), + PINCTRL_PIN(TEGRA_PIN_GEN1_I2C_SDA_PC5, "GEN1_I2C_SDA PC5"), + PINCTRL_PIN(TEGRA_PIN_LCD_PWR2_PC6, "LCD_PWR2 PC6"), + PINCTRL_PIN(TEGRA_PIN_GMI_WP_N_PC7, "GMI_WP_N PC7"), + PINCTRL_PIN(TEGRA_PIN_SDIO3_DAT5_PD0, "SDIO3_DAT5 PD0"), + PINCTRL_PIN(TEGRA_PIN_SDIO3_DAT4_PD1, "SDIO3_DAT4 PD1"), + PINCTRL_PIN(TEGRA_PIN_VI_GP5_PD2, "VI_GP5 PD2"), + PINCTRL_PIN(TEGRA_PIN_SDIO3_DAT6_PD3, "SDIO3_DAT6 PD3"), + PINCTRL_PIN(TEGRA_PIN_SDIO3_DAT7_PD4, "SDIO3_DAT7 PD4"), + PINCTRL_PIN(TEGRA_PIN_VI_D1_PD5, "VI_D1 PD5"), + PINCTRL_PIN(TEGRA_PIN_VI_VSYNC_PD6, "VI_VSYNC PD6"), + PINCTRL_PIN(TEGRA_PIN_VI_HSYNC_PD7, "VI_HSYNC PD7"), + PINCTRL_PIN(TEGRA_PIN_LCD_D0_PE0, "LCD_D0 PE0"), + PINCTRL_PIN(TEGRA_PIN_LCD_D1_PE1, "LCD_D1 PE1"), + PINCTRL_PIN(TEGRA_PIN_LCD_D2_PE2, "LCD_D2 PE2"), + PINCTRL_PIN(TEGRA_PIN_LCD_D3_PE3, "LCD_D3 PE3"), + PINCTRL_PIN(TEGRA_PIN_LCD_D4_PE4, "LCD_D4 PE4"), + PINCTRL_PIN(TEGRA_PIN_LCD_D5_PE5, "LCD_D5 PE5"), + PINCTRL_PIN(TEGRA_PIN_LCD_D6_PE6, "LCD_D6 PE6"), + PINCTRL_PIN(TEGRA_PIN_LCD_D7_PE7, "LCD_D7 PE7"), + PINCTRL_PIN(TEGRA_PIN_LCD_D8_PF0, "LCD_D8 PF0"), + PINCTRL_PIN(TEGRA_PIN_LCD_D9_PF1, "LCD_D9 PF1"), + PINCTRL_PIN(TEGRA_PIN_LCD_D10_PF2, "LCD_D10 PF2"), + PINCTRL_PIN(TEGRA_PIN_LCD_D11_PF3, "LCD_D11 PF3"), + PINCTRL_PIN(TEGRA_PIN_LCD_D12_PF4, "LCD_D12 PF4"), + PINCTRL_PIN(TEGRA_PIN_LCD_D13_PF5, "LCD_D13 PF5"), + PINCTRL_PIN(TEGRA_PIN_LCD_D14_PF6, "LCD_D14 PF6"), + PINCTRL_PIN(TEGRA_PIN_LCD_D15_PF7, "LCD_D15 PF7"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD0_PG0, "GMI_AD0 PG0"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD1_PG1, "GMI_AD1 PG1"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD2_PG2, "GMI_AD2 PG2"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD3_PG3, "GMI_AD3 PG3"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD4_PG4, "GMI_AD4 PG4"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD5_PG5, "GMI_AD5 PG5"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD6_PG6, "GMI_AD6 PG6"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD7_PG7, "GMI_AD7 PG7"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD8_PH0, "GMI_AD8 PH0"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD9_PH1, "GMI_AD9 PH1"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD10_PH2, "GMI_AD10 PH2"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD11_PH3, "GMI_AD11 PH3"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD12_PH4, "GMI_AD12 PH4"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD13_PH5, "GMI_AD13 PH5"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD14_PH6, "GMI_AD14 PH6"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD15_PH7, "GMI_AD15 PH7"), + PINCTRL_PIN(TEGRA_PIN_GMI_HIOW_N_PI0, "GMI_HIOW_N PI0"), + PINCTRL_PIN(TEGRA_PIN_GMI_HIOR_N_PI1, "GMI_HIOR_N PI1"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS5_N_PI2, "GMI_CS5_N PI2"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS6_N_PI3, "GMI_CS6_N PI3"), + PINCTRL_PIN(TEGRA_PIN_GMI_RST_N_PI4, "GMI_RST_N PI4"), + PINCTRL_PIN(TEGRA_PIN_GMI_IORDY_PI5, "GMI_IORDY PI5"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS7_N_PI6, "GMI_CS7_N PI6"), + PINCTRL_PIN(TEGRA_PIN_GMI_WAIT_PI7, "GMI_WAIT PI7"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS0_N_PJ0, "GMI_CS0_N PJ0"), + PINCTRL_PIN(TEGRA_PIN_LCD_DE_PJ1, "LCD_DE PJ1"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS1_N_PJ2, "GMI_CS1_N PJ2"), + PINCTRL_PIN(TEGRA_PIN_LCD_HSYNC_PJ3, "LCD_HSYNC PJ3"), + PINCTRL_PIN(TEGRA_PIN_LCD_VSYNC_PJ4, "LCD_VSYNC PJ4"), + PINCTRL_PIN(TEGRA_PIN_UART2_CTS_N_PJ5, "UART2_CTS_N PJ5"), + PINCTRL_PIN(TEGRA_PIN_UART2_RTS_N_PJ6, "UART2_RTS_N PJ6"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD16_PJ7, "GMI_AD16 PJ7"), + PINCTRL_PIN(TEGRA_PIN_GMI_ADV_N_PK0, "GMI_ADV_N PK0"), + PINCTRL_PIN(TEGRA_PIN_GMI_CLK_PK1, "GMI_CLK PK1"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS4_N_PK2, "GMI_CS4_N PK2"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS2_N_PK3, "GMI_CS2_N PK3"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS3_N_PK4, "GMI_CS3_N PK4"), + PINCTRL_PIN(TEGRA_PIN_SPDIF_OUT_PK5, "SPDIF_OUT PK5"), + PINCTRL_PIN(TEGRA_PIN_SPDIF_IN_PK6, "SPDIF_IN PK6"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD19_PK7, "GMI_AD19 PK7"), + PINCTRL_PIN(TEGRA_PIN_VI_D2_PL0, "VI_D2 PL0"), + PINCTRL_PIN(TEGRA_PIN_VI_D3_PL1, "VI_D3 PL1"), + PINCTRL_PIN(TEGRA_PIN_VI_D4_PL2, "VI_D4 PL2"), + PINCTRL_PIN(TEGRA_PIN_VI_D5_PL3, "VI_D5 PL3"), + PINCTRL_PIN(TEGRA_PIN_VI_D6_PL4, "VI_D6 PL4"), + PINCTRL_PIN(TEGRA_PIN_VI_D7_PL5, "VI_D7 PL5"), + PINCTRL_PIN(TEGRA_PIN_VI_D8_PL6, "VI_D8 PL6"), + PINCTRL_PIN(TEGRA_PIN_VI_D9_PL7, "VI_D9 PL7"), + PINCTRL_PIN(TEGRA_PIN_LCD_D16_PM0, "LCD_D16 PM0"), + PINCTRL_PIN(TEGRA_PIN_LCD_D17_PM1, "LCD_D17 PM1"), + PINCTRL_PIN(TEGRA_PIN_LCD_D18_PM2, "LCD_D18 PM2"), + PINCTRL_PIN(TEGRA_PIN_LCD_D19_PM3, "LCD_D19 PM3"), + PINCTRL_PIN(TEGRA_PIN_LCD_D20_PM4, "LCD_D20 PM4"), + PINCTRL_PIN(TEGRA_PIN_LCD_D21_PM5, "LCD_D21 PM5"), + PINCTRL_PIN(TEGRA_PIN_LCD_D22_PM6, "LCD_D22 PM6"), + PINCTRL_PIN(TEGRA_PIN_LCD_D23_PM7, "LCD_D23 PM7"), + PINCTRL_PIN(TEGRA_PIN_DAP1_FS_PN0, "DAP1_FS PN0"), + PINCTRL_PIN(TEGRA_PIN_DAP1_DIN_PN1, "DAP1_DIN PN1"), + PINCTRL_PIN(TEGRA_PIN_DAP1_DOUT_PN2, "DAP1_DOUT PN2"), + PINCTRL_PIN(TEGRA_PIN_DAP1_SCLK_PN3, "DAP1_SCLK PN3"), + PINCTRL_PIN(TEGRA_PIN_LCD_CS0_N_PN4, "LCD_CS0_N PN4"), + PINCTRL_PIN(TEGRA_PIN_LCD_SDOUT_PN5, "LCD_SDOUT PN5"), + PINCTRL_PIN(TEGRA_PIN_LCD_DC0_PN6, "LCD_DC0 PN6"), + PINCTRL_PIN(TEGRA_PIN_HDMI_INT_N_PN7, "HDMI_INT_N PN7"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA7_PO0, "ULPI_DATA7 PO0"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA0_PO1, "ULPI_DATA0 PO1"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA1_PO2, "ULPI_DATA1 PO2"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA2_PO3, "ULPI_DATA2 PO3"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA3_PO4, "ULPI_DATA3 PO4"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA4_PO5, "ULPI_DATA4 PO5"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA5_PO6, "ULPI_DATA5 PO6"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA6_PO7, "ULPI_DATA6 PO7"), + PINCTRL_PIN(TEGRA_PIN_DAP3_FS_PP0, "DAP3_FS PP0"), + PINCTRL_PIN(TEGRA_PIN_DAP3_DIN_PP1, "DAP3_DIN PP1"), + PINCTRL_PIN(TEGRA_PIN_DAP3_DOUT_PP2, "DAP3_DOUT PP2"), + PINCTRL_PIN(TEGRA_PIN_DAP3_SCLK_PP3, "DAP3_SCLK PP3"), + PINCTRL_PIN(TEGRA_PIN_DAP4_FS_PP4, "DAP4_FS PP4"), + PINCTRL_PIN(TEGRA_PIN_DAP4_DIN_PP5, "DAP4_DIN PP5"), + PINCTRL_PIN(TEGRA_PIN_DAP4_DOUT_PP6, "DAP4_DOUT PP6"), + PINCTRL_PIN(TEGRA_PIN_DAP4_SCLK_PP7, "DAP4_SCLK PP7"), + PINCTRL_PIN(TEGRA_PIN_KB_COL0_PQ0, "KB_COL0 PQ0"), + PINCTRL_PIN(TEGRA_PIN_KB_COL1_PQ1, "KB_COL1 PQ1"), + PINCTRL_PIN(TEGRA_PIN_KB_COL2_PQ2, "KB_COL2 PQ2"), + PINCTRL_PIN(TEGRA_PIN_KB_COL3_PQ3, "KB_COL3 PQ3"), + PINCTRL_PIN(TEGRA_PIN_KB_COL4_PQ4, "KB_COL4 PQ4"), + PINCTRL_PIN(TEGRA_PIN_KB_COL5_PQ5, "KB_COL5 PQ5"), + PINCTRL_PIN(TEGRA_PIN_KB_COL6_PQ6, "KB_COL6 PQ6"), + PINCTRL_PIN(TEGRA_PIN_KB_COL7_PQ7, "KB_COL7 PQ7"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW0_PR0, "KB_ROW0 PR0"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW1_PR1, "KB_ROW1 PR1"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW2_PR2, "KB_ROW2 PR2"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW3_PR3, "KB_ROW3 PR3"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW4_PR4, "KB_ROW4 PR4"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW5_PR5, "KB_ROW5 PR5"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW6_PR6, "KB_ROW6 PR6"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW7_PR7, "KB_ROW7 PR7"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW8_PS0, "KB_ROW8 PS0"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW9_PS1, "KB_ROW9 PS1"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW10_PS2, "KB_ROW10 PS2"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW11_PS3, "KB_ROW11 PS3"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW12_PS4, "KB_ROW12 PS4"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW13_PS5, "KB_ROW13 PS5"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW14_PS6, "KB_ROW14 PS6"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW15_PS7, "KB_ROW15 PS7"), + PINCTRL_PIN(TEGRA_PIN_VI_PCLK_PT0, "VI_PCLK PT0"), + PINCTRL_PIN(TEGRA_PIN_VI_MCLK_PT1, "VI_MCLK PT1"), + PINCTRL_PIN(TEGRA_PIN_VI_D10_PT2, "VD_D10 PT2"), + PINCTRL_PIN(TEGRA_PIN_VI_D11_PT3, "VI_D11 PT3"), + PINCTRL_PIN(TEGRA_PIN_VI_D0_PT4, "VI_D0 PT4"), + PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SCL_PT5, "GEN2_I2C_SCL PT5"), + PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SDA_PT6, "GEN2_I2C_SDA PT6"), + PINCTRL_PIN(TEGRA_PIN_GMI_DPD_PT7, "GMI_DPD PT7"), + /* PU0..6: GPIO only */ + PINCTRL_PIN(TEGRA_PIN_PU0, "PU0"), + PINCTRL_PIN(TEGRA_PIN_PU1, "PU1"), + PINCTRL_PIN(TEGRA_PIN_PU2, "PU2"), + PINCTRL_PIN(TEGRA_PIN_PU3, "PU3"), + PINCTRL_PIN(TEGRA_PIN_PU4, "PU4"), + PINCTRL_PIN(TEGRA_PIN_PU5, "PU5"), + PINCTRL_PIN(TEGRA_PIN_PU6, "PU6"), + PINCTRL_PIN(TEGRA_PIN_JTAG_RTCK_PU7, "JTAG_RTCK PU7"), + /* PV0..1: GPIO only */ + PINCTRL_PIN(TEGRA_PIN_PV0, "PV0"), + PINCTRL_PIN(TEGRA_PIN_PV1, "PV1"), + /* PV2..3: Balls are named after GPIO not function */ + PINCTRL_PIN(TEGRA_PIN_PV2, "PV2"), + PINCTRL_PIN(TEGRA_PIN_PV3, "PV3"), + /* PV4..6: GPIO only */ + PINCTRL_PIN(TEGRA_PIN_PV4, "PV4"), + PINCTRL_PIN(TEGRA_PIN_PV5, "PV5"), + PINCTRL_PIN(TEGRA_PIN_PV6, "PV6"), + PINCTRL_PIN(TEGRA_PIN_LCD_DC1_PV7, "LCD_DC1 PV7"), + PINCTRL_PIN(TEGRA_PIN_LCD_CS1_N_PW0, "LCD_CS1_N PW0"), + PINCTRL_PIN(TEGRA_PIN_LCD_M1_PW1, "LCD_M1 PW1"), + PINCTRL_PIN(TEGRA_PIN_SPI2_CS1_N_PW2, "SPI2_CS1_N PW2"), + PINCTRL_PIN(TEGRA_PIN_SPI2_CS2_N_PW3, "SPI2_CS2_N PW3"), + PINCTRL_PIN(TEGRA_PIN_DAP_MCLK1_PW4, "DAP_MCLK1 PW4"), + PINCTRL_PIN(TEGRA_PIN_DAP_MCLK2_PW5, "DAP_MCLK2 PW5"), + PINCTRL_PIN(TEGRA_PIN_UART3_TXD_PW6, "UART3_TXD PW6"), + PINCTRL_PIN(TEGRA_PIN_UART3_RXD_PW7, "UART3_RXD PW7"), + PINCTRL_PIN(TEGRA_PIN_SPI2_MOSI_PX0, "SPI2_MOSI PX0"), + PINCTRL_PIN(TEGRA_PIN_SPI2_MISO_PX1, "SPI2_MISO PX1"), + PINCTRL_PIN(TEGRA_PIN_SPI2_SCK_PX2, "SPI2_SCK PX2"), + PINCTRL_PIN(TEGRA_PIN_SPI2_CS0_N_PX3, "SPI2_CS0_N PX3"), + PINCTRL_PIN(TEGRA_PIN_SPI1_MOSI_PX4, "SPI1_MOSI PX4"), + PINCTRL_PIN(TEGRA_PIN_SPI1_SCK_PX5, "SPI1_SCK PX5"), + PINCTRL_PIN(TEGRA_PIN_SPI1_CS0_N_PX6, "SPI1_CS0_N PX6"), + PINCTRL_PIN(TEGRA_PIN_SPI1_MISO_PX7, "SPI1_MISO PX7"), + PINCTRL_PIN(TEGRA_PIN_ULPI_CLK_PY0, "ULPI_CLK PY0"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DIR_PY1, "ULPI_DIR PY1"), + PINCTRL_PIN(TEGRA_PIN_ULPI_NXT_PY2, "ULPI_NXT PY2"), + PINCTRL_PIN(TEGRA_PIN_ULPI_STP_PY3, "ULPI_STP PY3"), + PINCTRL_PIN(TEGRA_PIN_SDIO1_DAT3_PY4, "SDIO1_DAT3 PY4"), + PINCTRL_PIN(TEGRA_PIN_SDIO1_DAT2_PY5, "SDIO1_DAT2 PY5"), + PINCTRL_PIN(TEGRA_PIN_SDIO1_DAT1_PY6, "SDIO1_DAT1 PY6"), + PINCTRL_PIN(TEGRA_PIN_SDIO1_DAT0_PY7, "SDIO1_DAT0 PY7"), + PINCTRL_PIN(TEGRA_PIN_SDIO1_CLK_PZ0, "SDIO1_CLK PZ0"), + PINCTRL_PIN(TEGRA_PIN_SDIO1_CMD_PZ1, "SDIO1_CMD PZ1"), + PINCTRL_PIN(TEGRA_PIN_LCD_SDIN_PZ2, "LCD_SDIN PZ2"), + PINCTRL_PIN(TEGRA_PIN_LCD_WR_N_PZ3, "LCD_WR_N PZ3"), + PINCTRL_PIN(TEGRA_PIN_LCD_SCK_PZ4, "LCD_SCK PZ4"), + PINCTRL_PIN(TEGRA_PIN_SYS_CLK_REQ_PZ5, "SYS_CLK_REQ PZ5"), + PINCTRL_PIN(TEGRA_PIN_PWR_I2C_SCL_PZ6, "PWR_I2C_SCL PZ6"), + PINCTRL_PIN(TEGRA_PIN_PWR_I2C_SDA_PZ7, "PWR_I2C_SDA PZ7"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD20_PAA0, "GMI_AD20 PAA0"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD21_PAA1, "GMI_AD21 PAA1"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD22_PAA2, "GMI_AD22 PAA2"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD23_PAA3, "GMI_AD23 PAA3"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD24_PAA4, "GMI_AD24 PAA4"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD25_PAA5, "GMI_AD25 PAA5"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD26_PAA6, "GMI_AD26 PAA6"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD27_PAA7, "GMI_AD27 PAA7"), + PINCTRL_PIN(TEGRA_PIN_LED_BLINK_PBB0, "LED_BLINK PBB0"), + PINCTRL_PIN(TEGRA_PIN_VI_GP0_PBB1, "VI_GP0 PBB1"), + PINCTRL_PIN(TEGRA_PIN_CAM_I2C_SCL_PBB2, "CAM_I2C_SCL PBB2"), + PINCTRL_PIN(TEGRA_PIN_CAM_I2C_SDA_PBB3, "CAM_I2C_SDA PBB3"), + PINCTRL_PIN(TEGRA_PIN_VI_GP3_PBB4, "VI_GP3 PBB4"), + PINCTRL_PIN(TEGRA_PIN_VI_GP4_PBB5, "VI_GP4 PBB5"), + PINCTRL_PIN(TEGRA_PIN_PBB6, "PBB6"), + PINCTRL_PIN(TEGRA_PIN_PBB7, "PBB7"), + PINCTRL_PIN(TEGRA_PIN_CRT_HSYNC, "CRT_HSYNC"), + PINCTRL_PIN(TEGRA_PIN_CRT_VSYNC, "CRT_VSYNC"), + PINCTRL_PIN(TEGRA_PIN_DDC_SCL, "DDC_SCL"), + PINCTRL_PIN(TEGRA_PIN_DDC_SDA, "DDC_SDA"), + PINCTRL_PIN(TEGRA_PIN_OWC, "OWC"), + PINCTRL_PIN(TEGRA_PIN_CORE_PWR_REQ, "CORE_PWR_REQ"), + PINCTRL_PIN(TEGRA_PIN_CPU_PWR_REQ, "CPU_PWR_REQ"), + PINCTRL_PIN(TEGRA_PIN_PWR_INT_N, "PWR_INT_N"), + PINCTRL_PIN(TEGRA_PIN_CLK_32_K_IN, "CLK_32_K_IN"), + PINCTRL_PIN(TEGRA_PIN_DDR_COMP_PD, "DDR_COMP_PD"), + PINCTRL_PIN(TEGRA_PIN_DDR_COMP_PU, "DDR_COMP_PU"), + PINCTRL_PIN(TEGRA_PIN_DDR_A0, "DDR_A0"), + PINCTRL_PIN(TEGRA_PIN_DDR_A1, "DDR_A1"), + PINCTRL_PIN(TEGRA_PIN_DDR_A2, "DDR_A2"), + PINCTRL_PIN(TEGRA_PIN_DDR_A3, "DDR_A3"), + PINCTRL_PIN(TEGRA_PIN_DDR_A4, "DDR_A4"), + PINCTRL_PIN(TEGRA_PIN_DDR_A5, "DDR_A5"), + PINCTRL_PIN(TEGRA_PIN_DDR_A6, "DDR_A6"), + PINCTRL_PIN(TEGRA_PIN_DDR_A7, "DDR_A7"), + PINCTRL_PIN(TEGRA_PIN_DDR_A8, "DDR_A8"), + PINCTRL_PIN(TEGRA_PIN_DDR_A9, "DDR_A9"), + PINCTRL_PIN(TEGRA_PIN_DDR_A10, "DDR_A10"), + PINCTRL_PIN(TEGRA_PIN_DDR_A11, "DDR_A11"), + PINCTRL_PIN(TEGRA_PIN_DDR_A12, "DDR_A12"), + PINCTRL_PIN(TEGRA_PIN_DDR_A13, "DDR_A13"), + PINCTRL_PIN(TEGRA_PIN_DDR_A14, "DDR_A14"), + PINCTRL_PIN(TEGRA_PIN_DDR_CAS_N, "DDR_CAS_N"), + PINCTRL_PIN(TEGRA_PIN_DDR_BA0, "DDR_BA0"), + PINCTRL_PIN(TEGRA_PIN_DDR_BA1, "DDR_BA1"), + PINCTRL_PIN(TEGRA_PIN_DDR_BA2, "DDR_BA2"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQS0P, "DDR_DQS0P"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQS0N, "DDR_DQS0N"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQS1P, "DDR_DQS1P"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQS1N, "DDR_DQS1N"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQS2P, "DDR_DQS2P"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQS2N, "DDR_DQS2N"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQS3P, "DDR_DQS3P"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQS3N, "DDR_DQS3N"), + PINCTRL_PIN(TEGRA_PIN_DDR_CKE0, "DDR_CKE0"), + PINCTRL_PIN(TEGRA_PIN_DDR_CKE1, "DDR_CKE1"), + PINCTRL_PIN(TEGRA_PIN_DDR_CLK, "DDR_CLK"), + PINCTRL_PIN(TEGRA_PIN_DDR_CLK_N, "DDR_CLK_N"), + PINCTRL_PIN(TEGRA_PIN_DDR_DM0, "DDR_DM0"), + PINCTRL_PIN(TEGRA_PIN_DDR_DM1, "DDR_DM1"), + PINCTRL_PIN(TEGRA_PIN_DDR_DM2, "DDR_DM2"), + PINCTRL_PIN(TEGRA_PIN_DDR_DM3, "DDR_DM3"), + PINCTRL_PIN(TEGRA_PIN_DDR_ODT, "DDR_ODT"), + PINCTRL_PIN(TEGRA_PIN_DDR_QUSE0, "DDR_QUSE0"), + PINCTRL_PIN(TEGRA_PIN_DDR_QUSE1, "DDR_QUSE1"), + PINCTRL_PIN(TEGRA_PIN_DDR_QUSE2, "DDR_QUSE2"), + PINCTRL_PIN(TEGRA_PIN_DDR_QUSE3, "DDR_QUSE3"), + PINCTRL_PIN(TEGRA_PIN_DDR_RAS_N, "DDR_RAS_N"), + PINCTRL_PIN(TEGRA_PIN_DDR_WE_N, "DDR_WE_N"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ0, "DDR_DQ0"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ1, "DDR_DQ1"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ2, "DDR_DQ2"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ3, "DDR_DQ3"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ4, "DDR_DQ4"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ5, "DDR_DQ5"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ6, "DDR_DQ6"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ7, "DDR_DQ7"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ8, "DDR_DQ8"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ9, "DDR_DQ9"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ10, "DDR_DQ10"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ11, "DDR_DQ11"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ12, "DDR_DQ12"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ13, "DDR_DQ13"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ14, "DDR_DQ14"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ15, "DDR_DQ15"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ16, "DDR_DQ16"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ17, "DDR_DQ17"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ18, "DDR_DQ18"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ19, "DDR_DQ19"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ20, "DDR_DQ20"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ21, "DDR_DQ21"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ22, "DDR_DQ22"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ23, "DDR_DQ23"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ24, "DDR_DQ24"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ25, "DDR_DQ25"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ26, "DDR_DQ26"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ27, "DDR_DQ27"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ28, "DDR_DQ28"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ29, "DDR_DQ29"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ30, "DDR_DQ30"), + PINCTRL_PIN(TEGRA_PIN_DDR_DQ31, "DDR_DQ31"), + PINCTRL_PIN(TEGRA_PIN_DDR_CS0_N, "DDR_CS0_N"), + PINCTRL_PIN(TEGRA_PIN_DDR_CS1_N, "DDR_CS1_N"), + PINCTRL_PIN(TEGRA_PIN_SYS_RESET, "SYS_RESET"), + PINCTRL_PIN(TEGRA_PIN_JTAG_TRST_N, "JTAG_TRST_N"), + PINCTRL_PIN(TEGRA_PIN_JTAG_TDO, "JTAG_TDO"), + PINCTRL_PIN(TEGRA_PIN_JTAG_TMS, "JTAG_TMS"), + PINCTRL_PIN(TEGRA_PIN_JTAG_TCK, "JTAG_TCK"), + PINCTRL_PIN(TEGRA_PIN_JTAG_TDI, "JTAG_TDI"), + PINCTRL_PIN(TEGRA_PIN_TEST_MODE_EN, "TEST_MODE_EN"), +}; + +static const unsigned ata_pins[] = { + TEGRA_PIN_GMI_CS6_N_PI3, + TEGRA_PIN_GMI_CS7_N_PI6, + TEGRA_PIN_GMI_RST_N_PI4, +}; + +static const unsigned atb_pins[] = { + TEGRA_PIN_GMI_CS5_N_PI2, + TEGRA_PIN_GMI_DPD_PT7, +}; + +static const unsigned atc_pins[] = { + TEGRA_PIN_GMI_IORDY_PI5, + TEGRA_PIN_GMI_WAIT_PI7, + TEGRA_PIN_GMI_ADV_N_PK0, + TEGRA_PIN_GMI_CLK_PK1, + TEGRA_PIN_GMI_CS2_N_PK3, + TEGRA_PIN_GMI_CS3_N_PK4, + TEGRA_PIN_GMI_CS4_N_PK2, + TEGRA_PIN_GMI_AD0_PG0, + TEGRA_PIN_GMI_AD1_PG1, + TEGRA_PIN_GMI_AD2_PG2, + TEGRA_PIN_GMI_AD3_PG3, + TEGRA_PIN_GMI_AD4_PG4, + TEGRA_PIN_GMI_AD5_PG5, + TEGRA_PIN_GMI_AD6_PG6, + TEGRA_PIN_GMI_AD7_PG7, + TEGRA_PIN_GMI_HIOW_N_PI0, + TEGRA_PIN_GMI_HIOR_N_PI1, +}; + +static const unsigned atd_pins[] = { + TEGRA_PIN_GMI_AD8_PH0, + TEGRA_PIN_GMI_AD9_PH1, + TEGRA_PIN_GMI_AD10_PH2, + TEGRA_PIN_GMI_AD11_PH3, +}; + +static const unsigned ate_pins[] = { + TEGRA_PIN_GMI_AD12_PH4, + TEGRA_PIN_GMI_AD13_PH5, + TEGRA_PIN_GMI_AD14_PH6, + TEGRA_PIN_GMI_AD15_PH7, +}; + +static const unsigned cdev1_pins[] = { + TEGRA_PIN_DAP_MCLK1_PW4, +}; + +static const unsigned cdev2_pins[] = { + TEGRA_PIN_DAP_MCLK2_PW5, +}; + +static const unsigned crtp_pins[] = { + TEGRA_PIN_CRT_HSYNC, + TEGRA_PIN_CRT_VSYNC, +}; + +static const unsigned csus_pins[] = { + TEGRA_PIN_VI_MCLK_PT1, +}; + +static const unsigned dap1_pins[] = { + TEGRA_PIN_DAP1_FS_PN0, + TEGRA_PIN_DAP1_DIN_PN1, + TEGRA_PIN_DAP1_DOUT_PN2, + TEGRA_PIN_DAP1_SCLK_PN3, +}; + +static const unsigned dap2_pins[] = { + TEGRA_PIN_DAP2_FS_PA2, + TEGRA_PIN_DAP2_SCLK_PA3, + TEGRA_PIN_DAP2_DIN_PA4, + TEGRA_PIN_DAP2_DOUT_PA5, +}; + +static const unsigned dap3_pins[] = { + TEGRA_PIN_DAP3_FS_PP0, + TEGRA_PIN_DAP3_DIN_PP1, + TEGRA_PIN_DAP3_DOUT_PP2, + TEGRA_PIN_DAP3_SCLK_PP3, +}; + +static const unsigned dap4_pins[] = { + TEGRA_PIN_DAP4_FS_PP4, + TEGRA_PIN_DAP4_DIN_PP5, + TEGRA_PIN_DAP4_DOUT_PP6, + TEGRA_PIN_DAP4_SCLK_PP7, +}; + +static const unsigned ddc_pins[] = { + TEGRA_PIN_DDC_SCL, + TEGRA_PIN_DDC_SDA, +}; + +static const unsigned dta_pins[] = { + TEGRA_PIN_VI_D0_PT4, + TEGRA_PIN_VI_D1_PD5, +}; + +static const unsigned dtb_pins[] = { + TEGRA_PIN_VI_D10_PT2, + TEGRA_PIN_VI_D11_PT3, +}; + +static const unsigned dtc_pins[] = { + TEGRA_PIN_VI_HSYNC_PD7, + TEGRA_PIN_VI_VSYNC_PD6, +}; + +static const unsigned dtd_pins[] = { + TEGRA_PIN_VI_PCLK_PT0, + TEGRA_PIN_VI_D2_PL0, + TEGRA_PIN_VI_D3_PL1, + TEGRA_PIN_VI_D4_PL2, + TEGRA_PIN_VI_D5_PL3, + TEGRA_PIN_VI_D6_PL4, + TEGRA_PIN_VI_D7_PL5, + TEGRA_PIN_VI_D8_PL6, + TEGRA_PIN_VI_D9_PL7, +}; + +static const unsigned dte_pins[] = { + TEGRA_PIN_VI_GP0_PBB1, + TEGRA_PIN_VI_GP3_PBB4, + TEGRA_PIN_VI_GP4_PBB5, + TEGRA_PIN_VI_GP5_PD2, + TEGRA_PIN_VI_GP6_PA0, +}; + +static const unsigned dtf_pins[] = { + TEGRA_PIN_CAM_I2C_SCL_PBB2, + TEGRA_PIN_CAM_I2C_SDA_PBB3, +}; + +static const unsigned gma_pins[] = { + TEGRA_PIN_GMI_AD20_PAA0, + TEGRA_PIN_GMI_AD21_PAA1, + TEGRA_PIN_GMI_AD22_PAA2, + TEGRA_PIN_GMI_AD23_PAA3, +}; + +static const unsigned gmb_pins[] = { + TEGRA_PIN_GMI_WP_N_PC7, +}; + +static const unsigned gmc_pins[] = { + TEGRA_PIN_GMI_AD16_PJ7, + TEGRA_PIN_GMI_AD17_PB0, + TEGRA_PIN_GMI_AD18_PB1, + TEGRA_PIN_GMI_AD19_PK7, +}; + +static const unsigned gmd_pins[] = { + TEGRA_PIN_GMI_CS0_N_PJ0, + TEGRA_PIN_GMI_CS1_N_PJ2, +}; + +static const unsigned gme_pins[] = { + TEGRA_PIN_GMI_AD24_PAA4, + TEGRA_PIN_GMI_AD25_PAA5, + TEGRA_PIN_GMI_AD26_PAA6, + TEGRA_PIN_GMI_AD27_PAA7, +}; + +static const unsigned gpu_pins[] = { + TEGRA_PIN_PU0, + TEGRA_PIN_PU1, + TEGRA_PIN_PU2, + TEGRA_PIN_PU3, + TEGRA_PIN_PU4, + TEGRA_PIN_PU5, + TEGRA_PIN_PU6, +}; + +static const unsigned gpu7_pins[] = { + TEGRA_PIN_JTAG_RTCK_PU7, +}; + +static const unsigned gpv_pins[] = { + TEGRA_PIN_PV4, + TEGRA_PIN_PV5, + TEGRA_PIN_PV6, +}; + +static const unsigned hdint_pins[] = { + TEGRA_PIN_HDMI_INT_N_PN7, +}; + +static const unsigned i2cp_pins[] = { + TEGRA_PIN_PWR_I2C_SCL_PZ6, + TEGRA_PIN_PWR_I2C_SDA_PZ7, +}; + +static const unsigned irrx_pins[] = { + TEGRA_PIN_UART2_RTS_N_PJ6, +}; + +static const unsigned irtx_pins[] = { + TEGRA_PIN_UART2_CTS_N_PJ5, +}; + +static const unsigned kbca_pins[] = { + TEGRA_PIN_KB_ROW0_PR0, + TEGRA_PIN_KB_ROW1_PR1, + TEGRA_PIN_KB_ROW2_PR2, +}; + +static const unsigned kbcb_pins[] = { + TEGRA_PIN_KB_ROW7_PR7, + TEGRA_PIN_KB_ROW8_PS0, + TEGRA_PIN_KB_ROW9_PS1, + TEGRA_PIN_KB_ROW10_PS2, + TEGRA_PIN_KB_ROW11_PS3, + TEGRA_PIN_KB_ROW12_PS4, + TEGRA_PIN_KB_ROW13_PS5, + TEGRA_PIN_KB_ROW14_PS6, + TEGRA_PIN_KB_ROW15_PS7, +}; + +static const unsigned kbcc_pins[] = { + TEGRA_PIN_KB_COL0_PQ0, + TEGRA_PIN_KB_COL1_PQ1, +}; + +static const unsigned kbcd_pins[] = { + TEGRA_PIN_KB_ROW3_PR3, + TEGRA_PIN_KB_ROW4_PR4, + TEGRA_PIN_KB_ROW5_PR5, + TEGRA_PIN_KB_ROW6_PR6, +}; + +static const unsigned kbce_pins[] = { + TEGRA_PIN_KB_COL7_PQ7, +}; + +static const unsigned kbcf_pins[] = { + TEGRA_PIN_KB_COL2_PQ2, + TEGRA_PIN_KB_COL3_PQ3, + TEGRA_PIN_KB_COL4_PQ4, + TEGRA_PIN_KB_COL5_PQ5, + TEGRA_PIN_KB_COL6_PQ6, +}; + +static const unsigned lcsn_pins[] = { + TEGRA_PIN_LCD_CS0_N_PN4, +}; + +static const unsigned ld0_pins[] = { + TEGRA_PIN_LCD_D0_PE0, +}; + +static const unsigned ld1_pins[] = { + TEGRA_PIN_LCD_D1_PE1, +}; + +static const unsigned ld2_pins[] = { + TEGRA_PIN_LCD_D2_PE2, +}; + +static const unsigned ld3_pins[] = { + TEGRA_PIN_LCD_D3_PE3, +}; + +static const unsigned ld4_pins[] = { + TEGRA_PIN_LCD_D4_PE4, +}; + +static const unsigned ld5_pins[] = { + TEGRA_PIN_LCD_D5_PE5, +}; + +static const unsigned ld6_pins[] = { + TEGRA_PIN_LCD_D6_PE6, +}; + +static const unsigned ld7_pins[] = { + TEGRA_PIN_LCD_D7_PE7, +}; + +static const unsigned ld8_pins[] = { + TEGRA_PIN_LCD_D8_PF0, +}; + +static const unsigned ld9_pins[] = { + TEGRA_PIN_LCD_D9_PF1, +}; + +static const unsigned ld10_pins[] = { + TEGRA_PIN_LCD_D10_PF2, +}; + +static const unsigned ld11_pins[] = { + TEGRA_PIN_LCD_D11_PF3, +}; + +static const unsigned ld12_pins[] = { + TEGRA_PIN_LCD_D12_PF4, +}; + +static const unsigned ld13_pins[] = { + TEGRA_PIN_LCD_D13_PF5, +}; + +static const unsigned ld14_pins[] = { + TEGRA_PIN_LCD_D14_PF6, +}; + +static const unsigned ld15_pins[] = { + TEGRA_PIN_LCD_D15_PF7, +}; + +static const unsigned ld16_pins[] = { + TEGRA_PIN_LCD_D16_PM0, +}; + +static const unsigned ld17_pins[] = { + TEGRA_PIN_LCD_D17_PM1, +}; + +static const unsigned ldc_pins[] = { + TEGRA_PIN_LCD_DC0_PN6, +}; + +static const unsigned ldi_pins[] = { + TEGRA_PIN_LCD_D22_PM6, +}; + +static const unsigned lhp0_pins[] = { + TEGRA_PIN_LCD_D21_PM5, +}; + +static const unsigned lhp1_pins[] = { + TEGRA_PIN_LCD_D18_PM2, +}; + +static const unsigned lhp2_pins[] = { + TEGRA_PIN_LCD_D19_PM3, +}; + +static const unsigned lhs_pins[] = { + TEGRA_PIN_LCD_HSYNC_PJ3, +}; + +static const unsigned lm0_pins[] = { + TEGRA_PIN_LCD_CS1_N_PW0, +}; + +static const unsigned lm1_pins[] = { + TEGRA_PIN_LCD_M1_PW1, +}; + +static const unsigned lpp_pins[] = { + TEGRA_PIN_LCD_D23_PM7, +}; + +static const unsigned lpw0_pins[] = { + TEGRA_PIN_LCD_PWR0_PB2, +}; + +static const unsigned lpw1_pins[] = { + TEGRA_PIN_LCD_PWR1_PC1, +}; + +static const unsigned lpw2_pins[] = { + TEGRA_PIN_LCD_PWR2_PC6, +}; + +static const unsigned lsc0_pins[] = { + TEGRA_PIN_LCD_PCLK_PB3, +}; + +static const unsigned lsc1_pins[] = { + TEGRA_PIN_LCD_WR_N_PZ3, +}; + +static const unsigned lsck_pins[] = { + TEGRA_PIN_LCD_SCK_PZ4, +}; + +static const unsigned lsda_pins[] = { + TEGRA_PIN_LCD_SDOUT_PN5, +}; + +static const unsigned lsdi_pins[] = { + TEGRA_PIN_LCD_SDIN_PZ2, +}; + +static const unsigned lspi_pins[] = { + TEGRA_PIN_LCD_DE_PJ1, +}; + +static const unsigned lvp0_pins[] = { + TEGRA_PIN_LCD_DC1_PV7, +}; + +static const unsigned lvp1_pins[] = { + TEGRA_PIN_LCD_D20_PM4, +}; + +static const unsigned lvs_pins[] = { + TEGRA_PIN_LCD_VSYNC_PJ4, +}; + +static const unsigned ls_pins[] = { + TEGRA_PIN_LCD_PWR0_PB2, + TEGRA_PIN_LCD_PWR1_PC1, + TEGRA_PIN_LCD_PWR2_PC6, + TEGRA_PIN_LCD_SDIN_PZ2, + TEGRA_PIN_LCD_SDOUT_PN5, + TEGRA_PIN_LCD_WR_N_PZ3, + TEGRA_PIN_LCD_CS0_N_PN4, + TEGRA_PIN_LCD_DC0_PN6, + TEGRA_PIN_LCD_SCK_PZ4, +}; + +static const unsigned lc_pins[] = { + TEGRA_PIN_LCD_PCLK_PB3, + TEGRA_PIN_LCD_DE_PJ1, + TEGRA_PIN_LCD_HSYNC_PJ3, + TEGRA_PIN_LCD_VSYNC_PJ4, + TEGRA_PIN_LCD_CS1_N_PW0, + TEGRA_PIN_LCD_M1_PW1, + TEGRA_PIN_LCD_DC1_PV7, + TEGRA_PIN_HDMI_INT_N_PN7, +}; + +static const unsigned ld17_0_pins[] = { + TEGRA_PIN_LCD_D0_PE0, + TEGRA_PIN_LCD_D1_PE1, + TEGRA_PIN_LCD_D2_PE2, + TEGRA_PIN_LCD_D3_PE3, + TEGRA_PIN_LCD_D4_PE4, + TEGRA_PIN_LCD_D5_PE5, + TEGRA_PIN_LCD_D6_PE6, + TEGRA_PIN_LCD_D7_PE7, + TEGRA_PIN_LCD_D8_PF0, + TEGRA_PIN_LCD_D9_PF1, + TEGRA_PIN_LCD_D10_PF2, + TEGRA_PIN_LCD_D11_PF3, + TEGRA_PIN_LCD_D12_PF4, + TEGRA_PIN_LCD_D13_PF5, + TEGRA_PIN_LCD_D14_PF6, + TEGRA_PIN_LCD_D15_PF7, + TEGRA_PIN_LCD_D16_PM0, + TEGRA_PIN_LCD_D17_PM1, +}; + +static const unsigned ld19_18_pins[] = { + TEGRA_PIN_LCD_D18_PM2, + TEGRA_PIN_LCD_D19_PM3, +}; + +static const unsigned ld21_20_pins[] = { + TEGRA_PIN_LCD_D20_PM4, + TEGRA_PIN_LCD_D21_PM5, +}; + +static const unsigned ld23_22_pins[] = { + TEGRA_PIN_LCD_D22_PM6, + TEGRA_PIN_LCD_D23_PM7, +}; + +static const unsigned owc_pins[] = { + TEGRA_PIN_OWC, +}; + +static const unsigned pmc_pins[] = { + TEGRA_PIN_LED_BLINK_PBB0, + TEGRA_PIN_SYS_CLK_REQ_PZ5, + TEGRA_PIN_CORE_PWR_REQ, + TEGRA_PIN_CPU_PWR_REQ, + TEGRA_PIN_PWR_INT_N, +}; + +static const unsigned pta_pins[] = { + TEGRA_PIN_GEN2_I2C_SCL_PT5, + TEGRA_PIN_GEN2_I2C_SDA_PT6, +}; + +static const unsigned rm_pins[] = { + TEGRA_PIN_GEN1_I2C_SCL_PC4, + TEGRA_PIN_GEN1_I2C_SDA_PC5, +}; + +static const unsigned sdb_pins[] = { + TEGRA_PIN_SDIO3_CMD_PA7, +}; + +static const unsigned sdc_pins[] = { + TEGRA_PIN_SDIO3_DAT0_PB7, + TEGRA_PIN_SDIO3_DAT1_PB6, + TEGRA_PIN_SDIO3_DAT2_PB5, + TEGRA_PIN_SDIO3_DAT3_PB4, +}; + +static const unsigned sdd_pins[] = { + TEGRA_PIN_SDIO3_CLK_PA6, +}; + +static const unsigned sdio1_pins[] = { + TEGRA_PIN_SDIO1_CLK_PZ0, + TEGRA_PIN_SDIO1_CMD_PZ1, + TEGRA_PIN_SDIO1_DAT0_PY7, + TEGRA_PIN_SDIO1_DAT1_PY6, + TEGRA_PIN_SDIO1_DAT2_PY5, + TEGRA_PIN_SDIO1_DAT3_PY4, +}; + +static const unsigned slxa_pins[] = { + TEGRA_PIN_SDIO3_DAT4_PD1, +}; + +static const unsigned slxc_pins[] = { + TEGRA_PIN_SDIO3_DAT6_PD3, +}; + +static const unsigned slxd_pins[] = { + TEGRA_PIN_SDIO3_DAT7_PD4, +}; + +static const unsigned slxk_pins[] = { + TEGRA_PIN_SDIO3_DAT5_PD0, +}; + +static const unsigned spdi_pins[] = { + TEGRA_PIN_SPDIF_IN_PK6, +}; + +static const unsigned spdo_pins[] = { + TEGRA_PIN_SPDIF_OUT_PK5, +}; + +static const unsigned spia_pins[] = { + TEGRA_PIN_SPI2_MOSI_PX0, +}; + +static const unsigned spib_pins[] = { + TEGRA_PIN_SPI2_MISO_PX1, +}; + +static const unsigned spic_pins[] = { + TEGRA_PIN_SPI2_CS0_N_PX3, + TEGRA_PIN_SPI2_SCK_PX2, +}; + +static const unsigned spid_pins[] = { + TEGRA_PIN_SPI1_MOSI_PX4, +}; + +static const unsigned spie_pins[] = { + TEGRA_PIN_SPI1_CS0_N_PX6, + TEGRA_PIN_SPI1_SCK_PX5, +}; + +static const unsigned spif_pins[] = { + TEGRA_PIN_SPI1_MISO_PX7, +}; + +static const unsigned spig_pins[] = { + TEGRA_PIN_SPI2_CS1_N_PW2, +}; + +static const unsigned spih_pins[] = { + TEGRA_PIN_SPI2_CS2_N_PW3, +}; + +static const unsigned uaa_pins[] = { + TEGRA_PIN_ULPI_DATA0_PO1, + TEGRA_PIN_ULPI_DATA1_PO2, + TEGRA_PIN_ULPI_DATA2_PO3, + TEGRA_PIN_ULPI_DATA3_PO4, +}; + +static const unsigned uab_pins[] = { + TEGRA_PIN_ULPI_DATA4_PO5, + TEGRA_PIN_ULPI_DATA5_PO6, + TEGRA_PIN_ULPI_DATA6_PO7, + TEGRA_PIN_ULPI_DATA7_PO0, +}; + +static const unsigned uac_pins[] = { + TEGRA_PIN_PV0, + TEGRA_PIN_PV1, + TEGRA_PIN_PV2, + TEGRA_PIN_PV3, +}; + +static const unsigned ck32_pins[] = { + TEGRA_PIN_CLK_32_K_IN, +}; + +static const unsigned uad_pins[] = { + TEGRA_PIN_UART2_RXD_PC3, + TEGRA_PIN_UART2_TXD_PC2, +}; + +static const unsigned uca_pins[] = { + TEGRA_PIN_UART3_RXD_PW7, + TEGRA_PIN_UART3_TXD_PW6, +}; + +static const unsigned ucb_pins[] = { + TEGRA_PIN_UART3_CTS_N_PA1, + TEGRA_PIN_UART3_RTS_N_PC0, +}; + +static const unsigned uda_pins[] = { + TEGRA_PIN_ULPI_CLK_PY0, + TEGRA_PIN_ULPI_DIR_PY1, + TEGRA_PIN_ULPI_NXT_PY2, + TEGRA_PIN_ULPI_STP_PY3, +}; + +static const unsigned ddrc_pins[] = { + TEGRA_PIN_DDR_COMP_PD, + TEGRA_PIN_DDR_COMP_PU, +}; + +static const unsigned pmca_pins[] = { + TEGRA_PIN_LED_BLINK_PBB0, +}; + +static const unsigned pmcb_pins[] = { + TEGRA_PIN_SYS_CLK_REQ_PZ5, +}; + +static const unsigned pmcc_pins[] = { + TEGRA_PIN_CORE_PWR_REQ, +}; + +static const unsigned pmcd_pins[] = { + TEGRA_PIN_CPU_PWR_REQ, +}; + +static const unsigned pmce_pins[] = { + TEGRA_PIN_PWR_INT_N, +}; + +static const unsigned xm2c_pins[] = { + TEGRA_PIN_DDR_A0, + TEGRA_PIN_DDR_A1, + TEGRA_PIN_DDR_A2, + TEGRA_PIN_DDR_A3, + TEGRA_PIN_DDR_A4, + TEGRA_PIN_DDR_A5, + TEGRA_PIN_DDR_A6, + TEGRA_PIN_DDR_A7, + TEGRA_PIN_DDR_A8, + TEGRA_PIN_DDR_A9, + TEGRA_PIN_DDR_A10, + TEGRA_PIN_DDR_A11, + TEGRA_PIN_DDR_A12, + TEGRA_PIN_DDR_A13, + TEGRA_PIN_DDR_A14, + TEGRA_PIN_DDR_CAS_N, + TEGRA_PIN_DDR_BA0, + TEGRA_PIN_DDR_BA1, + TEGRA_PIN_DDR_BA2, + TEGRA_PIN_DDR_DQS0P, + TEGRA_PIN_DDR_DQS0N, + TEGRA_PIN_DDR_DQS1P, + TEGRA_PIN_DDR_DQS1N, + TEGRA_PIN_DDR_DQS2P, + TEGRA_PIN_DDR_DQS2N, + TEGRA_PIN_DDR_DQS3P, + TEGRA_PIN_DDR_DQS3N, + TEGRA_PIN_DDR_CS0_N, + TEGRA_PIN_DDR_CS1_N, + TEGRA_PIN_DDR_CKE0, + TEGRA_PIN_DDR_CKE1, + TEGRA_PIN_DDR_CLK, + TEGRA_PIN_DDR_CLK_N, + TEGRA_PIN_DDR_DM0, + TEGRA_PIN_DDR_DM1, + TEGRA_PIN_DDR_DM2, + TEGRA_PIN_DDR_DM3, + TEGRA_PIN_DDR_ODT, + TEGRA_PIN_DDR_RAS_N, + TEGRA_PIN_DDR_WE_N, + TEGRA_PIN_DDR_QUSE0, + TEGRA_PIN_DDR_QUSE1, + TEGRA_PIN_DDR_QUSE2, + TEGRA_PIN_DDR_QUSE3, +}; + +static const unsigned xm2d_pins[] = { + TEGRA_PIN_DDR_DQ0, + TEGRA_PIN_DDR_DQ1, + TEGRA_PIN_DDR_DQ2, + TEGRA_PIN_DDR_DQ3, + TEGRA_PIN_DDR_DQ4, + TEGRA_PIN_DDR_DQ5, + TEGRA_PIN_DDR_DQ6, + TEGRA_PIN_DDR_DQ7, + TEGRA_PIN_DDR_DQ8, + TEGRA_PIN_DDR_DQ9, + TEGRA_PIN_DDR_DQ10, + TEGRA_PIN_DDR_DQ11, + TEGRA_PIN_DDR_DQ12, + TEGRA_PIN_DDR_DQ13, + TEGRA_PIN_DDR_DQ14, + TEGRA_PIN_DDR_DQ15, + TEGRA_PIN_DDR_DQ16, + TEGRA_PIN_DDR_DQ17, + TEGRA_PIN_DDR_DQ18, + TEGRA_PIN_DDR_DQ19, + TEGRA_PIN_DDR_DQ20, + TEGRA_PIN_DDR_DQ21, + TEGRA_PIN_DDR_DQ22, + TEGRA_PIN_DDR_DQ23, + TEGRA_PIN_DDR_DQ24, + TEGRA_PIN_DDR_DQ25, + TEGRA_PIN_DDR_DQ26, + TEGRA_PIN_DDR_DQ27, + TEGRA_PIN_DDR_DQ28, + TEGRA_PIN_DDR_DQ29, + TEGRA_PIN_DDR_DQ30, + TEGRA_PIN_DDR_DQ31, +}; + +static const unsigned drive_ao1_pins[] = { + TEGRA_PIN_SYS_RESET, + TEGRA_PIN_PWR_I2C_SCL_PZ6, + TEGRA_PIN_PWR_I2C_SDA_PZ7, + TEGRA_PIN_KB_ROW0_PR0, + TEGRA_PIN_KB_ROW1_PR1, + TEGRA_PIN_KB_ROW2_PR2, + TEGRA_PIN_KB_ROW3_PR3, + TEGRA_PIN_KB_ROW4_PR4, + TEGRA_PIN_KB_ROW5_PR5, + TEGRA_PIN_KB_ROW6_PR6, + TEGRA_PIN_KB_ROW7_PR7, +}; + +static const unsigned drive_ao2_pins[] = { + TEGRA_PIN_KB_ROW8_PS0, + TEGRA_PIN_KB_ROW9_PS1, + TEGRA_PIN_KB_ROW10_PS2, + TEGRA_PIN_KB_ROW11_PS3, + TEGRA_PIN_KB_ROW12_PS4, + TEGRA_PIN_KB_ROW13_PS5, + TEGRA_PIN_KB_ROW14_PS6, + TEGRA_PIN_KB_ROW15_PS7, + TEGRA_PIN_KB_COL0_PQ0, + TEGRA_PIN_KB_COL1_PQ1, + TEGRA_PIN_KB_COL2_PQ2, + TEGRA_PIN_KB_COL3_PQ3, + TEGRA_PIN_KB_COL4_PQ4, + TEGRA_PIN_KB_COL5_PQ5, + TEGRA_PIN_KB_COL6_PQ6, + TEGRA_PIN_KB_COL7_PQ7, + TEGRA_PIN_LED_BLINK_PBB0, + TEGRA_PIN_SYS_CLK_REQ_PZ5, + TEGRA_PIN_CORE_PWR_REQ, + TEGRA_PIN_CPU_PWR_REQ, + TEGRA_PIN_PWR_INT_N, + TEGRA_PIN_CLK_32_K_IN, +}; + +static const unsigned drive_at1_pins[] = { + TEGRA_PIN_GMI_IORDY_PI5, + TEGRA_PIN_GMI_AD8_PH0, + TEGRA_PIN_GMI_AD9_PH1, + TEGRA_PIN_GMI_AD10_PH2, + TEGRA_PIN_GMI_AD11_PH3, + TEGRA_PIN_GMI_AD12_PH4, + TEGRA_PIN_GMI_AD13_PH5, + TEGRA_PIN_GMI_AD14_PH6, + TEGRA_PIN_GMI_AD15_PH7, + TEGRA_PIN_GMI_CS7_N_PI6, + TEGRA_PIN_GMI_DPD_PT7, + TEGRA_PIN_GEN2_I2C_SCL_PT5, + TEGRA_PIN_GEN2_I2C_SDA_PT6, +}; + +static const unsigned drive_at2_pins[] = { + TEGRA_PIN_GMI_WAIT_PI7, + TEGRA_PIN_GMI_ADV_N_PK0, + TEGRA_PIN_GMI_CLK_PK1, + TEGRA_PIN_GMI_CS6_N_PI3, + TEGRA_PIN_GMI_CS5_N_PI2, + TEGRA_PIN_GMI_CS4_N_PK2, + TEGRA_PIN_GMI_CS3_N_PK4, + TEGRA_PIN_GMI_CS2_N_PK3, + TEGRA_PIN_GMI_AD0_PG0, + TEGRA_PIN_GMI_AD1_PG1, + TEGRA_PIN_GMI_AD2_PG2, + TEGRA_PIN_GMI_AD3_PG3, + TEGRA_PIN_GMI_AD4_PG4, + TEGRA_PIN_GMI_AD5_PG5, + TEGRA_PIN_GMI_AD6_PG6, + TEGRA_PIN_GMI_AD7_PG7, + TEGRA_PIN_GMI_HIOW_N_PI0, + TEGRA_PIN_GMI_HIOR_N_PI1, + TEGRA_PIN_GMI_RST_N_PI4, +}; + +static const unsigned drive_cdev1_pins[] = { + TEGRA_PIN_DAP_MCLK1_PW4, +}; + +static const unsigned drive_cdev2_pins[] = { + TEGRA_PIN_DAP_MCLK2_PW5, +}; + +static const unsigned drive_csus_pins[] = { + TEGRA_PIN_VI_MCLK_PT1, +}; + +static const unsigned drive_dap1_pins[] = { + TEGRA_PIN_DAP1_FS_PN0, + TEGRA_PIN_DAP1_DIN_PN1, + TEGRA_PIN_DAP1_DOUT_PN2, + TEGRA_PIN_DAP1_SCLK_PN3, + TEGRA_PIN_SPDIF_OUT_PK5, + TEGRA_PIN_SPDIF_IN_PK6, +}; + +static const unsigned drive_dap2_pins[] = { + TEGRA_PIN_DAP2_FS_PA2, + TEGRA_PIN_DAP2_SCLK_PA3, + TEGRA_PIN_DAP2_DIN_PA4, + TEGRA_PIN_DAP2_DOUT_PA5, +}; + +static const unsigned drive_dap3_pins[] = { + TEGRA_PIN_DAP3_FS_PP0, + TEGRA_PIN_DAP3_DIN_PP1, + TEGRA_PIN_DAP3_DOUT_PP2, + TEGRA_PIN_DAP3_SCLK_PP3, +}; + +static const unsigned drive_dap4_pins[] = { + TEGRA_PIN_DAP4_FS_PP4, + TEGRA_PIN_DAP4_DIN_PP5, + TEGRA_PIN_DAP4_DOUT_PP6, + TEGRA_PIN_DAP4_SCLK_PP7, +}; + +static const unsigned drive_dbg_pins[] = { + TEGRA_PIN_PU0, + TEGRA_PIN_PU1, + TEGRA_PIN_PU2, + TEGRA_PIN_PU3, + TEGRA_PIN_PU4, + TEGRA_PIN_PU5, + TEGRA_PIN_PU6, + TEGRA_PIN_JTAG_RTCK_PU7, + TEGRA_PIN_GEN1_I2C_SDA_PC5, + TEGRA_PIN_GEN1_I2C_SCL_PC4, + TEGRA_PIN_JTAG_TRST_N, + TEGRA_PIN_JTAG_TDO, + TEGRA_PIN_JTAG_TMS, + TEGRA_PIN_JTAG_TCK, + TEGRA_PIN_JTAG_TDI, + TEGRA_PIN_TEST_MODE_EN, +}; + +static const unsigned drive_lcd1_pins[] = { + TEGRA_PIN_LCD_PWR1_PC1, + TEGRA_PIN_LCD_PWR2_PC6, + TEGRA_PIN_LCD_SDIN_PZ2, + TEGRA_PIN_LCD_SDOUT_PN5, + TEGRA_PIN_LCD_WR_N_PZ3, + TEGRA_PIN_LCD_CS0_N_PN4, + TEGRA_PIN_LCD_DC0_PN6, + TEGRA_PIN_LCD_SCK_PZ4, +}; + +static const unsigned drive_lcd2_pins[] = { + TEGRA_PIN_LCD_PWR0_PB2, + TEGRA_PIN_LCD_PCLK_PB3, + TEGRA_PIN_LCD_DE_PJ1, + TEGRA_PIN_LCD_HSYNC_PJ3, + TEGRA_PIN_LCD_VSYNC_PJ4, + TEGRA_PIN_LCD_D0_PE0, + TEGRA_PIN_LCD_D1_PE1, + TEGRA_PIN_LCD_D2_PE2, + TEGRA_PIN_LCD_D3_PE3, + TEGRA_PIN_LCD_D4_PE4, + TEGRA_PIN_LCD_D5_PE5, + TEGRA_PIN_LCD_D6_PE6, + TEGRA_PIN_LCD_D7_PE7, + TEGRA_PIN_LCD_D8_PF0, + TEGRA_PIN_LCD_D9_PF1, + TEGRA_PIN_LCD_D10_PF2, + TEGRA_PIN_LCD_D11_PF3, + TEGRA_PIN_LCD_D12_PF4, + TEGRA_PIN_LCD_D13_PF5, + TEGRA_PIN_LCD_D14_PF6, + TEGRA_PIN_LCD_D15_PF7, + TEGRA_PIN_LCD_D16_PM0, + TEGRA_PIN_LCD_D17_PM1, + TEGRA_PIN_LCD_D18_PM2, + TEGRA_PIN_LCD_D19_PM3, + TEGRA_PIN_LCD_D20_PM4, + TEGRA_PIN_LCD_D21_PM5, + TEGRA_PIN_LCD_D22_PM6, + TEGRA_PIN_LCD_D23_PM7, + TEGRA_PIN_LCD_CS1_N_PW0, + TEGRA_PIN_LCD_M1_PW1, + TEGRA_PIN_LCD_DC1_PV7, + TEGRA_PIN_HDMI_INT_N_PN7, +}; + +static const unsigned drive_sdmmc2_pins[] = { + TEGRA_PIN_SDIO3_DAT4_PD1, + TEGRA_PIN_SDIO3_DAT5_PD0, + TEGRA_PIN_SDIO3_DAT6_PD3, + TEGRA_PIN_SDIO3_DAT7_PD4, +}; + +static const unsigned drive_sdmmc3_pins[] = { + TEGRA_PIN_SDIO3_CLK_PA6, + TEGRA_PIN_SDIO3_CMD_PA7, + TEGRA_PIN_SDIO3_DAT0_PB7, + TEGRA_PIN_SDIO3_DAT1_PB6, + TEGRA_PIN_SDIO3_DAT2_PB5, + TEGRA_PIN_SDIO3_DAT3_PB4, + TEGRA_PIN_PV4, + TEGRA_PIN_PV5, + TEGRA_PIN_PV6, +}; + +static const unsigned drive_spi_pins[] = { + TEGRA_PIN_SPI2_MOSI_PX0, + TEGRA_PIN_SPI2_MISO_PX1, + TEGRA_PIN_SPI2_SCK_PX2, + TEGRA_PIN_SPI2_CS0_N_PX3, + TEGRA_PIN_SPI1_MOSI_PX4, + TEGRA_PIN_SPI1_SCK_PX5, + TEGRA_PIN_SPI1_CS0_N_PX6, + TEGRA_PIN_SPI1_MISO_PX7, + TEGRA_PIN_SPI2_CS1_N_PW2, + TEGRA_PIN_SPI2_CS2_N_PW3, +}; + +static const unsigned drive_uaa_pins[] = { + TEGRA_PIN_ULPI_DATA0_PO1, + TEGRA_PIN_ULPI_DATA1_PO2, + TEGRA_PIN_ULPI_DATA2_PO3, + TEGRA_PIN_ULPI_DATA3_PO4, +}; + +static const unsigned drive_uab_pins[] = { + TEGRA_PIN_ULPI_DATA4_PO5, + TEGRA_PIN_ULPI_DATA5_PO6, + TEGRA_PIN_ULPI_DATA6_PO7, + TEGRA_PIN_ULPI_DATA7_PO0, + TEGRA_PIN_PV0, + TEGRA_PIN_PV1, + TEGRA_PIN_PV2, + TEGRA_PIN_PV3, +}; + +static const unsigned drive_uart2_pins[] = { + TEGRA_PIN_UART2_TXD_PC2, + TEGRA_PIN_UART2_RXD_PC3, + TEGRA_PIN_UART2_RTS_N_PJ6, + TEGRA_PIN_UART2_CTS_N_PJ5, +}; + +static const unsigned drive_uart3_pins[] = { + TEGRA_PIN_UART3_TXD_PW6, + TEGRA_PIN_UART3_RXD_PW7, + TEGRA_PIN_UART3_RTS_N_PC0, + TEGRA_PIN_UART3_CTS_N_PA1, +}; + +static const unsigned drive_vi1_pins[] = { + TEGRA_PIN_VI_D0_PT4, + TEGRA_PIN_VI_D1_PD5, + TEGRA_PIN_VI_D2_PL0, + TEGRA_PIN_VI_D3_PL1, + TEGRA_PIN_VI_D4_PL2, + TEGRA_PIN_VI_D5_PL3, + TEGRA_PIN_VI_D6_PL4, + TEGRA_PIN_VI_D7_PL5, + TEGRA_PIN_VI_D8_PL6, + TEGRA_PIN_VI_D9_PL7, + TEGRA_PIN_VI_D10_PT2, + TEGRA_PIN_VI_D11_PT3, + TEGRA_PIN_VI_PCLK_PT0, + TEGRA_PIN_VI_VSYNC_PD6, + TEGRA_PIN_VI_HSYNC_PD7, +}; + +static const unsigned drive_vi2_pins[] = { + TEGRA_PIN_VI_GP0_PBB1, + TEGRA_PIN_CAM_I2C_SCL_PBB2, + TEGRA_PIN_CAM_I2C_SDA_PBB3, + TEGRA_PIN_VI_GP3_PBB4, + TEGRA_PIN_VI_GP4_PBB5, + TEGRA_PIN_VI_GP5_PD2, + TEGRA_PIN_VI_GP6_PA0, +}; + +static const unsigned drive_xm2a_pins[] = { + TEGRA_PIN_DDR_A0, + TEGRA_PIN_DDR_A1, + TEGRA_PIN_DDR_A2, + TEGRA_PIN_DDR_A3, + TEGRA_PIN_DDR_A4, + TEGRA_PIN_DDR_A5, + TEGRA_PIN_DDR_A6, + TEGRA_PIN_DDR_A7, + TEGRA_PIN_DDR_A8, + TEGRA_PIN_DDR_A9, + TEGRA_PIN_DDR_A10, + TEGRA_PIN_DDR_A11, + TEGRA_PIN_DDR_A12, + TEGRA_PIN_DDR_A13, + TEGRA_PIN_DDR_A14, + TEGRA_PIN_DDR_BA0, + TEGRA_PIN_DDR_BA1, + TEGRA_PIN_DDR_BA2, + TEGRA_PIN_DDR_CS0_N, + TEGRA_PIN_DDR_CS1_N, + TEGRA_PIN_DDR_ODT, + TEGRA_PIN_DDR_RAS_N, + TEGRA_PIN_DDR_CAS_N, + TEGRA_PIN_DDR_WE_N, + TEGRA_PIN_DDR_CKE0, + TEGRA_PIN_DDR_CKE1, +}; + +static const unsigned drive_xm2c_pins[] = { + TEGRA_PIN_DDR_DQS0P, + TEGRA_PIN_DDR_DQS0N, + TEGRA_PIN_DDR_DQS1P, + TEGRA_PIN_DDR_DQS1N, + TEGRA_PIN_DDR_DQS2P, + TEGRA_PIN_DDR_DQS2N, + TEGRA_PIN_DDR_DQS3P, + TEGRA_PIN_DDR_DQS3N, + TEGRA_PIN_DDR_QUSE0, + TEGRA_PIN_DDR_QUSE1, + TEGRA_PIN_DDR_QUSE2, + TEGRA_PIN_DDR_QUSE3, +}; + +static const unsigned drive_xm2d_pins[] = { + TEGRA_PIN_DDR_DQ0, + TEGRA_PIN_DDR_DQ1, + TEGRA_PIN_DDR_DQ2, + TEGRA_PIN_DDR_DQ3, + TEGRA_PIN_DDR_DQ4, + TEGRA_PIN_DDR_DQ5, + TEGRA_PIN_DDR_DQ6, + TEGRA_PIN_DDR_DQ7, + TEGRA_PIN_DDR_DQ8, + TEGRA_PIN_DDR_DQ9, + TEGRA_PIN_DDR_DQ10, + TEGRA_PIN_DDR_DQ11, + TEGRA_PIN_DDR_DQ12, + TEGRA_PIN_DDR_DQ13, + TEGRA_PIN_DDR_DQ14, + TEGRA_PIN_DDR_DQ15, + TEGRA_PIN_DDR_DQ16, + TEGRA_PIN_DDR_DQ17, + TEGRA_PIN_DDR_DQ18, + TEGRA_PIN_DDR_DQ19, + TEGRA_PIN_DDR_DQ20, + TEGRA_PIN_DDR_DQ21, + TEGRA_PIN_DDR_DQ22, + TEGRA_PIN_DDR_DQ23, + TEGRA_PIN_DDR_DQ24, + TEGRA_PIN_DDR_DQ25, + TEGRA_PIN_DDR_DQ26, + TEGRA_PIN_DDR_DQ27, + TEGRA_PIN_DDR_DQ28, + TEGRA_PIN_DDR_DQ29, + TEGRA_PIN_DDR_DQ30, + TEGRA_PIN_DDR_DQ31, + TEGRA_PIN_DDR_DM0, + TEGRA_PIN_DDR_DM1, + TEGRA_PIN_DDR_DM2, + TEGRA_PIN_DDR_DM3, +}; + +static const unsigned drive_xm2clk_pins[] = { + TEGRA_PIN_DDR_CLK, + TEGRA_PIN_DDR_CLK_N, +}; + +static const unsigned drive_sdio1_pins[] = { + TEGRA_PIN_SDIO1_CLK_PZ0, + TEGRA_PIN_SDIO1_CMD_PZ1, + TEGRA_PIN_SDIO1_DAT0_PY7, + TEGRA_PIN_SDIO1_DAT1_PY6, + TEGRA_PIN_SDIO1_DAT2_PY5, + TEGRA_PIN_SDIO1_DAT3_PY4, +}; + +static const unsigned drive_crt_pins[] = { + TEGRA_PIN_CRT_HSYNC, + TEGRA_PIN_CRT_VSYNC, +}; + +static const unsigned drive_ddc_pins[] = { + TEGRA_PIN_DDC_SCL, + TEGRA_PIN_DDC_SDA, +}; + +static const unsigned drive_gma_pins[] = { + TEGRA_PIN_GMI_AD20_PAA0, + TEGRA_PIN_GMI_AD21_PAA1, + TEGRA_PIN_GMI_AD22_PAA2, + TEGRA_PIN_GMI_AD23_PAA3, +}; + +static const unsigned drive_gmb_pins[] = { + TEGRA_PIN_GMI_WP_N_PC7, +}; + +static const unsigned drive_gmc_pins[] = { + TEGRA_PIN_GMI_AD16_PJ7, + TEGRA_PIN_GMI_AD17_PB0, + TEGRA_PIN_GMI_AD18_PB1, + TEGRA_PIN_GMI_AD19_PK7, +}; + +static const unsigned drive_gmd_pins[] = { + TEGRA_PIN_GMI_CS0_N_PJ0, + TEGRA_PIN_GMI_CS1_N_PJ2, +}; + +static const unsigned drive_gme_pins[] = { + TEGRA_PIN_GMI_AD24_PAA4, + TEGRA_PIN_GMI_AD25_PAA5, + TEGRA_PIN_GMI_AD26_PAA6, + TEGRA_PIN_GMI_AD27_PAA7, +}; + +static const unsigned drive_owr_pins[] = { + TEGRA_PIN_OWC, +}; + +static const unsigned drive_uda_pins[] = { + TEGRA_PIN_ULPI_CLK_PY0, + TEGRA_PIN_ULPI_DIR_PY1, + TEGRA_PIN_ULPI_NXT_PY2, + TEGRA_PIN_ULPI_STP_PY3, +}; + +enum tegra_mux { + TEGRA_MUX_AHB_CLK, + TEGRA_MUX_APB_CLK, + TEGRA_MUX_AUDIO_SYNC, + TEGRA_MUX_CRT, + TEGRA_MUX_DAP1, + TEGRA_MUX_DAP2, + TEGRA_MUX_DAP3, + TEGRA_MUX_DAP4, + TEGRA_MUX_DAP5, + TEGRA_MUX_DISPLAYA, + TEGRA_MUX_DISPLAYB, + TEGRA_MUX_EMC_TEST0_DLL, + TEGRA_MUX_EMC_TEST1_DLL, + TEGRA_MUX_GMI, + TEGRA_MUX_GMI_INT, + TEGRA_MUX_HDMI, + TEGRA_MUX_I2CP, + TEGRA_MUX_I2C1, + TEGRA_MUX_I2C2, + TEGRA_MUX_I2C3, + TEGRA_MUX_IDE, + TEGRA_MUX_IRDA, + TEGRA_MUX_KBC, + TEGRA_MUX_MIO, + TEGRA_MUX_MIPI_HS, + TEGRA_MUX_NAND, + TEGRA_MUX_OSC, + TEGRA_MUX_OWR, + TEGRA_MUX_PCIE, + TEGRA_MUX_PLLA_OUT, + TEGRA_MUX_PLLC_OUT1, + TEGRA_MUX_PLLM_OUT1, + TEGRA_MUX_PLLP_OUT2, + TEGRA_MUX_PLLP_OUT3, + TEGRA_MUX_PLLP_OUT4, + TEGRA_MUX_PWM, + TEGRA_MUX_PWR_INTR, + TEGRA_MUX_PWR_ON, + TEGRA_MUX_RSVD1, + TEGRA_MUX_RSVD2, + TEGRA_MUX_RSVD3, + TEGRA_MUX_RSVD4, + TEGRA_MUX_RTCK, + TEGRA_MUX_SDIO1, + TEGRA_MUX_SDIO2, + TEGRA_MUX_SDIO3, + TEGRA_MUX_SDIO4, + TEGRA_MUX_SFLASH, + TEGRA_MUX_SPDIF, + TEGRA_MUX_SPI1, + TEGRA_MUX_SPI2, + TEGRA_MUX_SPI2_ALT, + TEGRA_MUX_SPI3, + TEGRA_MUX_SPI4, + TEGRA_MUX_TRACE, + TEGRA_MUX_TWC, + TEGRA_MUX_UARTA, + TEGRA_MUX_UARTB, + TEGRA_MUX_UARTC, + TEGRA_MUX_UARTD, + TEGRA_MUX_UARTE, + TEGRA_MUX_ULPI, + TEGRA_MUX_VI, + TEGRA_MUX_VI_SENSOR_CLK, + TEGRA_MUX_XIO, +}; + +static const char * const ahb_clk_groups[] = { + "cdev2", +}; + +static const char * const apb_clk_groups[] = { + "cdev2", +}; + +static const char * const audio_sync_groups[] = { + "cdev1", +}; + +static const char * const crt_groups[] = { + "crtp", + "lm1", +}; + +static const char * const dap1_groups[] = { + "dap1", +}; + +static const char * const dap2_groups[] = { + "dap2", +}; + +static const char * const dap3_groups[] = { + "dap3", +}; + +static const char * const dap4_groups[] = { + "dap4", +}; + +static const char * const dap5_groups[] = { + "gme", +}; + +static const char * const displaya_groups[] = { + "lcsn", + "ld0", + "ld1", + "ld10", + "ld11", + "ld12", + "ld13", + "ld14", + "ld15", + "ld16", + "ld17", + "ld2", + "ld3", + "ld4", + "ld5", + "ld6", + "ld7", + "ld8", + "ld9", + "ldc", + "ldi", + "lhp0", + "lhp1", + "lhp2", + "lhs", + "lm0", + "lm1", + "lpp", + "lpw0", + "lpw1", + "lpw2", + "lsc0", + "lsc1", + "lsck", + "lsda", + "lsdi", + "lspi", + "lvp0", + "lvp1", + "lvs", +}; + +static const char * const displayb_groups[] = { + "lcsn", + "ld0", + "ld1", + "ld10", + "ld11", + "ld12", + "ld13", + "ld14", + "ld15", + "ld16", + "ld17", + "ld2", + "ld3", + "ld4", + "ld5", + "ld6", + "ld7", + "ld8", + "ld9", + "ldc", + "ldi", + "lhp0", + "lhp1", + "lhp2", + "lhs", + "lm0", + "lm1", + "lpp", + "lpw0", + "lpw1", + "lpw2", + "lsc0", + "lsc1", + "lsck", + "lsda", + "lsdi", + "lspi", + "lvp0", + "lvp1", + "lvs", +}; + +static const char * const emc_test0_dll_groups[] = { + "kbca", +}; + +static const char * const emc_test1_dll_groups[] = { + "kbcc", +}; + +static const char * const gmi_groups[] = { + "ata", + "atb", + "atc", + "atd", + "ate", + "dap1", + "dap2", + "dap4", + "gma", + "gmb", + "gmc", + "gmd", + "gme", + "gpu", + "irrx", + "irtx", + "pta", + "spia", + "spib", + "spic", + "spid", + "spie", + "uca", + "ucb", +}; + +static const char * const gmi_int_groups[] = { + "gmb", +}; + +static const char * const hdmi_groups[] = { + "hdint", + "lpw0", + "lpw2", + "lsc1", + "lsck", + "lsda", + "lspi", + "pta", +}; + +static const char * const i2cp_groups[] = { + "i2cp", +}; + +static const char * const i2c1_groups[] = { + "rm", + "spdi", + "spdo", + "spig", + "spih", +}; + +static const char * const i2c2_groups[] = { + "ddc", + "pta", +}; + +static const char * const i2c3_groups[] = { + "dtf", +}; + +static const char * const ide_groups[] = { + "ata", + "atb", + "atc", + "atd", + "ate", + "gmb", +}; + +static const char * const irda_groups[] = { + "uad", +}; + +static const char * const kbc_groups[] = { + "kbca", + "kbcb", + "kbcc", + "kbcd", + "kbce", + "kbcf", +}; + +static const char * const mio_groups[] = { + "kbcb", + "kbcd", + "kbcf", +}; + +static const char * const mipi_hs_groups[] = { + "uaa", + "uab", +}; + +static const char * const nand_groups[] = { + "ata", + "atb", + "atc", + "atd", + "ate", + "gmb", + "gmd", + "kbca", + "kbcb", + "kbcc", + "kbcd", + "kbce", + "kbcf", +}; + +static const char * const osc_groups[] = { + "cdev1", + "cdev2", +}; + +static const char * const owr_groups[] = { + "kbce", + "owc", + "uac", +}; + +static const char * const pcie_groups[] = { + "gpv", + "slxa", + "slxk", +}; + +static const char * const plla_out_groups[] = { + "cdev1", +}; + +static const char * const pllc_out1_groups[] = { + "csus", +}; + +static const char * const pllm_out1_groups[] = { + "cdev1", +}; + +static const char * const pllp_out2_groups[] = { + "csus", +}; + +static const char * const pllp_out3_groups[] = { + "csus", +}; + +static const char * const pllp_out4_groups[] = { + "cdev2", +}; + +static const char * const pwm_groups[] = { + "gpu", + "sdb", + "sdc", + "sdd", + "ucb", +}; + +static const char * const pwr_intr_groups[] = { + "pmc", +}; + +static const char * const pwr_on_groups[] = { + "pmc", +}; + +static const char * const rsvd1_groups[] = { + "dta", + "dtb", + "dtc", + "dtd", + "dte", + "gmd", + "gme", +}; + +static const char * const rsvd2_groups[] = { + "crtp", + "dap1", + "dap3", + "dap4", + "ddc", + "dtb", + "dtc", + "dte", + "dtf", + "gpu7", + "gpv", + "hdint", + "i2cp", + "owc", + "rm", + "sdio1", + "spdi", + "spdo", + "uac", + "uca", + "uda", +}; + +static const char * const rsvd3_groups[] = { + "crtp", + "dap2", + "dap3", + "ddc", + "gpu7", + "gpv", + "hdint", + "i2cp", + "ld17", + "ldc", + "ldi", + "lhp0", + "lhp1", + "lhp2", + "lm1", + "lpp", + "lpw1", + "lvp0", + "lvp1", + "owc", + "pmc", + "rm", + "uac", +}; + +static const char * const rsvd4_groups[] = { + "ata", + "ate", + "crtp", + "dap3", + "dap4", + "ddc", + "dta", + "dtc", + "dtd", + "dtf", + "gpu", + "gpu7", + "gpv", + "hdint", + "i2cp", + "kbce", + "lcsn", + "ld0", + "ld1", + "ld2", + "ld3", + "ld4", + "ld5", + "ld6", + "ld7", + "ld8", + "ld9", + "ld10", + "ld11", + "ld12", + "ld13", + "ld14", + "ld15", + "ld16", + "ld17", + "ldc", + "ldi", + "lhp0", + "lhp1", + "lhp2", + "lhs", + "lm0", + "lpp", + "lpw1", + "lsc0", + "lsdi", + "lvp0", + "lvp1", + "lvs", + "owc", + "pmc", + "pta", + "rm", + "spif", + "uac", + "uca", + "ucb", +}; + +static const char * const rtck_groups[] = { + "gpu7", +}; + +static const char * const sdio1_groups[] = { + "sdio1", +}; + +static const char * const sdio2_groups[] = { + "dap1", + "dta", + "dtd", + "kbca", + "kbcb", + "kbcd", + "spdi", + "spdo", +}; + +static const char * const sdio3_groups[] = { + "sdb", + "sdc", + "sdd", + "slxa", + "slxc", + "slxd", + "slxk", +}; + +static const char * const sdio4_groups[] = { + "atb", + "atc", + "atd", + "gma", + "gme", +}; + +static const char * const sflash_groups[] = { + "gmc", + "gmd", +}; + +static const char * const spdif_groups[] = { + "slxc", + "slxd", + "spdi", + "spdo", + "uad", +}; + +static const char * const spi1_groups[] = { + "dtb", + "dte", + "spia", + "spib", + "spic", + "spid", + "spie", + "spif", + "uda", +}; + +static const char * const spi2_groups[] = { + "sdb", + "slxa", + "slxc", + "slxd", + "slxk", + "spia", + "spib", + "spic", + "spid", + "spie", + "spif", + "spig", + "spih", + "uab", +}; + +static const char * const spi2_alt_groups[] = { + "spid", + "spie", + "spig", + "spih", +}; + +static const char * const spi3_groups[] = { + "gma", + "lcsn", + "lm0", + "lpw0", + "lpw2", + "lsc1", + "lsck", + "lsda", + "lsdi", + "sdc", + "sdd", + "spia", + "spib", + "spic", + "spif", + "spig", + "spih", + "uaa", +}; + +static const char * const spi4_groups[] = { + "gmc", + "irrx", + "irtx", + "slxa", + "slxc", + "slxd", + "slxk", + "uad", +}; + +static const char * const trace_groups[] = { + "kbcc", + "kbcf", +}; + +static const char * const twc_groups[] = { + "dap2", + "sdc", +}; + +static const char * const uarta_groups[] = { + "gpu", + "irrx", + "irtx", + "sdb", + "sdd", + "sdio1", + "uaa", + "uab", + "uad", +}; + +static const char * const uartb_groups[] = { + "irrx", + "irtx", +}; + +static const char * const uartc_groups[] = { + "uca", + "ucb", +}; + +static const char * const uartd_groups[] = { + "gmc", + "uda", +}; + +static const char * const uarte_groups[] = { + "gma", + "sdio1", +}; + +static const char * const ulpi_groups[] = { + "uaa", + "uab", + "uda", +}; + +static const char * const vi_groups[] = { + "dta", + "dtb", + "dtc", + "dtd", + "dte", + "dtf", +}; + +static const char * const vi_sensor_clk_groups[] = { + "csus", +}; + +static const char * const xio_groups[] = { + "ld0", + "ld1", + "ld10", + "ld11", + "ld12", + "ld13", + "ld14", + "ld15", + "ld16", + "ld2", + "ld3", + "ld4", + "ld5", + "ld6", + "ld7", + "ld8", + "ld9", + "lhs", + "lsc0", + "lspi", + "lvs", +}; + +#define FUNCTION(fname) \ + { \ + .name = #fname, \ + .groups = fname##_groups, \ + .ngroups = ARRAY_SIZE(fname##_groups), \ + } + +static const struct tegra_function tegra20_functions[] = { + FUNCTION(ahb_clk), + FUNCTION(apb_clk), + FUNCTION(audio_sync), + FUNCTION(crt), + FUNCTION(dap1), + FUNCTION(dap2), + FUNCTION(dap3), + FUNCTION(dap4), + FUNCTION(dap5), + FUNCTION(displaya), + FUNCTION(displayb), + FUNCTION(emc_test0_dll), + FUNCTION(emc_test1_dll), + FUNCTION(gmi), + FUNCTION(gmi_int), + FUNCTION(hdmi), + FUNCTION(i2cp), + FUNCTION(i2c1), + FUNCTION(i2c2), + FUNCTION(i2c3), + FUNCTION(ide), + FUNCTION(irda), + FUNCTION(kbc), + FUNCTION(mio), + FUNCTION(mipi_hs), + FUNCTION(nand), + FUNCTION(osc), + FUNCTION(owr), + FUNCTION(pcie), + FUNCTION(plla_out), + FUNCTION(pllc_out1), + FUNCTION(pllm_out1), + FUNCTION(pllp_out2), + FUNCTION(pllp_out3), + FUNCTION(pllp_out4), + FUNCTION(pwm), + FUNCTION(pwr_intr), + FUNCTION(pwr_on), + FUNCTION(rsvd1), + FUNCTION(rsvd2), + FUNCTION(rsvd3), + FUNCTION(rsvd4), + FUNCTION(rtck), + FUNCTION(sdio1), + FUNCTION(sdio2), + FUNCTION(sdio3), + FUNCTION(sdio4), + FUNCTION(sflash), + FUNCTION(spdif), + FUNCTION(spi1), + FUNCTION(spi2), + FUNCTION(spi2_alt), + FUNCTION(spi3), + FUNCTION(spi4), + FUNCTION(trace), + FUNCTION(twc), + FUNCTION(uarta), + FUNCTION(uartb), + FUNCTION(uartc), + FUNCTION(uartd), + FUNCTION(uarte), + FUNCTION(ulpi), + FUNCTION(vi), + FUNCTION(vi_sensor_clk), + FUNCTION(xio), +}; + +#define TRISTATE_REG_A 0x14 +#define PIN_MUX_CTL_REG_A 0x80 +#define PULLUPDOWN_REG_A 0xa0 +#define PINGROUP_REG_A 0x868 + +/* Pin group with mux control, and typically tri-state and pull-up/down too */ +#define MUX_PG(pg_name, f0, f1, f2, f3, f_safe, \ + tri_r, tri_b, mux_r, mux_b, pupd_r, pupd_b) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + .funcs = { \ + TEGRA_MUX_ ## f0, \ + TEGRA_MUX_ ## f1, \ + TEGRA_MUX_ ## f2, \ + TEGRA_MUX_ ## f3, \ + }, \ + .func_safe = TEGRA_MUX_ ## f_safe, \ + .mux_reg = ((mux_r) - PIN_MUX_CTL_REG_A), \ + .mux_bank = 1, \ + .mux_bit = mux_b, \ + .pupd_reg = ((pupd_r) - PULLUPDOWN_REG_A), \ + .pupd_bank = 2, \ + .pupd_bit = pupd_b, \ + .tri_reg = ((tri_r) - TRISTATE_REG_A), \ + .tri_bank = 0, \ + .tri_bit = tri_b, \ + .einput_reg = -1, \ + .odrain_reg = -1, \ + .lock_reg = -1, \ + .ioreset_reg = -1, \ + .drv_reg = -1, \ + } + +/* Pin groups with only pull up and pull down control */ +#define PULL_PG(pg_name, pupd_r, pupd_b) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + .mux_reg = -1, \ + .pupd_reg = ((pupd_r) - PULLUPDOWN_REG_A), \ + .pupd_bank = 2, \ + .pupd_bit = pupd_b, \ + .tri_reg = -1, \ + .einput_reg = -1, \ + .odrain_reg = -1, \ + .lock_reg = -1, \ + .ioreset_reg = -1, \ + .drv_reg = -1, \ + } + +/* Pin groups for drive strength registers (configurable version) */ +#define DRV_PG_EXT(pg_name, r, hsm_b, schmitt_b, lpmd_b, \ + drvdn_b, drvup_b, \ + slwr_b, slwr_w, slwf_b, slwf_w) \ + { \ + .name = "drive_" #pg_name, \ + .pins = drive_##pg_name##_pins, \ + .npins = ARRAY_SIZE(drive_##pg_name##_pins), \ + .mux_reg = -1, \ + .pupd_reg = -1, \ + .tri_reg = -1, \ + .einput_reg = -1, \ + .odrain_reg = -1, \ + .lock_reg = -1, \ + .ioreset_reg = -1, \ + .drv_reg = ((r) - PINGROUP_REG_A), \ + .drv_bank = 3, \ + .hsm_bit = hsm_b, \ + .schmitt_bit = schmitt_b, \ + .lpmd_bit = lpmd_b, \ + .drvdn_bit = drvdn_b, \ + .drvdn_width = 5, \ + .drvup_bit = drvup_b, \ + .drvup_width = 5, \ + .slwr_bit = slwr_b, \ + .slwr_width = slwr_w, \ + .slwf_bit = slwf_b, \ + .slwf_width = slwf_w, \ + } + +/* Pin groups for drive strength registers (simple version) */ +#define DRV_PG(pg_name, r) \ + DRV_PG_EXT(pg_name, r, 2, 3, 4, 12, 20, 28, 2, 30, 2) + +static const struct tegra_pingroup tegra20_groups[] = { + /* name, f0, f1, f2, f3, f_safe, tri r/b, mux r/b, pupd r/b */ + MUX_PG(ata, IDE, NAND, GMI, RSVD4, IDE, 0x14, 0, 0x80, 24, 0xa0, 0), + MUX_PG(atb, IDE, NAND, GMI, SDIO4, IDE, 0x14, 1, 0x80, 16, 0xa0, 2), + MUX_PG(atc, IDE, NAND, GMI, SDIO4, IDE, 0x14, 2, 0x80, 22, 0xa0, 4), + MUX_PG(atd, IDE, NAND, GMI, SDIO4, IDE, 0x14, 3, 0x80, 20, 0xa0, 6), + MUX_PG(ate, IDE, NAND, GMI, RSVD4, IDE, 0x18, 25, 0x80, 12, 0xa0, 8), + MUX_PG(cdev1, OSC, PLLA_OUT, PLLM_OUT1, AUDIO_SYNC, OSC, 0x14, 4, 0x88, 2, 0xa8, 0), + MUX_PG(cdev2, OSC, AHB_CLK, APB_CLK, PLLP_OUT4, OSC, 0x14, 5, 0x88, 4, 0xa8, 2), + MUX_PG(crtp, CRT, RSVD2, RSVD3, RSVD4, RSVD2, 0x20, 14, 0x98, 20, 0xa4, 24), + MUX_PG(csus, PLLC_OUT1, PLLP_OUT2, PLLP_OUT3, VI_SENSOR_CLK, PLLC_OUT1, 0x14, 6, 0x88, 6, 0xac, 24), + MUX_PG(dap1, DAP1, RSVD2, GMI, SDIO2, DAP1, 0x14, 7, 0x88, 20, 0xa0, 10), + MUX_PG(dap2, DAP2, TWC, RSVD3, GMI, DAP2, 0x14, 8, 0x88, 22, 0xa0, 12), + MUX_PG(dap3, DAP3, RSVD2, RSVD3, RSVD4, DAP3, 0x14, 9, 0x88, 24, 0xa0, 14), + MUX_PG(dap4, DAP4, RSVD2, GMI, RSVD4, DAP4, 0x14, 10, 0x88, 26, 0xa0, 16), + MUX_PG(ddc, I2C2, RSVD2, RSVD3, RSVD4, RSVD4, 0x18, 31, 0x88, 0, 0xb0, 28), + MUX_PG(dta, RSVD1, SDIO2, VI, RSVD4, RSVD4, 0x14, 11, 0x84, 20, 0xa0, 18), + MUX_PG(dtb, RSVD1, RSVD2, VI, SPI1, RSVD1, 0x14, 12, 0x84, 22, 0xa0, 20), + MUX_PG(dtc, RSVD1, RSVD2, VI, RSVD4, RSVD1, 0x14, 13, 0x84, 26, 0xa0, 22), + MUX_PG(dtd, RSVD1, SDIO2, VI, RSVD4, RSVD1, 0x14, 14, 0x84, 28, 0xa0, 24), + MUX_PG(dte, RSVD1, RSVD2, VI, SPI1, RSVD1, 0x14, 15, 0x84, 30, 0xa0, 26), + MUX_PG(dtf, I2C3, RSVD2, VI, RSVD4, RSVD4, 0x20, 12, 0x98, 30, 0xa0, 28), + MUX_PG(gma, UARTE, SPI3, GMI, SDIO4, SPI3, 0x14, 28, 0x84, 0, 0xb0, 20), + MUX_PG(gmb, IDE, NAND, GMI, GMI_INT, GMI, 0x18, 29, 0x88, 28, 0xb0, 22), + MUX_PG(gmc, UARTD, SPI4, GMI, SFLASH, SPI4, 0x14, 29, 0x84, 2, 0xb0, 24), + MUX_PG(gmd, RSVD1, NAND, GMI, SFLASH, GMI, 0x18, 30, 0x88, 30, 0xb0, 26), + MUX_PG(gme, RSVD1, DAP5, GMI, SDIO4, GMI, 0x18, 0, 0x8c, 0, 0xa8, 24), + MUX_PG(gpu, PWM, UARTA, GMI, RSVD4, RSVD4, 0x14, 16, 0x8c, 4, 0xa4, 20), + MUX_PG(gpu7, RTCK, RSVD2, RSVD3, RSVD4, RTCK, 0x20, 11, 0x98, 28, 0xa4, 6), + MUX_PG(gpv, PCIE, RSVD2, RSVD3, RSVD4, PCIE, 0x14, 17, 0x8c, 2, 0xa0, 30), + MUX_PG(hdint, HDMI, RSVD2, RSVD3, RSVD4, HDMI, 0x1c, 23, 0x84, 4, -1, -1), + MUX_PG(i2cp, I2CP, RSVD2, RSVD3, RSVD4, RSVD4, 0x14, 18, 0x88, 8, 0xa4, 2), + MUX_PG(irrx, UARTA, UARTB, GMI, SPI4, UARTB, 0x14, 20, 0x88, 18, 0xa8, 22), + MUX_PG(irtx, UARTA, UARTB, GMI, SPI4, UARTB, 0x14, 19, 0x88, 16, 0xa8, 20), + MUX_PG(kbca, KBC, NAND, SDIO2, EMC_TEST0_DLL, KBC, 0x14, 22, 0x88, 10, 0xa4, 8), + MUX_PG(kbcb, KBC, NAND, SDIO2, MIO, KBC, 0x14, 21, 0x88, 12, 0xa4, 10), + MUX_PG(kbcc, KBC, NAND, TRACE, EMC_TEST1_DLL, KBC, 0x18, 26, 0x88, 14, 0xa4, 12), + MUX_PG(kbcd, KBC, NAND, SDIO2, MIO, KBC, 0x20, 10, 0x98, 26, 0xa4, 14), + MUX_PG(kbce, KBC, NAND, OWR, RSVD4, KBC, 0x14, 26, 0x80, 28, 0xb0, 2), + MUX_PG(kbcf, KBC, NAND, TRACE, MIO, KBC, 0x14, 27, 0x80, 26, 0xb0, 0), + MUX_PG(lcsn, DISPLAYA, DISPLAYB, SPI3, RSVD4, RSVD4, 0x1c, 31, 0x90, 12, -1, -1), + MUX_PG(ld0, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 0, 0x94, 0, -1, -1), + MUX_PG(ld1, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 1, 0x94, 2, -1, -1), + MUX_PG(ld2, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 2, 0x94, 4, -1, -1), + MUX_PG(ld3, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 3, 0x94, 6, -1, -1), + MUX_PG(ld4, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 4, 0x94, 8, -1, -1), + MUX_PG(ld5, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 5, 0x94, 10, -1, -1), + MUX_PG(ld6, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 6, 0x94, 12, -1, -1), + MUX_PG(ld7, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 7, 0x94, 14, -1, -1), + MUX_PG(ld8, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 8, 0x94, 16, -1, -1), + MUX_PG(ld9, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 9, 0x94, 18, -1, -1), + MUX_PG(ld10, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 10, 0x94, 20, -1, -1), + MUX_PG(ld11, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 11, 0x94, 22, -1, -1), + MUX_PG(ld12, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 12, 0x94, 24, -1, -1), + MUX_PG(ld13, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 13, 0x94, 26, -1, -1), + MUX_PG(ld14, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 14, 0x94, 28, -1, -1), + MUX_PG(ld15, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 15, 0x94, 30, -1, -1), + MUX_PG(ld16, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 16, 0x98, 0, -1, -1), + MUX_PG(ld17, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x1c, 17, 0x98, 2, -1, -1), + MUX_PG(ldc, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x1c, 30, 0x90, 14, -1, -1), + MUX_PG(ldi, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x20, 6, 0x98, 16, -1, -1), + MUX_PG(lhp0, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x1c, 18, 0x98, 10, -1, -1), + MUX_PG(lhp1, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x1c, 19, 0x98, 4, -1, -1), + MUX_PG(lhp2, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x1c, 20, 0x98, 6, -1, -1), + MUX_PG(lhs, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x20, 7, 0x90, 22, -1, -1), + MUX_PG(lm0, DISPLAYA, DISPLAYB, SPI3, RSVD4, RSVD4, 0x1c, 24, 0x90, 26, -1, -1), + MUX_PG(lm1, DISPLAYA, DISPLAYB, RSVD3, CRT, RSVD3, 0x1c, 25, 0x90, 28, -1, -1), + MUX_PG(lpp, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x20, 8, 0x98, 14, -1, -1), + MUX_PG(lpw0, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x20, 3, 0x90, 0, -1, -1), + MUX_PG(lpw1, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x20, 4, 0x90, 2, -1, -1), + MUX_PG(lpw2, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x20, 5, 0x90, 4, -1, -1), + MUX_PG(lsc0, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 27, 0x90, 18, -1, -1), + MUX_PG(lsc1, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x1c, 28, 0x90, 20, -1, -1), + MUX_PG(lsck, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x1c, 29, 0x90, 16, -1, -1), + MUX_PG(lsda, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x20, 1, 0x90, 8, -1, -1), + MUX_PG(lsdi, DISPLAYA, DISPLAYB, SPI3, RSVD4, DISPLAYA, 0x20, 2, 0x90, 6, -1, -1), + MUX_PG(lspi, DISPLAYA, DISPLAYB, XIO, HDMI, DISPLAYA, 0x20, 0, 0x90, 10, -1, -1), + MUX_PG(lvp0, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x1c, 21, 0x90, 30, -1, -1), + MUX_PG(lvp1, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x1c, 22, 0x98, 8, -1, -1), + MUX_PG(lvs, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 26, 0x90, 24, -1, -1), + MUX_PG(owc, OWR, RSVD2, RSVD3, RSVD4, OWR, 0x14, 31, 0x84, 8, 0xb0, 30), + MUX_PG(pmc, PWR_ON, PWR_INTR, RSVD3, RSVD4, PWR_ON, 0x14, 23, 0x98, 18, -1, -1), + MUX_PG(pta, I2C2, HDMI, GMI, RSVD4, RSVD4, 0x14, 24, 0x98, 22, 0xa4, 4), + MUX_PG(rm, I2C1, RSVD2, RSVD3, RSVD4, RSVD4, 0x14, 25, 0x80, 14, 0xa4, 0), + MUX_PG(sdb, UARTA, PWM, SDIO3, SPI2, PWM, 0x20, 15, 0x8c, 10, -1, -1), + MUX_PG(sdc, PWM, TWC, SDIO3, SPI3, TWC, 0x18, 1, 0x8c, 12, 0xac, 28), + MUX_PG(sdd, UARTA, PWM, SDIO3, SPI3, PWM, 0x18, 2, 0x8c, 14, 0xac, 30), + MUX_PG(sdio1, SDIO1, RSVD2, UARTE, UARTA, RSVD2, 0x14, 30, 0x80, 30, 0xb0, 18), + MUX_PG(slxa, PCIE, SPI4, SDIO3, SPI2, PCIE, 0x18, 3, 0x84, 6, 0xa4, 22), + MUX_PG(slxc, SPDIF, SPI4, SDIO3, SPI2, SPI4, 0x18, 5, 0x84, 10, 0xa4, 26), + MUX_PG(slxd, SPDIF, SPI4, SDIO3, SPI2, SPI4, 0x18, 6, 0x84, 12, 0xa4, 28), + MUX_PG(slxk, PCIE, SPI4, SDIO3, SPI2, PCIE, 0x18, 7, 0x84, 14, 0xa4, 30), + MUX_PG(spdi, SPDIF, RSVD2, I2C1, SDIO2, RSVD2, 0x18, 8, 0x8c, 8, 0xa4, 16), + MUX_PG(spdo, SPDIF, RSVD2, I2C1, SDIO2, RSVD2, 0x18, 9, 0x8c, 6, 0xa4, 18), + MUX_PG(spia, SPI1, SPI2, SPI3, GMI, GMI, 0x18, 10, 0x8c, 30, 0xa8, 4), + MUX_PG(spib, SPI1, SPI2, SPI3, GMI, GMI, 0x18, 11, 0x8c, 28, 0xa8, 6), + MUX_PG(spic, SPI1, SPI2, SPI3, GMI, GMI, 0x18, 12, 0x8c, 26, 0xa8, 8), + MUX_PG(spid, SPI2, SPI1, SPI2_ALT, GMI, GMI, 0x18, 13, 0x8c, 24, 0xa8, 10), + MUX_PG(spie, SPI2, SPI1, SPI2_ALT, GMI, GMI, 0x18, 14, 0x8c, 22, 0xa8, 12), + MUX_PG(spif, SPI3, SPI1, SPI2, RSVD4, RSVD4, 0x18, 15, 0x8c, 20, 0xa8, 14), + MUX_PG(spig, SPI3, SPI2, SPI2_ALT, I2C1, SPI2_ALT, 0x18, 16, 0x8c, 18, 0xa8, 16), + MUX_PG(spih, SPI3, SPI2, SPI2_ALT, I2C1, SPI2_ALT, 0x18, 17, 0x8c, 16, 0xa8, 18), + MUX_PG(uaa, SPI3, MIPI_HS, UARTA, ULPI, MIPI_HS, 0x18, 18, 0x80, 0, 0xac, 0), + MUX_PG(uab, SPI2, MIPI_HS, UARTA, ULPI, MIPI_HS, 0x18, 19, 0x80, 2, 0xac, 2), + MUX_PG(uac, OWR, RSVD2, RSVD3, RSVD4, RSVD4, 0x18, 20, 0x80, 4, 0xac, 4), + MUX_PG(uad, IRDA, SPDIF, UARTA, SPI4, SPDIF, 0x18, 21, 0x80, 6, 0xac, 6), + MUX_PG(uca, UARTC, RSVD2, GMI, RSVD4, RSVD4, 0x18, 22, 0x84, 16, 0xac, 8), + MUX_PG(ucb, UARTC, PWM, GMI, RSVD4, RSVD4, 0x18, 23, 0x84, 18, 0xac, 10), + MUX_PG(uda, SPI1, RSVD2, UARTD, ULPI, RSVD2, 0x20, 13, 0x80, 8, 0xb0, 16), + /* pg_name, pupd_r/b */ + PULL_PG(ck32, 0xb0, 14), + PULL_PG(ddrc, 0xac, 26), + PULL_PG(pmca, 0xb0, 4), + PULL_PG(pmcb, 0xb0, 6), + PULL_PG(pmcc, 0xb0, 8), + PULL_PG(pmcd, 0xb0, 10), + PULL_PG(pmce, 0xb0, 12), + PULL_PG(xm2c, 0xa8, 30), + PULL_PG(xm2d, 0xa8, 28), + PULL_PG(ls, 0xac, 20), + PULL_PG(lc, 0xac, 22), + PULL_PG(ld17_0, 0xac, 12), + PULL_PG(ld19_18, 0xac, 14), + PULL_PG(ld21_20, 0xac, 16), + PULL_PG(ld23_22, 0xac, 18), + /* pg_name, r */ + DRV_PG(ao1, 0x868), + DRV_PG(ao2, 0x86c), + DRV_PG(at1, 0x870), + DRV_PG(at2, 0x874), + DRV_PG(cdev1, 0x878), + DRV_PG(cdev2, 0x87c), + DRV_PG(csus, 0x880), + DRV_PG(dap1, 0x884), + DRV_PG(dap2, 0x888), + DRV_PG(dap3, 0x88c), + DRV_PG(dap4, 0x890), + DRV_PG(dbg, 0x894), + DRV_PG(lcd1, 0x898), + DRV_PG(lcd2, 0x89c), + DRV_PG(sdmmc2, 0x8a0), + DRV_PG(sdmmc3, 0x8a4), + DRV_PG(spi, 0x8a8), + DRV_PG(uaa, 0x8ac), + DRV_PG(uab, 0x8b0), + DRV_PG(uart2, 0x8b4), + DRV_PG(uart3, 0x8b8), + DRV_PG(vi1, 0x8bc), + DRV_PG(vi2, 0x8c0), + /* pg_name, r, hsm_b, schmitt_b, lpmd_b, drvdn_b, drvup_b, slwr_b, slwr_w, slwf_b, slwf_w */ + DRV_PG_EXT(xm2a, 0x8c4, -1, -1, 4, 14, 19, 24, 4, 28, 4), + DRV_PG_EXT(xm2c, 0x8c8, -1, 3, -1, 14, 19, 24, 4, 28, 4), + DRV_PG_EXT(xm2d, 0x8cc, -1, 3, -1, 14, 19, 24, 4, 28, 4), + DRV_PG_EXT(xm2clk, 0x8d0, -1, -1, -1, 14, 19, 24, 4, 28, 4), + /* pg_name, r */ + DRV_PG(sdio1, 0x8e0), + DRV_PG(crt, 0x8ec), + DRV_PG(ddc, 0x8f0), + DRV_PG(gma, 0x8f4), + DRV_PG(gmb, 0x8f8), + DRV_PG(gmc, 0x8fc), + DRV_PG(gmd, 0x900), + DRV_PG(gme, 0x904), + DRV_PG(owr, 0x908), + DRV_PG(uda, 0x90c), +}; + +static const struct tegra_pinctrl_soc_data tegra20_pinctrl = { + .ngpios = NUM_GPIOS, + .pins = tegra20_pins, + .npins = ARRAY_SIZE(tegra20_pins), + .functions = tegra20_functions, + .nfunctions = ARRAY_SIZE(tegra20_functions), + .groups = tegra20_groups, + .ngroups = ARRAY_SIZE(tegra20_groups), +}; + +void __devinit tegra20_pinctrl_init(const struct tegra_pinctrl_soc_data **soc) +{ + *soc = &tegra20_pinctrl; +} diff --git a/drivers/pinctrl/pinctrl-tegra30.c b/drivers/pinctrl/pinctrl-tegra30.c new file mode 100644 index 000000000000..4d7571d4a431 --- /dev/null +++ b/drivers/pinctrl/pinctrl-tegra30.c @@ -0,0 +1,3726 @@ +/* + * Pinctrl data for the NVIDIA Tegra30 pinmux + * + * Copyright (c) 2011, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/platform_device.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> + +#include "pinctrl-tegra.h" + +/* + * Most pins affected by the pinmux can also be GPIOs. Define these first. + * These must match how the GPIO driver names/numbers its pins. + */ +#define _GPIO(offset) (offset) + +#define TEGRA_PIN_CLK_32K_OUT_PA0 _GPIO(0) +#define TEGRA_PIN_UART3_CTS_N_PA1 _GPIO(1) +#define TEGRA_PIN_DAP2_FS_PA2 _GPIO(2) +#define TEGRA_PIN_DAP2_SCLK_PA3 _GPIO(3) +#define TEGRA_PIN_DAP2_DIN_PA4 _GPIO(4) +#define TEGRA_PIN_DAP2_DOUT_PA5 _GPIO(5) +#define TEGRA_PIN_SDMMC3_CLK_PA6 _GPIO(6) +#define TEGRA_PIN_SDMMC3_CMD_PA7 _GPIO(7) +#define TEGRA_PIN_GMI_A17_PB0 _GPIO(8) +#define TEGRA_PIN_GMI_A18_PB1 _GPIO(9) +#define TEGRA_PIN_LCD_PWR0_PB2 _GPIO(10) +#define TEGRA_PIN_LCD_PCLK_PB3 _GPIO(11) +#define TEGRA_PIN_SDMMC3_DAT3_PB4 _GPIO(12) +#define TEGRA_PIN_SDMMC3_DAT2_PB5 _GPIO(13) +#define TEGRA_PIN_SDMMC3_DAT1_PB6 _GPIO(14) +#define TEGRA_PIN_SDMMC3_DAT0_PB7 _GPIO(15) +#define TEGRA_PIN_UART3_RTS_N_PC0 _GPIO(16) +#define TEGRA_PIN_LCD_PWR1_PC1 _GPIO(17) +#define TEGRA_PIN_UART2_TXD_PC2 _GPIO(18) +#define TEGRA_PIN_UART2_RXD_PC3 _GPIO(19) +#define TEGRA_PIN_GEN1_I2C_SCL_PC4 _GPIO(20) +#define TEGRA_PIN_GEN1_I2C_SDA_PC5 _GPIO(21) +#define TEGRA_PIN_LCD_PWR2_PC6 _GPIO(22) +#define TEGRA_PIN_GMI_WP_N_PC7 _GPIO(23) +#define TEGRA_PIN_SDMMC3_DAT5_PD0 _GPIO(24) +#define TEGRA_PIN_SDMMC3_DAT4_PD1 _GPIO(25) +#define TEGRA_PIN_LCD_DC1_PD2 _GPIO(26) +#define TEGRA_PIN_SDMMC3_DAT6_PD3 _GPIO(27) +#define TEGRA_PIN_SDMMC3_DAT7_PD4 _GPIO(28) +#define TEGRA_PIN_VI_D1_PD5 _GPIO(29) +#define TEGRA_PIN_VI_VSYNC_PD6 _GPIO(30) +#define TEGRA_PIN_VI_HSYNC_PD7 _GPIO(31) +#define TEGRA_PIN_LCD_D0_PE0 _GPIO(32) +#define TEGRA_PIN_LCD_D1_PE1 _GPIO(33) +#define TEGRA_PIN_LCD_D2_PE2 _GPIO(34) +#define TEGRA_PIN_LCD_D3_PE3 _GPIO(35) +#define TEGRA_PIN_LCD_D4_PE4 _GPIO(36) +#define TEGRA_PIN_LCD_D5_PE5 _GPIO(37) +#define TEGRA_PIN_LCD_D6_PE6 _GPIO(38) +#define TEGRA_PIN_LCD_D7_PE7 _GPIO(39) +#define TEGRA_PIN_LCD_D8_PF0 _GPIO(40) +#define TEGRA_PIN_LCD_D9_PF1 _GPIO(41) +#define TEGRA_PIN_LCD_D10_PF2 _GPIO(42) +#define TEGRA_PIN_LCD_D11_PF3 _GPIO(43) +#define TEGRA_PIN_LCD_D12_PF4 _GPIO(44) +#define TEGRA_PIN_LCD_D13_PF5 _GPIO(45) +#define TEGRA_PIN_LCD_D14_PF6 _GPIO(46) +#define TEGRA_PIN_LCD_D15_PF7 _GPIO(47) +#define TEGRA_PIN_GMI_AD0_PG0 _GPIO(48) +#define TEGRA_PIN_GMI_AD1_PG1 _GPIO(49) +#define TEGRA_PIN_GMI_AD2_PG2 _GPIO(50) +#define TEGRA_PIN_GMI_AD3_PG3 _GPIO(51) +#define TEGRA_PIN_GMI_AD4_PG4 _GPIO(52) +#define TEGRA_PIN_GMI_AD5_PG5 _GPIO(53) +#define TEGRA_PIN_GMI_AD6_PG6 _GPIO(54) +#define TEGRA_PIN_GMI_AD7_PG7 _GPIO(55) +#define TEGRA_PIN_GMI_AD8_PH0 _GPIO(56) +#define TEGRA_PIN_GMI_AD9_PH1 _GPIO(57) +#define TEGRA_PIN_GMI_AD10_PH2 _GPIO(58) +#define TEGRA_PIN_GMI_AD11_PH3 _GPIO(59) +#define TEGRA_PIN_GMI_AD12_PH4 _GPIO(60) +#define TEGRA_PIN_GMI_AD13_PH5 _GPIO(61) +#define TEGRA_PIN_GMI_AD14_PH6 _GPIO(62) +#define TEGRA_PIN_GMI_AD15_PH7 _GPIO(63) +#define TEGRA_PIN_GMI_WR_N_PI0 _GPIO(64) +#define TEGRA_PIN_GMI_OE_N_PI1 _GPIO(65) +#define TEGRA_PIN_GMI_DQS_PI2 _GPIO(66) +#define TEGRA_PIN_GMI_CS6_N_PI3 _GPIO(67) +#define TEGRA_PIN_GMI_RST_N_PI4 _GPIO(68) +#define TEGRA_PIN_GMI_IORDY_PI5 _GPIO(69) +#define TEGRA_PIN_GMI_CS7_N_PI6 _GPIO(70) +#define TEGRA_PIN_GMI_WAIT_PI7 _GPIO(71) +#define TEGRA_PIN_GMI_CS0_N_PJ0 _GPIO(72) +#define TEGRA_PIN_LCD_DE_PJ1 _GPIO(73) +#define TEGRA_PIN_GMI_CS1_N_PJ2 _GPIO(74) +#define TEGRA_PIN_LCD_HSYNC_PJ3 _GPIO(75) +#define TEGRA_PIN_LCD_VSYNC_PJ4 _GPIO(76) +#define TEGRA_PIN_UART2_CTS_N_PJ5 _GPIO(77) +#define TEGRA_PIN_UART2_RTS_N_PJ6 _GPIO(78) +#define TEGRA_PIN_GMI_A16_PJ7 _GPIO(79) +#define TEGRA_PIN_GMI_ADV_N_PK0 _GPIO(80) +#define TEGRA_PIN_GMI_CLK_PK1 _GPIO(81) +#define TEGRA_PIN_GMI_CS4_N_PK2 _GPIO(82) +#define TEGRA_PIN_GMI_CS2_N_PK3 _GPIO(83) +#define TEGRA_PIN_GMI_CS3_N_PK4 _GPIO(84) +#define TEGRA_PIN_SPDIF_OUT_PK5 _GPIO(85) +#define TEGRA_PIN_SPDIF_IN_PK6 _GPIO(86) +#define TEGRA_PIN_GMI_A19_PK7 _GPIO(87) +#define TEGRA_PIN_VI_D2_PL0 _GPIO(88) +#define TEGRA_PIN_VI_D3_PL1 _GPIO(89) +#define TEGRA_PIN_VI_D4_PL2 _GPIO(90) +#define TEGRA_PIN_VI_D5_PL3 _GPIO(91) +#define TEGRA_PIN_VI_D6_PL4 _GPIO(92) +#define TEGRA_PIN_VI_D7_PL5 _GPIO(93) +#define TEGRA_PIN_VI_D8_PL6 _GPIO(94) +#define TEGRA_PIN_VI_D9_PL7 _GPIO(95) +#define TEGRA_PIN_LCD_D16_PM0 _GPIO(96) +#define TEGRA_PIN_LCD_D17_PM1 _GPIO(97) +#define TEGRA_PIN_LCD_D18_PM2 _GPIO(98) +#define TEGRA_PIN_LCD_D19_PM3 _GPIO(99) +#define TEGRA_PIN_LCD_D20_PM4 _GPIO(100) +#define TEGRA_PIN_LCD_D21_PM5 _GPIO(101) +#define TEGRA_PIN_LCD_D22_PM6 _GPIO(102) +#define TEGRA_PIN_LCD_D23_PM7 _GPIO(103) +#define TEGRA_PIN_DAP1_FS_PN0 _GPIO(104) +#define TEGRA_PIN_DAP1_DIN_PN1 _GPIO(105) +#define TEGRA_PIN_DAP1_DOUT_PN2 _GPIO(106) +#define TEGRA_PIN_DAP1_SCLK_PN3 _GPIO(107) +#define TEGRA_PIN_LCD_CS0_N_PN4 _GPIO(108) +#define TEGRA_PIN_LCD_SDOUT_PN5 _GPIO(109) +#define TEGRA_PIN_LCD_DC0_PN6 _GPIO(110) +#define TEGRA_PIN_HDMI_INT_PN7 _GPIO(111) +#define TEGRA_PIN_ULPI_DATA7_PO0 _GPIO(112) +#define TEGRA_PIN_ULPI_DATA0_PO1 _GPIO(113) +#define TEGRA_PIN_ULPI_DATA1_PO2 _GPIO(114) +#define TEGRA_PIN_ULPI_DATA2_PO3 _GPIO(115) +#define TEGRA_PIN_ULPI_DATA3_PO4 _GPIO(116) +#define TEGRA_PIN_ULPI_DATA4_PO5 _GPIO(117) +#define TEGRA_PIN_ULPI_DATA5_PO6 _GPIO(118) +#define TEGRA_PIN_ULPI_DATA6_PO7 _GPIO(119) +#define TEGRA_PIN_DAP3_FS_PP0 _GPIO(120) +#define TEGRA_PIN_DAP3_DIN_PP1 _GPIO(121) +#define TEGRA_PIN_DAP3_DOUT_PP2 _GPIO(122) +#define TEGRA_PIN_DAP3_SCLK_PP3 _GPIO(123) +#define TEGRA_PIN_DAP4_FS_PP4 _GPIO(124) +#define TEGRA_PIN_DAP4_DIN_PP5 _GPIO(125) +#define TEGRA_PIN_DAP4_DOUT_PP6 _GPIO(126) +#define TEGRA_PIN_DAP4_SCLK_PP7 _GPIO(127) +#define TEGRA_PIN_KB_COL0_PQ0 _GPIO(128) +#define TEGRA_PIN_KB_COL1_PQ1 _GPIO(129) +#define TEGRA_PIN_KB_COL2_PQ2 _GPIO(130) +#define TEGRA_PIN_KB_COL3_PQ3 _GPIO(131) +#define TEGRA_PIN_KB_COL4_PQ4 _GPIO(132) +#define TEGRA_PIN_KB_COL5_PQ5 _GPIO(133) +#define TEGRA_PIN_KB_COL6_PQ6 _GPIO(134) +#define TEGRA_PIN_KB_COL7_PQ7 _GPIO(135) +#define TEGRA_PIN_KB_ROW0_PR0 _GPIO(136) +#define TEGRA_PIN_KB_ROW1_PR1 _GPIO(137) +#define TEGRA_PIN_KB_ROW2_PR2 _GPIO(138) +#define TEGRA_PIN_KB_ROW3_PR3 _GPIO(139) +#define TEGRA_PIN_KB_ROW4_PR4 _GPIO(140) +#define TEGRA_PIN_KB_ROW5_PR5 _GPIO(141) +#define TEGRA_PIN_KB_ROW6_PR6 _GPIO(142) +#define TEGRA_PIN_KB_ROW7_PR7 _GPIO(143) +#define TEGRA_PIN_KB_ROW8_PS0 _GPIO(144) +#define TEGRA_PIN_KB_ROW9_PS1 _GPIO(145) +#define TEGRA_PIN_KB_ROW10_PS2 _GPIO(146) +#define TEGRA_PIN_KB_ROW11_PS3 _GPIO(147) +#define TEGRA_PIN_KB_ROW12_PS4 _GPIO(148) +#define TEGRA_PIN_KB_ROW13_PS5 _GPIO(149) +#define TEGRA_PIN_KB_ROW14_PS6 _GPIO(150) +#define TEGRA_PIN_KB_ROW15_PS7 _GPIO(151) +#define TEGRA_PIN_VI_PCLK_PT0 _GPIO(152) +#define TEGRA_PIN_VI_MCLK_PT1 _GPIO(153) +#define TEGRA_PIN_VI_D10_PT2 _GPIO(154) +#define TEGRA_PIN_VI_D11_PT3 _GPIO(155) +#define TEGRA_PIN_VI_D0_PT4 _GPIO(156) +#define TEGRA_PIN_GEN2_I2C_SCL_PT5 _GPIO(157) +#define TEGRA_PIN_GEN2_I2C_SDA_PT6 _GPIO(158) +#define TEGRA_PIN_SDMMC4_CMD_PT7 _GPIO(159) +#define TEGRA_PIN_PU0 _GPIO(160) +#define TEGRA_PIN_PU1 _GPIO(161) +#define TEGRA_PIN_PU2 _GPIO(162) +#define TEGRA_PIN_PU3 _GPIO(163) +#define TEGRA_PIN_PU4 _GPIO(164) +#define TEGRA_PIN_PU5 _GPIO(165) +#define TEGRA_PIN_PU6 _GPIO(166) +#define TEGRA_PIN_JTAG_RTCK_PU7 _GPIO(167) +#define TEGRA_PIN_PV0 _GPIO(168) +#define TEGRA_PIN_PV1 _GPIO(169) +#define TEGRA_PIN_PV2 _GPIO(170) +#define TEGRA_PIN_PV3 _GPIO(171) +#define TEGRA_PIN_DDC_SCL_PV4 _GPIO(172) +#define TEGRA_PIN_DDC_SDA_PV5 _GPIO(173) +#define TEGRA_PIN_CRT_HSYNC_PV6 _GPIO(174) +#define TEGRA_PIN_CRT_VSYNC_PV7 _GPIO(175) +#define TEGRA_PIN_LCD_CS1_N_PW0 _GPIO(176) +#define TEGRA_PIN_LCD_M1_PW1 _GPIO(177) +#define TEGRA_PIN_SPI2_CS1_N_PW2 _GPIO(178) +#define TEGRA_PIN_SPI2_CS2_N_PW3 _GPIO(179) +#define TEGRA_PIN_CLK1_OUT_PW4 _GPIO(180) +#define TEGRA_PIN_CLK2_OUT_PW5 _GPIO(181) +#define TEGRA_PIN_UART3_TXD_PW6 _GPIO(182) +#define TEGRA_PIN_UART3_RXD_PW7 _GPIO(183) +#define TEGRA_PIN_SPI2_MOSI_PX0 _GPIO(184) +#define TEGRA_PIN_SPI2_MISO_PX1 _GPIO(185) +#define TEGRA_PIN_SPI2_SCK_PX2 _GPIO(186) +#define TEGRA_PIN_SPI2_CS0_N_PX3 _GPIO(187) +#define TEGRA_PIN_SPI1_MOSI_PX4 _GPIO(188) +#define TEGRA_PIN_SPI1_SCK_PX5 _GPIO(189) +#define TEGRA_PIN_SPI1_CS0_N_PX6 _GPIO(190) +#define TEGRA_PIN_SPI1_MISO_PX7 _GPIO(191) +#define TEGRA_PIN_ULPI_CLK_PY0 _GPIO(192) +#define TEGRA_PIN_ULPI_DIR_PY1 _GPIO(193) +#define TEGRA_PIN_ULPI_NXT_PY2 _GPIO(194) +#define TEGRA_PIN_ULPI_STP_PY3 _GPIO(195) +#define TEGRA_PIN_SDMMC1_DAT3_PY4 _GPIO(196) +#define TEGRA_PIN_SDMMC1_DAT2_PY5 _GPIO(197) +#define TEGRA_PIN_SDMMC1_DAT1_PY6 _GPIO(198) +#define TEGRA_PIN_SDMMC1_DAT0_PY7 _GPIO(199) +#define TEGRA_PIN_SDMMC1_CLK_PZ0 _GPIO(200) +#define TEGRA_PIN_SDMMC1_CMD_PZ1 _GPIO(201) +#define TEGRA_PIN_LCD_SDIN_PZ2 _GPIO(202) +#define TEGRA_PIN_LCD_WR_N_PZ3 _GPIO(203) +#define TEGRA_PIN_LCD_SCK_PZ4 _GPIO(204) +#define TEGRA_PIN_SYS_CLK_REQ_PZ5 _GPIO(205) +#define TEGRA_PIN_PWR_I2C_SCL_PZ6 _GPIO(206) +#define TEGRA_PIN_PWR_I2C_SDA_PZ7 _GPIO(207) +#define TEGRA_PIN_SDMMC4_DAT0_PAA0 _GPIO(208) +#define TEGRA_PIN_SDMMC4_DAT1_PAA1 _GPIO(209) +#define TEGRA_PIN_SDMMC4_DAT2_PAA2 _GPIO(210) +#define TEGRA_PIN_SDMMC4_DAT3_PAA3 _GPIO(211) +#define TEGRA_PIN_SDMMC4_DAT4_PAA4 _GPIO(212) +#define TEGRA_PIN_SDMMC4_DAT5_PAA5 _GPIO(213) +#define TEGRA_PIN_SDMMC4_DAT6_PAA6 _GPIO(214) +#define TEGRA_PIN_SDMMC4_DAT7_PAA7 _GPIO(215) +#define TEGRA_PIN_PBB0 _GPIO(216) +#define TEGRA_PIN_CAM_I2C_SCL_PBB1 _GPIO(217) +#define TEGRA_PIN_CAM_I2C_SDA_PBB2 _GPIO(218) +#define TEGRA_PIN_PBB3 _GPIO(219) +#define TEGRA_PIN_PBB4 _GPIO(220) +#define TEGRA_PIN_PBB5 _GPIO(221) +#define TEGRA_PIN_PBB6 _GPIO(222) +#define TEGRA_PIN_PBB7 _GPIO(223) +#define TEGRA_PIN_CAM_MCLK_PCC0 _GPIO(224) +#define TEGRA_PIN_PCC1 _GPIO(225) +#define TEGRA_PIN_PCC2 _GPIO(226) +#define TEGRA_PIN_SDMMC4_RST_N_PCC3 _GPIO(227) +#define TEGRA_PIN_SDMMC4_CLK_PCC4 _GPIO(228) +#define TEGRA_PIN_CLK2_REQ_PCC5 _GPIO(229) +#define TEGRA_PIN_PEX_L2_RST_N_PCC6 _GPIO(230) +#define TEGRA_PIN_PEX_L2_CLKREQ_N_PCC7 _GPIO(231) +#define TEGRA_PIN_PEX_L0_PRSNT_N_PDD0 _GPIO(232) +#define TEGRA_PIN_PEX_L0_RST_N_PDD1 _GPIO(233) +#define TEGRA_PIN_PEX_L0_CLKREQ_N_PDD2 _GPIO(234) +#define TEGRA_PIN_PEX_WAKE_N_PDD3 _GPIO(235) +#define TEGRA_PIN_PEX_L1_PRSNT_N_PDD4 _GPIO(236) +#define TEGRA_PIN_PEX_L1_RST_N_PDD5 _GPIO(237) +#define TEGRA_PIN_PEX_L1_CLKREQ_N_PDD6 _GPIO(238) +#define TEGRA_PIN_PEX_L2_PRSNT_N_PDD7 _GPIO(239) +#define TEGRA_PIN_CLK3_OUT_PEE0 _GPIO(240) +#define TEGRA_PIN_CLK3_REQ_PEE1 _GPIO(241) +#define TEGRA_PIN_CLK1_REQ_PEE2 _GPIO(242) +#define TEGRA_PIN_HDMI_CEC_PEE3 _GPIO(243) +#define TEGRA_PIN_PEE4 _GPIO(244) +#define TEGRA_PIN_PEE5 _GPIO(245) +#define TEGRA_PIN_PEE6 _GPIO(246) +#define TEGRA_PIN_PEE7 _GPIO(247) + +/* All non-GPIO pins follow */ +#define NUM_GPIOS (TEGRA_PIN_PEE7 + 1) +#define _PIN(offset) (NUM_GPIOS + (offset)) + +/* Non-GPIO pins */ +#define TEGRA_PIN_CLK_32K_IN _PIN(0) +#define TEGRA_PIN_CORE_PWR_REQ _PIN(1) +#define TEGRA_PIN_CPU_PWR_REQ _PIN(2) +#define TEGRA_PIN_JTAG_TCK _PIN(3) +#define TEGRA_PIN_JTAG_TDI _PIN(4) +#define TEGRA_PIN_JTAG_TDO _PIN(5) +#define TEGRA_PIN_JTAG_TMS _PIN(6) +#define TEGRA_PIN_JTAG_TRST_N _PIN(7) +#define TEGRA_PIN_OWR _PIN(8) +#define TEGRA_PIN_PWR_INT_N _PIN(9) +#define TEGRA_PIN_SYS_RESET_N _PIN(10) +#define TEGRA_PIN_TEST_MODE_EN _PIN(11) + +static const struct pinctrl_pin_desc tegra30_pins[] = { + PINCTRL_PIN(TEGRA_PIN_CLK_32K_OUT_PA0, "CLK_32K_OUT PA0"), + PINCTRL_PIN(TEGRA_PIN_UART3_CTS_N_PA1, "UART3_CTS_N PA1"), + PINCTRL_PIN(TEGRA_PIN_DAP2_FS_PA2, "DAP2_FS PA2"), + PINCTRL_PIN(TEGRA_PIN_DAP2_SCLK_PA3, "DAP2_SCLK PA3"), + PINCTRL_PIN(TEGRA_PIN_DAP2_DIN_PA4, "DAP2_DIN PA4"), + PINCTRL_PIN(TEGRA_PIN_DAP2_DOUT_PA5, "DAP2_DOUT PA5"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_CLK_PA6, "SDMMC3_CLK PA6"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_CMD_PA7, "SDMMC3_CMD PA7"), + PINCTRL_PIN(TEGRA_PIN_GMI_A17_PB0, "GMI_A17 PB0"), + PINCTRL_PIN(TEGRA_PIN_GMI_A18_PB1, "GMI_A18 PB1"), + PINCTRL_PIN(TEGRA_PIN_LCD_PWR0_PB2, "LCD_PWR0 PB2"), + PINCTRL_PIN(TEGRA_PIN_LCD_PCLK_PB3, "LCD_PCLK PB3"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT3_PB4, "SDMMC3_DAT3 PB4"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT2_PB5, "SDMMC3_DAT2 PB5"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT1_PB6, "SDMMC3_DAT1 PB6"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT0_PB7, "SDMMC3_DAT0 PB7"), + PINCTRL_PIN(TEGRA_PIN_UART3_RTS_N_PC0, "UART3_RTS_N PC0"), + PINCTRL_PIN(TEGRA_PIN_LCD_PWR1_PC1, "LCD_PWR1 PC1"), + PINCTRL_PIN(TEGRA_PIN_UART2_TXD_PC2, "UART2_TXD PC2"), + PINCTRL_PIN(TEGRA_PIN_UART2_RXD_PC3, "UART2_RXD PC3"), + PINCTRL_PIN(TEGRA_PIN_GEN1_I2C_SCL_PC4, "GEN1_I2C_SCL PC4"), + PINCTRL_PIN(TEGRA_PIN_GEN1_I2C_SDA_PC5, "GEN1_I2C_SDA PC5"), + PINCTRL_PIN(TEGRA_PIN_LCD_PWR2_PC6, "LCD_PWR2 PC6"), + PINCTRL_PIN(TEGRA_PIN_GMI_WP_N_PC7, "GMI_WP_N PC7"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT5_PD0, "SDMMC3_DAT5 PD0"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT4_PD1, "SDMMC3_DAT4 PD1"), + PINCTRL_PIN(TEGRA_PIN_LCD_DC1_PD2, "LCD_DC1 PD2"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT6_PD3, "SDMMC3_DAT6 PD3"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT7_PD4, "SDMMC3_DAT7 PD4"), + PINCTRL_PIN(TEGRA_PIN_VI_D1_PD5, "VI_D1 PD5"), + PINCTRL_PIN(TEGRA_PIN_VI_VSYNC_PD6, "VI_VSYNC PD6"), + PINCTRL_PIN(TEGRA_PIN_VI_HSYNC_PD7, "VI_HSYNC PD7"), + PINCTRL_PIN(TEGRA_PIN_LCD_D0_PE0, "LCD_D0 PE0"), + PINCTRL_PIN(TEGRA_PIN_LCD_D1_PE1, "LCD_D1 PE1"), + PINCTRL_PIN(TEGRA_PIN_LCD_D2_PE2, "LCD_D2 PE2"), + PINCTRL_PIN(TEGRA_PIN_LCD_D3_PE3, "LCD_D3 PE3"), + PINCTRL_PIN(TEGRA_PIN_LCD_D4_PE4, "LCD_D4 PE4"), + PINCTRL_PIN(TEGRA_PIN_LCD_D5_PE5, "LCD_D5 PE5"), + PINCTRL_PIN(TEGRA_PIN_LCD_D6_PE6, "LCD_D6 PE6"), + PINCTRL_PIN(TEGRA_PIN_LCD_D7_PE7, "LCD_D7 PE7"), + PINCTRL_PIN(TEGRA_PIN_LCD_D8_PF0, "LCD_D8 PF0"), + PINCTRL_PIN(TEGRA_PIN_LCD_D9_PF1, "LCD_D9 PF1"), + PINCTRL_PIN(TEGRA_PIN_LCD_D10_PF2, "LCD_D10 PF2"), + PINCTRL_PIN(TEGRA_PIN_LCD_D11_PF3, "LCD_D11 PF3"), + PINCTRL_PIN(TEGRA_PIN_LCD_D12_PF4, "LCD_D12 PF4"), + PINCTRL_PIN(TEGRA_PIN_LCD_D13_PF5, "LCD_D13 PF5"), + PINCTRL_PIN(TEGRA_PIN_LCD_D14_PF6, "LCD_D14 PF6"), + PINCTRL_PIN(TEGRA_PIN_LCD_D15_PF7, "LCD_D15 PF7"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD0_PG0, "GMI_AD0 PG0"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD1_PG1, "GMI_AD1 PG1"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD2_PG2, "GMI_AD2 PG2"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD3_PG3, "GMI_AD3 PG3"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD4_PG4, "GMI_AD4 PG4"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD5_PG5, "GMI_AD5 PG5"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD6_PG6, "GMI_AD6 PG6"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD7_PG7, "GMI_AD7 PG7"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD8_PH0, "GMI_AD8 PH0"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD9_PH1, "GMI_AD9 PH1"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD10_PH2, "GMI_AD10 PH2"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD11_PH3, "GMI_AD11 PH3"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD12_PH4, "GMI_AD12 PH4"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD13_PH5, "GMI_AD13 PH5"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD14_PH6, "GMI_AD14 PH6"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD15_PH7, "GMI_AD15 PH7"), + PINCTRL_PIN(TEGRA_PIN_GMI_WR_N_PI0, "GMI_WR_N PI0"), + PINCTRL_PIN(TEGRA_PIN_GMI_OE_N_PI1, "GMI_OE_N PI1"), + PINCTRL_PIN(TEGRA_PIN_GMI_DQS_PI2, "GMI_DQS PI2"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS6_N_PI3, "GMI_CS6_N PI3"), + PINCTRL_PIN(TEGRA_PIN_GMI_RST_N_PI4, "GMI_RST_N PI4"), + PINCTRL_PIN(TEGRA_PIN_GMI_IORDY_PI5, "GMI_IORDY PI5"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS7_N_PI6, "GMI_CS7_N PI6"), + PINCTRL_PIN(TEGRA_PIN_GMI_WAIT_PI7, "GMI_WAIT PI7"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS0_N_PJ0, "GMI_CS0_N PJ0"), + PINCTRL_PIN(TEGRA_PIN_LCD_DE_PJ1, "LCD_DE PJ1"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS1_N_PJ2, "GMI_CS1_N PJ2"), + PINCTRL_PIN(TEGRA_PIN_LCD_HSYNC_PJ3, "LCD_HSYNC PJ3"), + PINCTRL_PIN(TEGRA_PIN_LCD_VSYNC_PJ4, "LCD_VSYNC PJ4"), + PINCTRL_PIN(TEGRA_PIN_UART2_CTS_N_PJ5, "UART2_CTS_N PJ5"), + PINCTRL_PIN(TEGRA_PIN_UART2_RTS_N_PJ6, "UART2_RTS_N PJ6"), + PINCTRL_PIN(TEGRA_PIN_GMI_A16_PJ7, "GMI_A16 PJ7"), + PINCTRL_PIN(TEGRA_PIN_GMI_ADV_N_PK0, "GMI_ADV_N PK0"), + PINCTRL_PIN(TEGRA_PIN_GMI_CLK_PK1, "GMI_CLK PK1"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS4_N_PK2, "GMI_CS4_N PK2"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS2_N_PK3, "GMI_CS2_N PK3"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS3_N_PK4, "GMI_CS3_N PK4"), + PINCTRL_PIN(TEGRA_PIN_SPDIF_OUT_PK5, "SPDIF_OUT PK5"), + PINCTRL_PIN(TEGRA_PIN_SPDIF_IN_PK6, "SPDIF_IN PK6"), + PINCTRL_PIN(TEGRA_PIN_GMI_A19_PK7, "GMI_A19 PK7"), + PINCTRL_PIN(TEGRA_PIN_VI_D2_PL0, "VI_D2 PL0"), + PINCTRL_PIN(TEGRA_PIN_VI_D3_PL1, "VI_D3 PL1"), + PINCTRL_PIN(TEGRA_PIN_VI_D4_PL2, "VI_D4 PL2"), + PINCTRL_PIN(TEGRA_PIN_VI_D5_PL3, "VI_D5 PL3"), + PINCTRL_PIN(TEGRA_PIN_VI_D6_PL4, "VI_D6 PL4"), + PINCTRL_PIN(TEGRA_PIN_VI_D7_PL5, "VI_D7 PL5"), + PINCTRL_PIN(TEGRA_PIN_VI_D8_PL6, "VI_D8 PL6"), + PINCTRL_PIN(TEGRA_PIN_VI_D9_PL7, "VI_D9 PL7"), + PINCTRL_PIN(TEGRA_PIN_LCD_D16_PM0, "LCD_D16 PM0"), + PINCTRL_PIN(TEGRA_PIN_LCD_D17_PM1, "LCD_D17 PM1"), + PINCTRL_PIN(TEGRA_PIN_LCD_D18_PM2, "LCD_D18 PM2"), + PINCTRL_PIN(TEGRA_PIN_LCD_D19_PM3, "LCD_D19 PM3"), + PINCTRL_PIN(TEGRA_PIN_LCD_D20_PM4, "LCD_D20 PM4"), + PINCTRL_PIN(TEGRA_PIN_LCD_D21_PM5, "LCD_D21 PM5"), + PINCTRL_PIN(TEGRA_PIN_LCD_D22_PM6, "LCD_D22 PM6"), + PINCTRL_PIN(TEGRA_PIN_LCD_D23_PM7, "LCD_D23 PM7"), + PINCTRL_PIN(TEGRA_PIN_DAP1_FS_PN0, "DAP1_FS PN0"), + PINCTRL_PIN(TEGRA_PIN_DAP1_DIN_PN1, "DAP1_DIN PN1"), + PINCTRL_PIN(TEGRA_PIN_DAP1_DOUT_PN2, "DAP1_DOUT PN2"), + PINCTRL_PIN(TEGRA_PIN_DAP1_SCLK_PN3, "DAP1_SCLK PN3"), + PINCTRL_PIN(TEGRA_PIN_LCD_CS0_N_PN4, "LCD_CS0_N PN4"), + PINCTRL_PIN(TEGRA_PIN_LCD_SDOUT_PN5, "LCD_SDOUT PN5"), + PINCTRL_PIN(TEGRA_PIN_LCD_DC0_PN6, "LCD_DC0 PN6"), + PINCTRL_PIN(TEGRA_PIN_HDMI_INT_PN7, "HDMI_INT PN7"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA7_PO0, "ULPI_DATA7 PO0"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA0_PO1, "ULPI_DATA0 PO1"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA1_PO2, "ULPI_DATA1 PO2"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA2_PO3, "ULPI_DATA2 PO3"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA3_PO4, "ULPI_DATA3 PO4"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA4_PO5, "ULPI_DATA4 PO5"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA5_PO6, "ULPI_DATA5 PO6"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA6_PO7, "ULPI_DATA6 PO7"), + PINCTRL_PIN(TEGRA_PIN_DAP3_FS_PP0, "DAP3_FS PP0"), + PINCTRL_PIN(TEGRA_PIN_DAP3_DIN_PP1, "DAP3_DIN PP1"), + PINCTRL_PIN(TEGRA_PIN_DAP3_DOUT_PP2, "DAP3_DOUT PP2"), + PINCTRL_PIN(TEGRA_PIN_DAP3_SCLK_PP3, "DAP3_SCLK PP3"), + PINCTRL_PIN(TEGRA_PIN_DAP4_FS_PP4, "DAP4_FS PP4"), + PINCTRL_PIN(TEGRA_PIN_DAP4_DIN_PP5, "DAP4_DIN PP5"), + PINCTRL_PIN(TEGRA_PIN_DAP4_DOUT_PP6, "DAP4_DOUT PP6"), + PINCTRL_PIN(TEGRA_PIN_DAP4_SCLK_PP7, "DAP4_SCLK PP7"), + PINCTRL_PIN(TEGRA_PIN_KB_COL0_PQ0, "KB_COL0 PQ0"), + PINCTRL_PIN(TEGRA_PIN_KB_COL1_PQ1, "KB_COL1 PQ1"), + PINCTRL_PIN(TEGRA_PIN_KB_COL2_PQ2, "KB_COL2 PQ2"), + PINCTRL_PIN(TEGRA_PIN_KB_COL3_PQ3, "KB_COL3 PQ3"), + PINCTRL_PIN(TEGRA_PIN_KB_COL4_PQ4, "KB_COL4 PQ4"), + PINCTRL_PIN(TEGRA_PIN_KB_COL5_PQ5, "KB_COL5 PQ5"), + PINCTRL_PIN(TEGRA_PIN_KB_COL6_PQ6, "KB_COL6 PQ6"), + PINCTRL_PIN(TEGRA_PIN_KB_COL7_PQ7, "KB_COL7 PQ7"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW0_PR0, "KB_ROW0 PR0"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW1_PR1, "KB_ROW1 PR1"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW2_PR2, "KB_ROW2 PR2"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW3_PR3, "KB_ROW3 PR3"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW4_PR4, "KB_ROW4 PR4"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW5_PR5, "KB_ROW5 PR5"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW6_PR6, "KB_ROW6 PR6"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW7_PR7, "KB_ROW7 PR7"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW8_PS0, "KB_ROW8 PS0"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW9_PS1, "KB_ROW9 PS1"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW10_PS2, "KB_ROW10 PS2"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW11_PS3, "KB_ROW11 PS3"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW12_PS4, "KB_ROW12 PS4"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW13_PS5, "KB_ROW13 PS5"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW14_PS6, "KB_ROW14 PS6"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW15_PS7, "KB_ROW15 PS7"), + PINCTRL_PIN(TEGRA_PIN_VI_PCLK_PT0, "VI_PCLK PT0"), + PINCTRL_PIN(TEGRA_PIN_VI_MCLK_PT1, "VI_MCLK PT1"), + PINCTRL_PIN(TEGRA_PIN_VI_D10_PT2, "VI_D10 PT2"), + PINCTRL_PIN(TEGRA_PIN_VI_D11_PT3, "VI_D11 PT3"), + PINCTRL_PIN(TEGRA_PIN_VI_D0_PT4, "VI_D0 PT4"), + PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SCL_PT5, "GEN2_I2C_SCL PT5"), + PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SDA_PT6, "GEN2_I2C_SDA PT6"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_CMD_PT7, "SDMMC4_CMD PT7"), + PINCTRL_PIN(TEGRA_PIN_PU0, "PU0"), + PINCTRL_PIN(TEGRA_PIN_PU1, "PU1"), + PINCTRL_PIN(TEGRA_PIN_PU2, "PU2"), + PINCTRL_PIN(TEGRA_PIN_PU3, "PU3"), + PINCTRL_PIN(TEGRA_PIN_PU4, "PU4"), + PINCTRL_PIN(TEGRA_PIN_PU5, "PU5"), + PINCTRL_PIN(TEGRA_PIN_PU6, "PU6"), + PINCTRL_PIN(TEGRA_PIN_JTAG_RTCK_PU7, "JTAG_RTCK PU7"), + PINCTRL_PIN(TEGRA_PIN_PV0, "PV0"), + PINCTRL_PIN(TEGRA_PIN_PV1, "PV1"), + PINCTRL_PIN(TEGRA_PIN_PV2, "PV2"), + PINCTRL_PIN(TEGRA_PIN_PV3, "PV3"), + PINCTRL_PIN(TEGRA_PIN_DDC_SCL_PV4, "DDC_SCL PV4"), + PINCTRL_PIN(TEGRA_PIN_DDC_SDA_PV5, "DDC_SDA PV5"), + PINCTRL_PIN(TEGRA_PIN_CRT_HSYNC_PV6, "CRT_HSYNC PV6"), + PINCTRL_PIN(TEGRA_PIN_CRT_VSYNC_PV7, "CRT_VSYNC PV7"), + PINCTRL_PIN(TEGRA_PIN_LCD_CS1_N_PW0, "LCD_CS1_N PW0"), + PINCTRL_PIN(TEGRA_PIN_LCD_M1_PW1, "LCD_M1 PW1"), + PINCTRL_PIN(TEGRA_PIN_SPI2_CS1_N_PW2, "SPI2_CS1_N PW2"), + PINCTRL_PIN(TEGRA_PIN_SPI2_CS2_N_PW3, "SPI2_CS2_N PW3"), + PINCTRL_PIN(TEGRA_PIN_CLK1_OUT_PW4, "CLK1_OUT PW4"), + PINCTRL_PIN(TEGRA_PIN_CLK2_OUT_PW5, "CLK2_OUT PW5"), + PINCTRL_PIN(TEGRA_PIN_UART3_TXD_PW6, "UART3_TXD PW6"), + PINCTRL_PIN(TEGRA_PIN_UART3_RXD_PW7, "UART3_RXD PW7"), + PINCTRL_PIN(TEGRA_PIN_SPI2_MOSI_PX0, "SPI2_MOSI PX0"), + PINCTRL_PIN(TEGRA_PIN_SPI2_MISO_PX1, "SPI2_MISO PX1"), + PINCTRL_PIN(TEGRA_PIN_SPI2_SCK_PX2, "SPI2_SCK PX2"), + PINCTRL_PIN(TEGRA_PIN_SPI2_CS0_N_PX3, "SPI2_CS0_N PX3"), + PINCTRL_PIN(TEGRA_PIN_SPI1_MOSI_PX4, "SPI1_MOSI PX4"), + PINCTRL_PIN(TEGRA_PIN_SPI1_SCK_PX5, "SPI1_SCK PX5"), + PINCTRL_PIN(TEGRA_PIN_SPI1_CS0_N_PX6, "SPI1_CS0_N PX6"), + PINCTRL_PIN(TEGRA_PIN_SPI1_MISO_PX7, "SPI1_MISO PX7"), + PINCTRL_PIN(TEGRA_PIN_ULPI_CLK_PY0, "ULPI_CLK PY0"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DIR_PY1, "ULPI_DIR PY1"), + PINCTRL_PIN(TEGRA_PIN_ULPI_NXT_PY2, "ULPI_NXT PY2"), + PINCTRL_PIN(TEGRA_PIN_ULPI_STP_PY3, "ULPI_STP PY3"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT3_PY4, "SDMMC1_DAT3 PY4"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT2_PY5, "SDMMC1_DAT2 PY5"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT1_PY6, "SDMMC1_DAT1 PY6"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT0_PY7, "SDMMC1_DAT0 PY7"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_CLK_PZ0, "SDMMC1_CLK PZ0"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_CMD_PZ1, "SDMMC1_CMD PZ1"), + PINCTRL_PIN(TEGRA_PIN_LCD_SDIN_PZ2, "LCD_SDIN PZ2"), + PINCTRL_PIN(TEGRA_PIN_LCD_WR_N_PZ3, "LCD_WR_N PZ3"), + PINCTRL_PIN(TEGRA_PIN_LCD_SCK_PZ4, "LCD_SCK PZ4"), + PINCTRL_PIN(TEGRA_PIN_SYS_CLK_REQ_PZ5, "SYS_CLK_REQ PZ5"), + PINCTRL_PIN(TEGRA_PIN_PWR_I2C_SCL_PZ6, "PWR_I2C_SCL PZ6"), + PINCTRL_PIN(TEGRA_PIN_PWR_I2C_SDA_PZ7, "PWR_I2C_SDA PZ7"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT0_PAA0, "SDMMC4_DAT0 PAA0"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT1_PAA1, "SDMMC4_DAT1 PAA1"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT2_PAA2, "SDMMC4_DAT2 PAA2"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT3_PAA3, "SDMMC4_DAT3 PAA3"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT4_PAA4, "SDMMC4_DAT4 PAA4"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT5_PAA5, "SDMMC4_DAT5 PAA5"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT6_PAA6, "SDMMC4_DAT6 PAA6"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT7_PAA7, "SDMMC4_DAT7 PAA7"), + PINCTRL_PIN(TEGRA_PIN_PBB0, "PBB0"), + PINCTRL_PIN(TEGRA_PIN_CAM_I2C_SCL_PBB1, "CAM_I2C_SCL PBB1"), + PINCTRL_PIN(TEGRA_PIN_CAM_I2C_SDA_PBB2, "CAM_I2C_SDA PBB2"), + PINCTRL_PIN(TEGRA_PIN_PBB3, "PBB3"), + PINCTRL_PIN(TEGRA_PIN_PBB4, "PBB4"), + PINCTRL_PIN(TEGRA_PIN_PBB5, "PBB5"), + PINCTRL_PIN(TEGRA_PIN_PBB6, "PBB6"), + PINCTRL_PIN(TEGRA_PIN_PBB7, "PBB7"), + PINCTRL_PIN(TEGRA_PIN_CAM_MCLK_PCC0, "CAM_MCLK PCC0"), + PINCTRL_PIN(TEGRA_PIN_PCC1, "PCC1"), + PINCTRL_PIN(TEGRA_PIN_PCC2, "PCC2"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_RST_N_PCC3, "SDMMC4_RST_N PCC3"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_CLK_PCC4, "SDMMC4_CLK PCC4"), + PINCTRL_PIN(TEGRA_PIN_CLK2_REQ_PCC5, "CLK2_REQ PCC5"), + PINCTRL_PIN(TEGRA_PIN_PEX_L2_RST_N_PCC6, "PEX_L2_RST_N PCC6"), + PINCTRL_PIN(TEGRA_PIN_PEX_L2_CLKREQ_N_PCC7, "PEX_L2_CLKREQ_N PCC7"), + PINCTRL_PIN(TEGRA_PIN_PEX_L0_PRSNT_N_PDD0, "PEX_L0_PRSNT_N PDD0"), + PINCTRL_PIN(TEGRA_PIN_PEX_L0_RST_N_PDD1, "PEX_L0_RST_N PDD1"), + PINCTRL_PIN(TEGRA_PIN_PEX_L0_CLKREQ_N_PDD2, "PEX_L0_CLKREQ_N PDD2"), + PINCTRL_PIN(TEGRA_PIN_PEX_WAKE_N_PDD3, "PEX_WAKE_N PDD3"), + PINCTRL_PIN(TEGRA_PIN_PEX_L1_PRSNT_N_PDD4, "PEX_L1_PRSNT_N PDD4"), + PINCTRL_PIN(TEGRA_PIN_PEX_L1_RST_N_PDD5, "PEX_L1_RST_N PDD5"), + PINCTRL_PIN(TEGRA_PIN_PEX_L1_CLKREQ_N_PDD6, "PEX_L1_CLKREQ_N PDD6"), + PINCTRL_PIN(TEGRA_PIN_PEX_L2_PRSNT_N_PDD7, "PEX_L2_PRSNT_N PDD7"), + PINCTRL_PIN(TEGRA_PIN_CLK3_OUT_PEE0, "CLK3_OUT PEE0"), + PINCTRL_PIN(TEGRA_PIN_CLK3_REQ_PEE1, "CLK3_REQ PEE1"), + PINCTRL_PIN(TEGRA_PIN_CLK1_REQ_PEE2, "CLK1_REQ PEE2"), + PINCTRL_PIN(TEGRA_PIN_HDMI_CEC_PEE3, "HDMI_CEC PEE3"), + PINCTRL_PIN(TEGRA_PIN_PEE4, "PEE4"), + PINCTRL_PIN(TEGRA_PIN_PEE5, "PEE5"), + PINCTRL_PIN(TEGRA_PIN_PEE6, "PEE6"), + PINCTRL_PIN(TEGRA_PIN_PEE7, "PEE7"), + PINCTRL_PIN(TEGRA_PIN_CLK_32K_IN, "CLK_32K_IN"), + PINCTRL_PIN(TEGRA_PIN_CORE_PWR_REQ, "CORE_PWR_REQ"), + PINCTRL_PIN(TEGRA_PIN_CPU_PWR_REQ, "CPU_PWR_REQ"), + PINCTRL_PIN(TEGRA_PIN_JTAG_TCK, "JTAG_TCK"), + PINCTRL_PIN(TEGRA_PIN_JTAG_TDI, "JTAG_TDI"), + PINCTRL_PIN(TEGRA_PIN_JTAG_TDO, "JTAG_TDO"), + PINCTRL_PIN(TEGRA_PIN_JTAG_TMS, "JTAG_TMS"), + PINCTRL_PIN(TEGRA_PIN_JTAG_TRST_N, "JTAG_TRST_N"), + PINCTRL_PIN(TEGRA_PIN_OWR, "OWR"), + PINCTRL_PIN(TEGRA_PIN_PWR_INT_N, "PWR_INT_N"), + PINCTRL_PIN(TEGRA_PIN_SYS_RESET_N, "SYS_RESET_N"), + PINCTRL_PIN(TEGRA_PIN_TEST_MODE_EN, "TEST_MODE_EN"), +}; + +static const unsigned clk_32k_out_pa0_pins[] = { + TEGRA_PIN_CLK_32K_OUT_PA0, +}; + +static const unsigned uart3_cts_n_pa1_pins[] = { + TEGRA_PIN_UART3_CTS_N_PA1, +}; + +static const unsigned dap2_fs_pa2_pins[] = { + TEGRA_PIN_DAP2_FS_PA2, +}; + +static const unsigned dap2_sclk_pa3_pins[] = { + TEGRA_PIN_DAP2_SCLK_PA3, +}; + +static const unsigned dap2_din_pa4_pins[] = { + TEGRA_PIN_DAP2_DIN_PA4, +}; + +static const unsigned dap2_dout_pa5_pins[] = { + TEGRA_PIN_DAP2_DOUT_PA5, +}; + +static const unsigned sdmmc3_clk_pa6_pins[] = { + TEGRA_PIN_SDMMC3_CLK_PA6, +}; + +static const unsigned sdmmc3_cmd_pa7_pins[] = { + TEGRA_PIN_SDMMC3_CMD_PA7, +}; + +static const unsigned gmi_a17_pb0_pins[] = { + TEGRA_PIN_GMI_A17_PB0, +}; + +static const unsigned gmi_a18_pb1_pins[] = { + TEGRA_PIN_GMI_A18_PB1, +}; + +static const unsigned lcd_pwr0_pb2_pins[] = { + TEGRA_PIN_LCD_PWR0_PB2, +}; + +static const unsigned lcd_pclk_pb3_pins[] = { + TEGRA_PIN_LCD_PCLK_PB3, +}; + +static const unsigned sdmmc3_dat3_pb4_pins[] = { + TEGRA_PIN_SDMMC3_DAT3_PB4, +}; + +static const unsigned sdmmc3_dat2_pb5_pins[] = { + TEGRA_PIN_SDMMC3_DAT2_PB5, +}; + +static const unsigned sdmmc3_dat1_pb6_pins[] = { + TEGRA_PIN_SDMMC3_DAT1_PB6, +}; + +static const unsigned sdmmc3_dat0_pb7_pins[] = { + TEGRA_PIN_SDMMC3_DAT0_PB7, +}; + +static const unsigned uart3_rts_n_pc0_pins[] = { + TEGRA_PIN_UART3_RTS_N_PC0, +}; + +static const unsigned lcd_pwr1_pc1_pins[] = { + TEGRA_PIN_LCD_PWR1_PC1, +}; + +static const unsigned uart2_txd_pc2_pins[] = { + TEGRA_PIN_UART2_TXD_PC2, +}; + +static const unsigned uart2_rxd_pc3_pins[] = { + TEGRA_PIN_UART2_RXD_PC3, +}; + +static const unsigned gen1_i2c_scl_pc4_pins[] = { + TEGRA_PIN_GEN1_I2C_SCL_PC4, +}; + +static const unsigned gen1_i2c_sda_pc5_pins[] = { + TEGRA_PIN_GEN1_I2C_SDA_PC5, +}; + +static const unsigned lcd_pwr2_pc6_pins[] = { + TEGRA_PIN_LCD_PWR2_PC6, +}; + +static const unsigned gmi_wp_n_pc7_pins[] = { + TEGRA_PIN_GMI_WP_N_PC7, +}; + +static const unsigned sdmmc3_dat5_pd0_pins[] = { + TEGRA_PIN_SDMMC3_DAT5_PD0, +}; + +static const unsigned sdmmc3_dat4_pd1_pins[] = { + TEGRA_PIN_SDMMC3_DAT4_PD1, +}; + +static const unsigned lcd_dc1_pd2_pins[] = { + TEGRA_PIN_LCD_DC1_PD2, +}; + +static const unsigned sdmmc3_dat6_pd3_pins[] = { + TEGRA_PIN_SDMMC3_DAT6_PD3, +}; + +static const unsigned sdmmc3_dat7_pd4_pins[] = { + TEGRA_PIN_SDMMC3_DAT7_PD4, +}; + +static const unsigned vi_d1_pd5_pins[] = { + TEGRA_PIN_VI_D1_PD5, +}; + +static const unsigned vi_vsync_pd6_pins[] = { + TEGRA_PIN_VI_VSYNC_PD6, +}; + +static const unsigned vi_hsync_pd7_pins[] = { + TEGRA_PIN_VI_HSYNC_PD7, +}; + +static const unsigned lcd_d0_pe0_pins[] = { + TEGRA_PIN_LCD_D0_PE0, +}; + +static const unsigned lcd_d1_pe1_pins[] = { + TEGRA_PIN_LCD_D1_PE1, +}; + +static const unsigned lcd_d2_pe2_pins[] = { + TEGRA_PIN_LCD_D2_PE2, +}; + +static const unsigned lcd_d3_pe3_pins[] = { + TEGRA_PIN_LCD_D3_PE3, +}; + +static const unsigned lcd_d4_pe4_pins[] = { + TEGRA_PIN_LCD_D4_PE4, +}; + +static const unsigned lcd_d5_pe5_pins[] = { + TEGRA_PIN_LCD_D5_PE5, +}; + +static const unsigned lcd_d6_pe6_pins[] = { + TEGRA_PIN_LCD_D6_PE6, +}; + +static const unsigned lcd_d7_pe7_pins[] = { + TEGRA_PIN_LCD_D7_PE7, +}; + +static const unsigned lcd_d8_pf0_pins[] = { + TEGRA_PIN_LCD_D8_PF0, +}; + +static const unsigned lcd_d9_pf1_pins[] = { + TEGRA_PIN_LCD_D9_PF1, +}; + +static const unsigned lcd_d10_pf2_pins[] = { + TEGRA_PIN_LCD_D10_PF2, +}; + +static const unsigned lcd_d11_pf3_pins[] = { + TEGRA_PIN_LCD_D11_PF3, +}; + +static const unsigned lcd_d12_pf4_pins[] = { + TEGRA_PIN_LCD_D12_PF4, +}; + +static const unsigned lcd_d13_pf5_pins[] = { + TEGRA_PIN_LCD_D13_PF5, +}; + +static const unsigned lcd_d14_pf6_pins[] = { + TEGRA_PIN_LCD_D14_PF6, +}; + +static const unsigned lcd_d15_pf7_pins[] = { + TEGRA_PIN_LCD_D15_PF7, +}; + +static const unsigned gmi_ad0_pg0_pins[] = { + TEGRA_PIN_GMI_AD0_PG0, +}; + +static const unsigned gmi_ad1_pg1_pins[] = { + TEGRA_PIN_GMI_AD1_PG1, +}; + +static const unsigned gmi_ad2_pg2_pins[] = { + TEGRA_PIN_GMI_AD2_PG2, +}; + +static const unsigned gmi_ad3_pg3_pins[] = { + TEGRA_PIN_GMI_AD3_PG3, +}; + +static const unsigned gmi_ad4_pg4_pins[] = { + TEGRA_PIN_GMI_AD4_PG4, +}; + +static const unsigned gmi_ad5_pg5_pins[] = { + TEGRA_PIN_GMI_AD5_PG5, +}; + +static const unsigned gmi_ad6_pg6_pins[] = { + TEGRA_PIN_GMI_AD6_PG6, +}; + +static const unsigned gmi_ad7_pg7_pins[] = { + TEGRA_PIN_GMI_AD7_PG7, +}; + +static const unsigned gmi_ad8_ph0_pins[] = { + TEGRA_PIN_GMI_AD8_PH0, +}; + +static const unsigned gmi_ad9_ph1_pins[] = { + TEGRA_PIN_GMI_AD9_PH1, +}; + +static const unsigned gmi_ad10_ph2_pins[] = { + TEGRA_PIN_GMI_AD10_PH2, +}; + +static const unsigned gmi_ad11_ph3_pins[] = { + TEGRA_PIN_GMI_AD11_PH3, +}; + +static const unsigned gmi_ad12_ph4_pins[] = { + TEGRA_PIN_GMI_AD12_PH4, +}; + +static const unsigned gmi_ad13_ph5_pins[] = { + TEGRA_PIN_GMI_AD13_PH5, +}; + +static const unsigned gmi_ad14_ph6_pins[] = { + TEGRA_PIN_GMI_AD14_PH6, +}; + +static const unsigned gmi_ad15_ph7_pins[] = { + TEGRA_PIN_GMI_AD15_PH7, +}; + +static const unsigned gmi_wr_n_pi0_pins[] = { + TEGRA_PIN_GMI_WR_N_PI0, +}; + +static const unsigned gmi_oe_n_pi1_pins[] = { + TEGRA_PIN_GMI_OE_N_PI1, +}; + +static const unsigned gmi_dqs_pi2_pins[] = { + TEGRA_PIN_GMI_DQS_PI2, +}; + +static const unsigned gmi_cs6_n_pi3_pins[] = { + TEGRA_PIN_GMI_CS6_N_PI3, +}; + +static const unsigned gmi_rst_n_pi4_pins[] = { + TEGRA_PIN_GMI_RST_N_PI4, +}; + +static const unsigned gmi_iordy_pi5_pins[] = { + TEGRA_PIN_GMI_IORDY_PI5, +}; + +static const unsigned gmi_cs7_n_pi6_pins[] = { + TEGRA_PIN_GMI_CS7_N_PI6, +}; + +static const unsigned gmi_wait_pi7_pins[] = { + TEGRA_PIN_GMI_WAIT_PI7, +}; + +static const unsigned gmi_cs0_n_pj0_pins[] = { + TEGRA_PIN_GMI_CS0_N_PJ0, +}; + +static const unsigned lcd_de_pj1_pins[] = { + TEGRA_PIN_LCD_DE_PJ1, +}; + +static const unsigned gmi_cs1_n_pj2_pins[] = { + TEGRA_PIN_GMI_CS1_N_PJ2, +}; + +static const unsigned lcd_hsync_pj3_pins[] = { + TEGRA_PIN_LCD_HSYNC_PJ3, +}; + +static const unsigned lcd_vsync_pj4_pins[] = { + TEGRA_PIN_LCD_VSYNC_PJ4, +}; + +static const unsigned uart2_cts_n_pj5_pins[] = { + TEGRA_PIN_UART2_CTS_N_PJ5, +}; + +static const unsigned uart2_rts_n_pj6_pins[] = { + TEGRA_PIN_UART2_RTS_N_PJ6, +}; + +static const unsigned gmi_a16_pj7_pins[] = { + TEGRA_PIN_GMI_A16_PJ7, +}; + +static const unsigned gmi_adv_n_pk0_pins[] = { + TEGRA_PIN_GMI_ADV_N_PK0, +}; + +static const unsigned gmi_clk_pk1_pins[] = { + TEGRA_PIN_GMI_CLK_PK1, +}; + +static const unsigned gmi_cs4_n_pk2_pins[] = { + TEGRA_PIN_GMI_CS4_N_PK2, +}; + +static const unsigned gmi_cs2_n_pk3_pins[] = { + TEGRA_PIN_GMI_CS2_N_PK3, +}; + +static const unsigned gmi_cs3_n_pk4_pins[] = { + TEGRA_PIN_GMI_CS3_N_PK4, +}; + +static const unsigned spdif_out_pk5_pins[] = { + TEGRA_PIN_SPDIF_OUT_PK5, +}; + +static const unsigned spdif_in_pk6_pins[] = { + TEGRA_PIN_SPDIF_IN_PK6, +}; + +static const unsigned gmi_a19_pk7_pins[] = { + TEGRA_PIN_GMI_A19_PK7, +}; + +static const unsigned vi_d2_pl0_pins[] = { + TEGRA_PIN_VI_D2_PL0, +}; + +static const unsigned vi_d3_pl1_pins[] = { + TEGRA_PIN_VI_D3_PL1, +}; + +static const unsigned vi_d4_pl2_pins[] = { + TEGRA_PIN_VI_D4_PL2, +}; + +static const unsigned vi_d5_pl3_pins[] = { + TEGRA_PIN_VI_D5_PL3, +}; + +static const unsigned vi_d6_pl4_pins[] = { + TEGRA_PIN_VI_D6_PL4, +}; + +static const unsigned vi_d7_pl5_pins[] = { + TEGRA_PIN_VI_D7_PL5, +}; + +static const unsigned vi_d8_pl6_pins[] = { + TEGRA_PIN_VI_D8_PL6, +}; + +static const unsigned vi_d9_pl7_pins[] = { + TEGRA_PIN_VI_D9_PL7, +}; + +static const unsigned lcd_d16_pm0_pins[] = { + TEGRA_PIN_LCD_D16_PM0, +}; + +static const unsigned lcd_d17_pm1_pins[] = { + TEGRA_PIN_LCD_D17_PM1, +}; + +static const unsigned lcd_d18_pm2_pins[] = { + TEGRA_PIN_LCD_D18_PM2, +}; + +static const unsigned lcd_d19_pm3_pins[] = { + TEGRA_PIN_LCD_D19_PM3, +}; + +static const unsigned lcd_d20_pm4_pins[] = { + TEGRA_PIN_LCD_D20_PM4, +}; + +static const unsigned lcd_d21_pm5_pins[] = { + TEGRA_PIN_LCD_D21_PM5, +}; + +static const unsigned lcd_d22_pm6_pins[] = { + TEGRA_PIN_LCD_D22_PM6, +}; + +static const unsigned lcd_d23_pm7_pins[] = { + TEGRA_PIN_LCD_D23_PM7, +}; + +static const unsigned dap1_fs_pn0_pins[] = { + TEGRA_PIN_DAP1_FS_PN0, +}; + +static const unsigned dap1_din_pn1_pins[] = { + TEGRA_PIN_DAP1_DIN_PN1, +}; + +static const unsigned dap1_dout_pn2_pins[] = { + TEGRA_PIN_DAP1_DOUT_PN2, +}; + +static const unsigned dap1_sclk_pn3_pins[] = { + TEGRA_PIN_DAP1_SCLK_PN3, +}; + +static const unsigned lcd_cs0_n_pn4_pins[] = { + TEGRA_PIN_LCD_CS0_N_PN4, +}; + +static const unsigned lcd_sdout_pn5_pins[] = { + TEGRA_PIN_LCD_SDOUT_PN5, +}; + +static const unsigned lcd_dc0_pn6_pins[] = { + TEGRA_PIN_LCD_DC0_PN6, +}; + +static const unsigned hdmi_int_pn7_pins[] = { + TEGRA_PIN_HDMI_INT_PN7, +}; + +static const unsigned ulpi_data7_po0_pins[] = { + TEGRA_PIN_ULPI_DATA7_PO0, +}; + +static const unsigned ulpi_data0_po1_pins[] = { + TEGRA_PIN_ULPI_DATA0_PO1, +}; + +static const unsigned ulpi_data1_po2_pins[] = { + TEGRA_PIN_ULPI_DATA1_PO2, +}; + +static const unsigned ulpi_data2_po3_pins[] = { + TEGRA_PIN_ULPI_DATA2_PO3, +}; + +static const unsigned ulpi_data3_po4_pins[] = { + TEGRA_PIN_ULPI_DATA3_PO4, +}; + +static const unsigned ulpi_data4_po5_pins[] = { + TEGRA_PIN_ULPI_DATA4_PO5, +}; + +static const unsigned ulpi_data5_po6_pins[] = { + TEGRA_PIN_ULPI_DATA5_PO6, +}; + +static const unsigned ulpi_data6_po7_pins[] = { + TEGRA_PIN_ULPI_DATA6_PO7, +}; + +static const unsigned dap3_fs_pp0_pins[] = { + TEGRA_PIN_DAP3_FS_PP0, +}; + +static const unsigned dap3_din_pp1_pins[] = { + TEGRA_PIN_DAP3_DIN_PP1, +}; + +static const unsigned dap3_dout_pp2_pins[] = { + TEGRA_PIN_DAP3_DOUT_PP2, +}; + +static const unsigned dap3_sclk_pp3_pins[] = { + TEGRA_PIN_DAP3_SCLK_PP3, +}; + +static const unsigned dap4_fs_pp4_pins[] = { + TEGRA_PIN_DAP4_FS_PP4, +}; + +static const unsigned dap4_din_pp5_pins[] = { + TEGRA_PIN_DAP4_DIN_PP5, +}; + +static const unsigned dap4_dout_pp6_pins[] = { + TEGRA_PIN_DAP4_DOUT_PP6, +}; + +static const unsigned dap4_sclk_pp7_pins[] = { + TEGRA_PIN_DAP4_SCLK_PP7, +}; + +static const unsigned kb_col0_pq0_pins[] = { + TEGRA_PIN_KB_COL0_PQ0, +}; + +static const unsigned kb_col1_pq1_pins[] = { + TEGRA_PIN_KB_COL1_PQ1, +}; + +static const unsigned kb_col2_pq2_pins[] = { + TEGRA_PIN_KB_COL2_PQ2, +}; + +static const unsigned kb_col3_pq3_pins[] = { + TEGRA_PIN_KB_COL3_PQ3, +}; + +static const unsigned kb_col4_pq4_pins[] = { + TEGRA_PIN_KB_COL4_PQ4, +}; + +static const unsigned kb_col5_pq5_pins[] = { + TEGRA_PIN_KB_COL5_PQ5, +}; + +static const unsigned kb_col6_pq6_pins[] = { + TEGRA_PIN_KB_COL6_PQ6, +}; + +static const unsigned kb_col7_pq7_pins[] = { + TEGRA_PIN_KB_COL7_PQ7, +}; + +static const unsigned kb_row0_pr0_pins[] = { + TEGRA_PIN_KB_ROW0_PR0, +}; + +static const unsigned kb_row1_pr1_pins[] = { + TEGRA_PIN_KB_ROW1_PR1, +}; + +static const unsigned kb_row2_pr2_pins[] = { + TEGRA_PIN_KB_ROW2_PR2, +}; + +static const unsigned kb_row3_pr3_pins[] = { + TEGRA_PIN_KB_ROW3_PR3, +}; + +static const unsigned kb_row4_pr4_pins[] = { + TEGRA_PIN_KB_ROW4_PR4, +}; + +static const unsigned kb_row5_pr5_pins[] = { + TEGRA_PIN_KB_ROW5_PR5, +}; + +static const unsigned kb_row6_pr6_pins[] = { + TEGRA_PIN_KB_ROW6_PR6, +}; + +static const unsigned kb_row7_pr7_pins[] = { + TEGRA_PIN_KB_ROW7_PR7, +}; + +static const unsigned kb_row8_ps0_pins[] = { + TEGRA_PIN_KB_ROW8_PS0, +}; + +static const unsigned kb_row9_ps1_pins[] = { + TEGRA_PIN_KB_ROW9_PS1, +}; + +static const unsigned kb_row10_ps2_pins[] = { + TEGRA_PIN_KB_ROW10_PS2, +}; + +static const unsigned kb_row11_ps3_pins[] = { + TEGRA_PIN_KB_ROW11_PS3, +}; + +static const unsigned kb_row12_ps4_pins[] = { + TEGRA_PIN_KB_ROW12_PS4, +}; + +static const unsigned kb_row13_ps5_pins[] = { + TEGRA_PIN_KB_ROW13_PS5, +}; + +static const unsigned kb_row14_ps6_pins[] = { + TEGRA_PIN_KB_ROW14_PS6, +}; + +static const unsigned kb_row15_ps7_pins[] = { + TEGRA_PIN_KB_ROW15_PS7, +}; + +static const unsigned vi_pclk_pt0_pins[] = { + TEGRA_PIN_VI_PCLK_PT0, +}; + +static const unsigned vi_mclk_pt1_pins[] = { + TEGRA_PIN_VI_MCLK_PT1, +}; + +static const unsigned vi_d10_pt2_pins[] = { + TEGRA_PIN_VI_D10_PT2, +}; + +static const unsigned vi_d11_pt3_pins[] = { + TEGRA_PIN_VI_D11_PT3, +}; + +static const unsigned vi_d0_pt4_pins[] = { + TEGRA_PIN_VI_D0_PT4, +}; + +static const unsigned gen2_i2c_scl_pt5_pins[] = { + TEGRA_PIN_GEN2_I2C_SCL_PT5, +}; + +static const unsigned gen2_i2c_sda_pt6_pins[] = { + TEGRA_PIN_GEN2_I2C_SDA_PT6, +}; + +static const unsigned sdmmc4_cmd_pt7_pins[] = { + TEGRA_PIN_SDMMC4_CMD_PT7, +}; + +static const unsigned pu0_pins[] = { + TEGRA_PIN_PU0, +}; + +static const unsigned pu1_pins[] = { + TEGRA_PIN_PU1, +}; + +static const unsigned pu2_pins[] = { + TEGRA_PIN_PU2, +}; + +static const unsigned pu3_pins[] = { + TEGRA_PIN_PU3, +}; + +static const unsigned pu4_pins[] = { + TEGRA_PIN_PU4, +}; + +static const unsigned pu5_pins[] = { + TEGRA_PIN_PU5, +}; + +static const unsigned pu6_pins[] = { + TEGRA_PIN_PU6, +}; + +static const unsigned jtag_rtck_pu7_pins[] = { + TEGRA_PIN_JTAG_RTCK_PU7, +}; + +static const unsigned pv0_pins[] = { + TEGRA_PIN_PV0, +}; + +static const unsigned pv1_pins[] = { + TEGRA_PIN_PV1, +}; + +static const unsigned pv2_pins[] = { + TEGRA_PIN_PV2, +}; + +static const unsigned pv3_pins[] = { + TEGRA_PIN_PV3, +}; + +static const unsigned ddc_scl_pv4_pins[] = { + TEGRA_PIN_DDC_SCL_PV4, +}; + +static const unsigned ddc_sda_pv5_pins[] = { + TEGRA_PIN_DDC_SDA_PV5, +}; + +static const unsigned crt_hsync_pv6_pins[] = { + TEGRA_PIN_CRT_HSYNC_PV6, +}; + +static const unsigned crt_vsync_pv7_pins[] = { + TEGRA_PIN_CRT_VSYNC_PV7, +}; + +static const unsigned lcd_cs1_n_pw0_pins[] = { + TEGRA_PIN_LCD_CS1_N_PW0, +}; + +static const unsigned lcd_m1_pw1_pins[] = { + TEGRA_PIN_LCD_M1_PW1, +}; + +static const unsigned spi2_cs1_n_pw2_pins[] = { + TEGRA_PIN_SPI2_CS1_N_PW2, +}; + +static const unsigned spi2_cs2_n_pw3_pins[] = { + TEGRA_PIN_SPI2_CS2_N_PW3, +}; + +static const unsigned clk1_out_pw4_pins[] = { + TEGRA_PIN_CLK1_OUT_PW4, +}; + +static const unsigned clk2_out_pw5_pins[] = { + TEGRA_PIN_CLK2_OUT_PW5, +}; + +static const unsigned uart3_txd_pw6_pins[] = { + TEGRA_PIN_UART3_TXD_PW6, +}; + +static const unsigned uart3_rxd_pw7_pins[] = { + TEGRA_PIN_UART3_RXD_PW7, +}; + +static const unsigned spi2_mosi_px0_pins[] = { + TEGRA_PIN_SPI2_MOSI_PX0, +}; + +static const unsigned spi2_miso_px1_pins[] = { + TEGRA_PIN_SPI2_MISO_PX1, +}; + +static const unsigned spi2_sck_px2_pins[] = { + TEGRA_PIN_SPI2_SCK_PX2, +}; + +static const unsigned spi2_cs0_n_px3_pins[] = { + TEGRA_PIN_SPI2_CS0_N_PX3, +}; + +static const unsigned spi1_mosi_px4_pins[] = { + TEGRA_PIN_SPI1_MOSI_PX4, +}; + +static const unsigned spi1_sck_px5_pins[] = { + TEGRA_PIN_SPI1_SCK_PX5, +}; + +static const unsigned spi1_cs0_n_px6_pins[] = { + TEGRA_PIN_SPI1_CS0_N_PX6, +}; + +static const unsigned spi1_miso_px7_pins[] = { + TEGRA_PIN_SPI1_MISO_PX7, +}; + +static const unsigned ulpi_clk_py0_pins[] = { + TEGRA_PIN_ULPI_CLK_PY0, +}; + +static const unsigned ulpi_dir_py1_pins[] = { + TEGRA_PIN_ULPI_DIR_PY1, +}; + +static const unsigned ulpi_nxt_py2_pins[] = { + TEGRA_PIN_ULPI_NXT_PY2, +}; + +static const unsigned ulpi_stp_py3_pins[] = { + TEGRA_PIN_ULPI_STP_PY3, +}; + +static const unsigned sdmmc1_dat3_py4_pins[] = { + TEGRA_PIN_SDMMC1_DAT3_PY4, +}; + +static const unsigned sdmmc1_dat2_py5_pins[] = { + TEGRA_PIN_SDMMC1_DAT2_PY5, +}; + +static const unsigned sdmmc1_dat1_py6_pins[] = { + TEGRA_PIN_SDMMC1_DAT1_PY6, +}; + +static const unsigned sdmmc1_dat0_py7_pins[] = { + TEGRA_PIN_SDMMC1_DAT0_PY7, +}; + +static const unsigned sdmmc1_clk_pz0_pins[] = { + TEGRA_PIN_SDMMC1_CLK_PZ0, +}; + +static const unsigned sdmmc1_cmd_pz1_pins[] = { + TEGRA_PIN_SDMMC1_CMD_PZ1, +}; + +static const unsigned lcd_sdin_pz2_pins[] = { + TEGRA_PIN_LCD_SDIN_PZ2, +}; + +static const unsigned lcd_wr_n_pz3_pins[] = { + TEGRA_PIN_LCD_WR_N_PZ3, +}; + +static const unsigned lcd_sck_pz4_pins[] = { + TEGRA_PIN_LCD_SCK_PZ4, +}; + +static const unsigned sys_clk_req_pz5_pins[] = { + TEGRA_PIN_SYS_CLK_REQ_PZ5, +}; + +static const unsigned pwr_i2c_scl_pz6_pins[] = { + TEGRA_PIN_PWR_I2C_SCL_PZ6, +}; + +static const unsigned pwr_i2c_sda_pz7_pins[] = { + TEGRA_PIN_PWR_I2C_SDA_PZ7, +}; + +static const unsigned sdmmc4_dat0_paa0_pins[] = { + TEGRA_PIN_SDMMC4_DAT0_PAA0, +}; + +static const unsigned sdmmc4_dat1_paa1_pins[] = { + TEGRA_PIN_SDMMC4_DAT1_PAA1, +}; + +static const unsigned sdmmc4_dat2_paa2_pins[] = { + TEGRA_PIN_SDMMC4_DAT2_PAA2, +}; + +static const unsigned sdmmc4_dat3_paa3_pins[] = { + TEGRA_PIN_SDMMC4_DAT3_PAA3, +}; + +static const unsigned sdmmc4_dat4_paa4_pins[] = { + TEGRA_PIN_SDMMC4_DAT4_PAA4, +}; + +static const unsigned sdmmc4_dat5_paa5_pins[] = { + TEGRA_PIN_SDMMC4_DAT5_PAA5, +}; + +static const unsigned sdmmc4_dat6_paa6_pins[] = { + TEGRA_PIN_SDMMC4_DAT6_PAA6, +}; + +static const unsigned sdmmc4_dat7_paa7_pins[] = { + TEGRA_PIN_SDMMC4_DAT7_PAA7, +}; + +static const unsigned pbb0_pins[] = { + TEGRA_PIN_PBB0, +}; + +static const unsigned cam_i2c_scl_pbb1_pins[] = { + TEGRA_PIN_CAM_I2C_SCL_PBB1, +}; + +static const unsigned cam_i2c_sda_pbb2_pins[] = { + TEGRA_PIN_CAM_I2C_SDA_PBB2, +}; + +static const unsigned pbb3_pins[] = { + TEGRA_PIN_PBB3, +}; + +static const unsigned pbb4_pins[] = { + TEGRA_PIN_PBB4, +}; + +static const unsigned pbb5_pins[] = { + TEGRA_PIN_PBB5, +}; + +static const unsigned pbb6_pins[] = { + TEGRA_PIN_PBB6, +}; + +static const unsigned pbb7_pins[] = { + TEGRA_PIN_PBB7, +}; + +static const unsigned cam_mclk_pcc0_pins[] = { + TEGRA_PIN_CAM_MCLK_PCC0, +}; + +static const unsigned pcc1_pins[] = { + TEGRA_PIN_PCC1, +}; + +static const unsigned pcc2_pins[] = { + TEGRA_PIN_PCC2, +}; + +static const unsigned sdmmc4_rst_n_pcc3_pins[] = { + TEGRA_PIN_SDMMC4_RST_N_PCC3, +}; + +static const unsigned sdmmc4_clk_pcc4_pins[] = { + TEGRA_PIN_SDMMC4_CLK_PCC4, +}; + +static const unsigned clk2_req_pcc5_pins[] = { + TEGRA_PIN_CLK2_REQ_PCC5, +}; + +static const unsigned pex_l2_rst_n_pcc6_pins[] = { + TEGRA_PIN_PEX_L2_RST_N_PCC6, +}; + +static const unsigned pex_l2_clkreq_n_pcc7_pins[] = { + TEGRA_PIN_PEX_L2_CLKREQ_N_PCC7, +}; + +static const unsigned pex_l0_prsnt_n_pdd0_pins[] = { + TEGRA_PIN_PEX_L0_PRSNT_N_PDD0, +}; + +static const unsigned pex_l0_rst_n_pdd1_pins[] = { + TEGRA_PIN_PEX_L0_RST_N_PDD1, +}; + +static const unsigned pex_l0_clkreq_n_pdd2_pins[] = { + TEGRA_PIN_PEX_L0_CLKREQ_N_PDD2, +}; + +static const unsigned pex_wake_n_pdd3_pins[] = { + TEGRA_PIN_PEX_WAKE_N_PDD3, +}; + +static const unsigned pex_l1_prsnt_n_pdd4_pins[] = { + TEGRA_PIN_PEX_L1_PRSNT_N_PDD4, +}; + +static const unsigned pex_l1_rst_n_pdd5_pins[] = { + TEGRA_PIN_PEX_L1_RST_N_PDD5, +}; + +static const unsigned pex_l1_clkreq_n_pdd6_pins[] = { + TEGRA_PIN_PEX_L1_CLKREQ_N_PDD6, +}; + +static const unsigned pex_l2_prsnt_n_pdd7_pins[] = { + TEGRA_PIN_PEX_L2_PRSNT_N_PDD7, +}; + +static const unsigned clk3_out_pee0_pins[] = { + TEGRA_PIN_CLK3_OUT_PEE0, +}; + +static const unsigned clk3_req_pee1_pins[] = { + TEGRA_PIN_CLK3_REQ_PEE1, +}; + +static const unsigned clk1_req_pee2_pins[] = { + TEGRA_PIN_CLK1_REQ_PEE2, +}; + +static const unsigned hdmi_cec_pee3_pins[] = { + TEGRA_PIN_HDMI_CEC_PEE3, +}; + +static const unsigned clk_32k_in_pins[] = { + TEGRA_PIN_CLK_32K_IN, +}; + +static const unsigned core_pwr_req_pins[] = { + TEGRA_PIN_CORE_PWR_REQ, +}; + +static const unsigned cpu_pwr_req_pins[] = { + TEGRA_PIN_CPU_PWR_REQ, +}; + +static const unsigned owr_pins[] = { + TEGRA_PIN_OWR, +}; + +static const unsigned pwr_int_n_pins[] = { + TEGRA_PIN_PWR_INT_N, +}; + +static const unsigned drive_ao1_pins[] = { + TEGRA_PIN_KB_ROW0_PR0, + TEGRA_PIN_KB_ROW1_PR1, + TEGRA_PIN_KB_ROW2_PR2, + TEGRA_PIN_KB_ROW3_PR3, + TEGRA_PIN_KB_ROW4_PR4, + TEGRA_PIN_KB_ROW5_PR5, + TEGRA_PIN_KB_ROW6_PR6, + TEGRA_PIN_KB_ROW7_PR7, + TEGRA_PIN_PWR_I2C_SCL_PZ6, + TEGRA_PIN_PWR_I2C_SDA_PZ7, + TEGRA_PIN_SYS_RESET_N, +}; + +static const unsigned drive_ao2_pins[] = { + TEGRA_PIN_CLK_32K_OUT_PA0, + TEGRA_PIN_KB_COL0_PQ0, + TEGRA_PIN_KB_COL1_PQ1, + TEGRA_PIN_KB_COL2_PQ2, + TEGRA_PIN_KB_COL3_PQ3, + TEGRA_PIN_KB_COL4_PQ4, + TEGRA_PIN_KB_COL5_PQ5, + TEGRA_PIN_KB_COL6_PQ6, + TEGRA_PIN_KB_COL7_PQ7, + TEGRA_PIN_KB_ROW8_PS0, + TEGRA_PIN_KB_ROW9_PS1, + TEGRA_PIN_KB_ROW10_PS2, + TEGRA_PIN_KB_ROW11_PS3, + TEGRA_PIN_KB_ROW12_PS4, + TEGRA_PIN_KB_ROW13_PS5, + TEGRA_PIN_KB_ROW14_PS6, + TEGRA_PIN_KB_ROW15_PS7, + TEGRA_PIN_SYS_CLK_REQ_PZ5, + TEGRA_PIN_CLK_32K_IN, + TEGRA_PIN_CORE_PWR_REQ, + TEGRA_PIN_CPU_PWR_REQ, + TEGRA_PIN_PWR_INT_N, +}; + +static const unsigned drive_at1_pins[] = { + TEGRA_PIN_GMI_AD8_PH0, + TEGRA_PIN_GMI_AD9_PH1, + TEGRA_PIN_GMI_AD10_PH2, + TEGRA_PIN_GMI_AD11_PH3, + TEGRA_PIN_GMI_AD12_PH4, + TEGRA_PIN_GMI_AD13_PH5, + TEGRA_PIN_GMI_AD14_PH6, + TEGRA_PIN_GMI_AD15_PH7, + TEGRA_PIN_GMI_IORDY_PI5, + TEGRA_PIN_GMI_CS7_N_PI6, +}; + +static const unsigned drive_at2_pins[] = { + TEGRA_PIN_GMI_AD0_PG0, + TEGRA_PIN_GMI_AD1_PG1, + TEGRA_PIN_GMI_AD2_PG2, + TEGRA_PIN_GMI_AD3_PG3, + TEGRA_PIN_GMI_AD4_PG4, + TEGRA_PIN_GMI_AD5_PG5, + TEGRA_PIN_GMI_AD6_PG6, + TEGRA_PIN_GMI_AD7_PG7, + TEGRA_PIN_GMI_WR_N_PI0, + TEGRA_PIN_GMI_OE_N_PI1, + TEGRA_PIN_GMI_DQS_PI2, + TEGRA_PIN_GMI_CS6_N_PI3, + TEGRA_PIN_GMI_RST_N_PI4, + TEGRA_PIN_GMI_WAIT_PI7, + TEGRA_PIN_GMI_ADV_N_PK0, + TEGRA_PIN_GMI_CLK_PK1, + TEGRA_PIN_GMI_CS4_N_PK2, + TEGRA_PIN_GMI_CS2_N_PK3, + TEGRA_PIN_GMI_CS3_N_PK4, +}; + +static const unsigned drive_at3_pins[] = { + TEGRA_PIN_GMI_WP_N_PC7, + TEGRA_PIN_GMI_CS0_N_PJ0, +}; + +static const unsigned drive_at4_pins[] = { + TEGRA_PIN_GMI_A17_PB0, + TEGRA_PIN_GMI_A18_PB1, + TEGRA_PIN_GMI_CS1_N_PJ2, + TEGRA_PIN_GMI_A16_PJ7, + TEGRA_PIN_GMI_A19_PK7, +}; + +static const unsigned drive_at5_pins[] = { + TEGRA_PIN_GEN2_I2C_SCL_PT5, + TEGRA_PIN_GEN2_I2C_SDA_PT6, +}; + +static const unsigned drive_cdev1_pins[] = { + TEGRA_PIN_CLK1_OUT_PW4, + TEGRA_PIN_CLK1_REQ_PEE2, +}; + +static const unsigned drive_cdev2_pins[] = { + TEGRA_PIN_CLK2_OUT_PW5, + TEGRA_PIN_CLK2_REQ_PCC5, +}; + +static const unsigned drive_cec_pins[] = { + TEGRA_PIN_HDMI_CEC_PEE3, +}; + +static const unsigned drive_crt_pins[] = { + TEGRA_PIN_CRT_HSYNC_PV6, + TEGRA_PIN_CRT_VSYNC_PV7, +}; + +static const unsigned drive_csus_pins[] = { + TEGRA_PIN_VI_MCLK_PT1, +}; + +static const unsigned drive_dap1_pins[] = { + TEGRA_PIN_SPDIF_OUT_PK5, + TEGRA_PIN_SPDIF_IN_PK6, + TEGRA_PIN_DAP1_FS_PN0, + TEGRA_PIN_DAP1_DIN_PN1, + TEGRA_PIN_DAP1_DOUT_PN2, + TEGRA_PIN_DAP1_SCLK_PN3, +}; + +static const unsigned drive_dap2_pins[] = { + TEGRA_PIN_DAP2_FS_PA2, + TEGRA_PIN_DAP2_SCLK_PA3, + TEGRA_PIN_DAP2_DIN_PA4, + TEGRA_PIN_DAP2_DOUT_PA5, +}; + +static const unsigned drive_dap3_pins[] = { + TEGRA_PIN_DAP3_FS_PP0, + TEGRA_PIN_DAP3_DIN_PP1, + TEGRA_PIN_DAP3_DOUT_PP2, + TEGRA_PIN_DAP3_SCLK_PP3, +}; + +static const unsigned drive_dap4_pins[] = { + TEGRA_PIN_DAP4_FS_PP4, + TEGRA_PIN_DAP4_DIN_PP5, + TEGRA_PIN_DAP4_DOUT_PP6, + TEGRA_PIN_DAP4_SCLK_PP7, +}; + +static const unsigned drive_dbg_pins[] = { + TEGRA_PIN_GEN1_I2C_SCL_PC4, + TEGRA_PIN_GEN1_I2C_SDA_PC5, + TEGRA_PIN_PU0, + TEGRA_PIN_PU1, + TEGRA_PIN_PU2, + TEGRA_PIN_PU3, + TEGRA_PIN_PU4, + TEGRA_PIN_PU5, + TEGRA_PIN_PU6, + TEGRA_PIN_JTAG_RTCK_PU7, + TEGRA_PIN_JTAG_TCK, + TEGRA_PIN_JTAG_TDI, + TEGRA_PIN_JTAG_TDO, + TEGRA_PIN_JTAG_TMS, + TEGRA_PIN_JTAG_TRST_N, + TEGRA_PIN_TEST_MODE_EN, +}; + +static const unsigned drive_ddc_pins[] = { + TEGRA_PIN_DDC_SCL_PV4, + TEGRA_PIN_DDC_SDA_PV5, +}; + +static const unsigned drive_dev3_pins[] = { + TEGRA_PIN_CLK3_OUT_PEE0, + TEGRA_PIN_CLK3_REQ_PEE1, +}; + +static const unsigned drive_gma_pins[] = { + TEGRA_PIN_SDMMC4_DAT0_PAA0, + TEGRA_PIN_SDMMC4_DAT1_PAA1, + TEGRA_PIN_SDMMC4_DAT2_PAA2, + TEGRA_PIN_SDMMC4_DAT3_PAA3, + TEGRA_PIN_SDMMC4_RST_N_PCC3, +}; + +static const unsigned drive_gmb_pins[] = { + TEGRA_PIN_SDMMC4_DAT4_PAA4, + TEGRA_PIN_SDMMC4_DAT5_PAA5, + TEGRA_PIN_SDMMC4_DAT6_PAA6, + TEGRA_PIN_SDMMC4_DAT7_PAA7, +}; + +static const unsigned drive_gmc_pins[] = { + TEGRA_PIN_SDMMC4_CLK_PCC4, +}; + +static const unsigned drive_gmd_pins[] = { + TEGRA_PIN_SDMMC4_CMD_PT7, +}; + +static const unsigned drive_gme_pins[] = { + TEGRA_PIN_PBB0, + TEGRA_PIN_CAM_I2C_SCL_PBB1, + TEGRA_PIN_CAM_I2C_SDA_PBB2, + TEGRA_PIN_PBB3, + TEGRA_PIN_PCC2, +}; + +static const unsigned drive_gmf_pins[] = { + TEGRA_PIN_PBB4, + TEGRA_PIN_PBB5, + TEGRA_PIN_PBB6, + TEGRA_PIN_PBB7, +}; + +static const unsigned drive_gmg_pins[] = { + TEGRA_PIN_CAM_MCLK_PCC0, +}; + +static const unsigned drive_gmh_pins[] = { + TEGRA_PIN_PCC1, +}; + +static const unsigned drive_gpv_pins[] = { + TEGRA_PIN_PEX_L2_RST_N_PCC6, + TEGRA_PIN_PEX_L2_CLKREQ_N_PCC7, + TEGRA_PIN_PEX_L0_PRSNT_N_PDD0, + TEGRA_PIN_PEX_L0_RST_N_PDD1, + TEGRA_PIN_PEX_L0_CLKREQ_N_PDD2, + TEGRA_PIN_PEX_WAKE_N_PDD3, + TEGRA_PIN_PEX_L1_PRSNT_N_PDD4, + TEGRA_PIN_PEX_L1_RST_N_PDD5, + TEGRA_PIN_PEX_L1_CLKREQ_N_PDD6, + TEGRA_PIN_PEX_L2_PRSNT_N_PDD7, +}; + +static const unsigned drive_lcd1_pins[] = { + TEGRA_PIN_LCD_PWR1_PC1, + TEGRA_PIN_LCD_PWR2_PC6, + TEGRA_PIN_LCD_CS0_N_PN4, + TEGRA_PIN_LCD_SDOUT_PN5, + TEGRA_PIN_LCD_DC0_PN6, + TEGRA_PIN_LCD_SDIN_PZ2, + TEGRA_PIN_LCD_WR_N_PZ3, + TEGRA_PIN_LCD_SCK_PZ4, +}; + +static const unsigned drive_lcd2_pins[] = { + TEGRA_PIN_LCD_PWR0_PB2, + TEGRA_PIN_LCD_PCLK_PB3, + TEGRA_PIN_LCD_DC1_PD2, + TEGRA_PIN_LCD_D0_PE0, + TEGRA_PIN_LCD_D1_PE1, + TEGRA_PIN_LCD_D2_PE2, + TEGRA_PIN_LCD_D3_PE3, + TEGRA_PIN_LCD_D4_PE4, + TEGRA_PIN_LCD_D5_PE5, + TEGRA_PIN_LCD_D6_PE6, + TEGRA_PIN_LCD_D7_PE7, + TEGRA_PIN_LCD_D8_PF0, + TEGRA_PIN_LCD_D9_PF1, + TEGRA_PIN_LCD_D10_PF2, + TEGRA_PIN_LCD_D11_PF3, + TEGRA_PIN_LCD_D12_PF4, + TEGRA_PIN_LCD_D13_PF5, + TEGRA_PIN_LCD_D14_PF6, + TEGRA_PIN_LCD_D15_PF7, + TEGRA_PIN_LCD_DE_PJ1, + TEGRA_PIN_LCD_HSYNC_PJ3, + TEGRA_PIN_LCD_VSYNC_PJ4, + TEGRA_PIN_LCD_D16_PM0, + TEGRA_PIN_LCD_D17_PM1, + TEGRA_PIN_LCD_D18_PM2, + TEGRA_PIN_LCD_D19_PM3, + TEGRA_PIN_LCD_D20_PM4, + TEGRA_PIN_LCD_D21_PM5, + TEGRA_PIN_LCD_D22_PM6, + TEGRA_PIN_LCD_D23_PM7, + TEGRA_PIN_HDMI_INT_PN7, + TEGRA_PIN_LCD_CS1_N_PW0, + TEGRA_PIN_LCD_M1_PW1, +}; + +static const unsigned drive_owr_pins[] = { + TEGRA_PIN_OWR, +}; + +static const unsigned drive_sdio1_pins[] = { + TEGRA_PIN_SDMMC1_DAT3_PY4, + TEGRA_PIN_SDMMC1_DAT2_PY5, + TEGRA_PIN_SDMMC1_DAT1_PY6, + TEGRA_PIN_SDMMC1_DAT0_PY7, + TEGRA_PIN_SDMMC1_CLK_PZ0, + TEGRA_PIN_SDMMC1_CMD_PZ1, +}; + +static const unsigned drive_sdio2_pins[] = { + TEGRA_PIN_SDMMC3_DAT5_PD0, + TEGRA_PIN_SDMMC3_DAT4_PD1, + TEGRA_PIN_SDMMC3_DAT6_PD3, + TEGRA_PIN_SDMMC3_DAT7_PD4, +}; + +static const unsigned drive_sdio3_pins[] = { + TEGRA_PIN_SDMMC3_CLK_PA6, + TEGRA_PIN_SDMMC3_CMD_PA7, + TEGRA_PIN_SDMMC3_DAT3_PB4, + TEGRA_PIN_SDMMC3_DAT2_PB5, + TEGRA_PIN_SDMMC3_DAT1_PB6, + TEGRA_PIN_SDMMC3_DAT0_PB7, +}; + +static const unsigned drive_spi_pins[] = { + TEGRA_PIN_SPI2_CS1_N_PW2, + TEGRA_PIN_SPI2_CS2_N_PW3, + TEGRA_PIN_SPI2_MOSI_PX0, + TEGRA_PIN_SPI2_MISO_PX1, + TEGRA_PIN_SPI2_SCK_PX2, + TEGRA_PIN_SPI2_CS0_N_PX3, + TEGRA_PIN_SPI1_MOSI_PX4, + TEGRA_PIN_SPI1_SCK_PX5, + TEGRA_PIN_SPI1_CS0_N_PX6, + TEGRA_PIN_SPI1_MISO_PX7, +}; + +static const unsigned drive_uaa_pins[] = { + TEGRA_PIN_ULPI_DATA0_PO1, + TEGRA_PIN_ULPI_DATA1_PO2, + TEGRA_PIN_ULPI_DATA2_PO3, + TEGRA_PIN_ULPI_DATA3_PO4, +}; + +static const unsigned drive_uab_pins[] = { + TEGRA_PIN_ULPI_DATA7_PO0, + TEGRA_PIN_ULPI_DATA4_PO5, + TEGRA_PIN_ULPI_DATA5_PO6, + TEGRA_PIN_ULPI_DATA6_PO7, + TEGRA_PIN_PV0, + TEGRA_PIN_PV1, + TEGRA_PIN_PV2, + TEGRA_PIN_PV3, +}; + +static const unsigned drive_uart2_pins[] = { + TEGRA_PIN_UART2_TXD_PC2, + TEGRA_PIN_UART2_RXD_PC3, + TEGRA_PIN_UART2_CTS_N_PJ5, + TEGRA_PIN_UART2_RTS_N_PJ6, +}; + +static const unsigned drive_uart3_pins[] = { + TEGRA_PIN_UART3_CTS_N_PA1, + TEGRA_PIN_UART3_RTS_N_PC0, + TEGRA_PIN_UART3_TXD_PW6, + TEGRA_PIN_UART3_RXD_PW7, +}; + +static const unsigned drive_uda_pins[] = { + TEGRA_PIN_ULPI_CLK_PY0, + TEGRA_PIN_ULPI_DIR_PY1, + TEGRA_PIN_ULPI_NXT_PY2, + TEGRA_PIN_ULPI_STP_PY3, +}; + +static const unsigned drive_vi1_pins[] = { + TEGRA_PIN_VI_D1_PD5, + TEGRA_PIN_VI_VSYNC_PD6, + TEGRA_PIN_VI_HSYNC_PD7, + TEGRA_PIN_VI_D2_PL0, + TEGRA_PIN_VI_D3_PL1, + TEGRA_PIN_VI_D4_PL2, + TEGRA_PIN_VI_D5_PL3, + TEGRA_PIN_VI_D6_PL4, + TEGRA_PIN_VI_D7_PL5, + TEGRA_PIN_VI_D8_PL6, + TEGRA_PIN_VI_D9_PL7, + TEGRA_PIN_VI_PCLK_PT0, + TEGRA_PIN_VI_D10_PT2, + TEGRA_PIN_VI_D11_PT3, + TEGRA_PIN_VI_D0_PT4, +}; + +enum tegra_mux { + TEGRA_MUX_BLINK, + TEGRA_MUX_CEC, + TEGRA_MUX_CLK_12M_OUT, + TEGRA_MUX_CLK_32K_IN, + TEGRA_MUX_CORE_PWR_REQ, + TEGRA_MUX_CPU_PWR_REQ, + TEGRA_MUX_CRT, + TEGRA_MUX_DAP, + TEGRA_MUX_DDR, + TEGRA_MUX_DEV3, + TEGRA_MUX_DISPLAYA, + TEGRA_MUX_DISPLAYB, + TEGRA_MUX_DTV, + TEGRA_MUX_EXTPERIPH1, + TEGRA_MUX_EXTPERIPH2, + TEGRA_MUX_EXTPERIPH3, + TEGRA_MUX_GMI, + TEGRA_MUX_GMI_ALT, + TEGRA_MUX_HDA, + TEGRA_MUX_HDCP, + TEGRA_MUX_HDMI, + TEGRA_MUX_HSI, + TEGRA_MUX_I2C1, + TEGRA_MUX_I2C2, + TEGRA_MUX_I2C3, + TEGRA_MUX_I2C4, + TEGRA_MUX_I2CPWR, + TEGRA_MUX_I2S0, + TEGRA_MUX_I2S1, + TEGRA_MUX_I2S2, + TEGRA_MUX_I2S3, + TEGRA_MUX_I2S4, + TEGRA_MUX_INVALID, + TEGRA_MUX_KBC, + TEGRA_MUX_MIO, + TEGRA_MUX_NAND, + TEGRA_MUX_NAND_ALT, + TEGRA_MUX_OWR, + TEGRA_MUX_PCIE, + TEGRA_MUX_PWM0, + TEGRA_MUX_PWM1, + TEGRA_MUX_PWM2, + TEGRA_MUX_PWM3, + TEGRA_MUX_PWR_INT_N, + TEGRA_MUX_RSVD1, + TEGRA_MUX_RSVD2, + TEGRA_MUX_RSVD3, + TEGRA_MUX_RSVD4, + TEGRA_MUX_RTCK, + TEGRA_MUX_SATA, + TEGRA_MUX_SDMMC1, + TEGRA_MUX_SDMMC2, + TEGRA_MUX_SDMMC3, + TEGRA_MUX_SDMMC4, + TEGRA_MUX_SPDIF, + TEGRA_MUX_SPI1, + TEGRA_MUX_SPI2, + TEGRA_MUX_SPI2_ALT, + TEGRA_MUX_SPI3, + TEGRA_MUX_SPI4, + TEGRA_MUX_SPI5, + TEGRA_MUX_SPI6, + TEGRA_MUX_SYSCLK, + TEGRA_MUX_TEST, + TEGRA_MUX_TRACE, + TEGRA_MUX_UARTA, + TEGRA_MUX_UARTB, + TEGRA_MUX_UARTC, + TEGRA_MUX_UARTD, + TEGRA_MUX_UARTE, + TEGRA_MUX_ULPI, + TEGRA_MUX_VGP1, + TEGRA_MUX_VGP2, + TEGRA_MUX_VGP3, + TEGRA_MUX_VGP4, + TEGRA_MUX_VGP5, + TEGRA_MUX_VGP6, + TEGRA_MUX_VI, + TEGRA_MUX_VI_ALT1, + TEGRA_MUX_VI_ALT2, + TEGRA_MUX_VI_ALT3, +}; +static const char * const blink_groups[] = { + "clk_32k_out_pa0", +}; + +static const char * const cec_groups[] = { + "hdmi_cec_pee3", + "owr", +}; + +static const char * const clk_12m_out_groups[] = { + "pv3", +}; + +static const char * const clk_32k_in_groups[] = { + "clk_32k_in", +}; + +static const char * const core_pwr_req_groups[] = { + "core_pwr_req", +}; + +static const char * const cpu_pwr_req_groups[] = { + "cpu_pwr_req", +}; + +static const char * const crt_groups[] = { + "crt_hsync_pv6", + "crt_vsync_pv7", +}; + +static const char * const dap_groups[] = { + "clk1_req_pee2", + "clk2_req_pcc5", +}; + +static const char * const ddr_groups[] = { + "vi_d0_pt4", + "vi_d1_pd5", + "vi_d10_pt2", + "vi_d11_pt3", + "vi_d2_pl0", + "vi_d3_pl1", + "vi_d4_pl2", + "vi_d5_pl3", + "vi_d6_pl4", + "vi_d7_pl5", + "vi_d8_pl6", + "vi_d9_pl7", + "vi_hsync_pd7", + "vi_vsync_pd6", +}; + +static const char * const dev3_groups[] = { + "clk3_req_pee1", +}; + +static const char * const displaya_groups[] = { + "dap3_din_pp1", + "dap3_dout_pp2", + "dap3_fs_pp0", + "dap3_sclk_pp3", + "pbb3", + "pbb4", + "pbb5", + "pbb6", + "lcd_cs0_n_pn4", + "lcd_cs1_n_pw0", + "lcd_d0_pe0", + "lcd_d1_pe1", + "lcd_d10_pf2", + "lcd_d11_pf3", + "lcd_d12_pf4", + "lcd_d13_pf5", + "lcd_d14_pf6", + "lcd_d15_pf7", + "lcd_d16_pm0", + "lcd_d17_pm1", + "lcd_d18_pm2", + "lcd_d19_pm3", + "lcd_d2_pe2", + "lcd_d20_pm4", + "lcd_d21_pm5", + "lcd_d22_pm6", + "lcd_d23_pm7", + "lcd_d3_pe3", + "lcd_d4_pe4", + "lcd_d5_pe5", + "lcd_d6_pe6", + "lcd_d7_pe7", + "lcd_d8_pf0", + "lcd_d9_pf1", + "lcd_dc0_pn6", + "lcd_dc1_pd2", + "lcd_de_pj1", + "lcd_hsync_pj3", + "lcd_m1_pw1", + "lcd_pclk_pb3", + "lcd_pwr0_pb2", + "lcd_pwr1_pc1", + "lcd_pwr2_pc6", + "lcd_sck_pz4", + "lcd_sdin_pz2", + "lcd_sdout_pn5", + "lcd_vsync_pj4", + "lcd_wr_n_pz3", +}; + +static const char * const displayb_groups[] = { + "dap3_din_pp1", + "dap3_dout_pp2", + "dap3_fs_pp0", + "dap3_sclk_pp3", + "pbb3", + "pbb4", + "pbb5", + "pbb6", + "lcd_cs0_n_pn4", + "lcd_cs1_n_pw0", + "lcd_d0_pe0", + "lcd_d1_pe1", + "lcd_d10_pf2", + "lcd_d11_pf3", + "lcd_d12_pf4", + "lcd_d13_pf5", + "lcd_d14_pf6", + "lcd_d15_pf7", + "lcd_d16_pm0", + "lcd_d17_pm1", + "lcd_d18_pm2", + "lcd_d19_pm3", + "lcd_d2_pe2", + "lcd_d20_pm4", + "lcd_d21_pm5", + "lcd_d22_pm6", + "lcd_d23_pm7", + "lcd_d3_pe3", + "lcd_d4_pe4", + "lcd_d5_pe5", + "lcd_d6_pe6", + "lcd_d7_pe7", + "lcd_d8_pf0", + "lcd_d9_pf1", + "lcd_dc0_pn6", + "lcd_dc1_pd2", + "lcd_de_pj1", + "lcd_hsync_pj3", + "lcd_m1_pw1", + "lcd_pclk_pb3", + "lcd_pwr0_pb2", + "lcd_pwr1_pc1", + "lcd_pwr2_pc6", + "lcd_sck_pz4", + "lcd_sdin_pz2", + "lcd_sdout_pn5", + "lcd_vsync_pj4", + "lcd_wr_n_pz3", +}; + +static const char * const dtv_groups[] = { + "gmi_a17_pb0", + "gmi_a18_pb1", + "gmi_cs0_n_pj0", + "gmi_cs1_n_pj2", +}; + +static const char * const extperiph1_groups[] = { + "clk1_out_pw4", +}; + +static const char * const extperiph2_groups[] = { + "clk2_out_pw5", +}; + +static const char * const extperiph3_groups[] = { + "clk3_out_pee0", +}; + +static const char * const gmi_groups[] = { + "dap1_din_pn1", + "dap1_dout_pn2", + "dap1_fs_pn0", + "dap1_sclk_pn3", + "dap2_din_pa4", + "dap2_dout_pa5", + "dap2_fs_pa2", + "dap2_sclk_pa3", + "dap4_din_pp5", + "dap4_dout_pp6", + "dap4_fs_pp4", + "dap4_sclk_pp7", + "gen2_i2c_scl_pt5", + "gen2_i2c_sda_pt6", + "gmi_a16_pj7", + "gmi_a17_pb0", + "gmi_a18_pb1", + "gmi_a19_pk7", + "gmi_ad0_pg0", + "gmi_ad1_pg1", + "gmi_ad10_ph2", + "gmi_ad11_ph3", + "gmi_ad12_ph4", + "gmi_ad13_ph5", + "gmi_ad14_ph6", + "gmi_ad15_ph7", + "gmi_ad2_pg2", + "gmi_ad3_pg3", + "gmi_ad4_pg4", + "gmi_ad5_pg5", + "gmi_ad6_pg6", + "gmi_ad7_pg7", + "gmi_ad8_ph0", + "gmi_ad9_ph1", + "gmi_adv_n_pk0", + "gmi_clk_pk1", + "gmi_cs0_n_pj0", + "gmi_cs1_n_pj2", + "gmi_cs2_n_pk3", + "gmi_cs3_n_pk4", + "gmi_cs4_n_pk2", + "gmi_cs6_n_pi3", + "gmi_cs7_n_pi6", + "gmi_dqs_pi2", + "gmi_iordy_pi5", + "gmi_oe_n_pi1", + "gmi_rst_n_pi4", + "gmi_wait_pi7", + "gmi_wp_n_pc7", + "gmi_wr_n_pi0", + "pu0", + "pu1", + "pu2", + "pu3", + "pu4", + "pu5", + "pu6", + "sdmmc4_clk_pcc4", + "sdmmc4_cmd_pt7", + "sdmmc4_dat0_paa0", + "sdmmc4_dat1_paa1", + "sdmmc4_dat2_paa2", + "sdmmc4_dat3_paa3", + "sdmmc4_dat4_paa4", + "sdmmc4_dat5_paa5", + "sdmmc4_dat6_paa6", + "sdmmc4_dat7_paa7", + "spi1_cs0_n_px6", + "spi1_mosi_px4", + "spi1_sck_px5", + "spi2_cs0_n_px3", + "spi2_miso_px1", + "spi2_mosi_px0", + "spi2_sck_px2", + "uart2_cts_n_pj5", + "uart2_rts_n_pj6", + "uart3_cts_n_pa1", + "uart3_rts_n_pc0", + "uart3_rxd_pw7", + "uart3_txd_pw6", +}; + +static const char * const gmi_alt_groups[] = { + "gmi_a16_pj7", + "gmi_cs3_n_pk4", + "gmi_cs7_n_pi6", + "gmi_wp_n_pc7", +}; + +static const char * const hda_groups[] = { + "clk1_req_pee2", + "dap1_din_pn1", + "dap1_dout_pn2", + "dap1_fs_pn0", + "dap1_sclk_pn3", + "dap2_din_pa4", + "dap2_dout_pa5", + "dap2_fs_pa2", + "dap2_sclk_pa3", + "pex_l0_clkreq_n_pdd2", + "pex_l0_prsnt_n_pdd0", + "pex_l0_rst_n_pdd1", + "pex_l1_clkreq_n_pdd6", + "pex_l1_prsnt_n_pdd4", + "pex_l1_rst_n_pdd5", + "pex_l2_clkreq_n_pcc7", + "pex_l2_prsnt_n_pdd7", + "pex_l2_rst_n_pcc6", + "pex_wake_n_pdd3", + "spdif_in_pk6", +}; + +static const char * const hdcp_groups[] = { + "gen2_i2c_scl_pt5", + "gen2_i2c_sda_pt6", + "lcd_pwr0_pb2", + "lcd_pwr2_pc6", + "lcd_sck_pz4", + "lcd_sdout_pn5", + "lcd_wr_n_pz3", +}; + +static const char * const hdmi_groups[] = { + "hdmi_int_pn7", +}; + +static const char * const hsi_groups[] = { + "ulpi_data0_po1", + "ulpi_data1_po2", + "ulpi_data2_po3", + "ulpi_data3_po4", + "ulpi_data4_po5", + "ulpi_data5_po6", + "ulpi_data6_po7", + "ulpi_data7_po0", +}; + +static const char * const i2c1_groups[] = { + "gen1_i2c_scl_pc4", + "gen1_i2c_sda_pc5", + "spdif_in_pk6", + "spdif_out_pk5", + "spi2_cs1_n_pw2", + "spi2_cs2_n_pw3", +}; + +static const char * const i2c2_groups[] = { + "gen2_i2c_scl_pt5", + "gen2_i2c_sda_pt6", +}; + +static const char * const i2c3_groups[] = { + "cam_i2c_scl_pbb1", + "cam_i2c_sda_pbb2", + "sdmmc4_cmd_pt7", + "sdmmc4_dat4_paa4", +}; + +static const char * const i2c4_groups[] = { + "ddc_scl_pv4", + "ddc_sda_pv5", +}; + +static const char * const i2cpwr_groups[] = { + "pwr_i2c_scl_pz6", + "pwr_i2c_sda_pz7", +}; + +static const char * const i2s0_groups[] = { + "dap1_din_pn1", + "dap1_dout_pn2", + "dap1_fs_pn0", + "dap1_sclk_pn3", +}; + +static const char * const i2s1_groups[] = { + "dap2_din_pa4", + "dap2_dout_pa5", + "dap2_fs_pa2", + "dap2_sclk_pa3", +}; + +static const char * const i2s2_groups[] = { + "dap3_din_pp1", + "dap3_dout_pp2", + "dap3_fs_pp0", + "dap3_sclk_pp3", +}; + +static const char * const i2s3_groups[] = { + "dap4_din_pp5", + "dap4_dout_pp6", + "dap4_fs_pp4", + "dap4_sclk_pp7", +}; + +static const char * const i2s4_groups[] = { + "pbb0", + "pbb7", + "pcc1", + "pcc2", + "sdmmc4_dat4_paa4", + "sdmmc4_dat5_paa5", + "sdmmc4_dat6_paa6", + "sdmmc4_dat7_paa7", +}; + +static const char * const invalid_groups[] = { + "kb_row3_pr3", + "sdmmc4_clk_pcc4", +}; + +static const char * const kbc_groups[] = { + "kb_col0_pq0", + "kb_col1_pq1", + "kb_col2_pq2", + "kb_col3_pq3", + "kb_col4_pq4", + "kb_col5_pq5", + "kb_col6_pq6", + "kb_col7_pq7", + "kb_row0_pr0", + "kb_row1_pr1", + "kb_row10_ps2", + "kb_row11_ps3", + "kb_row12_ps4", + "kb_row13_ps5", + "kb_row14_ps6", + "kb_row15_ps7", + "kb_row2_pr2", + "kb_row3_pr3", + "kb_row4_pr4", + "kb_row5_pr5", + "kb_row6_pr6", + "kb_row7_pr7", + "kb_row8_ps0", + "kb_row9_ps1", +}; + +static const char * const mio_groups[] = { + "kb_col6_pq6", + "kb_col7_pq7", + "kb_row10_ps2", + "kb_row11_ps3", + "kb_row12_ps4", + "kb_row13_ps5", + "kb_row14_ps6", + "kb_row15_ps7", + "kb_row6_pr6", + "kb_row7_pr7", + "kb_row8_ps0", + "kb_row9_ps1", +}; + +static const char * const nand_groups[] = { + "gmi_ad0_pg0", + "gmi_ad1_pg1", + "gmi_ad10_ph2", + "gmi_ad11_ph3", + "gmi_ad12_ph4", + "gmi_ad13_ph5", + "gmi_ad14_ph6", + "gmi_ad15_ph7", + "gmi_ad2_pg2", + "gmi_ad3_pg3", + "gmi_ad4_pg4", + "gmi_ad5_pg5", + "gmi_ad6_pg6", + "gmi_ad7_pg7", + "gmi_ad8_ph0", + "gmi_ad9_ph1", + "gmi_adv_n_pk0", + "gmi_clk_pk1", + "gmi_cs0_n_pj0", + "gmi_cs1_n_pj2", + "gmi_cs2_n_pk3", + "gmi_cs3_n_pk4", + "gmi_cs4_n_pk2", + "gmi_cs6_n_pi3", + "gmi_cs7_n_pi6", + "gmi_dqs_pi2", + "gmi_iordy_pi5", + "gmi_oe_n_pi1", + "gmi_rst_n_pi4", + "gmi_wait_pi7", + "gmi_wp_n_pc7", + "gmi_wr_n_pi0", + "kb_col0_pq0", + "kb_col1_pq1", + "kb_col2_pq2", + "kb_col3_pq3", + "kb_col4_pq4", + "kb_col5_pq5", + "kb_col6_pq6", + "kb_col7_pq7", + "kb_row0_pr0", + "kb_row1_pr1", + "kb_row10_ps2", + "kb_row11_ps3", + "kb_row12_ps4", + "kb_row13_ps5", + "kb_row14_ps6", + "kb_row15_ps7", + "kb_row2_pr2", + "kb_row3_pr3", + "kb_row4_pr4", + "kb_row5_pr5", + "kb_row6_pr6", + "kb_row7_pr7", + "kb_row8_ps0", + "kb_row9_ps1", + "sdmmc4_clk_pcc4", + "sdmmc4_cmd_pt7", +}; + +static const char * const nand_alt_groups[] = { + "gmi_cs6_n_pi3", + "gmi_cs7_n_pi6", + "gmi_rst_n_pi4", +}; + +static const char * const owr_groups[] = { + "pu0", + "pv2", + "kb_row5_pr5", + "owr", +}; + +static const char * const pcie_groups[] = { + "pex_l0_clkreq_n_pdd2", + "pex_l0_prsnt_n_pdd0", + "pex_l0_rst_n_pdd1", + "pex_l1_clkreq_n_pdd6", + "pex_l1_prsnt_n_pdd4", + "pex_l1_rst_n_pdd5", + "pex_l2_clkreq_n_pcc7", + "pex_l2_prsnt_n_pdd7", + "pex_l2_rst_n_pcc6", + "pex_wake_n_pdd3", +}; + +static const char * const pwm0_groups[] = { + "gmi_ad8_ph0", + "pu3", + "sdmmc3_dat3_pb4", + "sdmmc3_dat5_pd0", + "uart3_rts_n_pc0", +}; + +static const char * const pwm1_groups[] = { + "gmi_ad9_ph1", + "pu4", + "sdmmc3_dat2_pb5", + "sdmmc3_dat4_pd1", +}; + +static const char * const pwm2_groups[] = { + "gmi_ad10_ph2", + "pu5", + "sdmmc3_clk_pa6", +}; + +static const char * const pwm3_groups[] = { + "gmi_ad11_ph3", + "pu6", + "sdmmc3_cmd_pa7", +}; + +static const char * const pwr_int_n_groups[] = { + "pwr_int_n", +}; + +static const char * const rsvd1_groups[] = { + "gmi_ad0_pg0", + "gmi_ad1_pg1", + "gmi_ad12_ph4", + "gmi_ad13_ph5", + "gmi_ad14_ph6", + "gmi_ad15_ph7", + "gmi_ad2_pg2", + "gmi_ad3_pg3", + "gmi_ad4_pg4", + "gmi_ad5_pg5", + "gmi_ad6_pg6", + "gmi_ad7_pg7", + "gmi_adv_n_pk0", + "gmi_clk_pk1", + "gmi_cs0_n_pj0", + "gmi_cs1_n_pj2", + "gmi_cs2_n_pk3", + "gmi_cs3_n_pk4", + "gmi_cs4_n_pk2", + "gmi_dqs_pi2", + "gmi_iordy_pi5", + "gmi_oe_n_pi1", + "gmi_wait_pi7", + "gmi_wp_n_pc7", + "gmi_wr_n_pi0", + "pu1", + "pu2", + "pv0", + "pv1", + "sdmmc3_dat0_pb7", + "sdmmc3_dat1_pb6", + "sdmmc3_dat2_pb5", + "sdmmc3_dat3_pb4", + "vi_pclk_pt0", +}; + +static const char * const rsvd2_groups[] = { + "clk1_out_pw4", + "clk2_out_pw5", + "clk2_req_pcc5", + "clk3_out_pee0", + "clk3_req_pee1", + "clk_32k_in", + "clk_32k_out_pa0", + "core_pwr_req", + "cpu_pwr_req", + "crt_hsync_pv6", + "crt_vsync_pv7", + "dap3_din_pp1", + "dap3_dout_pp2", + "dap3_fs_pp0", + "dap3_sclk_pp3", + "dap4_din_pp5", + "dap4_dout_pp6", + "dap4_fs_pp4", + "dap4_sclk_pp7", + "ddc_scl_pv4", + "ddc_sda_pv5", + "gen1_i2c_scl_pc4", + "gen1_i2c_sda_pc5", + "pbb0", + "pbb7", + "pcc1", + "pcc2", + "pv0", + "pv1", + "pv2", + "pv3", + "hdmi_cec_pee3", + "hdmi_int_pn7", + "jtag_rtck_pu7", + "pwr_i2c_scl_pz6", + "pwr_i2c_sda_pz7", + "pwr_int_n", + "sdmmc1_clk_pz0", + "sdmmc1_cmd_pz1", + "sdmmc1_dat0_py7", + "sdmmc1_dat1_py6", + "sdmmc1_dat2_py5", + "sdmmc1_dat3_py4", + "sdmmc3_dat0_pb7", + "sdmmc3_dat1_pb6", + "sdmmc4_rst_n_pcc3", + "spdif_out_pk5", + "sys_clk_req_pz5", + "uart3_cts_n_pa1", + "uart3_rxd_pw7", + "uart3_txd_pw6", + "ulpi_clk_py0", + "ulpi_dir_py1", + "ulpi_nxt_py2", + "ulpi_stp_py3", + "vi_d0_pt4", + "vi_d10_pt2", + "vi_d11_pt3", + "vi_hsync_pd7", + "vi_vsync_pd6", +}; + +static const char * const rsvd3_groups[] = { + "cam_i2c_scl_pbb1", + "cam_i2c_sda_pbb2", + "clk1_out_pw4", + "clk1_req_pee2", + "clk2_out_pw5", + "clk2_req_pcc5", + "clk3_out_pee0", + "clk3_req_pee1", + "clk_32k_in", + "clk_32k_out_pa0", + "core_pwr_req", + "cpu_pwr_req", + "crt_hsync_pv6", + "crt_vsync_pv7", + "dap2_din_pa4", + "dap2_dout_pa5", + "dap2_fs_pa2", + "dap2_sclk_pa3", + "ddc_scl_pv4", + "ddc_sda_pv5", + "gen1_i2c_scl_pc4", + "gen1_i2c_sda_pc5", + "pbb0", + "pbb7", + "pcc1", + "pcc2", + "pv0", + "pv1", + "pv2", + "pv3", + "hdmi_cec_pee3", + "hdmi_int_pn7", + "jtag_rtck_pu7", + "kb_row0_pr0", + "kb_row1_pr1", + "kb_row2_pr2", + "kb_row3_pr3", + "lcd_d0_pe0", + "lcd_d1_pe1", + "lcd_d10_pf2", + "lcd_d11_pf3", + "lcd_d12_pf4", + "lcd_d13_pf5", + "lcd_d14_pf6", + "lcd_d15_pf7", + "lcd_d16_pm0", + "lcd_d17_pm1", + "lcd_d18_pm2", + "lcd_d19_pm3", + "lcd_d2_pe2", + "lcd_d20_pm4", + "lcd_d21_pm5", + "lcd_d22_pm6", + "lcd_d23_pm7", + "lcd_d3_pe3", + "lcd_d4_pe4", + "lcd_d5_pe5", + "lcd_d6_pe6", + "lcd_d7_pe7", + "lcd_d8_pf0", + "lcd_d9_pf1", + "lcd_dc0_pn6", + "lcd_dc1_pd2", + "lcd_de_pj1", + "lcd_hsync_pj3", + "lcd_m1_pw1", + "lcd_pclk_pb3", + "lcd_pwr1_pc1", + "lcd_vsync_pj4", + "owr", + "pex_l0_clkreq_n_pdd2", + "pex_l0_prsnt_n_pdd0", + "pex_l0_rst_n_pdd1", + "pex_l1_clkreq_n_pdd6", + "pex_l1_prsnt_n_pdd4", + "pex_l1_rst_n_pdd5", + "pex_l2_clkreq_n_pcc7", + "pex_l2_prsnt_n_pdd7", + "pex_l2_rst_n_pcc6", + "pex_wake_n_pdd3", + "pwr_i2c_scl_pz6", + "pwr_i2c_sda_pz7", + "pwr_int_n", + "sdmmc1_clk_pz0", + "sdmmc1_cmd_pz1", + "sdmmc4_rst_n_pcc3", + "sys_clk_req_pz5", +}; + +static const char * const rsvd4_groups[] = { + "clk1_out_pw4", + "clk1_req_pee2", + "clk2_out_pw5", + "clk2_req_pcc5", + "clk3_out_pee0", + "clk3_req_pee1", + "clk_32k_in", + "clk_32k_out_pa0", + "core_pwr_req", + "cpu_pwr_req", + "crt_hsync_pv6", + "crt_vsync_pv7", + "dap4_din_pp5", + "dap4_dout_pp6", + "dap4_fs_pp4", + "dap4_sclk_pp7", + "ddc_scl_pv4", + "ddc_sda_pv5", + "gen1_i2c_scl_pc4", + "gen1_i2c_sda_pc5", + "gen2_i2c_scl_pt5", + "gen2_i2c_sda_pt6", + "gmi_a19_pk7", + "gmi_ad0_pg0", + "gmi_ad1_pg1", + "gmi_ad10_ph2", + "gmi_ad11_ph3", + "gmi_ad12_ph4", + "gmi_ad13_ph5", + "gmi_ad14_ph6", + "gmi_ad15_ph7", + "gmi_ad2_pg2", + "gmi_ad3_pg3", + "gmi_ad4_pg4", + "gmi_ad5_pg5", + "gmi_ad6_pg6", + "gmi_ad7_pg7", + "gmi_ad8_ph0", + "gmi_ad9_ph1", + "gmi_adv_n_pk0", + "gmi_clk_pk1", + "gmi_cs2_n_pk3", + "gmi_cs4_n_pk2", + "gmi_dqs_pi2", + "gmi_iordy_pi5", + "gmi_oe_n_pi1", + "gmi_rst_n_pi4", + "gmi_wait_pi7", + "gmi_wr_n_pi0", + "pcc2", + "pu0", + "pu1", + "pu2", + "pu3", + "pu4", + "pu5", + "pu6", + "pv0", + "pv1", + "pv2", + "pv3", + "hdmi_cec_pee3", + "hdmi_int_pn7", + "jtag_rtck_pu7", + "kb_col2_pq2", + "kb_col3_pq3", + "kb_col4_pq4", + "kb_col5_pq5", + "kb_row0_pr0", + "kb_row1_pr1", + "kb_row2_pr2", + "kb_row4_pr4", + "lcd_cs0_n_pn4", + "lcd_cs1_n_pw0", + "lcd_d0_pe0", + "lcd_d1_pe1", + "lcd_d10_pf2", + "lcd_d11_pf3", + "lcd_d12_pf4", + "lcd_d13_pf5", + "lcd_d14_pf6", + "lcd_d15_pf7", + "lcd_d16_pm0", + "lcd_d17_pm1", + "lcd_d18_pm2", + "lcd_d19_pm3", + "lcd_d2_pe2", + "lcd_d20_pm4", + "lcd_d21_pm5", + "lcd_d22_pm6", + "lcd_d23_pm7", + "lcd_d3_pe3", + "lcd_d4_pe4", + "lcd_d5_pe5", + "lcd_d6_pe6", + "lcd_d7_pe7", + "lcd_d8_pf0", + "lcd_d9_pf1", + "lcd_dc0_pn6", + "lcd_dc1_pd2", + "lcd_de_pj1", + "lcd_hsync_pj3", + "lcd_m1_pw1", + "lcd_pclk_pb3", + "lcd_pwr1_pc1", + "lcd_sdin_pz2", + "lcd_vsync_pj4", + "owr", + "pex_l0_clkreq_n_pdd2", + "pex_l0_prsnt_n_pdd0", + "pex_l0_rst_n_pdd1", + "pex_l1_clkreq_n_pdd6", + "pex_l1_prsnt_n_pdd4", + "pex_l1_rst_n_pdd5", + "pex_l2_clkreq_n_pcc7", + "pex_l2_prsnt_n_pdd7", + "pex_l2_rst_n_pcc6", + "pex_wake_n_pdd3", + "pwr_i2c_scl_pz6", + "pwr_i2c_sda_pz7", + "pwr_int_n", + "spi1_miso_px7", + "sys_clk_req_pz5", + "uart3_cts_n_pa1", + "uart3_rts_n_pc0", + "uart3_rxd_pw7", + "uart3_txd_pw6", + "vi_d0_pt4", + "vi_d1_pd5", + "vi_d10_pt2", + "vi_d11_pt3", + "vi_d2_pl0", + "vi_d3_pl1", + "vi_d4_pl2", + "vi_d5_pl3", + "vi_d6_pl4", + "vi_d7_pl5", + "vi_d8_pl6", + "vi_d9_pl7", + "vi_hsync_pd7", + "vi_pclk_pt0", + "vi_vsync_pd6", +}; + +static const char * const rtck_groups[] = { + "jtag_rtck_pu7", +}; + +static const char * const sata_groups[] = { + "gmi_cs6_n_pi3", +}; + +static const char * const sdmmc1_groups[] = { + "sdmmc1_clk_pz0", + "sdmmc1_cmd_pz1", + "sdmmc1_dat0_py7", + "sdmmc1_dat1_py6", + "sdmmc1_dat2_py5", + "sdmmc1_dat3_py4", +}; + +static const char * const sdmmc2_groups[] = { + "dap1_din_pn1", + "dap1_dout_pn2", + "dap1_fs_pn0", + "dap1_sclk_pn3", + "kb_row10_ps2", + "kb_row11_ps3", + "kb_row12_ps4", + "kb_row13_ps5", + "kb_row14_ps6", + "kb_row15_ps7", + "kb_row6_pr6", + "kb_row7_pr7", + "kb_row8_ps0", + "kb_row9_ps1", + "spdif_in_pk6", + "spdif_out_pk5", + "vi_d1_pd5", + "vi_d2_pl0", + "vi_d3_pl1", + "vi_d4_pl2", + "vi_d5_pl3", + "vi_d6_pl4", + "vi_d7_pl5", + "vi_d8_pl6", + "vi_d9_pl7", + "vi_pclk_pt0", +}; + +static const char * const sdmmc3_groups[] = { + "sdmmc3_clk_pa6", + "sdmmc3_cmd_pa7", + "sdmmc3_dat0_pb7", + "sdmmc3_dat1_pb6", + "sdmmc3_dat2_pb5", + "sdmmc3_dat3_pb4", + "sdmmc3_dat4_pd1", + "sdmmc3_dat5_pd0", + "sdmmc3_dat6_pd3", + "sdmmc3_dat7_pd4", +}; + +static const char * const sdmmc4_groups[] = { + "cam_i2c_scl_pbb1", + "cam_i2c_sda_pbb2", + "cam_mclk_pcc0", + "pbb0", + "pbb3", + "pbb4", + "pbb5", + "pbb6", + "pbb7", + "pcc1", + "sdmmc4_clk_pcc4", + "sdmmc4_cmd_pt7", + "sdmmc4_dat0_paa0", + "sdmmc4_dat1_paa1", + "sdmmc4_dat2_paa2", + "sdmmc4_dat3_paa3", + "sdmmc4_dat4_paa4", + "sdmmc4_dat5_paa5", + "sdmmc4_dat6_paa6", + "sdmmc4_dat7_paa7", + "sdmmc4_rst_n_pcc3", +}; + +static const char * const spdif_groups[] = { + "sdmmc3_dat6_pd3", + "sdmmc3_dat7_pd4", + "spdif_in_pk6", + "spdif_out_pk5", + "uart2_rxd_pc3", + "uart2_txd_pc2", +}; + +static const char * const spi1_groups[] = { + "spi1_cs0_n_px6", + "spi1_miso_px7", + "spi1_mosi_px4", + "spi1_sck_px5", + "ulpi_clk_py0", + "ulpi_dir_py1", + "ulpi_nxt_py2", + "ulpi_stp_py3", +}; + +static const char * const spi2_groups[] = { + "sdmmc3_cmd_pa7", + "sdmmc3_dat4_pd1", + "sdmmc3_dat5_pd0", + "sdmmc3_dat6_pd3", + "sdmmc3_dat7_pd4", + "spi1_cs0_n_px6", + "spi1_mosi_px4", + "spi1_sck_px5", + "spi2_cs0_n_px3", + "spi2_cs1_n_pw2", + "spi2_cs2_n_pw3", + "spi2_miso_px1", + "spi2_mosi_px0", + "spi2_sck_px2", + "ulpi_data4_po5", + "ulpi_data5_po6", + "ulpi_data6_po7", + "ulpi_data7_po0", +}; + +static const char * const spi2_alt_groups[] = { + "spi1_cs0_n_px6", + "spi1_miso_px7", + "spi1_mosi_px4", + "spi1_sck_px5", + "spi2_cs1_n_pw2", + "spi2_cs2_n_pw3", +}; + +static const char * const spi3_groups[] = { + "sdmmc3_clk_pa6", + "sdmmc3_dat0_pb7", + "sdmmc3_dat1_pb6", + "sdmmc3_dat2_pb5", + "sdmmc3_dat3_pb4", + "sdmmc4_dat0_paa0", + "sdmmc4_dat1_paa1", + "sdmmc4_dat2_paa2", + "sdmmc4_dat3_paa3", + "spi1_miso_px7", + "spi2_cs0_n_px3", + "spi2_cs1_n_pw2", + "spi2_cs2_n_pw3", + "spi2_miso_px1", + "spi2_mosi_px0", + "spi2_sck_px2", + "ulpi_data0_po1", + "ulpi_data1_po2", + "ulpi_data2_po3", + "ulpi_data3_po4", +}; + +static const char * const spi4_groups[] = { + "gmi_a16_pj7", + "gmi_a17_pb0", + "gmi_a18_pb1", + "gmi_a19_pk7", + "sdmmc3_dat4_pd1", + "sdmmc3_dat5_pd0", + "sdmmc3_dat6_pd3", + "sdmmc3_dat7_pd4", + "uart2_cts_n_pj5", + "uart2_rts_n_pj6", + "uart2_rxd_pc3", + "uart2_txd_pc2", +}; + +static const char * const spi5_groups[] = { + "lcd_cs0_n_pn4", + "lcd_cs1_n_pw0", + "lcd_pwr0_pb2", + "lcd_pwr2_pc6", + "lcd_sck_pz4", + "lcd_sdin_pz2", + "lcd_sdout_pn5", + "lcd_wr_n_pz3", +}; + +static const char * const spi6_groups[] = { + "spi2_cs0_n_px3", + "spi2_miso_px1", + "spi2_mosi_px0", + "spi2_sck_px2", +}; + +static const char * const sysclk_groups[] = { + "sys_clk_req_pz5", +}; + +static const char * const test_groups[] = { + "kb_col0_pq0", + "kb_col1_pq1", +}; + +static const char * const trace_groups[] = { + "kb_col0_pq0", + "kb_col1_pq1", + "kb_col2_pq2", + "kb_col3_pq3", + "kb_col4_pq4", + "kb_col5_pq5", + "kb_col6_pq6", + "kb_col7_pq7", + "kb_row4_pr4", + "kb_row5_pr5", +}; + +static const char * const uarta_groups[] = { + "pu0", + "pu1", + "pu2", + "pu3", + "pu4", + "pu5", + "pu6", + "sdmmc1_clk_pz0", + "sdmmc1_cmd_pz1", + "sdmmc1_dat0_py7", + "sdmmc1_dat1_py6", + "sdmmc1_dat2_py5", + "sdmmc1_dat3_py4", + "sdmmc3_clk_pa6", + "sdmmc3_cmd_pa7", + "uart2_cts_n_pj5", + "uart2_rts_n_pj6", + "uart2_rxd_pc3", + "uart2_txd_pc2", + "ulpi_data0_po1", + "ulpi_data1_po2", + "ulpi_data2_po3", + "ulpi_data3_po4", + "ulpi_data4_po5", + "ulpi_data5_po6", + "ulpi_data6_po7", + "ulpi_data7_po0", +}; + +static const char * const uartb_groups[] = { + "uart2_cts_n_pj5", + "uart2_rts_n_pj6", + "uart2_rxd_pc3", + "uart2_txd_pc2", +}; + +static const char * const uartc_groups[] = { + "uart3_cts_n_pa1", + "uart3_rts_n_pc0", + "uart3_rxd_pw7", + "uart3_txd_pw6", +}; + +static const char * const uartd_groups[] = { + "gmi_a16_pj7", + "gmi_a17_pb0", + "gmi_a18_pb1", + "gmi_a19_pk7", + "ulpi_clk_py0", + "ulpi_dir_py1", + "ulpi_nxt_py2", + "ulpi_stp_py3", +}; + +static const char * const uarte_groups[] = { + "sdmmc1_dat0_py7", + "sdmmc1_dat1_py6", + "sdmmc1_dat2_py5", + "sdmmc1_dat3_py4", + "sdmmc4_dat0_paa0", + "sdmmc4_dat1_paa1", + "sdmmc4_dat2_paa2", + "sdmmc4_dat3_paa3", +}; + +static const char * const ulpi_groups[] = { + "ulpi_clk_py0", + "ulpi_data0_po1", + "ulpi_data1_po2", + "ulpi_data2_po3", + "ulpi_data3_po4", + "ulpi_data4_po5", + "ulpi_data5_po6", + "ulpi_data6_po7", + "ulpi_data7_po0", + "ulpi_dir_py1", + "ulpi_nxt_py2", + "ulpi_stp_py3", +}; + +static const char * const vgp1_groups[] = { + "cam_i2c_scl_pbb1", +}; + +static const char * const vgp2_groups[] = { + "cam_i2c_sda_pbb2", +}; + +static const char * const vgp3_groups[] = { + "pbb3", + "sdmmc4_dat5_paa5", +}; + +static const char * const vgp4_groups[] = { + "pbb4", + "sdmmc4_dat6_paa6", +}; + +static const char * const vgp5_groups[] = { + "pbb5", + "sdmmc4_dat7_paa7", +}; + +static const char * const vgp6_groups[] = { + "pbb6", + "sdmmc4_rst_n_pcc3", +}; + +static const char * const vi_groups[] = { + "cam_mclk_pcc0", + "vi_d0_pt4", + "vi_d1_pd5", + "vi_d10_pt2", + "vi_d11_pt3", + "vi_d2_pl0", + "vi_d3_pl1", + "vi_d4_pl2", + "vi_d5_pl3", + "vi_d6_pl4", + "vi_d7_pl5", + "vi_d8_pl6", + "vi_d9_pl7", + "vi_hsync_pd7", + "vi_mclk_pt1", + "vi_pclk_pt0", + "vi_vsync_pd6", +}; + +static const char * const vi_alt1_groups[] = { + "cam_mclk_pcc0", + "vi_mclk_pt1", +}; + +static const char * const vi_alt2_groups[] = { + "vi_mclk_pt1", +}; + +static const char * const vi_alt3_groups[] = { + "cam_mclk_pcc0", + "vi_mclk_pt1", +}; + +#define FUNCTION(fname) \ + { \ + .name = #fname, \ + .groups = fname##_groups, \ + .ngroups = ARRAY_SIZE(fname##_groups), \ + } + +static const struct tegra_function tegra30_functions[] = { + FUNCTION(blink), + FUNCTION(cec), + FUNCTION(clk_12m_out), + FUNCTION(clk_32k_in), + FUNCTION(core_pwr_req), + FUNCTION(cpu_pwr_req), + FUNCTION(crt), + FUNCTION(dap), + FUNCTION(ddr), + FUNCTION(dev3), + FUNCTION(displaya), + FUNCTION(displayb), + FUNCTION(dtv), + FUNCTION(extperiph1), + FUNCTION(extperiph2), + FUNCTION(extperiph3), + FUNCTION(gmi), + FUNCTION(gmi_alt), + FUNCTION(hda), + FUNCTION(hdcp), + FUNCTION(hdmi), + FUNCTION(hsi), + FUNCTION(i2c1), + FUNCTION(i2c2), + FUNCTION(i2c3), + FUNCTION(i2c4), + FUNCTION(i2cpwr), + FUNCTION(i2s0), + FUNCTION(i2s1), + FUNCTION(i2s2), + FUNCTION(i2s3), + FUNCTION(i2s4), + FUNCTION(invalid), + FUNCTION(kbc), + FUNCTION(mio), + FUNCTION(nand), + FUNCTION(nand_alt), + FUNCTION(owr), + FUNCTION(pcie), + FUNCTION(pwm0), + FUNCTION(pwm1), + FUNCTION(pwm2), + FUNCTION(pwm3), + FUNCTION(pwr_int_n), + FUNCTION(rsvd1), + FUNCTION(rsvd2), + FUNCTION(rsvd3), + FUNCTION(rsvd4), + FUNCTION(rtck), + FUNCTION(sata), + FUNCTION(sdmmc1), + FUNCTION(sdmmc2), + FUNCTION(sdmmc3), + FUNCTION(sdmmc4), + FUNCTION(spdif), + FUNCTION(spi1), + FUNCTION(spi2), + FUNCTION(spi2_alt), + FUNCTION(spi3), + FUNCTION(spi4), + FUNCTION(spi5), + FUNCTION(spi6), + FUNCTION(sysclk), + FUNCTION(test), + FUNCTION(trace), + FUNCTION(uarta), + FUNCTION(uartb), + FUNCTION(uartc), + FUNCTION(uartd), + FUNCTION(uarte), + FUNCTION(ulpi), + FUNCTION(vgp1), + FUNCTION(vgp2), + FUNCTION(vgp3), + FUNCTION(vgp4), + FUNCTION(vgp5), + FUNCTION(vgp6), + FUNCTION(vi), + FUNCTION(vi_alt1), + FUNCTION(vi_alt2), + FUNCTION(vi_alt3), +}; + +#define MUXCTL_REG_A 0x3000 +#define PINGROUP_REG_A 0x868 + +#define PINGROUP_REG_Y(r) ((r) - MUXCTL_REG_A) +#define PINGROUP_REG_N(r) -1 + +#define PINGROUP(pg_name, f0, f1, f2, f3, f_safe, r, od, ior) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + .funcs = { \ + TEGRA_MUX_ ## f0, \ + TEGRA_MUX_ ## f1, \ + TEGRA_MUX_ ## f2, \ + TEGRA_MUX_ ## f3, \ + }, \ + .func_safe = TEGRA_MUX_ ## f_safe, \ + .mux_reg = PINGROUP_REG_Y(r), \ + .mux_bank = 0, \ + .mux_bit = 0, \ + .pupd_reg = PINGROUP_REG_Y(r), \ + .pupd_bank = 0, \ + .pupd_bit = 2, \ + .tri_reg = PINGROUP_REG_Y(r), \ + .tri_bank = 0, \ + .tri_bit = 4, \ + .einput_reg = PINGROUP_REG_Y(r), \ + .einput_bank = 0, \ + .einput_bit = 5, \ + .odrain_reg = PINGROUP_REG_##od(r), \ + .odrain_bank = 0, \ + .odrain_bit = 6, \ + .lock_reg = PINGROUP_REG_Y(r), \ + .lock_bank = 0, \ + .lock_bit = 7, \ + .ioreset_reg = PINGROUP_REG_##ior(r), \ + .ioreset_bank = 0, \ + .ioreset_bit = 8, \ + .drv_reg = -1, \ + } + +#define DRV_PINGROUP(pg_name, r, hsm_b, schmitt_b, lpmd_b, \ + drvdn_b, drvdn_w, drvup_b, drvup_w, \ + slwr_b, slwr_w, slwf_b, slwf_w) \ + { \ + .name = "drive_" #pg_name, \ + .pins = drive_##pg_name##_pins, \ + .npins = ARRAY_SIZE(drive_##pg_name##_pins), \ + .mux_reg = -1, \ + .pupd_reg = -1, \ + .tri_reg = -1, \ + .einput_reg = -1, \ + .odrain_reg = -1, \ + .lock_reg = -1, \ + .ioreset_reg = -1, \ + .drv_reg = ((r) - PINGROUP_REG_A), \ + .drv_bank = 1, \ + .hsm_bit = hsm_b, \ + .schmitt_bit = schmitt_b, \ + .lpmd_bit = lpmd_b, \ + .drvdn_bit = drvdn_b, \ + .drvdn_width = drvdn_w, \ + .drvup_bit = drvup_b, \ + .drvup_width = drvup_w, \ + .slwr_bit = slwr_b, \ + .slwr_width = slwr_w, \ + .slwf_bit = slwf_b, \ + .slwf_width = slwf_w, \ + } + +static const struct tegra_pingroup tegra30_groups[] = { + /* pg_name, f0, f1, f2, f3, safe, r, od, ior */ + /* FIXME: Fill in correct data in safe column */ + PINGROUP(clk_32k_out_pa0, BLINK, RSVD2, RSVD3, RSVD4, RSVD4, 0x331c, N, N), + PINGROUP(uart3_cts_n_pa1, UARTC, RSVD2, GMI, RSVD4, RSVD4, 0x317c, N, N), + PINGROUP(dap2_fs_pa2, I2S1, HDA, RSVD3, GMI, RSVD3, 0x3358, N, N), + PINGROUP(dap2_sclk_pa3, I2S1, HDA, RSVD3, GMI, RSVD3, 0x3364, N, N), + PINGROUP(dap2_din_pa4, I2S1, HDA, RSVD3, GMI, RSVD3, 0x335c, N, N), + PINGROUP(dap2_dout_pa5, I2S1, HDA, RSVD3, GMI, RSVD3, 0x3360, N, N), + PINGROUP(sdmmc3_clk_pa6, UARTA, PWM2, SDMMC3, SPI3, SPI3, 0x3390, N, N), + PINGROUP(sdmmc3_cmd_pa7, UARTA, PWM3, SDMMC3, SPI2, SPI2, 0x3394, N, N), + PINGROUP(gmi_a17_pb0, UARTD, SPI4, GMI, DTV, DTV, 0x3234, N, N), + PINGROUP(gmi_a18_pb1, UARTD, SPI4, GMI, DTV, DTV, 0x3238, N, N), + PINGROUP(lcd_pwr0_pb2, DISPLAYA, DISPLAYB, SPI5, HDCP, HDCP, 0x3090, N, N), + PINGROUP(lcd_pclk_pb3, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x3094, N, N), + PINGROUP(sdmmc3_dat3_pb4, RSVD1, PWM0, SDMMC3, SPI3, RSVD1, 0x33a4, N, N), + PINGROUP(sdmmc3_dat2_pb5, RSVD1, PWM1, SDMMC3, SPI3, RSVD1, 0x33a0, N, N), + PINGROUP(sdmmc3_dat1_pb6, RSVD1, RSVD2, SDMMC3, SPI3, RSVD2, 0x339c, N, N), + PINGROUP(sdmmc3_dat0_pb7, RSVD1, RSVD2, SDMMC3, SPI3, RSVD2, 0x3398, N, N), + PINGROUP(uart3_rts_n_pc0, UARTC, PWM0, GMI, RSVD4, RSVD4, 0x3180, N, N), + PINGROUP(lcd_pwr1_pc1, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x3070, N, N), + PINGROUP(uart2_txd_pc2, UARTB, SPDIF, UARTA, SPI4, SPI4, 0x3168, N, N), + PINGROUP(uart2_rxd_pc3, UARTB, SPDIF, UARTA, SPI4, SPI4, 0x3164, N, N), + PINGROUP(gen1_i2c_scl_pc4, I2C1, RSVD2, RSVD3, RSVD4, RSVD4, 0x31a4, Y, N), + PINGROUP(gen1_i2c_sda_pc5, I2C1, RSVD2, RSVD3, RSVD4, RSVD4, 0x31a0, Y, N), + PINGROUP(lcd_pwr2_pc6, DISPLAYA, DISPLAYB, SPI5, HDCP, HDCP, 0x3074, N, N), + PINGROUP(gmi_wp_n_pc7, RSVD1, NAND, GMI, GMI_ALT, RSVD1, 0x31c0, N, N), + PINGROUP(sdmmc3_dat5_pd0, PWM0, SPI4, SDMMC3, SPI2, SPI2, 0x33ac, N, N), + PINGROUP(sdmmc3_dat4_pd1, PWM1, SPI4, SDMMC3, SPI2, SPI2, 0x33a8, N, N), + PINGROUP(lcd_dc1_pd2, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x310c, N, N), + PINGROUP(sdmmc3_dat6_pd3, SPDIF, SPI4, SDMMC3, SPI2, SPI2, 0x33b0, N, N), + PINGROUP(sdmmc3_dat7_pd4, SPDIF, SPI4, SDMMC3, SPI2, SPI2, 0x33b4, N, N), + PINGROUP(vi_d1_pd5, DDR, SDMMC2, VI, RSVD4, RSVD4, 0x3128, N, Y), + PINGROUP(vi_vsync_pd6, DDR, RSVD2, VI, RSVD4, RSVD4, 0x315c, N, Y), + PINGROUP(vi_hsync_pd7, DDR, RSVD2, VI, RSVD4, RSVD4, 0x3160, N, Y), + PINGROUP(lcd_d0_pe0, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30a4, N, N), + PINGROUP(lcd_d1_pe1, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30a8, N, N), + PINGROUP(lcd_d2_pe2, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30ac, N, N), + PINGROUP(lcd_d3_pe3, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30b0, N, N), + PINGROUP(lcd_d4_pe4, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30b4, N, N), + PINGROUP(lcd_d5_pe5, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30b8, N, N), + PINGROUP(lcd_d6_pe6, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30bc, N, N), + PINGROUP(lcd_d7_pe7, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30c0, N, N), + PINGROUP(lcd_d8_pf0, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30c4, N, N), + PINGROUP(lcd_d9_pf1, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30c8, N, N), + PINGROUP(lcd_d10_pf2, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30cc, N, N), + PINGROUP(lcd_d11_pf3, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30d0, N, N), + PINGROUP(lcd_d12_pf4, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30d4, N, N), + PINGROUP(lcd_d13_pf5, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30d8, N, N), + PINGROUP(lcd_d14_pf6, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30dc, N, N), + PINGROUP(lcd_d15_pf7, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30e0, N, N), + PINGROUP(gmi_ad0_pg0, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31f0, N, N), + PINGROUP(gmi_ad1_pg1, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31f4, N, N), + PINGROUP(gmi_ad2_pg2, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31f8, N, N), + PINGROUP(gmi_ad3_pg3, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31fc, N, N), + PINGROUP(gmi_ad4_pg4, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3200, N, N), + PINGROUP(gmi_ad5_pg5, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3204, N, N), + PINGROUP(gmi_ad6_pg6, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3208, N, N), + PINGROUP(gmi_ad7_pg7, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x320c, N, N), + PINGROUP(gmi_ad8_ph0, PWM0, NAND, GMI, RSVD4, RSVD4, 0x3210, N, N), + PINGROUP(gmi_ad9_ph1, PWM1, NAND, GMI, RSVD4, RSVD4, 0x3214, N, N), + PINGROUP(gmi_ad10_ph2, PWM2, NAND, GMI, RSVD4, RSVD4, 0x3218, N, N), + PINGROUP(gmi_ad11_ph3, PWM3, NAND, GMI, RSVD4, RSVD4, 0x321c, N, N), + PINGROUP(gmi_ad12_ph4, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3220, N, N), + PINGROUP(gmi_ad13_ph5, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3224, N, N), + PINGROUP(gmi_ad14_ph6, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3228, N, N), + PINGROUP(gmi_ad15_ph7, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x322c, N, N), + PINGROUP(gmi_wr_n_pi0, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3240, N, N), + PINGROUP(gmi_oe_n_pi1, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3244, N, N), + PINGROUP(gmi_dqs_pi2, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3248, N, N), + PINGROUP(gmi_cs6_n_pi3, NAND, NAND_ALT, GMI, SATA, SATA, 0x31e8, N, N), + PINGROUP(gmi_rst_n_pi4, NAND, NAND_ALT, GMI, RSVD4, RSVD4, 0x324c, N, N), + PINGROUP(gmi_iordy_pi5, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31c4, N, N), + PINGROUP(gmi_cs7_n_pi6, NAND, NAND_ALT, GMI, GMI_ALT, GMI_ALT, 0x31ec, N, N), + PINGROUP(gmi_wait_pi7, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31c8, N, N), + PINGROUP(gmi_cs0_n_pj0, RSVD1, NAND, GMI, DTV, RSVD1, 0x31d4, N, N), + PINGROUP(lcd_de_pj1, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x3098, N, N), + PINGROUP(gmi_cs1_n_pj2, RSVD1, NAND, GMI, DTV, RSVD1, 0x31d8, N, N), + PINGROUP(lcd_hsync_pj3, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x309c, N, N), + PINGROUP(lcd_vsync_pj4, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30a0, N, N), + PINGROUP(uart2_cts_n_pj5, UARTA, UARTB, GMI, SPI4, SPI4, 0x3170, N, N), + PINGROUP(uart2_rts_n_pj6, UARTA, UARTB, GMI, SPI4, SPI4, 0x316c, N, N), + PINGROUP(gmi_a16_pj7, UARTD, SPI4, GMI, GMI_ALT, GMI_ALT, 0x3230, N, N), + PINGROUP(gmi_adv_n_pk0, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31cc, N, N), + PINGROUP(gmi_clk_pk1, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31d0, N, N), + PINGROUP(gmi_cs4_n_pk2, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31e4, N, N), + PINGROUP(gmi_cs2_n_pk3, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31dc, N, N), + PINGROUP(gmi_cs3_n_pk4, RSVD1, NAND, GMI, GMI_ALT, RSVD1, 0x31e0, N, N), + PINGROUP(spdif_out_pk5, SPDIF, RSVD2, I2C1, SDMMC2, RSVD2, 0x3354, N, N), + PINGROUP(spdif_in_pk6, SPDIF, HDA, I2C1, SDMMC2, SDMMC2, 0x3350, N, N), + PINGROUP(gmi_a19_pk7, UARTD, SPI4, GMI, RSVD4, RSVD4, 0x323c, N, N), + PINGROUP(vi_d2_pl0, DDR, SDMMC2, VI, RSVD4, RSVD4, 0x312c, N, Y), + PINGROUP(vi_d3_pl1, DDR, SDMMC2, VI, RSVD4, RSVD4, 0x3130, N, Y), + PINGROUP(vi_d4_pl2, DDR, SDMMC2, VI, RSVD4, RSVD4, 0x3134, N, Y), + PINGROUP(vi_d5_pl3, DDR, SDMMC2, VI, RSVD4, RSVD4, 0x3138, N, Y), + PINGROUP(vi_d6_pl4, DDR, SDMMC2, VI, RSVD4, RSVD4, 0x313c, N, Y), + PINGROUP(vi_d7_pl5, DDR, SDMMC2, VI, RSVD4, RSVD4, 0x3140, N, Y), + PINGROUP(vi_d8_pl6, DDR, SDMMC2, VI, RSVD4, RSVD4, 0x3144, N, Y), + PINGROUP(vi_d9_pl7, DDR, SDMMC2, VI, RSVD4, RSVD4, 0x3148, N, Y), + PINGROUP(lcd_d16_pm0, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30e4, N, N), + PINGROUP(lcd_d17_pm1, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30e8, N, N), + PINGROUP(lcd_d18_pm2, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30ec, N, N), + PINGROUP(lcd_d19_pm3, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30f0, N, N), + PINGROUP(lcd_d20_pm4, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30f4, N, N), + PINGROUP(lcd_d21_pm5, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30f8, N, N), + PINGROUP(lcd_d22_pm6, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30fc, N, N), + PINGROUP(lcd_d23_pm7, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x3100, N, N), + PINGROUP(dap1_fs_pn0, I2S0, HDA, GMI, SDMMC2, SDMMC2, 0x3338, N, N), + PINGROUP(dap1_din_pn1, I2S0, HDA, GMI, SDMMC2, SDMMC2, 0x333c, N, N), + PINGROUP(dap1_dout_pn2, I2S0, HDA, GMI, SDMMC2, SDMMC2, 0x3340, N, N), + PINGROUP(dap1_sclk_pn3, I2S0, HDA, GMI, SDMMC2, SDMMC2, 0x3344, N, N), + PINGROUP(lcd_cs0_n_pn4, DISPLAYA, DISPLAYB, SPI5, RSVD4, RSVD4, 0x3084, N, N), + PINGROUP(lcd_sdout_pn5, DISPLAYA, DISPLAYB, SPI5, HDCP, HDCP, 0x307c, N, N), + PINGROUP(lcd_dc0_pn6, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x3088, N, N), + PINGROUP(hdmi_int_pn7, HDMI, RSVD2, RSVD3, RSVD4, RSVD4, 0x3110, N, N), + PINGROUP(ulpi_data7_po0, SPI2, HSI, UARTA, ULPI, ULPI, 0x301c, N, N), + PINGROUP(ulpi_data0_po1, SPI3, HSI, UARTA, ULPI, ULPI, 0x3000, N, N), + PINGROUP(ulpi_data1_po2, SPI3, HSI, UARTA, ULPI, ULPI, 0x3004, N, N), + PINGROUP(ulpi_data2_po3, SPI3, HSI, UARTA, ULPI, ULPI, 0x3008, N, N), + PINGROUP(ulpi_data3_po4, SPI3, HSI, UARTA, ULPI, ULPI, 0x300c, N, N), + PINGROUP(ulpi_data4_po5, SPI2, HSI, UARTA, ULPI, ULPI, 0x3010, N, N), + PINGROUP(ulpi_data5_po6, SPI2, HSI, UARTA, ULPI, ULPI, 0x3014, N, N), + PINGROUP(ulpi_data6_po7, SPI2, HSI, UARTA, ULPI, ULPI, 0x3018, N, N), + PINGROUP(dap3_fs_pp0, I2S2, RSVD2, DISPLAYA, DISPLAYB, RSVD2, 0x3030, N, N), + PINGROUP(dap3_din_pp1, I2S2, RSVD2, DISPLAYA, DISPLAYB, RSVD2, 0x3034, N, N), + PINGROUP(dap3_dout_pp2, I2S2, RSVD2, DISPLAYA, DISPLAYB, RSVD2, 0x3038, N, N), + PINGROUP(dap3_sclk_pp3, I2S2, RSVD2, DISPLAYA, DISPLAYB, RSVD2, 0x303c, N, N), + PINGROUP(dap4_fs_pp4, I2S3, RSVD2, GMI, RSVD4, RSVD4, 0x31a8, N, N), + PINGROUP(dap4_din_pp5, I2S3, RSVD2, GMI, RSVD4, RSVD4, 0x31ac, N, N), + PINGROUP(dap4_dout_pp6, I2S3, RSVD2, GMI, RSVD4, RSVD4, 0x31b0, N, N), + PINGROUP(dap4_sclk_pp7, I2S3, RSVD2, GMI, RSVD4, RSVD4, 0x31b4, N, N), + PINGROUP(kb_col0_pq0, KBC, NAND, TRACE, TEST, TEST, 0x32fc, N, N), + PINGROUP(kb_col1_pq1, KBC, NAND, TRACE, TEST, TEST, 0x3300, N, N), + PINGROUP(kb_col2_pq2, KBC, NAND, TRACE, RSVD4, RSVD4, 0x3304, N, N), + PINGROUP(kb_col3_pq3, KBC, NAND, TRACE, RSVD4, RSVD4, 0x3308, N, N), + PINGROUP(kb_col4_pq4, KBC, NAND, TRACE, RSVD4, RSVD4, 0x330c, N, N), + PINGROUP(kb_col5_pq5, KBC, NAND, TRACE, RSVD4, RSVD4, 0x3310, N, N), + PINGROUP(kb_col6_pq6, KBC, NAND, TRACE, MIO, MIO, 0x3314, N, N), + PINGROUP(kb_col7_pq7, KBC, NAND, TRACE, MIO, MIO, 0x3318, N, N), + PINGROUP(kb_row0_pr0, KBC, NAND, RSVD3, RSVD4, RSVD4, 0x32bc, N, N), + PINGROUP(kb_row1_pr1, KBC, NAND, RSVD3, RSVD4, RSVD4, 0x32c0, N, N), + PINGROUP(kb_row2_pr2, KBC, NAND, RSVD3, RSVD4, RSVD4, 0x32c4, N, N), + PINGROUP(kb_row3_pr3, KBC, NAND, RSVD3, INVALID, RSVD3, 0x32c8, N, N), + PINGROUP(kb_row4_pr4, KBC, NAND, TRACE, RSVD4, RSVD4, 0x32cc, N, N), + PINGROUP(kb_row5_pr5, KBC, NAND, TRACE, OWR, OWR, 0x32d0, N, N), + PINGROUP(kb_row6_pr6, KBC, NAND, SDMMC2, MIO, MIO, 0x32d4, N, N), + PINGROUP(kb_row7_pr7, KBC, NAND, SDMMC2, MIO, MIO, 0x32d8, N, N), + PINGROUP(kb_row8_ps0, KBC, NAND, SDMMC2, MIO, MIO, 0x32dc, N, N), + PINGROUP(kb_row9_ps1, KBC, NAND, SDMMC2, MIO, MIO, 0x32e0, N, N), + PINGROUP(kb_row10_ps2, KBC, NAND, SDMMC2, MIO, MIO, 0x32e4, N, N), + PINGROUP(kb_row11_ps3, KBC, NAND, SDMMC2, MIO, MIO, 0x32e8, N, N), + PINGROUP(kb_row12_ps4, KBC, NAND, SDMMC2, MIO, MIO, 0x32ec, N, N), + PINGROUP(kb_row13_ps5, KBC, NAND, SDMMC2, MIO, MIO, 0x32f0, N, N), + PINGROUP(kb_row14_ps6, KBC, NAND, SDMMC2, MIO, MIO, 0x32f4, N, N), + PINGROUP(kb_row15_ps7, KBC, NAND, SDMMC2, MIO, MIO, 0x32f8, N, N), + PINGROUP(vi_pclk_pt0, RSVD1, SDMMC2, VI, RSVD4, RSVD4, 0x3154, N, Y), + PINGROUP(vi_mclk_pt1, VI, VI_ALT1, VI_ALT2, VI_ALT3, VI_ALT3, 0x3158, N, Y), + PINGROUP(vi_d10_pt2, DDR, RSVD2, VI, RSVD4, RSVD4, 0x314c, N, Y), + PINGROUP(vi_d11_pt3, DDR, RSVD2, VI, RSVD4, RSVD4, 0x3150, N, Y), + PINGROUP(vi_d0_pt4, DDR, RSVD2, VI, RSVD4, RSVD4, 0x3124, N, Y), + PINGROUP(gen2_i2c_scl_pt5, I2C2, HDCP, GMI, RSVD4, RSVD4, 0x3250, Y, N), + PINGROUP(gen2_i2c_sda_pt6, I2C2, HDCP, GMI, RSVD4, RSVD4, 0x3254, Y, N), + PINGROUP(sdmmc4_cmd_pt7, I2C3, NAND, GMI, SDMMC4, SDMMC4, 0x325c, N, Y), + PINGROUP(pu0, OWR, UARTA, GMI, RSVD4, RSVD4, 0x3184, N, N), + PINGROUP(pu1, RSVD1, UARTA, GMI, RSVD4, RSVD4, 0x3188, N, N), + PINGROUP(pu2, RSVD1, UARTA, GMI, RSVD4, RSVD4, 0x318c, N, N), + PINGROUP(pu3, PWM0, UARTA, GMI, RSVD4, RSVD4, 0x3190, N, N), + PINGROUP(pu4, PWM1, UARTA, GMI, RSVD4, RSVD4, 0x3194, N, N), + PINGROUP(pu5, PWM2, UARTA, GMI, RSVD4, RSVD4, 0x3198, N, N), + PINGROUP(pu6, PWM3, UARTA, GMI, RSVD4, RSVD4, 0x319c, N, N), + PINGROUP(jtag_rtck_pu7, RTCK, RSVD2, RSVD3, RSVD4, RSVD4, 0x32b0, N, N), + PINGROUP(pv0, RSVD1, RSVD2, RSVD3, RSVD4, RSVD4, 0x3040, N, N), + PINGROUP(pv1, RSVD1, RSVD2, RSVD3, RSVD4, RSVD4, 0x3044, N, N), + PINGROUP(pv2, OWR, RSVD2, RSVD3, RSVD4, RSVD4, 0x3060, N, N), + PINGROUP(pv3, CLK_12M_OUT, RSVD2, RSVD3, RSVD4, RSVD4, 0x3064, N, N), + PINGROUP(ddc_scl_pv4, I2C4, RSVD2, RSVD3, RSVD4, RSVD4, 0x3114, N, N), + PINGROUP(ddc_sda_pv5, I2C4, RSVD2, RSVD3, RSVD4, RSVD4, 0x3118, N, N), + PINGROUP(crt_hsync_pv6, CRT, RSVD2, RSVD3, RSVD4, RSVD4, 0x311c, N, N), + PINGROUP(crt_vsync_pv7, CRT, RSVD2, RSVD3, RSVD4, RSVD4, 0x3120, N, N), + PINGROUP(lcd_cs1_n_pw0, DISPLAYA, DISPLAYB, SPI5, RSVD4, RSVD4, 0x3104, N, N), + PINGROUP(lcd_m1_pw1, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x3108, N, N), + PINGROUP(spi2_cs1_n_pw2, SPI3, SPI2, SPI2_ALT, I2C1, I2C1, 0x3388, N, N), + PINGROUP(spi2_cs2_n_pw3, SPI3, SPI2, SPI2_ALT, I2C1, I2C1, 0x338c, N, N), + PINGROUP(clk1_out_pw4, EXTPERIPH1, RSVD2, RSVD3, RSVD4, RSVD4, 0x334c, N, N), + PINGROUP(clk2_out_pw5, EXTPERIPH2, RSVD2, RSVD3, RSVD4, RSVD4, 0x3068, N, N), + PINGROUP(uart3_txd_pw6, UARTC, RSVD2, GMI, RSVD4, RSVD4, 0x3174, N, N), + PINGROUP(uart3_rxd_pw7, UARTC, RSVD2, GMI, RSVD4, RSVD4, 0x3178, N, N), + PINGROUP(spi2_mosi_px0, SPI6, SPI2, SPI3, GMI, GMI, 0x3368, N, N), + PINGROUP(spi2_miso_px1, SPI6, SPI2, SPI3, GMI, GMI, 0x336c, N, N), + PINGROUP(spi2_sck_px2, SPI6, SPI2, SPI3, GMI, GMI, 0x3374, N, N), + PINGROUP(spi2_cs0_n_px3, SPI6, SPI2, SPI3, GMI, GMI, 0x3370, N, N), + PINGROUP(spi1_mosi_px4, SPI2, SPI1, SPI2_ALT, GMI, GMI, 0x3378, N, N), + PINGROUP(spi1_sck_px5, SPI2, SPI1, SPI2_ALT, GMI, GMI, 0x337c, N, N), + PINGROUP(spi1_cs0_n_px6, SPI2, SPI1, SPI2_ALT, GMI, GMI, 0x3380, N, N), + PINGROUP(spi1_miso_px7, SPI3, SPI1, SPI2_ALT, RSVD4, RSVD4, 0x3384, N, N), + PINGROUP(ulpi_clk_py0, SPI1, RSVD2, UARTD, ULPI, RSVD2, 0x3020, N, N), + PINGROUP(ulpi_dir_py1, SPI1, RSVD2, UARTD, ULPI, RSVD2, 0x3024, N, N), + PINGROUP(ulpi_nxt_py2, SPI1, RSVD2, UARTD, ULPI, RSVD2, 0x3028, N, N), + PINGROUP(ulpi_stp_py3, SPI1, RSVD2, UARTD, ULPI, RSVD2, 0x302c, N, N), + PINGROUP(sdmmc1_dat3_py4, SDMMC1, RSVD2, UARTE, UARTA, RSVD2, 0x3050, N, N), + PINGROUP(sdmmc1_dat2_py5, SDMMC1, RSVD2, UARTE, UARTA, RSVD2, 0x3054, N, N), + PINGROUP(sdmmc1_dat1_py6, SDMMC1, RSVD2, UARTE, UARTA, RSVD2, 0x3058, N, N), + PINGROUP(sdmmc1_dat0_py7, SDMMC1, RSVD2, UARTE, UARTA, RSVD2, 0x305c, N, N), + PINGROUP(sdmmc1_clk_pz0, SDMMC1, RSVD2, RSVD3, UARTA, RSVD3, 0x3048, N, N), + PINGROUP(sdmmc1_cmd_pz1, SDMMC1, RSVD2, RSVD3, UARTA, RSVD3, 0x304c, N, N), + PINGROUP(lcd_sdin_pz2, DISPLAYA, DISPLAYB, SPI5, RSVD4, RSVD4, 0x3078, N, N), + PINGROUP(lcd_wr_n_pz3, DISPLAYA, DISPLAYB, SPI5, HDCP, HDCP, 0x3080, N, N), + PINGROUP(lcd_sck_pz4, DISPLAYA, DISPLAYB, SPI5, HDCP, HDCP, 0x308c, N, N), + PINGROUP(sys_clk_req_pz5, SYSCLK, RSVD2, RSVD3, RSVD4, RSVD4, 0x3320, N, N), + PINGROUP(pwr_i2c_scl_pz6, I2CPWR, RSVD2, RSVD3, RSVD4, RSVD4, 0x32b4, Y, N), + PINGROUP(pwr_i2c_sda_pz7, I2CPWR, RSVD2, RSVD3, RSVD4, RSVD4, 0x32b8, Y, N), + PINGROUP(sdmmc4_dat0_paa0, UARTE, SPI3, GMI, SDMMC4, SDMMC4, 0x3260, N, Y), + PINGROUP(sdmmc4_dat1_paa1, UARTE, SPI3, GMI, SDMMC4, SDMMC4, 0x3264, N, Y), + PINGROUP(sdmmc4_dat2_paa2, UARTE, SPI3, GMI, SDMMC4, SDMMC4, 0x3268, N, Y), + PINGROUP(sdmmc4_dat3_paa3, UARTE, SPI3, GMI, SDMMC4, SDMMC4, 0x326c, N, Y), + PINGROUP(sdmmc4_dat4_paa4, I2C3, I2S4, GMI, SDMMC4, SDMMC4, 0x3270, N, Y), + PINGROUP(sdmmc4_dat5_paa5, VGP3, I2S4, GMI, SDMMC4, SDMMC4, 0x3274, N, Y), + PINGROUP(sdmmc4_dat6_paa6, VGP4, I2S4, GMI, SDMMC4, SDMMC4, 0x3278, N, Y), + PINGROUP(sdmmc4_dat7_paa7, VGP5, I2S4, GMI, SDMMC4, SDMMC4, 0x327c, N, Y), + PINGROUP(pbb0, I2S4, RSVD2, RSVD3, SDMMC4, RSVD3, 0x328c, N, N), + PINGROUP(cam_i2c_scl_pbb1, VGP1, I2C3, RSVD3, SDMMC4, RSVD3, 0x3290, Y, N), + PINGROUP(cam_i2c_sda_pbb2, VGP2, I2C3, RSVD3, SDMMC4, RSVD3, 0x3294, Y, N), + PINGROUP(pbb3, VGP3, DISPLAYA, DISPLAYB, SDMMC4, SDMMC4, 0x3298, N, N), + PINGROUP(pbb4, VGP4, DISPLAYA, DISPLAYB, SDMMC4, SDMMC4, 0x329c, N, N), + PINGROUP(pbb5, VGP5, DISPLAYA, DISPLAYB, SDMMC4, SDMMC4, 0x32a0, N, N), + PINGROUP(pbb6, VGP6, DISPLAYA, DISPLAYB, SDMMC4, SDMMC4, 0x32a4, N, N), + PINGROUP(pbb7, I2S4, RSVD2, RSVD3, SDMMC4, RSVD3, 0x32a8, N, N), + PINGROUP(cam_mclk_pcc0, VI, VI_ALT1, VI_ALT3, SDMMC4, SDMMC4, 0x3284, N, N), + PINGROUP(pcc1, I2S4, RSVD2, RSVD3, SDMMC4, RSVD3, 0x3288, N, N), + PINGROUP(pcc2, I2S4, RSVD2, RSVD3, RSVD4, RSVD4, 0x32ac, N, N), + PINGROUP(sdmmc4_rst_n_pcc3, VGP6, RSVD2, RSVD3, SDMMC4, RSVD3, 0x3280, N, Y), + PINGROUP(sdmmc4_clk_pcc4, INVALID, NAND, GMI, SDMMC4, SDMMC4, 0x3258, N, Y), + PINGROUP(clk2_req_pcc5, DAP, RSVD2, RSVD3, RSVD4, RSVD4, 0x306c, N, N), + PINGROUP(pex_l2_rst_n_pcc6, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33d8, N, N), + PINGROUP(pex_l2_clkreq_n_pcc7, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33dc, N, N), + PINGROUP(pex_l0_prsnt_n_pdd0, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33b8, N, N), + PINGROUP(pex_l0_rst_n_pdd1, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33bc, N, N), + PINGROUP(pex_l0_clkreq_n_pdd2, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33c0, N, N), + PINGROUP(pex_wake_n_pdd3, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33c4, N, N), + PINGROUP(pex_l1_prsnt_n_pdd4, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33c8, N, N), + PINGROUP(pex_l1_rst_n_pdd5, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33cc, N, N), + PINGROUP(pex_l1_clkreq_n_pdd6, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33d0, N, N), + PINGROUP(pex_l2_prsnt_n_pdd7, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33d4, N, N), + PINGROUP(clk3_out_pee0, EXTPERIPH3, RSVD2, RSVD3, RSVD4, RSVD4, 0x31b8, N, N), + PINGROUP(clk3_req_pee1, DEV3, RSVD2, RSVD3, RSVD4, RSVD4, 0x31bc, N, N), + PINGROUP(clk1_req_pee2, DAP, HDA, RSVD3, RSVD4, RSVD4, 0x3348, N, N), + PINGROUP(hdmi_cec_pee3, CEC, RSVD2, RSVD3, RSVD4, RSVD4, 0x33e0, Y, N), + PINGROUP(clk_32k_in, CLK_32K_IN, RSVD2, RSVD3, RSVD4, RSVD4, 0x3330, N, N), + PINGROUP(core_pwr_req, CORE_PWR_REQ, RSVD2, RSVD3, RSVD4, RSVD4, 0x3324, N, N), + PINGROUP(cpu_pwr_req, CPU_PWR_REQ, RSVD2, RSVD3, RSVD4, RSVD4, 0x3328, N, N), + PINGROUP(owr, OWR, CEC, RSVD3, RSVD4, RSVD4, 0x3334, N, N), + PINGROUP(pwr_int_n, PWR_INT_N, RSVD2, RSVD3, RSVD4, RSVD4, 0x332c, N, N), + /* pg_name, r, hsm_b, schmitt_b, lpmd_b, drvdn_b, drvdn_w, drvup_b, drvup_w, slwr_b, slwr_w, slwf_b, slwf_w */ + DRV_PINGROUP(ao1, 0x868, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(ao2, 0x86c, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(at1, 0x870, 2, 3, 4, 14, 5, 19, 5, 24, 2, 28, 2), + DRV_PINGROUP(at2, 0x874, 2, 3, 4, 14, 5, 19, 5, 24, 2, 28, 2), + DRV_PINGROUP(at3, 0x878, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2), + DRV_PINGROUP(at4, 0x87c, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2), + DRV_PINGROUP(at5, 0x880, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2), + DRV_PINGROUP(cdev1, 0x884, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(cdev2, 0x888, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(cec, 0x938, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(crt, 0x8f8, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(csus, 0x88c, -1, -1, -1, 12, 5, 19, 5, 24, 4, 28, 4), + DRV_PINGROUP(dap1, 0x890, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(dap2, 0x894, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(dap3, 0x898, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(dap4, 0x89c, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(dbg, 0x8a0, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(ddc, 0x8fc, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(dev3, 0x92c, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(gma, 0x900, -1, -1, -1, 14, 5, 19, 5, 24, 4, 28, 4), + DRV_PINGROUP(gmb, 0x904, -1, -1, -1, 14, 5, 19, 5, 24, 4, 28, 4), + DRV_PINGROUP(gmc, 0x908, -1, -1, -1, 14, 5, 19, 5, 24, 4, 28, 4), + DRV_PINGROUP(gmd, 0x90c, -1, -1, -1, 14, 5, 19, 5, 24, 4, 28, 4), + DRV_PINGROUP(gme, 0x910, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2), + DRV_PINGROUP(gmf, 0x914, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2), + DRV_PINGROUP(gmg, 0x918, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2), + DRV_PINGROUP(gmh, 0x91c, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2), + DRV_PINGROUP(gpv, 0x928, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(lcd1, 0x8a4, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(lcd2, 0x8a8, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(owr, 0x920, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(sdio1, 0x8ec, 2, 3, -1, 12, 7, 20, 7, 28, 2, 30, 2), + DRV_PINGROUP(sdio2, 0x8ac, 2, 3, -1, 12, 7, 20, 7, 28, 2, 30, 2), + DRV_PINGROUP(sdio3, 0x8b0, 2, 3, -1, 12, 7, 20, 7, 28, 2, 30, 2), + DRV_PINGROUP(spi, 0x8b4, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(uaa, 0x8b8, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(uab, 0x8bc, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(uart2, 0x8c0, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(uart3, 0x8c4, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(uda, 0x924, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2), + DRV_PINGROUP(vi1, 0x8c8, -1, -1, -1, 14, 5, 19, 5, 24, 4, 28, 4), +}; + +static const struct tegra_pinctrl_soc_data tegra30_pinctrl = { + .ngpios = NUM_GPIOS, + .pins = tegra30_pins, + .npins = ARRAY_SIZE(tegra30_pins), + .functions = tegra30_functions, + .nfunctions = ARRAY_SIZE(tegra30_functions), + .groups = tegra30_groups, + .ngroups = ARRAY_SIZE(tegra30_groups), +}; + +void __devinit tegra30_pinctrl_init(const struct tegra_pinctrl_soc_data **soc) +{ + *soc = &tegra30_pinctrl; +} diff --git a/drivers/pinctrl/pinctrl-u300.c b/drivers/pinctrl/pinctrl-u300.c index c8d02f1c2b5e..26eb8ccd72d5 100644 --- a/drivers/pinctrl/pinctrl-u300.c +++ b/drivers/pinctrl/pinctrl-u300.c @@ -19,6 +19,9 @@ #include <linux/err.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> +#include "pinctrl-coh901.h" /* * Register definitions for the U300 Padmux control registers in the @@ -162,7 +165,7 @@ #define U300_SYSCON_PMC4R_APP_MISC_16_APP_UART1_CTS 0x0100 #define U300_SYSCON_PMC4R_APP_MISC_16_EMIF_1_STATIC_CS5_N 0x0200 -#define DRIVER_NAME "pinmux-u300" +#define DRIVER_NAME "pinctrl-u300" /* * The DB3350 has 467 pads, I have enumerated the pads clockwise around the @@ -1044,22 +1047,82 @@ static struct pinctrl_gpio_range u300_gpio_ranges[] = { U300_GPIO_RANGE(25, 181, 1), }; +static struct pinctrl_gpio_range *u300_match_gpio_range(unsigned pin) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(u300_gpio_ranges); i++) { + struct pinctrl_gpio_range *range; + + range = &u300_gpio_ranges[i]; + if (pin >= range->pin_base && + pin <= (range->pin_base + range->npins - 1)) + return range; + } + return NULL; +} + +int u300_pin_config_get(struct pinctrl_dev *pctldev, + unsigned pin, + unsigned long *config) +{ + struct pinctrl_gpio_range *range = u300_match_gpio_range(pin); + + /* We get config for those pins we CAN get it for and that's it */ + if (!range) + return -ENOTSUPP; + + return u300_gpio_config_get(range->gc, + (pin - range->pin_base + range->base), + config); +} + +int u300_pin_config_set(struct pinctrl_dev *pctldev, + unsigned pin, + unsigned long config) +{ + struct pinctrl_gpio_range *range = u300_match_gpio_range(pin); + int ret; + + if (!range) + return -EINVAL; + + /* Note: none of these configurations take any argument */ + ret = u300_gpio_config_set(range->gc, + (pin - range->pin_base + range->base), + pinconf_to_config_param(config)); + if (ret) + return ret; + + return 0; +} + +static struct pinconf_ops u300_pconf_ops = { + .is_generic = true, + .pin_config_get = u300_pin_config_get, + .pin_config_set = u300_pin_config_set, +}; + static struct pinctrl_desc u300_pmx_desc = { .name = DRIVER_NAME, .pins = u300_pads, .npins = ARRAY_SIZE(u300_pads), .pctlops = &u300_pctrl_ops, .pmxops = &u300_pmx_ops, + .confops = &u300_pconf_ops, .owner = THIS_MODULE, }; -static int __init u300_pmx_probe(struct platform_device *pdev) +static int __devinit u300_pmx_probe(struct platform_device *pdev) { struct u300_pmx *upmx; struct resource *res; + struct gpio_chip *gpio_chip = dev_get_platdata(&pdev->dev); int ret; int i; + pr_err("U300 PMX PROBE\n"); + /* Create state holders etc for this driver */ upmx = devm_kzalloc(&pdev->dev, sizeof(*upmx), GFP_KERNEL); if (!upmx) @@ -1095,12 +1158,14 @@ static int __init u300_pmx_probe(struct platform_device *pdev) } /* We will handle a range of GPIO pins */ - for (i = 0; i < ARRAY_SIZE(u300_gpio_ranges); i++) + for (i = 0; i < ARRAY_SIZE(u300_gpio_ranges); i++) { + u300_gpio_ranges[i].gc = gpio_chip; pinctrl_add_gpio_range(upmx->pctl, &u300_gpio_ranges[i]); + } platform_set_drvdata(pdev, upmx); - dev_info(&pdev->dev, "initialized U300 pinmux driver\n"); + dev_info(&pdev->dev, "initialized U300 pin control driver\n"); return 0; @@ -1115,7 +1180,7 @@ out_no_resource: return ret; } -static int __exit u300_pmx_remove(struct platform_device *pdev) +static int __devexit u300_pmx_remove(struct platform_device *pdev) { struct u300_pmx *upmx = platform_get_drvdata(pdev); int i; @@ -1136,12 +1201,13 @@ static struct platform_driver u300_pmx_driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, }, - .remove = __exit_p(u300_pmx_remove), + .probe = u300_pmx_probe, + .remove = __devexit_p(u300_pmx_remove), }; static int __init u300_pmx_init(void) { - return platform_driver_probe(&u300_pmx_driver, u300_pmx_probe); + return platform_driver_register(&u300_pmx_driver); } arch_initcall(u300_pmx_init); diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index 7c3193f7a044..4e62783a573a 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -1,12 +1,14 @@ /* * Core driver for the pin muxing portions of the pin control subsystem * - * Copyright (C) 2011 ST-Ericsson SA + * Copyright (C) 2011-2012 ST-Ericsson SA * Written on behalf of Linaro for ST-Ericsson * Based on bits of regulator core, gpio core and clk core * * Author: Linus Walleij <linus.walleij@linaro.org> * + * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. + * * License terms: GNU General Public License (GPL) version 2 */ #define pr_fmt(fmt) "pinmux core: " fmt @@ -19,8 +21,6 @@ #include <linux/radix-tree.h> #include <linux/err.h> #include <linux/list.h> -#include <linux/mutex.h> -#include <linux/spinlock.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/debugfs.h> @@ -28,80 +28,64 @@ #include <linux/pinctrl/machine.h> #include <linux/pinctrl/pinmux.h> #include "core.h" +#include "pinmux.h" -/* List of pinmuxes */ -static DEFINE_MUTEX(pinmux_list_mutex); -static LIST_HEAD(pinmux_list); +int pinmux_check_ops(struct pinctrl_dev *pctldev) +{ + const struct pinmux_ops *ops = pctldev->desc->pmxops; + unsigned selector = 0; -/* Global pinmux maps */ -static struct pinmux_map *pinmux_maps; -static unsigned pinmux_maps_num; + /* Check that we implement required operations */ + if (!ops->list_functions || + !ops->get_function_name || + !ops->get_function_groups || + !ops->enable || + !ops->disable) + return -EINVAL; -/** - * struct pinmux_group - group list item for pinmux groups - * @node: pinmux group list node - * @group_selector: the group selector for this group - */ -struct pinmux_group { - struct list_head node; - unsigned group_selector; -}; + /* Check that all functions registered have names */ + while (ops->list_functions(pctldev, selector) >= 0) { + const char *fname = ops->get_function_name(pctldev, + selector); + if (!fname) { + pr_err("pinmux ops has no name for function%u\n", + selector); + return -EINVAL; + } + selector++; + } -/** - * struct pinmux - per-device pinmux state holder - * @node: global list node - * @dev: the device using this pinmux - * @usecount: the number of active users of this mux setting, used to keep - * track of nested use cases - * @pctldev: pin control device handling this pinmux - * @func_selector: the function selector for the pinmux device handling - * this pinmux - * @groups: the group selectors for the pinmux device and - * selector combination handling this pinmux, this is a list that - * will be traversed on all pinmux operations such as - * get/put/enable/disable - * @mutex: a lock for the pinmux state holder - */ -struct pinmux { - struct list_head node; - struct device *dev; - unsigned usecount; - struct pinctrl_dev *pctldev; - unsigned func_selector; - struct list_head groups; - struct mutex mutex; -}; + return 0; +} -/** - * struct pinmux_hog - a list item to stash mux hogs - * @node: pinmux hog list node - * @map: map entry responsible for this hogging - * @pmx: the pinmux hogged by this item - */ -struct pinmux_hog { - struct list_head node; - struct pinmux_map const *map; - struct pinmux *pmx; -}; +int pinmux_validate_map(struct pinctrl_map const *map, int i) +{ + if (!map->data.mux.function) { + pr_err("failed to register map %s (%d): no function given\n", + map->name, i); + return -EINVAL; + } + + return 0; +} /** * pin_request() - request a single pin to be muxed in, typically for GPIO * @pin: the pin number in the global pin space - * @function: a functional name to give to this pin, passed to the driver - * so it knows what function to mux in, e.g. the string "gpioNN" - * means that you want to mux in the pin for use as GPIO number NN + * @owner: a representation of the owner of this pin; typically the device + * name that controls its mux function, or the requested GPIO name * @gpio_range: the range matching the GPIO pin if this is a request for a * single GPIO pin */ static int pin_request(struct pinctrl_dev *pctldev, - int pin, const char *function, + int pin, const char *owner, struct pinctrl_gpio_range *gpio_range) { struct pin_desc *desc; const struct pinmux_ops *ops = pctldev->desc->pmxops; int status = -EINVAL; - dev_dbg(pctldev->dev, "request pin %d for %s\n", pin, function); + dev_dbg(pctldev->dev, "request pin %d for %s\n", pin, owner); desc = pin_desc_get(pctldev, pin); if (desc == NULL) { @@ -110,20 +94,28 @@ static int pin_request(struct pinctrl_dev *pctldev, goto out; } - if (!function) { - dev_err(pctldev->dev, "no function name given\n"); - return -EINVAL; - } + if (gpio_range) { + /* There's no need to support multiple GPIO requests */ + if (desc->gpio_owner) { + dev_err(pctldev->dev, + "pin already requested\n"); + goto out; + } - spin_lock(&desc->lock); - if (desc->mux_function) { - spin_unlock(&desc->lock); - dev_err(pctldev->dev, - "pin already requested\n"); - goto out; + desc->gpio_owner = owner; + } else { + if (desc->mux_usecount && strcmp(desc->mux_owner, owner)) { + dev_err(pctldev->dev, + "pin already requested\n"); + goto out; + } + + desc->mux_usecount++; + if (desc->mux_usecount > 1) + return 0; + + desc->mux_owner = owner; } - desc->mux_function = function; - spin_unlock(&desc->lock); /* Let each pin increase references to this module */ if (!try_module_get(pctldev->owner)) { @@ -146,19 +138,26 @@ static int pin_request(struct pinctrl_dev *pctldev, else status = 0; - if (status) + if (status) { dev_err(pctldev->dev, "->request on device %s failed for pin %d\n", pctldev->desc->name, pin); + module_put(pctldev->owner); + } + out_free_pin: if (status) { - spin_lock(&desc->lock); - desc->mux_function = NULL; - spin_unlock(&desc->lock); + if (gpio_range) { + desc->gpio_owner = NULL; + } else { + desc->mux_usecount--; + if (!desc->mux_usecount) + desc->mux_owner = NULL; + } } out: if (status) dev_err(pctldev->dev, "pin-%d (%s) status %d\n", - pin, function ? : "?", status); + pin, owner, status); return status; } @@ -170,8 +169,8 @@ out: * @gpio_range: the range matching the GPIO pin if this is a request for a * single GPIO pin * - * This function returns a pointer to the function name in use. This is used - * for callers that dynamically allocate a function name so it can be freed + * This function returns a pointer to the previous owner. This is used + * for callers that dynamically allocate an owner name so it can be freed * once the pin is free. This is done for GPIO request functions. */ static const char *pin_free(struct pinctrl_dev *pctldev, int pin, @@ -179,7 +178,7 @@ static const char *pin_free(struct pinctrl_dev *pctldev, int pin, { const struct pinmux_ops *ops = pctldev->desc->pmxops; struct pin_desc *desc; - const char *func; + const char *owner; desc = pin_desc_get(pctldev, pin); if (desc == NULL) { @@ -188,6 +187,12 @@ static const char *pin_free(struct pinctrl_dev *pctldev, int pin, return NULL; } + if (!gpio_range) { + desc->mux_usecount--; + if (desc->mux_usecount) + return NULL; + } + /* * If there is no kind of request function for the pin we just assume * we got it by default and proceed. @@ -197,99 +202,79 @@ static const char *pin_free(struct pinctrl_dev *pctldev, int pin, else if (ops->free) ops->free(pctldev, pin); - spin_lock(&desc->lock); - func = desc->mux_function; - desc->mux_function = NULL; - spin_unlock(&desc->lock); + if (gpio_range) { + owner = desc->gpio_owner; + desc->gpio_owner = NULL; + } else { + owner = desc->mux_owner; + desc->mux_owner = NULL; + desc->mux_setting = NULL; + } + module_put(pctldev->owner); - return func; + return owner; } /** - * pinmux_request_gpio() - request a single pin to be muxed in as GPIO - * @gpio: the GPIO pin number from the GPIO subsystem number space - * - * This function should *ONLY* be used from gpiolib-based GPIO drivers, - * as part of their gpio_request() semantics, platforms and individual drivers - * shall *NOT* request GPIO pins to be muxed in. + * pinmux_request_gpio() - request pinmuxing for a GPIO pin + * @pctldev: pin controller device affected + * @pin: the pin to mux in for GPIO + * @range: the applicable GPIO range */ -int pinmux_request_gpio(unsigned gpio) +int pinmux_request_gpio(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned pin, unsigned gpio) { char gpiostr[16]; - const char *function; - struct pinctrl_dev *pctldev; - struct pinctrl_gpio_range *range; + const char *owner; int ret; - int pin; - - ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); - if (ret) - return -EINVAL; - - /* Convert to the pin controllers number space */ - pin = gpio - range->base + range->pin_base; /* Conjure some name stating what chip and pin this is taken by */ snprintf(gpiostr, 15, "%s:%d", range->name, gpio); - function = kstrdup(gpiostr, GFP_KERNEL); - if (!function) + owner = kstrdup(gpiostr, GFP_KERNEL); + if (!owner) return -EINVAL; - ret = pin_request(pctldev, pin, function, range); + ret = pin_request(pctldev, pin, owner, range); if (ret < 0) - kfree(function); + kfree(owner); return ret; } -EXPORT_SYMBOL_GPL(pinmux_request_gpio); /** - * pinmux_free_gpio() - free a single pin, currently used as GPIO - * @gpio: the GPIO pin number from the GPIO subsystem number space - * - * This function should *ONLY* be used from gpiolib-based GPIO drivers, - * as part of their gpio_free() semantics, platforms and individual drivers - * shall *NOT* request GPIO pins to be muxed out. + * pinmux_free_gpio() - release a pin from GPIO muxing + * @pctldev: the pin controller device for the pin + * @pin: the affected currently GPIO-muxed in pin + * @range: applicable GPIO range */ -void pinmux_free_gpio(unsigned gpio) +void pinmux_free_gpio(struct pinctrl_dev *pctldev, unsigned pin, + struct pinctrl_gpio_range *range) { - struct pinctrl_dev *pctldev; - struct pinctrl_gpio_range *range; - int ret; - int pin; - const char *func; + const char *owner; - ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); - if (ret) - return; - - /* Convert to the pin controllers number space */ - pin = gpio - range->base + range->pin_base; - - func = pin_free(pctldev, pin, range); - kfree(func); + owner = pin_free(pctldev, pin, range); + kfree(owner); } -EXPORT_SYMBOL_GPL(pinmux_free_gpio); -static int pinmux_gpio_direction(unsigned gpio, bool input) +/** + * pinmux_gpio_direction() - set the direction of a single muxed-in GPIO pin + * @pctldev: the pin controller handling this pin + * @range: applicable GPIO range + * @pin: the affected GPIO pin in this controller + * @input: true if we set the pin as input, false for output + */ +int pinmux_gpio_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned pin, bool input) { - struct pinctrl_dev *pctldev; - struct pinctrl_gpio_range *range; const struct pinmux_ops *ops; int ret; - int pin; - - ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); - if (ret) - return ret; ops = pctldev->desc->pmxops; - /* Convert to the pin controllers number space */ - pin = gpio - range->base + range->pin_base; - if (ops->gpio_set_direction) ret = ops->gpio_set_direction(pctldev, range, pin, input); else @@ -298,146 +283,89 @@ static int pinmux_gpio_direction(unsigned gpio, bool input) return ret; } -/** - * pinmux_gpio_direction_input() - request a GPIO pin to go into input mode - * @gpio: the GPIO pin number from the GPIO subsystem number space - * - * This function should *ONLY* be used from gpiolib-based GPIO drivers, - * as part of their gpio_direction_input() semantics, platforms and individual - * drivers shall *NOT* touch pinmux GPIO calls. - */ -int pinmux_gpio_direction_input(unsigned gpio) -{ - return pinmux_gpio_direction(gpio, true); -} -EXPORT_SYMBOL_GPL(pinmux_gpio_direction_input); - -/** - * pinmux_gpio_direction_output() - request a GPIO pin to go into output mode - * @gpio: the GPIO pin number from the GPIO subsystem number space - * - * This function should *ONLY* be used from gpiolib-based GPIO drivers, - * as part of their gpio_direction_output() semantics, platforms and individual - * drivers shall *NOT* touch pinmux GPIO calls. - */ -int pinmux_gpio_direction_output(unsigned gpio) -{ - return pinmux_gpio_direction(gpio, false); -} -EXPORT_SYMBOL_GPL(pinmux_gpio_direction_output); - -/** - * pinmux_register_mappings() - register a set of pinmux mappings - * @maps: the pinmux mappings table to register, this should be marked with - * __initdata so it can be discarded after boot, this function will - * perform a shallow copy for the mapping entries. - * @num_maps: the number of maps in the mapping table - * - * Only call this once during initialization of your machine, the function is - * tagged as __init and won't be callable after init has completed. The map - * passed into this function will be owned by the pinmux core and cannot be - * freed. - */ -int __init pinmux_register_mappings(struct pinmux_map const *maps, - unsigned num_maps) +static int pinmux_func_name_to_selector(struct pinctrl_dev *pctldev, + const char *function) { - void *tmp_maps; - int i; - - pr_debug("add %d pinmux maps\n", num_maps); - - /* First sanity check the new mapping */ - for (i = 0; i < num_maps; i++) { - if (!maps[i].name) { - pr_err("failed to register map %d: no map name given\n", - i); - return -EINVAL; - } - - if (!maps[i].ctrl_dev && !maps[i].ctrl_dev_name) { - pr_err("failed to register map %s (%d): no pin control device given\n", - maps[i].name, i); - return -EINVAL; - } + const struct pinmux_ops *ops = pctldev->desc->pmxops; + unsigned selector = 0; - if (!maps[i].function) { - pr_err("failed to register map %s (%d): no function ID given\n", - maps[i].name, i); - return -EINVAL; - } + /* See if this pctldev has this function */ + while (ops->list_functions(pctldev, selector) >= 0) { + const char *fname = ops->get_function_name(pctldev, + selector); - if (!maps[i].dev && !maps[i].dev_name) - pr_debug("add system map %s function %s with no device\n", - maps[i].name, - maps[i].function); - else - pr_debug("register map %s, function %s\n", - maps[i].name, - maps[i].function); - } + if (!strcmp(function, fname)) + return selector; - /* - * Make a copy of the map array - string pointers will end up in the - * kernel const section anyway so these do not need to be deep copied. - */ - if (!pinmux_maps_num) { - /* On first call, just copy them */ - tmp_maps = kmemdup(maps, - sizeof(struct pinmux_map) * num_maps, - GFP_KERNEL); - if (!tmp_maps) - return -ENOMEM; - } else { - /* Subsequent calls, reallocate array to new size */ - size_t oldsize = sizeof(struct pinmux_map) * pinmux_maps_num; - size_t newsize = sizeof(struct pinmux_map) * num_maps; - - tmp_maps = krealloc(pinmux_maps, oldsize + newsize, GFP_KERNEL); - if (!tmp_maps) - return -ENOMEM; - memcpy((tmp_maps + oldsize), maps, newsize); + selector++; } - pinmux_maps = tmp_maps; - pinmux_maps_num += num_maps; - return 0; + pr_err("%s does not support function %s\n", + pinctrl_dev_get_name(pctldev), function); + return -EINVAL; } -/** - * acquire_pins() - acquire all the pins for a certain function on a pinmux - * @pctldev: the device to take the pins on - * @func_selector: the function selector to acquire the pins for - * @group_selector: the group selector containing the pins to acquire - */ -static int acquire_pins(struct pinctrl_dev *pctldev, - unsigned func_selector, - unsigned group_selector) +int pinmux_map_to_setting(struct pinctrl_map const *map, + struct pinctrl_setting *setting) { - const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; + struct pinctrl_dev *pctldev = setting->pctldev; const struct pinmux_ops *pmxops = pctldev->desc->pmxops; - const char *func = pmxops->get_function_name(pctldev, - func_selector); - const unsigned *pins; - unsigned num_pins; + const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; + char const * const *groups; + unsigned num_groups; int ret; + const char *group; int i; + const unsigned *pins; + unsigned num_pins; - ret = pctlops->get_group_pins(pctldev, group_selector, - &pins, &num_pins); - if (ret) + setting->data.mux.func = + pinmux_func_name_to_selector(pctldev, map->data.mux.function); + if (setting->data.mux.func < 0) + return setting->data.mux.func; + + ret = pmxops->get_function_groups(pctldev, setting->data.mux.func, + &groups, &num_groups); + if (ret < 0) return ret; + if (!num_groups) + return -EINVAL; - dev_dbg(pctldev->dev, "requesting the %u pins from group %u\n", - num_pins, group_selector); + if (map->data.mux.group) { + bool found = false; + group = map->data.mux.group; + for (i = 0; i < num_groups; i++) { + if (!strcmp(group, groups[i])) { + found = true; + break; + } + } + if (!found) + return -EINVAL; + } else { + group = groups[0]; + } + + setting->data.mux.group = pinctrl_get_group_selector(pctldev, group); + if (setting->data.mux.group < 0) + return setting->data.mux.group; + + ret = pctlops->get_group_pins(pctldev, setting->data.mux.group, &pins, + &num_pins); + if (ret) { + dev_err(pctldev->dev, + "could not get pins for device %s group selector %d\n", + pinctrl_dev_get_name(pctldev), setting->data.mux.group); + return -ENODEV; + } /* Try to allocate all pins in this group, one by one */ for (i = 0; i < num_pins; i++) { - ret = pin_request(pctldev, pins[i], func, NULL); + ret = pin_request(pctldev, pins[i], map->dev_name, NULL); if (ret) { dev_err(pctldev->dev, - "could not get pin %d for function %s on device %s - conflicting mux mappings?\n", - pins[i], func ? : "(undefined)", - pinctrl_dev_get_name(pctldev)); + "could not get request pin %d on device %s\n", + pins[i], pinctrl_dev_get_name(pctldev)); /* On error release all taken pins */ i--; /* this pin just failed */ for (; i >= 0; i--) @@ -445,587 +373,101 @@ static int acquire_pins(struct pinctrl_dev *pctldev, return -ENODEV; } } + return 0; } -/** - * release_pins() - release pins taken by earlier acquirement - * @pctldev: the device to free the pins on - * @group_selector: the group selector containing the pins to free - */ -static void release_pins(struct pinctrl_dev *pctldev, - unsigned group_selector) +void pinmux_free_setting(struct pinctrl_setting const *setting) { + struct pinctrl_dev *pctldev = setting->pctldev; const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; const unsigned *pins; unsigned num_pins; int ret; int i; - ret = pctlops->get_group_pins(pctldev, group_selector, + ret = pctlops->get_group_pins(pctldev, setting->data.mux.group, &pins, &num_pins); if (ret) { - dev_err(pctldev->dev, "could not get pins to release for group selector %d\n", - group_selector); + dev_err(pctldev->dev, + "could not get pins for device %s group selector %d\n", + pinctrl_dev_get_name(pctldev), setting->data.mux.group); return; } + for (i = 0; i < num_pins; i++) pin_free(pctldev, pins[i], NULL); } -/** - * pinmux_check_pin_group() - check function and pin group combo - * @pctldev: device to check the pin group vs function for - * @func_selector: the function selector to check the pin group for, we have - * already looked this up in the calling function - * @pin_group: the pin group to match to the function - * - * This function will check that the pinmux driver can supply the - * selected pin group for a certain function, returns the group selector if - * the group and function selector will work fine together, else returns - * negative - */ -static int pinmux_check_pin_group(struct pinctrl_dev *pctldev, - unsigned func_selector, - const char *pin_group) +int pinmux_enable_setting(struct pinctrl_setting const *setting) { - const struct pinmux_ops *pmxops = pctldev->desc->pmxops; + struct pinctrl_dev *pctldev = setting->pctldev; const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; - int ret; - - /* - * If the driver does not support different pin groups for the - * functions, we only support group 0, and assume this exists. - */ - if (!pctlops || !pctlops->list_groups) - return 0; - - /* - * Passing NULL (no specific group) will select the first and - * hopefully only group of pins available for this function. - */ - if (!pin_group) { - char const * const *groups; - unsigned num_groups; - - ret = pmxops->get_function_groups(pctldev, func_selector, - &groups, &num_groups); - if (ret) - return ret; - if (num_groups < 1) - return -EINVAL; - ret = pinctrl_get_group_selector(pctldev, groups[0]); - if (ret < 0) { - dev_err(pctldev->dev, - "function %s wants group %s but the pin controller does not seem to have that group\n", - pmxops->get_function_name(pctldev, func_selector), - groups[0]); - return ret; - } - - if (num_groups > 1) - dev_dbg(pctldev->dev, - "function %s support more than one group, default-selecting first group %s (%d)\n", - pmxops->get_function_name(pctldev, func_selector), - groups[0], - ret); - - return ret; - } - - dev_dbg(pctldev->dev, - "check if we have pin group %s on controller %s\n", - pin_group, pinctrl_dev_get_name(pctldev)); - - ret = pinctrl_get_group_selector(pctldev, pin_group); - if (ret < 0) { - dev_dbg(pctldev->dev, - "%s does not support pin group %s with function %s\n", - pinctrl_dev_get_name(pctldev), - pin_group, - pmxops->get_function_name(pctldev, func_selector)); - } - return ret; -} - -/** - * pinmux_search_function() - check pin control driver for a certain function - * @pctldev: device to check for function and position - * @map: function map containing the function and position to look for - * @func_selector: returns the applicable function selector if found - * @group_selector: returns the applicable group selector if found - * - * This will search the pinmux driver for an applicable - * function with a specific pin group, returns 0 if these can be mapped - * negative otherwise - */ -static int pinmux_search_function(struct pinctrl_dev *pctldev, - struct pinmux_map const *map, - unsigned *func_selector, - unsigned *group_selector) -{ const struct pinmux_ops *ops = pctldev->desc->pmxops; - unsigned selector = 0; - - /* See if this pctldev has this function */ - while (ops->list_functions(pctldev, selector) >= 0) { - const char *fname = ops->get_function_name(pctldev, - selector); - int ret; - - if (!strcmp(map->function, fname)) { - /* Found the function, check pin group */ - ret = pinmux_check_pin_group(pctldev, selector, - map->group); - if (ret < 0) - return ret; - - /* This function and group selector can be used */ - *func_selector = selector; - *group_selector = ret; - return 0; - - } - selector++; - } - - pr_err("%s does not support function %s\n", - pinctrl_dev_get_name(pctldev), map->function); - return -EINVAL; -} - -/** - * pinmux_enable_muxmap() - enable a map entry for a certain pinmux - */ -static int pinmux_enable_muxmap(struct pinctrl_dev *pctldev, - struct pinmux *pmx, - struct device *dev, - const char *devname, - struct pinmux_map const *map) -{ - unsigned func_selector; - unsigned group_selector; - struct pinmux_group *grp; int ret; + const unsigned *pins; + unsigned num_pins; + int i; + struct pin_desc *desc; - /* - * Note that we're not locking the pinmux mutex here, because - * this is only called at pinmux initialization time when it - * has not been added to any list and thus is not reachable - * by anyone else. - */ - - if (pmx->pctldev && pmx->pctldev != pctldev) { - dev_err(pctldev->dev, - "different pin control devices given for device %s, function %s\n", - devname, map->function); - return -EINVAL; - } - pmx->dev = dev; - pmx->pctldev = pctldev; - - /* Now go into the driver and try to match a function and group */ - ret = pinmux_search_function(pctldev, map, &func_selector, - &group_selector); - if (ret < 0) - return ret; - - /* - * If the function selector is already set, it needs to be identical, - * we support several groups with one function but not several - * functions with one or several groups in the same pinmux. - */ - if (pmx->func_selector != UINT_MAX && - pmx->func_selector != func_selector) { - dev_err(pctldev->dev, - "dual function defines in the map for device %s\n", - devname); - return -EINVAL; - } - pmx->func_selector = func_selector; - - /* Now add this group selector, we may have many of them */ - grp = kmalloc(sizeof(struct pinmux_group), GFP_KERNEL); - if (!grp) - return -ENOMEM; - grp->group_selector = group_selector; - ret = acquire_pins(pctldev, func_selector, group_selector); + ret = pctlops->get_group_pins(pctldev, setting->data.mux.group, + &pins, &num_pins); if (ret) { - kfree(grp); - return ret; + /* errors only affect debug data, so just warn */ + dev_warn(pctldev->dev, + "could not get pins for group selector %d\n", + setting->data.mux.group); + num_pins = 0; } - list_add(&grp->node, &pmx->groups); - - return 0; -} - -static void pinmux_free_groups(struct pinmux *pmx) -{ - struct list_head *node, *tmp; - - list_for_each_safe(node, tmp, &pmx->groups) { - struct pinmux_group *grp = - list_entry(node, struct pinmux_group, node); - /* Release all pins taken by this group */ - release_pins(pmx->pctldev, grp->group_selector); - list_del(node); - kfree(grp); - } -} - -/** - * pinmux_get() - retrieves the pinmux for a certain device - * @dev: the device to get the pinmux for - * @name: an optional specific mux mapping name or NULL, the name is only - * needed if you want to have more than one mapping per device, or if you - * need an anonymous pinmux (not tied to any specific device) - */ -struct pinmux *pinmux_get(struct device *dev, const char *name) -{ - struct pinmux_map const *map = NULL; - struct pinctrl_dev *pctldev = NULL; - const char *devname = NULL; - struct pinmux *pmx; - bool found_map; - unsigned num_maps = 0; - int ret = -ENODEV; - int i; - - /* We must have dev or ID or both */ - if (!dev && !name) - return ERR_PTR(-EINVAL); - - if (dev) - devname = dev_name(dev); - - pr_debug("get mux %s for device %s\n", name, - devname ? devname : "(none)"); - /* - * create the state cookie holder struct pinmux for each - * mapping, this is what consumers will get when requesting - * a pinmux handle with pinmux_get() - */ - pmx = kzalloc(sizeof(struct pinmux), GFP_KERNEL); - if (pmx == NULL) - return ERR_PTR(-ENOMEM); - mutex_init(&pmx->mutex); - pmx->func_selector = UINT_MAX; - INIT_LIST_HEAD(&pmx->groups); - - /* Iterate over the pinmux maps to locate the right ones */ - for (i = 0; i < pinmux_maps_num; i++) { - map = &pinmux_maps[i]; - found_map = false; - - /* - * First, try to find the pctldev given in the map - */ - pctldev = get_pinctrl_dev_from_dev(map->ctrl_dev, - map->ctrl_dev_name); - if (!pctldev) { - const char *devname = NULL; - - if (map->ctrl_dev) - devname = dev_name(map->ctrl_dev); - else if (map->ctrl_dev_name) - devname = map->ctrl_dev_name; - - pr_warning("could not find a pinctrl device for pinmux function %s, fishy, they shall all have one\n", - map->function); - pr_warning("given pinctrl device name: %s", - devname ? devname : "UNDEFINED"); - - /* Continue to check the other mappings anyway... */ + for (i = 0; i < num_pins; i++) { + desc = pin_desc_get(pctldev, pins[i]); + if (desc == NULL) { + dev_warn(pctldev->dev, + "could not get pin desc for pin %d\n", + pins[i]); continue; } - - pr_debug("in map, found pctldev %s to handle function %s", - dev_name(pctldev->dev), map->function); - - - /* - * If we're looking for a specific named map, this must match, - * else we loop and look for the next. - */ - if (name != NULL) { - if (map->name == NULL) - continue; - if (strcmp(map->name, name)) - continue; - } - - /* - * This is for the case where no device name is given, we - * already know that the function name matches from above - * code. - */ - if (!map->dev_name && (name != NULL)) - found_map = true; - - /* If the mapping has a device set up it must match */ - if (map->dev_name && - (!devname || !strcmp(map->dev_name, devname))) - /* MATCH! */ - found_map = true; - - /* If this map is applicable, then apply it */ - if (found_map) { - ret = pinmux_enable_muxmap(pctldev, pmx, dev, - devname, map); - if (ret) { - pinmux_free_groups(pmx); - kfree(pmx); - return ERR_PTR(ret); - } - num_maps++; - } - } - - - /* We should have atleast one map, right */ - if (!num_maps) { - pr_err("could not find any mux maps for device %s, ID %s\n", - devname ? devname : "(anonymous)", - name ? name : "(undefined)"); - kfree(pmx); - return ERR_PTR(-EINVAL); + desc->mux_setting = &(setting->data.mux); } - pr_debug("found %u mux maps for device %s, UD %s\n", - num_maps, - devname ? devname : "(anonymous)", - name ? name : "(undefined)"); - - /* Add the pinmux to the global list */ - mutex_lock(&pinmux_list_mutex); - list_add(&pmx->node, &pinmux_list); - mutex_unlock(&pinmux_list_mutex); - - return pmx; + return ops->enable(pctldev, setting->data.mux.func, + setting->data.mux.group); } -EXPORT_SYMBOL_GPL(pinmux_get); -/** - * pinmux_put() - release a previously claimed pinmux - * @pmx: a pinmux previously claimed by pinmux_get() - */ -void pinmux_put(struct pinmux *pmx) -{ - if (pmx == NULL) - return; - - mutex_lock(&pmx->mutex); - if (pmx->usecount) - pr_warn("releasing pinmux with active users!\n"); - /* Free the groups and all acquired pins */ - pinmux_free_groups(pmx); - mutex_unlock(&pmx->mutex); - - /* Remove from list */ - mutex_lock(&pinmux_list_mutex); - list_del(&pmx->node); - mutex_unlock(&pinmux_list_mutex); - - kfree(pmx); -} -EXPORT_SYMBOL_GPL(pinmux_put); - -/** - * pinmux_enable() - enable a certain pinmux setting - * @pmx: the pinmux to enable, previously claimed by pinmux_get() - */ -int pinmux_enable(struct pinmux *pmx) -{ - int ret = 0; - - if (pmx == NULL) - return -EINVAL; - mutex_lock(&pmx->mutex); - if (pmx->usecount++ == 0) { - struct pinctrl_dev *pctldev = pmx->pctldev; - const struct pinmux_ops *ops = pctldev->desc->pmxops; - struct pinmux_group *grp; - - list_for_each_entry(grp, &pmx->groups, node) { - ret = ops->enable(pctldev, pmx->func_selector, - grp->group_selector); - if (ret) { - /* - * TODO: call disable() on all groups we called - * enable() on to this point? - */ - pmx->usecount--; - break; - } - } - } - mutex_unlock(&pmx->mutex); - return ret; -} -EXPORT_SYMBOL_GPL(pinmux_enable); - -/** - * pinmux_disable() - disable a certain pinmux setting - * @pmx: the pinmux to disable, previously claimed by pinmux_get() - */ -void pinmux_disable(struct pinmux *pmx) -{ - if (pmx == NULL) - return; - - mutex_lock(&pmx->mutex); - if (--pmx->usecount == 0) { - struct pinctrl_dev *pctldev = pmx->pctldev; - const struct pinmux_ops *ops = pctldev->desc->pmxops; - struct pinmux_group *grp; - - list_for_each_entry(grp, &pmx->groups, node) { - ops->disable(pctldev, pmx->func_selector, - grp->group_selector); - } - } - mutex_unlock(&pmx->mutex); -} -EXPORT_SYMBOL_GPL(pinmux_disable); - -int pinmux_check_ops(struct pinctrl_dev *pctldev) +void pinmux_disable_setting(struct pinctrl_setting const *setting) { + struct pinctrl_dev *pctldev = setting->pctldev; + const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; const struct pinmux_ops *ops = pctldev->desc->pmxops; - unsigned selector = 0; - - /* Check that we implement required operations */ - if (!ops->list_functions || - !ops->get_function_name || - !ops->get_function_groups || - !ops->enable || - !ops->disable) - return -EINVAL; - - /* Check that all functions registered have names */ - while (ops->list_functions(pctldev, selector) >= 0) { - const char *fname = ops->get_function_name(pctldev, - selector); - if (!fname) { - pr_err("pinmux ops has no name for function%u\n", - selector); - return -EINVAL; - } - selector++; - } - - return 0; -} - -/* Hog a single map entry and add to the hoglist */ -static int pinmux_hog_map(struct pinctrl_dev *pctldev, - struct pinmux_map const *map) -{ - struct pinmux_hog *hog; - struct pinmux *pmx; int ret; + const unsigned *pins; + unsigned num_pins; + int i; + struct pin_desc *desc; - if (map->dev || map->dev_name) { - /* - * TODO: the day we have device tree support, we can - * traverse the device tree and hog to specific device nodes - * without any problems, so then we can hog pinmuxes for - * all devices that just want a static pin mux at this point. - */ - dev_err(pctldev->dev, "map %s wants to hog a non-system pinmux, this is not going to work\n", - map->name); - return -EINVAL; - } - - hog = kzalloc(sizeof(struct pinmux_hog), GFP_KERNEL); - if (!hog) - return -ENOMEM; - - pmx = pinmux_get(NULL, map->name); - if (IS_ERR(pmx)) { - kfree(hog); - dev_err(pctldev->dev, - "could not get the %s pinmux mapping for hogging\n", - map->name); - return PTR_ERR(pmx); - } - - ret = pinmux_enable(pmx); + ret = pctlops->get_group_pins(pctldev, setting->data.mux.group, + &pins, &num_pins); if (ret) { - pinmux_put(pmx); - kfree(hog); - dev_err(pctldev->dev, - "could not enable the %s pinmux mapping for hogging\n", - map->name); - return ret; + /* errors only affect debug data, so just warn */ + dev_warn(pctldev->dev, + "could not get pins for group selector %d\n", + setting->data.mux.group); + num_pins = 0; } - hog->map = map; - hog->pmx = pmx; - - dev_info(pctldev->dev, "hogged map %s, function %s\n", map->name, - map->function); - mutex_lock(&pctldev->pinmux_hogs_lock); - list_add(&hog->node, &pctldev->pinmux_hogs); - mutex_unlock(&pctldev->pinmux_hogs_lock); - - return 0; -} - -/** - * pinmux_hog_maps() - hog specific map entries on controller device - * @pctldev: the pin control device to hog entries on - * - * When the pin controllers are registered, there may be some specific pinmux - * map entries that need to be hogged, i.e. get+enabled until the system shuts - * down. - */ -int pinmux_hog_maps(struct pinctrl_dev *pctldev) -{ - struct device *dev = pctldev->dev; - const char *devname = dev_name(dev); - int ret; - int i; - - INIT_LIST_HEAD(&pctldev->pinmux_hogs); - mutex_init(&pctldev->pinmux_hogs_lock); - - for (i = 0; i < pinmux_maps_num; i++) { - struct pinmux_map const *map = &pinmux_maps[i]; - - if (!map->hog_on_boot) + for (i = 0; i < num_pins; i++) { + desc = pin_desc_get(pctldev, pins[i]); + if (desc == NULL) { + dev_warn(pctldev->dev, + "could not get pin desc for pin %d\n", + pins[i]); continue; - - if ((map->ctrl_dev == dev) || - (map->ctrl_dev_name && - !strcmp(map->ctrl_dev_name, devname))) { - /* OK time to hog! */ - ret = pinmux_hog_map(pctldev, map); - if (ret) - return ret; } + desc->mux_setting = NULL; } - return 0; -} -/** - * pinmux_unhog_maps() - unhog specific map entries on controller device - * @pctldev: the pin control device to unhog entries on - */ -void pinmux_unhog_maps(struct pinctrl_dev *pctldev) -{ - struct list_head *node, *tmp; - - mutex_lock(&pctldev->pinmux_hogs_lock); - list_for_each_safe(node, tmp, &pctldev->pinmux_hogs) { - struct pinmux_hog *hog = - list_entry(node, struct pinmux_hog, node); - pinmux_disable(hog->pmx); - pinmux_put(hog->pmx); - list_del(node); - kfree(hog); - } - mutex_unlock(&pctldev->pinmux_hogs_lock); + ops->disable(pctldev, setting->data.mux.func, setting->data.mux.group); } #ifdef CONFIG_DEBUG_FS @@ -1037,6 +479,8 @@ static int pinmux_functions_show(struct seq_file *s, void *what) const struct pinmux_ops *pmxops = pctldev->desc->pmxops; unsigned func_selector = 0; + mutex_lock(&pinctrl_mutex); + while (pmxops->list_functions(pctldev, func_selector) >= 0) { const char *func = pmxops->get_function_name(pctldev, func_selector); @@ -1057,24 +501,29 @@ static int pinmux_functions_show(struct seq_file *s, void *what) seq_puts(s, "]\n"); func_selector++; - } + mutex_unlock(&pinctrl_mutex); + return 0; } static int pinmux_pins_show(struct seq_file *s, void *what) { struct pinctrl_dev *pctldev = s->private; + const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; + const struct pinmux_ops *pmxops = pctldev->desc->pmxops; unsigned i, pin; seq_puts(s, "Pinmux settings per pin\n"); - seq_puts(s, "Format: pin (name): pinmuxfunction\n"); + seq_puts(s, "Format: pin (name): mux_owner gpio_owner hog?\n"); + + mutex_lock(&pinctrl_mutex); /* The pin number can be retrived from the pin controller descriptor */ for (i = 0; i < pctldev->desc->npins; i++) { - struct pin_desc *desc; + bool is_hog = false; pin = pctldev->desc->pins[i].number; desc = pin_desc_get(pctldev, pin); @@ -1082,94 +531,52 @@ static int pinmux_pins_show(struct seq_file *s, void *what) if (desc == NULL) continue; - seq_printf(s, "pin %d (%s): %s\n", pin, + if (desc->mux_owner && + !strcmp(desc->mux_owner, pinctrl_dev_get_name(pctldev))) + is_hog = true; + + seq_printf(s, "pin %d (%s): %s %s%s", pin, desc->name ? desc->name : "unnamed", - desc->mux_function ? desc->mux_function - : "UNCLAIMED"); + desc->mux_owner ? desc->mux_owner + : "(MUX UNCLAIMED)", + desc->gpio_owner ? desc->gpio_owner + : "(GPIO UNCLAIMED)", + is_hog ? " (HOG)" : ""); + + if (desc->mux_setting) + seq_printf(s, " function %s group %s\n", + pmxops->get_function_name(pctldev, + desc->mux_setting->func), + pctlops->get_group_name(pctldev, + desc->mux_setting->group)); + else + seq_printf(s, "\n"); } - return 0; -} - -static int pinmux_hogs_show(struct seq_file *s, void *what) -{ - struct pinctrl_dev *pctldev = s->private; - struct pinmux_hog *hog; - - seq_puts(s, "Pinmux map hogs held by device\n"); - - list_for_each_entry(hog, &pctldev->pinmux_hogs, node) - seq_printf(s, "%s\n", hog->map->name); + mutex_unlock(&pinctrl_mutex); return 0; } -static int pinmux_show(struct seq_file *s, void *what) +void pinmux_show_map(struct seq_file *s, struct pinctrl_map const *map) { - struct pinmux *pmx; - - seq_puts(s, "Requested pinmuxes and their maps:\n"); - list_for_each_entry(pmx, &pinmux_list, node) { - struct pinctrl_dev *pctldev = pmx->pctldev; - const struct pinmux_ops *pmxops; - const struct pinctrl_ops *pctlops; - struct pinmux_group *grp; - - if (!pctldev) { - seq_puts(s, "NO PIN CONTROLLER DEVICE\n"); - continue; - } - - pmxops = pctldev->desc->pmxops; - pctlops = pctldev->desc->pctlops; - - seq_printf(s, "device: %s function: %s (%u),", - pinctrl_dev_get_name(pmx->pctldev), - pmxops->get_function_name(pctldev, - pmx->func_selector), - pmx->func_selector); - - seq_printf(s, " groups: ["); - list_for_each_entry(grp, &pmx->groups, node) { - seq_printf(s, " %s (%u)", - pctlops->get_group_name(pctldev, - grp->group_selector), - grp->group_selector); - } - seq_printf(s, " ]"); - - seq_printf(s, " users: %u map-> %s\n", - pmx->usecount, - pmx->dev ? dev_name(pmx->dev) : "(system)"); - } - - return 0; + seq_printf(s, "group %s\nfunction %s\n", + map->data.mux.group ? map->data.mux.group : "(default)", + map->data.mux.function); } -static int pinmux_maps_show(struct seq_file *s, void *what) +void pinmux_show_setting(struct seq_file *s, + struct pinctrl_setting const *setting) { - int i; - - seq_puts(s, "Pinmux maps:\n"); - - for (i = 0; i < pinmux_maps_num; i++) { - struct pinmux_map const *map = &pinmux_maps[i]; + struct pinctrl_dev *pctldev = setting->pctldev; + const struct pinmux_ops *pmxops = pctldev->desc->pmxops; + const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; - seq_printf(s, "%s:\n", map->name); - if (map->dev || map->dev_name) - seq_printf(s, " device: %s\n", - map->dev ? dev_name(map->dev) : - map->dev_name); - else - seq_printf(s, " SYSTEM MUX\n"); - seq_printf(s, " controlling device %s\n", - map->ctrl_dev ? dev_name(map->ctrl_dev) : - map->ctrl_dev_name); - seq_printf(s, " function: %s\n", map->function); - seq_printf(s, " group: %s\n", map->group ? map->group : - "(default)"); - } - return 0; + seq_printf(s, "group: %s (%u) function: %s (%u)\n", + pctlops->get_group_name(pctldev, setting->data.mux.group), + setting->data.mux.group, + pmxops->get_function_name(pctldev, setting->data.mux.func), + setting->data.mux.func); } static int pinmux_functions_open(struct inode *inode, struct file *file) @@ -1182,21 +589,6 @@ static int pinmux_pins_open(struct inode *inode, struct file *file) return single_open(file, pinmux_pins_show, inode->i_private); } -static int pinmux_hogs_open(struct inode *inode, struct file *file) -{ - return single_open(file, pinmux_hogs_show, inode->i_private); -} - -static int pinmux_open(struct inode *inode, struct file *file) -{ - return single_open(file, pinmux_show, NULL); -} - -static int pinmux_maps_open(struct inode *inode, struct file *file) -{ - return single_open(file, pinmux_maps_show, NULL); -} - static const struct file_operations pinmux_functions_ops = { .open = pinmux_functions_open, .read = seq_read, @@ -1211,27 +603,6 @@ static const struct file_operations pinmux_pins_ops = { .release = single_release, }; -static const struct file_operations pinmux_hogs_ops = { - .open = pinmux_hogs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations pinmux_ops = { - .open = pinmux_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations pinmux_maps_ops = { - .open = pinmux_maps_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - void pinmux_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev) { @@ -1239,16 +610,6 @@ void pinmux_init_device_debugfs(struct dentry *devroot, devroot, pctldev, &pinmux_functions_ops); debugfs_create_file("pinmux-pins", S_IFREG | S_IRUGO, devroot, pctldev, &pinmux_pins_ops); - debugfs_create_file("pinmux-hogs", S_IFREG | S_IRUGO, - devroot, pctldev, &pinmux_hogs_ops); -} - -void pinmux_init_debugfs(struct dentry *subsys_root) -{ - debugfs_create_file("pinmuxes", S_IFREG | S_IRUGO, - subsys_root, NULL, &pinmux_ops); - debugfs_create_file("pinmux-maps", S_IFREG | S_IRUGO, - subsys_root, NULL, &pinmux_maps_ops); } #endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/pinctrl/pinmux.h b/drivers/pinctrl/pinmux.h index 97f52223fbc2..6fc47003e95d 100644 --- a/drivers/pinctrl/pinmux.h +++ b/drivers/pinctrl/pinmux.h @@ -13,11 +13,29 @@ #ifdef CONFIG_PINMUX int pinmux_check_ops(struct pinctrl_dev *pctldev); + +int pinmux_validate_map(struct pinctrl_map const *map, int i); + +int pinmux_request_gpio(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned pin, unsigned gpio); +void pinmux_free_gpio(struct pinctrl_dev *pctldev, unsigned pin, + struct pinctrl_gpio_range *range); +int pinmux_gpio_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned pin, bool input); + +int pinmux_map_to_setting(struct pinctrl_map const *map, + struct pinctrl_setting *setting); +void pinmux_free_setting(struct pinctrl_setting const *setting); +int pinmux_enable_setting(struct pinctrl_setting const *setting); +void pinmux_disable_setting(struct pinctrl_setting const *setting); + +void pinmux_show_map(struct seq_file *s, struct pinctrl_map const *map); +void pinmux_show_setting(struct seq_file *s, + struct pinctrl_setting const *setting); void pinmux_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev); -void pinmux_init_debugfs(struct dentry *subsys_root); -int pinmux_hog_maps(struct pinctrl_dev *pctldev); -void pinmux_unhog_maps(struct pinctrl_dev *pctldev); #else @@ -26,21 +44,63 @@ static inline int pinmux_check_ops(struct pinctrl_dev *pctldev) return 0; } -static inline void pinmux_init_device_debugfs(struct dentry *devroot, - struct pinctrl_dev *pctldev) +static inline int pinmux_validate_map(struct pinctrl_map const *map, int i) { + return 0; } -static inline void pinmux_init_debugfs(struct dentry *subsys_root) +static inline int pinmux_request_gpio(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned pin, unsigned gpio) { + return 0; } -static inline int pinmux_hog_maps(struct pinctrl_dev *pctldev) +static inline void pinmux_free_gpio(struct pinctrl_dev *pctldev, + unsigned pin, + struct pinctrl_gpio_range *range) +{ +} + +static inline int pinmux_gpio_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned pin, bool input) +{ + return 0; +} + +static inline int pinmux_map_to_setting(struct pinctrl_map const *map, + struct pinctrl_setting *setting) { return 0; } -static inline void pinmux_unhog_maps(struct pinctrl_dev *pctldev) +static inline void pinmux_free_setting(struct pinctrl_setting const *setting) +{ +} + +static inline int pinmux_enable_setting(struct pinctrl_setting const *setting) +{ + return 0; +} + +static inline void pinmux_disable_setting( + struct pinctrl_setting const *setting) +{ +} + +static inline void pinmux_show_map(struct seq_file *s, + struct pinctrl_map const *map) +{ +} + +static inline void pinmux_show_setting(struct seq_file *s, + struct pinctrl_setting const *setting) +{ +} + +static inline void pinmux_init_device_debugfs(struct dentry *devroot, + struct pinctrl_dev *pctldev) { } diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 72d731c21d45..9929246895de 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -571,7 +571,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus) } else { dev = pci_get_slot(bus, 0); if (dev) { - pci_remove_bus_device(dev); + pci_stop_and_remove_bus_device(dev); pci_dev_put(dev); } } diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index ea44abd8df48..d9a9e2bedb30 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -646,7 +646,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle) } else { dev = pci_get_slot(bus, 0); if (dev) { - pci_remove_bus_device(dev); + pci_stop_and_remove_bus_device(dev); pci_dev_put(dev); } } diff --git a/drivers/power/apm_power.c b/drivers/power/apm_power.c index 8a612dec9139..39763015b360 100644 --- a/drivers/power/apm_power.c +++ b/drivers/power/apm_power.c @@ -10,6 +10,7 @@ */ #include <linux/module.h> +#include <linux/device.h> #include <linux/power_supply.h> #include <linux/apm-emulation.h> diff --git a/drivers/power/max8998_charger.c b/drivers/power/max8998_charger.c index 9b3f2bf56e70..6dc01c255592 100644 --- a/drivers/power/max8998_charger.c +++ b/drivers/power/max8998_charger.c @@ -19,7 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <linux/module.h> #include <linux/err.h> #include <linux/module.h> #include <linux/slab.h> diff --git a/drivers/power/power_supply.h b/drivers/power/power_supply.h index 018de2b26998..cc439fd89d8d 100644 --- a/drivers/power/power_supply.h +++ b/drivers/power/power_supply.h @@ -10,6 +10,10 @@ * You may use this code as per GPL version 2 */ +struct device; +struct device_type; +struct power_supply; + #ifdef CONFIG_SYSFS extern void power_supply_init_attrs(struct device_type *dev_type); diff --git a/drivers/power/power_supply_leds.c b/drivers/power/power_supply_leds.c index da25eb94e5c6..995f966ed5b7 100644 --- a/drivers/power/power_supply_leds.c +++ b/drivers/power/power_supply_leds.c @@ -11,6 +11,7 @@ */ #include <linux/kernel.h> +#include <linux/device.h> #include <linux/power_supply.h> #include <linux/slab.h> diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index b52b57ca3084..4368e7d61316 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -12,6 +12,7 @@ */ #include <linux/ctype.h> +#include <linux/device.h> #include <linux/power_supply.h> #include <linux/slab.h> #include <linux/stat.h> diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index a229de98ae6f..36db5a441eba 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -258,14 +258,6 @@ config REGULATOR_DB8500_PRCMU This driver supports the voltage domain regulators controlled by the DB8500 PRCMU -config REGULATOR_BQ24022 - tristate "TI bq24022 Dual Input 1-Cell Li-Ion Charger IC" - help - This driver controls a TI bq24022 Charger attached via - GPIOs. The provided current regulator can enable/disable - charging select between 100 mA and 500 mA charging current - limit. - config REGULATOR_TPS6105X tristate "TI TPS6105X Power regulators" depends on TPS6105X diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index b5042c885d89..94b52745e957 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -16,7 +16,6 @@ obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o -obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o obj-$(CONFIG_REGULATOR_DA903X) += da903x.o obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o diff --git a/drivers/regulator/bq24022.c b/drivers/regulator/bq24022.c deleted file mode 100644 index 9fab6d1bbe80..000000000000 --- a/drivers/regulator/bq24022.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Support for TI bq24022 (bqTINY-II) Dual Input (USB/AC Adpater) - * 1-Cell Li-Ion Charger connected via GPIOs. - * - * Copyright (c) 2008 Philipp Zabel - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/err.h> -#include <linux/module.h> -#include <linux/gpio.h> -#include <linux/regulator/bq24022.h> -#include <linux/regulator/driver.h> - - -static int bq24022_set_current_limit(struct regulator_dev *rdev, - int min_uA, int max_uA) -{ - struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev); - - dev_dbg(rdev_get_dev(rdev), "setting current limit to %s mA\n", - max_uA >= 500000 ? "500" : "100"); - - /* REVISIT: maybe return error if min_uA != 0 ? */ - gpio_set_value(pdata->gpio_iset2, max_uA >= 500000); - return 0; -} - -static int bq24022_get_current_limit(struct regulator_dev *rdev) -{ - struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev); - - return gpio_get_value(pdata->gpio_iset2) ? 500000 : 100000; -} - -static int bq24022_enable(struct regulator_dev *rdev) -{ - struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev); - - dev_dbg(rdev_get_dev(rdev), "enabling charger\n"); - - gpio_set_value(pdata->gpio_nce, 0); - return 0; -} - -static int bq24022_disable(struct regulator_dev *rdev) -{ - struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev); - - dev_dbg(rdev_get_dev(rdev), "disabling charger\n"); - - gpio_set_value(pdata->gpio_nce, 1); - return 0; -} - -static int bq24022_is_enabled(struct regulator_dev *rdev) -{ - struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev); - - return !gpio_get_value(pdata->gpio_nce); -} - -static struct regulator_ops bq24022_ops = { - .set_current_limit = bq24022_set_current_limit, - .get_current_limit = bq24022_get_current_limit, - .enable = bq24022_enable, - .disable = bq24022_disable, - .is_enabled = bq24022_is_enabled, -}; - -static struct regulator_desc bq24022_desc = { - .name = "bq24022", - .ops = &bq24022_ops, - .type = REGULATOR_CURRENT, - .owner = THIS_MODULE, -}; - -static int __init bq24022_probe(struct platform_device *pdev) -{ - struct bq24022_mach_info *pdata = pdev->dev.platform_data; - struct regulator_dev *bq24022; - int ret; - - if (!pdata || !pdata->gpio_nce || !pdata->gpio_iset2) - return -EINVAL; - - ret = gpio_request(pdata->gpio_nce, "ncharge_en"); - if (ret) { - dev_dbg(&pdev->dev, "couldn't request nCE GPIO: %d\n", - pdata->gpio_nce); - goto err_ce; - } - ret = gpio_request(pdata->gpio_iset2, "charge_mode"); - if (ret) { - dev_dbg(&pdev->dev, "couldn't request ISET2 GPIO: %d\n", - pdata->gpio_iset2); - goto err_iset2; - } - ret = gpio_direction_output(pdata->gpio_iset2, 0); - ret = gpio_direction_output(pdata->gpio_nce, 1); - - bq24022 = regulator_register(&bq24022_desc, &pdev->dev, - pdata->init_data, pdata, NULL); - if (IS_ERR(bq24022)) { - dev_dbg(&pdev->dev, "couldn't register regulator\n"); - ret = PTR_ERR(bq24022); - goto err_reg; - } - platform_set_drvdata(pdev, bq24022); - dev_dbg(&pdev->dev, "registered regulator\n"); - - return 0; -err_reg: - gpio_free(pdata->gpio_iset2); -err_iset2: - gpio_free(pdata->gpio_nce); -err_ce: - return ret; -} - -static int __devexit bq24022_remove(struct platform_device *pdev) -{ - struct bq24022_mach_info *pdata = pdev->dev.platform_data; - struct regulator_dev *bq24022 = platform_get_drvdata(pdev); - - regulator_unregister(bq24022); - gpio_free(pdata->gpio_iset2); - gpio_free(pdata->gpio_nce); - - return 0; -} - -static struct platform_driver bq24022_driver = { - .driver = { - .name = "bq24022", - }, - .remove = __devexit_p(bq24022_remove), -}; - -static int __init bq24022_init(void) -{ - return platform_driver_probe(&bq24022_driver, bq24022_probe); -} - -static void __exit bq24022_exit(void) -{ - platform_driver_unregister(&bq24022_driver); -} - -module_init(bq24022_init); -module_exit(bq24022_exit); - -MODULE_AUTHOR("Philipp Zabel"); -MODULE_DESCRIPTION("TI bq24022 Li-Ion Charger driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig new file mode 100644 index 000000000000..24d880e78ec6 --- /dev/null +++ b/drivers/remoteproc/Kconfig @@ -0,0 +1,28 @@ +menu "Remoteproc drivers (EXPERIMENTAL)" + +# REMOTEPROC gets selected by whoever wants it +config REMOTEPROC + tristate + depends on EXPERIMENTAL + +config OMAP_REMOTEPROC + tristate "OMAP remoteproc support" + depends on ARCH_OMAP4 + depends on OMAP_IOMMU + select REMOTEPROC + select OMAP_MBOX_FWK + select RPMSG + help + Say y here to support OMAP's remote processors (dual M3 + and DSP on OMAP4) via the remote processor framework. + + Currently only supported on OMAP4. + + Usually you want to say y here, in order to enable multimedia + use-cases to run on your platform (multimedia codecs are + offloaded to remote DSP processors using this framework). + + It's safe to say n here if you're not interested in multimedia + offloading or just want a bare minimum kernel. + +endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile new file mode 100644 index 000000000000..5445d9b23294 --- /dev/null +++ b/drivers/remoteproc/Makefile @@ -0,0 +1,9 @@ +# +# Generic framework for controlling remote processors +# + +obj-$(CONFIG_REMOTEPROC) += remoteproc.o +remoteproc-y := remoteproc_core.o +remoteproc-y += remoteproc_debugfs.o +remoteproc-y += remoteproc_virtio.o +obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c new file mode 100644 index 000000000000..69425c4e86f3 --- /dev/null +++ b/drivers/remoteproc/omap_remoteproc.c @@ -0,0 +1,229 @@ +/* + * OMAP Remote Processor driver + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * + * Ohad Ben-Cohen <ohad@wizery.com> + * Brian Swetland <swetland@google.com> + * Fernando Guzman Lugo <fernando.lugo@ti.com> + * Mark Grosen <mgrosen@ti.com> + * Suman Anna <s-anna@ti.com> + * Hari Kanigeri <h-kanigeri2@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/remoteproc.h> + +#include <plat/mailbox.h> +#include <plat/remoteproc.h> + +#include "omap_remoteproc.h" +#include "remoteproc_internal.h" + +/** + * struct omap_rproc - omap remote processor state + * @mbox: omap mailbox handle + * @nb: notifier block that will be invoked on inbound mailbox messages + * @rproc: rproc handle + */ +struct omap_rproc { + struct omap_mbox *mbox; + struct notifier_block nb; + struct rproc *rproc; +}; + +/** + * omap_rproc_mbox_callback() - inbound mailbox message handler + * @this: notifier block + * @index: unused + * @data: mailbox payload + * + * This handler is invoked by omap's mailbox driver whenever a mailbox + * message is received. Usually, the mailbox payload simply contains + * the index of the virtqueue that is kicked by the remote processor, + * and we let remoteproc core handle it. + * + * In addition to virtqueue indices, we also have some out-of-band values + * that indicates different events. Those values are deliberately very + * big so they don't coincide with virtqueue indices. + */ +static int omap_rproc_mbox_callback(struct notifier_block *this, + unsigned long index, void *data) +{ + mbox_msg_t msg = (mbox_msg_t) data; + struct omap_rproc *oproc = container_of(this, struct omap_rproc, nb); + struct device *dev = oproc->rproc->dev; + const char *name = oproc->rproc->name; + + dev_dbg(dev, "mbox msg: 0x%x\n", msg); + + switch (msg) { + case RP_MBOX_CRASH: + /* just log this for now. later, we'll also do recovery */ + dev_err(dev, "omap rproc %s crashed\n", name); + break; + case RP_MBOX_ECHO_REPLY: + dev_info(dev, "received echo reply from %s\n", name); + break; + default: + /* msg contains the index of the triggered vring */ + if (rproc_vq_interrupt(oproc->rproc, msg) == IRQ_NONE) + dev_dbg(dev, "no message was found in vqid %d\n", msg); + } + + return NOTIFY_DONE; +} + +/* kick a virtqueue */ +static void omap_rproc_kick(struct rproc *rproc, int vqid) +{ + struct omap_rproc *oproc = rproc->priv; + int ret; + + /* send the index of the triggered virtqueue in the mailbox payload */ + ret = omap_mbox_msg_send(oproc->mbox, vqid); + if (ret) + dev_err(rproc->dev, "omap_mbox_msg_send failed: %d\n", ret); +} + +/* + * Power up the remote processor. + * + * This function will be invoked only after the firmware for this rproc + * was loaded, parsed successfully, and all of its resource requirements + * were met. + */ +static int omap_rproc_start(struct rproc *rproc) +{ + struct omap_rproc *oproc = rproc->priv; + struct platform_device *pdev = to_platform_device(rproc->dev); + struct omap_rproc_pdata *pdata = pdev->dev.platform_data; + int ret; + + oproc->nb.notifier_call = omap_rproc_mbox_callback; + + /* every omap rproc is assigned a mailbox instance for messaging */ + oproc->mbox = omap_mbox_get(pdata->mbox_name, &oproc->nb); + if (IS_ERR(oproc->mbox)) { + ret = PTR_ERR(oproc->mbox); + dev_err(rproc->dev, "omap_mbox_get failed: %d\n", ret); + return ret; + } + + /* + * Ping the remote processor. this is only for sanity-sake; + * there is no functional effect whatsoever. + * + * Note that the reply will _not_ arrive immediately: this message + * will wait in the mailbox fifo until the remote processor is booted. + */ + ret = omap_mbox_msg_send(oproc->mbox, RP_MBOX_ECHO_REQUEST); + if (ret) { + dev_err(rproc->dev, "omap_mbox_get failed: %d\n", ret); + goto put_mbox; + } + + ret = pdata->device_enable(pdev); + if (ret) { + dev_err(rproc->dev, "omap_device_enable failed: %d\n", ret); + goto put_mbox; + } + + return 0; + +put_mbox: + omap_mbox_put(oproc->mbox, &oproc->nb); + return ret; +} + +/* power off the remote processor */ +static int omap_rproc_stop(struct rproc *rproc) +{ + struct platform_device *pdev = to_platform_device(rproc->dev); + struct omap_rproc_pdata *pdata = pdev->dev.platform_data; + struct omap_rproc *oproc = rproc->priv; + int ret; + + ret = pdata->device_shutdown(pdev); + if (ret) + return ret; + + omap_mbox_put(oproc->mbox, &oproc->nb); + + return 0; +} + +static struct rproc_ops omap_rproc_ops = { + .start = omap_rproc_start, + .stop = omap_rproc_stop, + .kick = omap_rproc_kick, +}; + +static int __devinit omap_rproc_probe(struct platform_device *pdev) +{ + struct omap_rproc_pdata *pdata = pdev->dev.platform_data; + struct omap_rproc *oproc; + struct rproc *rproc; + int ret; + + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(pdev->dev.parent, "dma_set_coherent_mask: %d\n", ret); + return ret; + } + + rproc = rproc_alloc(&pdev->dev, pdata->name, &omap_rproc_ops, + pdata->firmware, sizeof(*oproc)); + if (!rproc) + return -ENOMEM; + + oproc = rproc->priv; + oproc->rproc = rproc; + + platform_set_drvdata(pdev, rproc); + + ret = rproc_register(rproc); + if (ret) + goto free_rproc; + + return 0; + +free_rproc: + rproc_free(rproc); + return ret; +} + +static int __devexit omap_rproc_remove(struct platform_device *pdev) +{ + struct rproc *rproc = platform_get_drvdata(pdev); + + return rproc_unregister(rproc); +} + +static struct platform_driver omap_rproc_driver = { + .probe = omap_rproc_probe, + .remove = __devexit_p(omap_rproc_remove), + .driver = { + .name = "omap-rproc", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(omap_rproc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("OMAP Remote Processor control driver"); diff --git a/drivers/remoteproc/omap_remoteproc.h b/drivers/remoteproc/omap_remoteproc.h new file mode 100644 index 000000000000..f6d2036d383d --- /dev/null +++ b/drivers/remoteproc/omap_remoteproc.h @@ -0,0 +1,69 @@ +/* + * Remote processor messaging + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Texas Instruments nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _OMAP_RPMSG_H +#define _OMAP_RPMSG_H + +/* + * enum - Predefined Mailbox Messages + * + * @RP_MBOX_READY: informs the M3's that we're up and running. this is + * part of the init sequence sent that the M3 expects to see immediately + * after it is booted. + * + * @RP_MBOX_PENDING_MSG: informs the receiver that there is an inbound + * message waiting in its own receive-side vring. please note that currently + * this message is optional: alternatively, one can explicitly send the index + * of the triggered virtqueue itself. the preferred approach will be decided + * as we progress and experiment with those two different approaches. + * + * @RP_MBOX_CRASH: this message is sent if BIOS crashes + * + * @RP_MBOX_ECHO_REQUEST: a mailbox-level "ping" message. + * + * @RP_MBOX_ECHO_REPLY: a mailbox-level reply to a "ping" + * + * @RP_MBOX_ABORT_REQUEST: a "please crash" request, used for testing the + * recovery mechanism (to some extent). + */ +enum omap_rp_mbox_messages { + RP_MBOX_READY = 0xFFFFFF00, + RP_MBOX_PENDING_MSG = 0xFFFFFF01, + RP_MBOX_CRASH = 0xFFFFFF02, + RP_MBOX_ECHO_REQUEST = 0xFFFFFF03, + RP_MBOX_ECHO_REPLY = 0xFFFFFF04, + RP_MBOX_ABORT_REQUEST = 0xFFFFFF05, +}; + +#endif /* _OMAP_RPMSG_H */ diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c new file mode 100644 index 000000000000..ee15c68fb519 --- /dev/null +++ b/drivers/remoteproc/remoteproc_core.c @@ -0,0 +1,1586 @@ +/* + * Remote Processor Framework + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * + * Ohad Ben-Cohen <ohad@wizery.com> + * Brian Swetland <swetland@google.com> + * Mark Grosen <mgrosen@ti.com> + * Fernando Guzman Lugo <fernando.lugo@ti.com> + * Suman Anna <s-anna@ti.com> + * Robert Tivy <rtivy@ti.com> + * Armando Uribe De Leon <x0095078@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/dma-mapping.h> +#include <linux/firmware.h> +#include <linux/string.h> +#include <linux/debugfs.h> +#include <linux/remoteproc.h> +#include <linux/iommu.h> +#include <linux/klist.h> +#include <linux/elf.h> +#include <linux/virtio_ids.h> +#include <linux/virtio_ring.h> +#include <asm/byteorder.h> + +#include "remoteproc_internal.h" + +static void klist_rproc_get(struct klist_node *n); +static void klist_rproc_put(struct klist_node *n); + +/* + * klist of the available remote processors. + * + * We need this in order to support name-based lookups (needed by the + * rproc_get_by_name()). + * + * That said, we don't use rproc_get_by_name() at this point. + * The use cases that do require its existence should be + * scrutinized, and hopefully migrated to rproc_boot() using device-based + * binding. + * + * If/when this materializes, we could drop the klist (and the by_name + * API). + */ +static DEFINE_KLIST(rprocs, klist_rproc_get, klist_rproc_put); + +typedef int (*rproc_handle_resources_t)(struct rproc *rproc, + struct resource_table *table, int len); +typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail); + +/* + * This is the IOMMU fault handler we register with the IOMMU API + * (when relevant; not all remote processors access memory through + * an IOMMU). + * + * IOMMU core will invoke this handler whenever the remote processor + * will try to access an unmapped device address. + * + * Currently this is mostly a stub, but it will be later used to trigger + * the recovery of the remote processor. + */ +static int rproc_iommu_fault(struct iommu_domain *domain, struct device *dev, + unsigned long iova, int flags) +{ + dev_err(dev, "iommu fault: da 0x%lx flags 0x%x\n", iova, flags); + + /* + * Let the iommu core know we're not really handling this fault; + * we just plan to use this as a recovery trigger. + */ + return -ENOSYS; +} + +static int rproc_enable_iommu(struct rproc *rproc) +{ + struct iommu_domain *domain; + struct device *dev = rproc->dev; + int ret; + + /* + * We currently use iommu_present() to decide if an IOMMU + * setup is needed. + * + * This works for simple cases, but will easily fail with + * platforms that do have an IOMMU, but not for this specific + * rproc. + * + * This will be easily solved by introducing hw capabilities + * that will be set by the remoteproc driver. + */ + if (!iommu_present(dev->bus)) { + dev_dbg(dev, "iommu not found\n"); + return 0; + } + + domain = iommu_domain_alloc(dev->bus); + if (!domain) { + dev_err(dev, "can't alloc iommu domain\n"); + return -ENOMEM; + } + + iommu_set_fault_handler(domain, rproc_iommu_fault); + + ret = iommu_attach_device(domain, dev); + if (ret) { + dev_err(dev, "can't attach iommu device: %d\n", ret); + goto free_domain; + } + + rproc->domain = domain; + + return 0; + +free_domain: + iommu_domain_free(domain); + return ret; +} + +static void rproc_disable_iommu(struct rproc *rproc) +{ + struct iommu_domain *domain = rproc->domain; + struct device *dev = rproc->dev; + + if (!domain) + return; + + iommu_detach_device(domain, dev); + iommu_domain_free(domain); + + return; +} + +/* + * Some remote processors will ask us to allocate them physically contiguous + * memory regions (which we call "carveouts"), and map them to specific + * device addresses (which are hardcoded in the firmware). + * + * They may then ask us to copy objects into specific device addresses (e.g. + * code/data sections) or expose us certain symbols in other device address + * (e.g. their trace buffer). + * + * This function is an internal helper with which we can go over the allocated + * carveouts and translate specific device address to kernel virtual addresses + * so we can access the referenced memory. + * + * Note: phys_to_virt(iommu_iova_to_phys(rproc->domain, da)) will work too, + * but only on kernel direct mapped RAM memory. Instead, we're just using + * here the output of the DMA API, which should be more correct. + */ +static void *rproc_da_to_va(struct rproc *rproc, u64 da, int len) +{ + struct rproc_mem_entry *carveout; + void *ptr = NULL; + + list_for_each_entry(carveout, &rproc->carveouts, node) { + int offset = da - carveout->da; + + /* try next carveout if da is too small */ + if (offset < 0) + continue; + + /* try next carveout if da is too large */ + if (offset + len > carveout->len) + continue; + + ptr = carveout->va + offset; + + break; + } + + return ptr; +} + +/** + * rproc_load_segments() - load firmware segments to memory + * @rproc: remote processor which will be booted using these fw segments + * @elf_data: the content of the ELF firmware image + * @len: firmware size (in bytes) + * + * This function loads the firmware segments to memory, where the remote + * processor expects them. + * + * Some remote processors will expect their code and data to be placed + * in specific device addresses, and can't have them dynamically assigned. + * + * We currently support only those kind of remote processors, and expect + * the program header's paddr member to contain those addresses. We then go + * through the physically contiguous "carveout" memory regions which we + * allocated (and mapped) earlier on behalf of the remote processor, + * and "translate" device address to kernel addresses, so we can copy the + * segments where they are expected. + * + * Currently we only support remote processors that required carveout + * allocations and got them mapped onto their iommus. Some processors + * might be different: they might not have iommus, and would prefer to + * directly allocate memory for every segment/resource. This is not yet + * supported, though. + */ +static int +rproc_load_segments(struct rproc *rproc, const u8 *elf_data, size_t len) +{ + struct device *dev = rproc->dev; + struct elf32_hdr *ehdr; + struct elf32_phdr *phdr; + int i, ret = 0; + + ehdr = (struct elf32_hdr *)elf_data; + phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff); + + /* go through the available ELF segments */ + for (i = 0; i < ehdr->e_phnum; i++, phdr++) { + u32 da = phdr->p_paddr; + u32 memsz = phdr->p_memsz; + u32 filesz = phdr->p_filesz; + u32 offset = phdr->p_offset; + void *ptr; + + if (phdr->p_type != PT_LOAD) + continue; + + dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n", + phdr->p_type, da, memsz, filesz); + + if (filesz > memsz) { + dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n", + filesz, memsz); + ret = -EINVAL; + break; + } + + if (offset + filesz > len) { + dev_err(dev, "truncated fw: need 0x%x avail 0x%x\n", + offset + filesz, len); + ret = -EINVAL; + break; + } + + /* grab the kernel address for this device address */ + ptr = rproc_da_to_va(rproc, da, memsz); + if (!ptr) { + dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz); + ret = -EINVAL; + break; + } + + /* put the segment where the remote processor expects it */ + if (phdr->p_filesz) + memcpy(ptr, elf_data + phdr->p_offset, filesz); + + /* + * Zero out remaining memory for this segment. + * + * This isn't strictly required since dma_alloc_coherent already + * did this for us. albeit harmless, we may consider removing + * this. + */ + if (memsz > filesz) + memset(ptr + filesz, 0, memsz - filesz); + } + + return ret; +} + +static int +__rproc_handle_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i) +{ + struct rproc *rproc = rvdev->rproc; + struct device *dev = rproc->dev; + struct fw_rsc_vdev_vring *vring = &rsc->vring[i]; + dma_addr_t dma; + void *va; + int ret, size, notifyid; + + dev_dbg(dev, "vdev rsc: vring%d: da %x, qsz %d, align %d\n", + i, vring->da, vring->num, vring->align); + + /* make sure reserved bytes are zeroes */ + if (vring->reserved) { + dev_err(dev, "vring rsc has non zero reserved bytes\n"); + return -EINVAL; + } + + /* verify queue size and vring alignment are sane */ + if (!vring->num || !vring->align) { + dev_err(dev, "invalid qsz (%d) or alignment (%d)\n", + vring->num, vring->align); + return -EINVAL; + } + + /* actual size of vring (in bytes) */ + size = PAGE_ALIGN(vring_size(vring->num, vring->align)); + + if (!idr_pre_get(&rproc->notifyids, GFP_KERNEL)) { + dev_err(dev, "idr_pre_get failed\n"); + return -ENOMEM; + } + + /* + * Allocate non-cacheable memory for the vring. In the future + * this call will also configure the IOMMU for us + */ + va = dma_alloc_coherent(dev, size, &dma, GFP_KERNEL); + if (!va) { + dev_err(dev, "dma_alloc_coherent failed\n"); + return -EINVAL; + } + + /* assign an rproc-wide unique index for this vring */ + /* TODO: assign a notifyid for rvdev updates as well */ + ret = idr_get_new(&rproc->notifyids, &rvdev->vring[i], ¬ifyid); + if (ret) { + dev_err(dev, "idr_get_new failed: %d\n", ret); + dma_free_coherent(dev, size, va, dma); + return ret; + } + + /* let the rproc know the da and notifyid of this vring */ + /* TODO: expose this to remote processor */ + vring->da = dma; + vring->notifyid = notifyid; + + dev_dbg(dev, "vring%d: va %p dma %x size %x idr %d\n", i, va, + dma, size, notifyid); + + rvdev->vring[i].len = vring->num; + rvdev->vring[i].align = vring->align; + rvdev->vring[i].va = va; + rvdev->vring[i].dma = dma; + rvdev->vring[i].notifyid = notifyid; + rvdev->vring[i].rvdev = rvdev; + + return 0; +} + +static void __rproc_free_vrings(struct rproc_vdev *rvdev, int i) +{ + struct rproc *rproc = rvdev->rproc; + + for (i--; i > 0; i--) { + struct rproc_vring *rvring = &rvdev->vring[i]; + int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align)); + + dma_free_coherent(rproc->dev, size, rvring->va, rvring->dma); + idr_remove(&rproc->notifyids, rvring->notifyid); + } +} + +/** + * rproc_handle_vdev() - handle a vdev fw resource + * @rproc: the remote processor + * @rsc: the vring resource descriptor + * @avail: size of available data (for sanity checking the image) + * + * This resource entry requests the host to statically register a virtio + * device (vdev), and setup everything needed to support it. It contains + * everything needed to make it possible: the virtio device id, virtio + * device features, vrings information, virtio config space, etc... + * + * Before registering the vdev, the vrings are allocated from non-cacheable + * physically contiguous memory. Currently we only support two vrings per + * remote processor (temporary limitation). We might also want to consider + * doing the vring allocation only later when ->find_vqs() is invoked, and + * then release them upon ->del_vqs(). + * + * Note: @da is currently not really handled correctly: we dynamically + * allocate it using the DMA API, ignoring requested hard coded addresses, + * and we don't take care of any required IOMMU programming. This is all + * going to be taken care of when the generic iommu-based DMA API will be + * merged. Meanwhile, statically-addressed iommu-based firmware images should + * use RSC_DEVMEM resource entries to map their required @da to the physical + * address of their base CMA region (ouch, hacky!). + * + * Returns 0 on success, or an appropriate error code otherwise + */ +static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, + int avail) +{ + struct device *dev = rproc->dev; + struct rproc_vdev *rvdev; + int i, ret; + + /* make sure resource isn't truncated */ + if (sizeof(*rsc) + rsc->num_of_vrings * sizeof(struct fw_rsc_vdev_vring) + + rsc->config_len > avail) { + dev_err(rproc->dev, "vdev rsc is truncated\n"); + return -EINVAL; + } + + /* make sure reserved bytes are zeroes */ + if (rsc->reserved[0] || rsc->reserved[1]) { + dev_err(dev, "vdev rsc has non zero reserved bytes\n"); + return -EINVAL; + } + + dev_dbg(dev, "vdev rsc: id %d, dfeatures %x, cfg len %d, %d vrings\n", + rsc->id, rsc->dfeatures, rsc->config_len, rsc->num_of_vrings); + + /* we currently support only two vrings per rvdev */ + if (rsc->num_of_vrings > ARRAY_SIZE(rvdev->vring)) { + dev_err(dev, "too many vrings: %d\n", rsc->num_of_vrings); + return -EINVAL; + } + + rvdev = kzalloc(sizeof(struct rproc_vdev), GFP_KERNEL); + if (!rvdev) + return -ENOMEM; + + rvdev->rproc = rproc; + + /* allocate the vrings */ + for (i = 0; i < rsc->num_of_vrings; i++) { + ret = __rproc_handle_vring(rvdev, rsc, i); + if (ret) + goto free_vrings; + } + + /* remember the device features */ + rvdev->dfeatures = rsc->dfeatures; + + list_add_tail(&rvdev->node, &rproc->rvdevs); + + /* it is now safe to add the virtio device */ + ret = rproc_add_virtio_dev(rvdev, rsc->id); + if (ret) + goto free_vrings; + + return 0; + +free_vrings: + __rproc_free_vrings(rvdev, i); + kfree(rvdev); + return ret; +} + +/** + * rproc_handle_trace() - handle a shared trace buffer resource + * @rproc: the remote processor + * @rsc: the trace resource descriptor + * @avail: size of available data (for sanity checking the image) + * + * In case the remote processor dumps trace logs into memory, + * export it via debugfs. + * + * Currently, the 'da' member of @rsc should contain the device address + * where the remote processor is dumping the traces. Later we could also + * support dynamically allocating this address using the generic + * DMA API (but currently there isn't a use case for that). + * + * Returns 0 on success, or an appropriate error code otherwise + */ +static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, + int avail) +{ + struct rproc_mem_entry *trace; + struct device *dev = rproc->dev; + void *ptr; + char name[15]; + + if (sizeof(*rsc) > avail) { + dev_err(rproc->dev, "trace rsc is truncated\n"); + return -EINVAL; + } + + /* make sure reserved bytes are zeroes */ + if (rsc->reserved) { + dev_err(dev, "trace rsc has non zero reserved bytes\n"); + return -EINVAL; + } + + /* what's the kernel address of this resource ? */ + ptr = rproc_da_to_va(rproc, rsc->da, rsc->len); + if (!ptr) { + dev_err(dev, "erroneous trace resource entry\n"); + return -EINVAL; + } + + trace = kzalloc(sizeof(*trace), GFP_KERNEL); + if (!trace) { + dev_err(dev, "kzalloc trace failed\n"); + return -ENOMEM; + } + + /* set the trace buffer dma properties */ + trace->len = rsc->len; + trace->va = ptr; + + /* make sure snprintf always null terminates, even if truncating */ + snprintf(name, sizeof(name), "trace%d", rproc->num_traces); + + /* create the debugfs entry */ + trace->priv = rproc_create_trace_file(name, rproc, trace); + if (!trace->priv) { + trace->va = NULL; + kfree(trace); + return -EINVAL; + } + + list_add_tail(&trace->node, &rproc->traces); + + rproc->num_traces++; + + dev_dbg(dev, "%s added: va %p, da 0x%x, len 0x%x\n", name, ptr, + rsc->da, rsc->len); + + return 0; +} + +/** + * rproc_handle_devmem() - handle devmem resource entry + * @rproc: remote processor handle + * @rsc: the devmem resource entry + * @avail: size of available data (for sanity checking the image) + * + * Remote processors commonly need to access certain on-chip peripherals. + * + * Some of these remote processors access memory via an iommu device, + * and might require us to configure their iommu before they can access + * the on-chip peripherals they need. + * + * This resource entry is a request to map such a peripheral device. + * + * These devmem entries will contain the physical address of the device in + * the 'pa' member. If a specific device address is expected, then 'da' will + * contain it (currently this is the only use case supported). 'len' will + * contain the size of the physical region we need to map. + * + * Currently we just "trust" those devmem entries to contain valid physical + * addresses, but this is going to change: we want the implementations to + * tell us ranges of physical addresses the firmware is allowed to request, + * and not allow firmwares to request access to physical addresses that + * are outside those ranges. + */ +static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc, + int avail) +{ + struct rproc_mem_entry *mapping; + int ret; + + /* no point in handling this resource without a valid iommu domain */ + if (!rproc->domain) + return -EINVAL; + + if (sizeof(*rsc) > avail) { + dev_err(rproc->dev, "devmem rsc is truncated\n"); + return -EINVAL; + } + + /* make sure reserved bytes are zeroes */ + if (rsc->reserved) { + dev_err(rproc->dev, "devmem rsc has non zero reserved bytes\n"); + return -EINVAL; + } + + mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); + if (!mapping) { + dev_err(rproc->dev, "kzalloc mapping failed\n"); + return -ENOMEM; + } + + ret = iommu_map(rproc->domain, rsc->da, rsc->pa, rsc->len, rsc->flags); + if (ret) { + dev_err(rproc->dev, "failed to map devmem: %d\n", ret); + goto out; + } + + /* + * We'll need this info later when we'll want to unmap everything + * (e.g. on shutdown). + * + * We can't trust the remote processor not to change the resource + * table, so we must maintain this info independently. + */ + mapping->da = rsc->da; + mapping->len = rsc->len; + list_add_tail(&mapping->node, &rproc->mappings); + + dev_dbg(rproc->dev, "mapped devmem pa 0x%x, da 0x%x, len 0x%x\n", + rsc->pa, rsc->da, rsc->len); + + return 0; + +out: + kfree(mapping); + return ret; +} + +/** + * rproc_handle_carveout() - handle phys contig memory allocation requests + * @rproc: rproc handle + * @rsc: the resource entry + * @avail: size of available data (for image validation) + * + * This function will handle firmware requests for allocation of physically + * contiguous memory regions. + * + * These request entries should come first in the firmware's resource table, + * as other firmware entries might request placing other data objects inside + * these memory regions (e.g. data/code segments, trace resource entries, ...). + * + * Allocating memory this way helps utilizing the reserved physical memory + * (e.g. CMA) more efficiently, and also minimizes the number of TLB entries + * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB + * pressure is important; it may have a substantial impact on performance. + */ +static int rproc_handle_carveout(struct rproc *rproc, + struct fw_rsc_carveout *rsc, int avail) +{ + struct rproc_mem_entry *carveout, *mapping; + struct device *dev = rproc->dev; + dma_addr_t dma; + void *va; + int ret; + + if (sizeof(*rsc) > avail) { + dev_err(rproc->dev, "carveout rsc is truncated\n"); + return -EINVAL; + } + + /* make sure reserved bytes are zeroes */ + if (rsc->reserved) { + dev_err(dev, "carveout rsc has non zero reserved bytes\n"); + return -EINVAL; + } + + dev_dbg(dev, "carveout rsc: da %x, pa %x, len %x, flags %x\n", + rsc->da, rsc->pa, rsc->len, rsc->flags); + + mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); + if (!mapping) { + dev_err(dev, "kzalloc mapping failed\n"); + return -ENOMEM; + } + + carveout = kzalloc(sizeof(*carveout), GFP_KERNEL); + if (!carveout) { + dev_err(dev, "kzalloc carveout failed\n"); + ret = -ENOMEM; + goto free_mapping; + } + + va = dma_alloc_coherent(dev, rsc->len, &dma, GFP_KERNEL); + if (!va) { + dev_err(dev, "failed to dma alloc carveout: %d\n", rsc->len); + ret = -ENOMEM; + goto free_carv; + } + + dev_dbg(dev, "carveout va %p, dma %x, len 0x%x\n", va, dma, rsc->len); + + /* + * Ok, this is non-standard. + * + * Sometimes we can't rely on the generic iommu-based DMA API + * to dynamically allocate the device address and then set the IOMMU + * tables accordingly, because some remote processors might + * _require_ us to use hard coded device addresses that their + * firmware was compiled with. + * + * In this case, we must use the IOMMU API directly and map + * the memory to the device address as expected by the remote + * processor. + * + * Obviously such remote processor devices should not be configured + * to use the iommu-based DMA API: we expect 'dma' to contain the + * physical address in this case. + */ + if (rproc->domain) { + ret = iommu_map(rproc->domain, rsc->da, dma, rsc->len, + rsc->flags); + if (ret) { + dev_err(dev, "iommu_map failed: %d\n", ret); + goto dma_free; + } + + /* + * We'll need this info later when we'll want to unmap + * everything (e.g. on shutdown). + * + * We can't trust the remote processor not to change the + * resource table, so we must maintain this info independently. + */ + mapping->da = rsc->da; + mapping->len = rsc->len; + list_add_tail(&mapping->node, &rproc->mappings); + + dev_dbg(dev, "carveout mapped 0x%x to 0x%x\n", rsc->da, dma); + + /* + * Some remote processors might need to know the pa + * even though they are behind an IOMMU. E.g., OMAP4's + * remote M3 processor needs this so it can control + * on-chip hardware accelerators that are not behind + * the IOMMU, and therefor must know the pa. + * + * Generally we don't want to expose physical addresses + * if we don't have to (remote processors are generally + * _not_ trusted), so we might want to do this only for + * remote processor that _must_ have this (e.g. OMAP4's + * dual M3 subsystem). + */ + rsc->pa = dma; + } + + carveout->va = va; + carveout->len = rsc->len; + carveout->dma = dma; + carveout->da = rsc->da; + + list_add_tail(&carveout->node, &rproc->carveouts); + + return 0; + +dma_free: + dma_free_coherent(dev, rsc->len, va, dma); +free_carv: + kfree(carveout); +free_mapping: + kfree(mapping); + return ret; +} + +/* + * A lookup table for resource handlers. The indices are defined in + * enum fw_resource_type. + */ +static rproc_handle_resource_t rproc_handle_rsc[] = { + [RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout, + [RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem, + [RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace, + [RSC_VDEV] = NULL, /* VDEVs were handled upon registrarion */ +}; + +/* handle firmware resource entries before booting the remote processor */ +static int +rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len) +{ + struct device *dev = rproc->dev; + rproc_handle_resource_t handler; + int ret = 0, i; + + for (i = 0; i < table->num; i++) { + int offset = table->offset[i]; + struct fw_rsc_hdr *hdr = (void *)table + offset; + int avail = len - offset - sizeof(*hdr); + void *rsc = (void *)hdr + sizeof(*hdr); + + /* make sure table isn't truncated */ + if (avail < 0) { + dev_err(dev, "rsc table is truncated\n"); + return -EINVAL; + } + + dev_dbg(dev, "rsc: type %d\n", hdr->type); + + if (hdr->type >= RSC_LAST) { + dev_warn(dev, "unsupported resource %d\n", hdr->type); + continue; + } + + handler = rproc_handle_rsc[hdr->type]; + if (!handler) + continue; + + ret = handler(rproc, rsc, avail); + if (ret) + break; + } + + return ret; +} + +/* handle firmware resource entries while registering the remote processor */ +static int +rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int len) +{ + struct device *dev = rproc->dev; + int ret = 0, i; + + for (i = 0; i < table->num; i++) { + int offset = table->offset[i]; + struct fw_rsc_hdr *hdr = (void *)table + offset; + int avail = len - offset - sizeof(*hdr); + struct fw_rsc_vdev *vrsc; + + /* make sure table isn't truncated */ + if (avail < 0) { + dev_err(dev, "rsc table is truncated\n"); + return -EINVAL; + } + + dev_dbg(dev, "%s: rsc type %d\n", __func__, hdr->type); + + if (hdr->type != RSC_VDEV) + continue; + + vrsc = (struct fw_rsc_vdev *)hdr->data; + + ret = rproc_handle_vdev(rproc, vrsc, avail); + if (ret) + break; + } + + return ret; +} + +/** + * rproc_find_rsc_table() - find the resource table + * @rproc: the rproc handle + * @elf_data: the content of the ELF firmware image + * @len: firmware size (in bytes) + * @tablesz: place holder for providing back the table size + * + * This function finds the resource table inside the remote processor's + * firmware. It is used both upon the registration of @rproc (in order + * to look for and register the supported virito devices), and when the + * @rproc is booted. + * + * Returns the pointer to the resource table if it is found, and write its + * size into @tablesz. If a valid table isn't found, NULL is returned + * (and @tablesz isn't set). + */ +static struct resource_table * +rproc_find_rsc_table(struct rproc *rproc, const u8 *elf_data, size_t len, + int *tablesz) +{ + struct elf32_hdr *ehdr; + struct elf32_shdr *shdr; + const char *name_table; + struct device *dev = rproc->dev; + struct resource_table *table = NULL; + int i; + + ehdr = (struct elf32_hdr *)elf_data; + shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff); + name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset; + + /* look for the resource table and handle it */ + for (i = 0; i < ehdr->e_shnum; i++, shdr++) { + int size = shdr->sh_size; + int offset = shdr->sh_offset; + + if (strcmp(name_table + shdr->sh_name, ".resource_table")) + continue; + + table = (struct resource_table *)(elf_data + offset); + + /* make sure we have the entire table */ + if (offset + size > len) { + dev_err(dev, "resource table truncated\n"); + return NULL; + } + + /* make sure table has at least the header */ + if (sizeof(struct resource_table) > size) { + dev_err(dev, "header-less resource table\n"); + return NULL; + } + + /* we don't support any version beyond the first */ + if (table->ver != 1) { + dev_err(dev, "unsupported fw ver: %d\n", table->ver); + return NULL; + } + + /* make sure reserved bytes are zeroes */ + if (table->reserved[0] || table->reserved[1]) { + dev_err(dev, "non zero reserved bytes\n"); + return NULL; + } + + /* make sure the offsets array isn't truncated */ + if (table->num * sizeof(table->offset[0]) + + sizeof(struct resource_table) > size) { + dev_err(dev, "resource table incomplete\n"); + return NULL; + } + + *tablesz = shdr->sh_size; + break; + } + + return table; +} + +/** + * rproc_resource_cleanup() - clean up and free all acquired resources + * @rproc: rproc handle + * + * This function will free all resources acquired for @rproc, and it + * is called whenever @rproc either shuts down or fails to boot. + */ +static void rproc_resource_cleanup(struct rproc *rproc) +{ + struct rproc_mem_entry *entry, *tmp; + struct device *dev = rproc->dev; + + /* clean up debugfs trace entries */ + list_for_each_entry_safe(entry, tmp, &rproc->traces, node) { + rproc_remove_trace_file(entry->priv); + rproc->num_traces--; + list_del(&entry->node); + kfree(entry); + } + + /* clean up carveout allocations */ + list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { + dma_free_coherent(dev, entry->len, entry->va, entry->dma); + list_del(&entry->node); + kfree(entry); + } + + /* clean up iommu mapping entries */ + list_for_each_entry_safe(entry, tmp, &rproc->mappings, node) { + size_t unmapped; + + unmapped = iommu_unmap(rproc->domain, entry->da, entry->len); + if (unmapped != entry->len) { + /* nothing much to do besides complaining */ + dev_err(dev, "failed to unmap %u/%u\n", entry->len, + unmapped); + } + + list_del(&entry->node); + kfree(entry); + } +} + +/* make sure this fw image is sane */ +static int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw) +{ + const char *name = rproc->firmware; + struct device *dev = rproc->dev; + struct elf32_hdr *ehdr; + char class; + + if (!fw) { + dev_err(dev, "failed to load %s\n", name); + return -EINVAL; + } + + if (fw->size < sizeof(struct elf32_hdr)) { + dev_err(dev, "Image is too small\n"); + return -EINVAL; + } + + ehdr = (struct elf32_hdr *)fw->data; + + /* We only support ELF32 at this point */ + class = ehdr->e_ident[EI_CLASS]; + if (class != ELFCLASS32) { + dev_err(dev, "Unsupported class: %d\n", class); + return -EINVAL; + } + + /* We assume the firmware has the same endianess as the host */ +# ifdef __LITTLE_ENDIAN + if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { +# else /* BIG ENDIAN */ + if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) { +# endif + dev_err(dev, "Unsupported firmware endianess\n"); + return -EINVAL; + } + + if (fw->size < ehdr->e_shoff + sizeof(struct elf32_shdr)) { + dev_err(dev, "Image is too small\n"); + return -EINVAL; + } + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) { + dev_err(dev, "Image is corrupted (bad magic)\n"); + return -EINVAL; + } + + if (ehdr->e_phnum == 0) { + dev_err(dev, "No loadable segments\n"); + return -EINVAL; + } + + if (ehdr->e_phoff > fw->size) { + dev_err(dev, "Firmware size is too small\n"); + return -EINVAL; + } + + return 0; +} + +/* + * take a firmware and boot a remote processor with it. + */ +static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) +{ + struct device *dev = rproc->dev; + const char *name = rproc->firmware; + struct elf32_hdr *ehdr; + struct resource_table *table; + int ret, tablesz; + + ret = rproc_fw_sanity_check(rproc, fw); + if (ret) + return ret; + + ehdr = (struct elf32_hdr *)fw->data; + + dev_info(dev, "Booting fw image %s, size %d\n", name, fw->size); + + /* + * if enabling an IOMMU isn't relevant for this rproc, this is + * just a nop + */ + ret = rproc_enable_iommu(rproc); + if (ret) { + dev_err(dev, "can't enable iommu: %d\n", ret); + return ret; + } + + /* + * The ELF entry point is the rproc's boot addr (though this is not + * a configurable property of all remote processors: some will always + * boot at a specific hardcoded address). + */ + rproc->bootaddr = ehdr->e_entry; + + /* look for the resource table */ + table = rproc_find_rsc_table(rproc, fw->data, fw->size, &tablesz); + if (!table) + goto clean_up; + + /* handle fw resources which are required to boot rproc */ + ret = rproc_handle_boot_rsc(rproc, table, tablesz); + if (ret) { + dev_err(dev, "Failed to process resources: %d\n", ret); + goto clean_up; + } + + /* load the ELF segments to memory */ + ret = rproc_load_segments(rproc, fw->data, fw->size); + if (ret) { + dev_err(dev, "Failed to load program segments: %d\n", ret); + goto clean_up; + } + + /* power up the remote processor */ + ret = rproc->ops->start(rproc); + if (ret) { + dev_err(dev, "can't start rproc %s: %d\n", rproc->name, ret); + goto clean_up; + } + + rproc->state = RPROC_RUNNING; + + dev_info(dev, "remote processor %s is now up\n", rproc->name); + + return 0; + +clean_up: + rproc_resource_cleanup(rproc); + rproc_disable_iommu(rproc); + return ret; +} + +/* + * take a firmware and look for virtio devices to register. + * + * Note: this function is called asynchronously upon registration of the + * remote processor (so we must wait until it completes before we try + * to unregister the device. one other option is just to use kref here, + * that might be cleaner). + */ +static void rproc_fw_config_virtio(const struct firmware *fw, void *context) +{ + struct rproc *rproc = context; + struct resource_table *table; + int ret, tablesz; + + if (rproc_fw_sanity_check(rproc, fw) < 0) + goto out; + + /* look for the resource table */ + table = rproc_find_rsc_table(rproc, fw->data, fw->size, &tablesz); + if (!table) + goto out; + + /* look for virtio devices and register them */ + ret = rproc_handle_virtio_rsc(rproc, table, tablesz); + if (ret) + goto out; + +out: + if (fw) + release_firmware(fw); + /* allow rproc_unregister() contexts, if any, to proceed */ + complete_all(&rproc->firmware_loading_complete); +} + +/** + * rproc_boot() - boot a remote processor + * @rproc: handle of a remote processor + * + * Boot a remote processor (i.e. load its firmware, power it on, ...). + * + * If the remote processor is already powered on, this function immediately + * returns (successfully). + * + * Returns 0 on success, and an appropriate error value otherwise. + */ +int rproc_boot(struct rproc *rproc) +{ + const struct firmware *firmware_p; + struct device *dev; + int ret; + + if (!rproc) { + pr_err("invalid rproc handle\n"); + return -EINVAL; + } + + dev = rproc->dev; + + ret = mutex_lock_interruptible(&rproc->lock); + if (ret) { + dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret); + return ret; + } + + /* loading a firmware is required */ + if (!rproc->firmware) { + dev_err(dev, "%s: no firmware to load\n", __func__); + ret = -EINVAL; + goto unlock_mutex; + } + + /* prevent underlying implementation from being removed */ + if (!try_module_get(dev->driver->owner)) { + dev_err(dev, "%s: can't get owner\n", __func__); + ret = -EINVAL; + goto unlock_mutex; + } + + /* skip the boot process if rproc is already powered up */ + if (atomic_inc_return(&rproc->power) > 1) { + ret = 0; + goto unlock_mutex; + } + + dev_info(dev, "powering up %s\n", rproc->name); + + /* load firmware */ + ret = request_firmware(&firmware_p, rproc->firmware, dev); + if (ret < 0) { + dev_err(dev, "request_firmware failed: %d\n", ret); + goto downref_rproc; + } + + ret = rproc_fw_boot(rproc, firmware_p); + + release_firmware(firmware_p); + +downref_rproc: + if (ret) { + module_put(dev->driver->owner); + atomic_dec(&rproc->power); + } +unlock_mutex: + mutex_unlock(&rproc->lock); + return ret; +} +EXPORT_SYMBOL(rproc_boot); + +/** + * rproc_shutdown() - power off the remote processor + * @rproc: the remote processor + * + * Power off a remote processor (previously booted with rproc_boot()). + * + * In case @rproc is still being used by an additional user(s), then + * this function will just decrement the power refcount and exit, + * without really powering off the device. + * + * Every call to rproc_boot() must (eventually) be accompanied by a call + * to rproc_shutdown(). Calling rproc_shutdown() redundantly is a bug. + * + * Notes: + * - we're not decrementing the rproc's refcount, only the power refcount. + * which means that the @rproc handle stays valid even after rproc_shutdown() + * returns, and users can still use it with a subsequent rproc_boot(), if + * needed. + * - don't call rproc_shutdown() to unroll rproc_get_by_name(), exactly + * because rproc_shutdown() _does not_ decrement the refcount of @rproc. + * To decrement the refcount of @rproc, use rproc_put() (but _only_ if + * you acquired @rproc using rproc_get_by_name()). + */ +void rproc_shutdown(struct rproc *rproc) +{ + struct device *dev = rproc->dev; + int ret; + + ret = mutex_lock_interruptible(&rproc->lock); + if (ret) { + dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret); + return; + } + + /* if the remote proc is still needed, bail out */ + if (!atomic_dec_and_test(&rproc->power)) + goto out; + + /* power off the remote processor */ + ret = rproc->ops->stop(rproc); + if (ret) { + atomic_inc(&rproc->power); + dev_err(dev, "can't stop rproc: %d\n", ret); + goto out; + } + + /* clean up all acquired resources */ + rproc_resource_cleanup(rproc); + + rproc_disable_iommu(rproc); + + rproc->state = RPROC_OFFLINE; + + dev_info(dev, "stopped remote processor %s\n", rproc->name); + +out: + mutex_unlock(&rproc->lock); + if (!ret) + module_put(dev->driver->owner); +} +EXPORT_SYMBOL(rproc_shutdown); + +/** + * rproc_release() - completely deletes the existence of a remote processor + * @kref: the rproc's kref + * + * This function should _never_ be called directly. + * + * The only reasonable location to use it is as an argument when kref_put'ing + * @rproc's refcount. + * + * This way it will be called when no one holds a valid pointer to this @rproc + * anymore (and obviously after it is removed from the rprocs klist). + * + * Note: this function is not static because rproc_vdev_release() needs it when + * it decrements @rproc's refcount. + */ +void rproc_release(struct kref *kref) +{ + struct rproc *rproc = container_of(kref, struct rproc, refcount); + struct rproc_vdev *rvdev, *rvtmp; + + dev_info(rproc->dev, "removing %s\n", rproc->name); + + rproc_delete_debug_dir(rproc); + + /* clean up remote vdev entries */ + list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node) { + __rproc_free_vrings(rvdev, RVDEV_NUM_VRINGS); + list_del(&rvdev->node); + } + + /* + * At this point no one holds a reference to rproc anymore, + * so we can directly unroll rproc_alloc() + */ + rproc_free(rproc); +} + +/* will be called when an rproc is added to the rprocs klist */ +static void klist_rproc_get(struct klist_node *n) +{ + struct rproc *rproc = container_of(n, struct rproc, node); + + kref_get(&rproc->refcount); +} + +/* will be called when an rproc is removed from the rprocs klist */ +static void klist_rproc_put(struct klist_node *n) +{ + struct rproc *rproc = container_of(n, struct rproc, node); + + kref_put(&rproc->refcount, rproc_release); +} + +static struct rproc *next_rproc(struct klist_iter *i) +{ + struct klist_node *n; + + n = klist_next(i); + if (!n) + return NULL; + + return container_of(n, struct rproc, node); +} + +/** + * rproc_get_by_name() - find a remote processor by name and boot it + * @name: name of the remote processor + * + * Finds an rproc handle using the remote processor's name, and then + * boot it. If it's already powered on, then just immediately return + * (successfully). + * + * Returns the rproc handle on success, and NULL on failure. + * + * This function increments the remote processor's refcount, so always + * use rproc_put() to decrement it back once rproc isn't needed anymore. + * + * Note: currently this function (and its counterpart rproc_put()) are not + * being used. We need to scrutinize the use cases + * that still need them, and see if we can migrate them to use the non + * name-based boot/shutdown interface. + */ +struct rproc *rproc_get_by_name(const char *name) +{ + struct rproc *rproc; + struct klist_iter i; + int ret; + + /* find the remote processor, and upref its refcount */ + klist_iter_init(&rprocs, &i); + while ((rproc = next_rproc(&i)) != NULL) + if (!strcmp(rproc->name, name)) { + kref_get(&rproc->refcount); + break; + } + klist_iter_exit(&i); + + /* can't find this rproc ? */ + if (!rproc) { + pr_err("can't find remote processor %s\n", name); + return NULL; + } + + ret = rproc_boot(rproc); + if (ret < 0) { + kref_put(&rproc->refcount, rproc_release); + return NULL; + } + + return rproc; +} +EXPORT_SYMBOL(rproc_get_by_name); + +/** + * rproc_put() - decrement the refcount of a remote processor, and shut it down + * @rproc: the remote processor + * + * This function tries to shutdown @rproc, and it then decrements its + * refcount. + * + * After this function returns, @rproc may _not_ be used anymore, and its + * handle should be considered invalid. + * + * This function should be called _iff_ the @rproc handle was grabbed by + * calling rproc_get_by_name(). + */ +void rproc_put(struct rproc *rproc) +{ + /* try to power off the remote processor */ + rproc_shutdown(rproc); + + /* downref rproc's refcount */ + kref_put(&rproc->refcount, rproc_release); +} +EXPORT_SYMBOL(rproc_put); + +/** + * rproc_register() - register a remote processor + * @rproc: the remote processor handle to register + * + * Registers @rproc with the remoteproc framework, after it has been + * allocated with rproc_alloc(). + * + * This is called by the platform-specific rproc implementation, whenever + * a new remote processor device is probed. + * + * Returns 0 on success and an appropriate error code otherwise. + * + * Note: this function initiates an asynchronous firmware loading + * context, which will look for virtio devices supported by the rproc's + * firmware. + * + * If found, those virtio devices will be created and added, so as a result + * of registering this remote processor, additional virtio drivers might be + * probed. + */ +int rproc_register(struct rproc *rproc) +{ + struct device *dev = rproc->dev; + int ret = 0; + + /* expose to rproc_get_by_name users */ + klist_add_tail(&rproc->node, &rprocs); + + dev_info(rproc->dev, "%s is available\n", rproc->name); + + dev_info(dev, "Note: remoteproc is still under development and considered experimental.\n"); + dev_info(dev, "THE BINARY FORMAT IS NOT YET FINALIZED, and backward compatibility isn't yet guaranteed.\n"); + + /* create debugfs entries */ + rproc_create_debug_dir(rproc); + + /* rproc_unregister() calls must wait until async loader completes */ + init_completion(&rproc->firmware_loading_complete); + + /* + * We must retrieve early virtio configuration info from + * the firmware (e.g. whether to register a virtio device, + * what virtio features does it support, ...). + * + * We're initiating an asynchronous firmware loading, so we can + * be built-in kernel code, without hanging the boot process. + */ + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + rproc->firmware, dev, GFP_KERNEL, + rproc, rproc_fw_config_virtio); + if (ret < 0) { + dev_err(dev, "request_firmware_nowait failed: %d\n", ret); + complete_all(&rproc->firmware_loading_complete); + klist_remove(&rproc->node); + } + + return ret; +} +EXPORT_SYMBOL(rproc_register); + +/** + * rproc_alloc() - allocate a remote processor handle + * @dev: the underlying device + * @name: name of this remote processor + * @ops: platform-specific handlers (mainly start/stop) + * @firmware: name of firmware file to load + * @len: length of private data needed by the rproc driver (in bytes) + * + * Allocates a new remote processor handle, but does not register + * it yet. + * + * This function should be used by rproc implementations during initialization + * of the remote processor. + * + * After creating an rproc handle using this function, and when ready, + * implementations should then call rproc_register() to complete + * the registration of the remote processor. + * + * On success the new rproc is returned, and on failure, NULL. + * + * Note: _never_ directly deallocate @rproc, even if it was not registered + * yet. Instead, if you just need to unroll rproc_alloc(), use rproc_free(). + */ +struct rproc *rproc_alloc(struct device *dev, const char *name, + const struct rproc_ops *ops, + const char *firmware, int len) +{ + struct rproc *rproc; + + if (!dev || !name || !ops) + return NULL; + + rproc = kzalloc(sizeof(struct rproc) + len, GFP_KERNEL); + if (!rproc) { + dev_err(dev, "%s: kzalloc failed\n", __func__); + return NULL; + } + + rproc->dev = dev; + rproc->name = name; + rproc->ops = ops; + rproc->firmware = firmware; + rproc->priv = &rproc[1]; + + atomic_set(&rproc->power, 0); + + kref_init(&rproc->refcount); + + mutex_init(&rproc->lock); + + idr_init(&rproc->notifyids); + + INIT_LIST_HEAD(&rproc->carveouts); + INIT_LIST_HEAD(&rproc->mappings); + INIT_LIST_HEAD(&rproc->traces); + INIT_LIST_HEAD(&rproc->rvdevs); + + rproc->state = RPROC_OFFLINE; + + return rproc; +} +EXPORT_SYMBOL(rproc_alloc); + +/** + * rproc_free() - free an rproc handle that was allocated by rproc_alloc + * @rproc: the remote processor handle + * + * This function should _only_ be used if @rproc was only allocated, + * but not registered yet. + * + * If @rproc was already successfully registered (by calling rproc_register()), + * then use rproc_unregister() instead. + */ +void rproc_free(struct rproc *rproc) +{ + idr_remove_all(&rproc->notifyids); + idr_destroy(&rproc->notifyids); + + kfree(rproc); +} +EXPORT_SYMBOL(rproc_free); + +/** + * rproc_unregister() - unregister a remote processor + * @rproc: rproc handle to unregister + * + * Unregisters a remote processor, and decrements its refcount. + * If its refcount drops to zero, then @rproc will be freed. If not, + * it will be freed later once the last reference is dropped. + * + * This function should be called when the platform specific rproc + * implementation decides to remove the rproc device. it should + * _only_ be called if a previous invocation of rproc_register() + * has completed successfully. + * + * After rproc_unregister() returns, @rproc is _not_ valid anymore and + * it shouldn't be used. More specifically, don't call rproc_free() + * or try to directly free @rproc after rproc_unregister() returns; + * none of these are needed, and calling them is a bug. + * + * Returns 0 on success and -EINVAL if @rproc isn't valid. + */ +int rproc_unregister(struct rproc *rproc) +{ + struct rproc_vdev *rvdev; + + if (!rproc) + return -EINVAL; + + /* if rproc is just being registered, wait */ + wait_for_completion(&rproc->firmware_loading_complete); + + /* clean up remote vdev entries */ + list_for_each_entry(rvdev, &rproc->rvdevs, node) + rproc_remove_virtio_dev(rvdev); + + /* the rproc is downref'ed as soon as it's removed from the klist */ + klist_del(&rproc->node); + + /* the rproc will only be released after its refcount drops to zero */ + kref_put(&rproc->refcount, rproc_release); + + return 0; +} +EXPORT_SYMBOL(rproc_unregister); + +static int __init remoteproc_init(void) +{ + rproc_init_debugfs(); + return 0; +} +module_init(remoteproc_init); + +static void __exit remoteproc_exit(void) +{ + rproc_exit_debugfs(); +} +module_exit(remoteproc_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Generic Remote Processor Framework"); diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c new file mode 100644 index 000000000000..70277a530133 --- /dev/null +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -0,0 +1,179 @@ +/* + * Remote Processor Framework + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * + * Ohad Ben-Cohen <ohad@wizery.com> + * Mark Grosen <mgrosen@ti.com> + * Brian Swetland <swetland@google.com> + * Fernando Guzman Lugo <fernando.lugo@ti.com> + * Suman Anna <s-anna@ti.com> + * Robert Tivy <rtivy@ti.com> + * Armando Uribe De Leon <x0095078@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/kernel.h> +#include <linux/debugfs.h> +#include <linux/remoteproc.h> +#include <linux/device.h> + +/* remoteproc debugfs parent dir */ +static struct dentry *rproc_dbg; + +/* + * Some remote processors may support dumping trace logs into a shared + * memory buffer. We expose this trace buffer using debugfs, so users + * can easily tell what's going on remotely. + * + * We will most probably improve the rproc tracing facilities later on, + * but this kind of lightweight and simple mechanism is always good to have, + * as it provides very early tracing with little to no dependencies at all. + */ +static ssize_t rproc_trace_read(struct file *filp, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct rproc_mem_entry *trace = filp->private_data; + int len = strnlen(trace->va, trace->len); + + return simple_read_from_buffer(userbuf, count, ppos, trace->va, len); +} + +static int rproc_open_generic(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + + return 0; +} + +static const struct file_operations trace_rproc_ops = { + .read = rproc_trace_read, + .open = rproc_open_generic, + .llseek = generic_file_llseek, +}; + +/* + * A state-to-string lookup table, for exposing a human readable state + * via debugfs. Always keep in sync with enum rproc_state + */ +static const char * const rproc_state_string[] = { + "offline", + "suspended", + "running", + "crashed", + "invalid", +}; + +/* expose the state of the remote processor via debugfs */ +static ssize_t rproc_state_read(struct file *filp, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct rproc *rproc = filp->private_data; + unsigned int state; + char buf[30]; + int i; + + state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state; + + i = snprintf(buf, 30, "%.28s (%d)\n", rproc_state_string[state], + rproc->state); + + return simple_read_from_buffer(userbuf, count, ppos, buf, i); +} + +static const struct file_operations rproc_state_ops = { + .read = rproc_state_read, + .open = rproc_open_generic, + .llseek = generic_file_llseek, +}; + +/* expose the name of the remote processor via debugfs */ +static ssize_t rproc_name_read(struct file *filp, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct rproc *rproc = filp->private_data; + /* need room for the name, a newline and a terminating null */ + char buf[100]; + int i; + + i = snprintf(buf, sizeof(buf), "%.98s\n", rproc->name); + + return simple_read_from_buffer(userbuf, count, ppos, buf, i); +} + +static const struct file_operations rproc_name_ops = { + .read = rproc_name_read, + .open = rproc_open_generic, + .llseek = generic_file_llseek, +}; + +void rproc_remove_trace_file(struct dentry *tfile) +{ + debugfs_remove(tfile); +} + +struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc, + struct rproc_mem_entry *trace) +{ + struct dentry *tfile; + + tfile = debugfs_create_file(name, 0400, rproc->dbg_dir, + trace, &trace_rproc_ops); + if (!tfile) { + dev_err(rproc->dev, "failed to create debugfs trace entry\n"); + return NULL; + } + + return tfile; +} + +void rproc_delete_debug_dir(struct rproc *rproc) +{ + if (!rproc->dbg_dir) + return; + + debugfs_remove_recursive(rproc->dbg_dir); +} + +void rproc_create_debug_dir(struct rproc *rproc) +{ + struct device *dev = rproc->dev; + + if (!rproc_dbg) + return; + + rproc->dbg_dir = debugfs_create_dir(dev_name(dev), rproc_dbg); + if (!rproc->dbg_dir) + return; + + debugfs_create_file("name", 0400, rproc->dbg_dir, + rproc, &rproc_name_ops); + debugfs_create_file("state", 0400, rproc->dbg_dir, + rproc, &rproc_state_ops); +} + +void __init rproc_init_debugfs(void) +{ + if (debugfs_initialized()) { + rproc_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); + if (!rproc_dbg) + pr_err("can't create debugfs dir\n"); + } +} + +void __exit rproc_exit_debugfs(void) +{ + if (rproc_dbg) + debugfs_remove(rproc_dbg); +} diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h new file mode 100644 index 000000000000..9f336d6bdef3 --- /dev/null +++ b/drivers/remoteproc/remoteproc_internal.h @@ -0,0 +1,44 @@ +/* + * Remote processor framework + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * + * Ohad Ben-Cohen <ohad@wizery.com> + * Brian Swetland <swetland@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef REMOTEPROC_INTERNAL_H +#define REMOTEPROC_INTERNAL_H + +#include <linux/irqreturn.h> + +struct rproc; + +/* from remoteproc_core.c */ +void rproc_release(struct kref *kref); +irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id); + +/* from remoteproc_virtio.c */ +int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id); +void rproc_remove_virtio_dev(struct rproc_vdev *rvdev); + +/* from remoteproc_debugfs.c */ +void rproc_remove_trace_file(struct dentry *tfile); +struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc, + struct rproc_mem_entry *trace); +void rproc_delete_debug_dir(struct rproc *rproc); +void rproc_create_debug_dir(struct rproc *rproc); +void rproc_init_debugfs(void); +void rproc_exit_debugfs(void); + +#endif /* REMOTEPROC_INTERNAL_H */ diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c new file mode 100644 index 000000000000..ecf612130750 --- /dev/null +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -0,0 +1,289 @@ +/* + * Remote processor messaging transport (OMAP platform-specific bits) + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * + * Ohad Ben-Cohen <ohad@wizery.com> + * Brian Swetland <swetland@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/export.h> +#include <linux/remoteproc.h> +#include <linux/virtio.h> +#include <linux/virtio_config.h> +#include <linux/virtio_ids.h> +#include <linux/virtio_ring.h> +#include <linux/err.h> +#include <linux/kref.h> +#include <linux/slab.h> + +#include "remoteproc_internal.h" + +/* kick the remote processor, and let it know which virtqueue to poke at */ +static void rproc_virtio_notify(struct virtqueue *vq) +{ + struct rproc_vring *rvring = vq->priv; + struct rproc *rproc = rvring->rvdev->rproc; + int notifyid = rvring->notifyid; + + dev_dbg(rproc->dev, "kicking vq index: %d\n", notifyid); + + rproc->ops->kick(rproc, notifyid); +} + +/** + * rproc_vq_interrupt() - tell remoteproc that a virtqueue is interrupted + * @rproc: handle to the remote processor + * @notifyid: index of the signalled virtqueue (unique per this @rproc) + * + * This function should be called by the platform-specific rproc driver, + * when the remote processor signals that a specific virtqueue has pending + * messages available. + * + * Returns IRQ_NONE if no message was found in the @notifyid virtqueue, + * and otherwise returns IRQ_HANDLED. + */ +irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid) +{ + struct rproc_vring *rvring; + + dev_dbg(rproc->dev, "vq index %d is interrupted\n", notifyid); + + rvring = idr_find(&rproc->notifyids, notifyid); + if (!rvring || !rvring->vq) + return IRQ_NONE; + + return vring_interrupt(0, rvring->vq); +} +EXPORT_SYMBOL(rproc_vq_interrupt); + +static struct virtqueue *rp_find_vq(struct virtio_device *vdev, + unsigned id, + void (*callback)(struct virtqueue *vq), + const char *name) +{ + struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); + struct rproc *rproc = vdev_to_rproc(vdev); + struct rproc_vring *rvring; + struct virtqueue *vq; + void *addr; + int len, size; + + /* we're temporarily limited to two virtqueues per rvdev */ + if (id >= ARRAY_SIZE(rvdev->vring)) + return ERR_PTR(-EINVAL); + + rvring = &rvdev->vring[id]; + + addr = rvring->va; + len = rvring->len; + + /* zero vring */ + size = vring_size(len, rvring->align); + memset(addr, 0, size); + + dev_dbg(rproc->dev, "vring%d: va %p qsz %d notifyid %d\n", + id, addr, len, rvring->notifyid); + + /* + * Create the new vq, and tell virtio we're not interested in + * the 'weak' smp barriers, since we're talking with a real device. + */ + vq = vring_new_virtqueue(len, rvring->align, vdev, false, addr, + rproc_virtio_notify, callback, name); + if (!vq) { + dev_err(rproc->dev, "vring_new_virtqueue %s failed\n", name); + return ERR_PTR(-ENOMEM); + } + + rvring->vq = vq; + vq->priv = rvring; + + return vq; +} + +static void rproc_virtio_del_vqs(struct virtio_device *vdev) +{ + struct virtqueue *vq, *n; + struct rproc *rproc = vdev_to_rproc(vdev); + struct rproc_vring *rvring; + + /* power down the remote processor before deleting vqs */ + rproc_shutdown(rproc); + + list_for_each_entry_safe(vq, n, &vdev->vqs, list) { + rvring = vq->priv; + rvring->vq = NULL; + vring_del_virtqueue(vq); + } +} + +static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, + struct virtqueue *vqs[], + vq_callback_t *callbacks[], + const char *names[]) +{ + struct rproc *rproc = vdev_to_rproc(vdev); + int i, ret; + + for (i = 0; i < nvqs; ++i) { + vqs[i] = rp_find_vq(vdev, i, callbacks[i], names[i]); + if (IS_ERR(vqs[i])) { + ret = PTR_ERR(vqs[i]); + goto error; + } + } + + /* now that the vqs are all set, boot the remote processor */ + ret = rproc_boot(rproc); + if (ret) { + dev_err(rproc->dev, "rproc_boot() failed %d\n", ret); + goto error; + } + + return 0; + +error: + rproc_virtio_del_vqs(vdev); + return ret; +} + +/* + * We don't support yet real virtio status semantics. + * + * The plan is to provide this via the VDEV resource entry + * which is part of the firmware: this way the remote processor + * will be able to access the status values as set by us. + */ +static u8 rproc_virtio_get_status(struct virtio_device *vdev) +{ + return 0; +} + +static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status) +{ + dev_dbg(&vdev->dev, "status: %d\n", status); +} + +static void rproc_virtio_reset(struct virtio_device *vdev) +{ + dev_dbg(&vdev->dev, "reset !\n"); +} + +/* provide the vdev features as retrieved from the firmware */ +static u32 rproc_virtio_get_features(struct virtio_device *vdev) +{ + struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); + + return rvdev->dfeatures; +} + +static void rproc_virtio_finalize_features(struct virtio_device *vdev) +{ + struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); + + /* Give virtio_ring a chance to accept features */ + vring_transport_features(vdev); + + /* + * Remember the finalized features of our vdev, and provide it + * to the remote processor once it is powered on. + * + * Similarly to the status field, we don't expose yet the negotiated + * features to the remote processors at this point. This will be + * fixed as part of a small resource table overhaul and then an + * extension of the virtio resource entries. + */ + rvdev->gfeatures = vdev->features[0]; +} + +static struct virtio_config_ops rproc_virtio_config_ops = { + .get_features = rproc_virtio_get_features, + .finalize_features = rproc_virtio_finalize_features, + .find_vqs = rproc_virtio_find_vqs, + .del_vqs = rproc_virtio_del_vqs, + .reset = rproc_virtio_reset, + .set_status = rproc_virtio_set_status, + .get_status = rproc_virtio_get_status, +}; + +/* + * This function is called whenever vdev is released, and is responsible + * to decrement the remote processor's refcount taken when vdev was + * added. + * + * Never call this function directly; it will be called by the driver + * core when needed. + */ +static void rproc_vdev_release(struct device *dev) +{ + struct virtio_device *vdev = dev_to_virtio(dev); + struct rproc *rproc = vdev_to_rproc(vdev); + + kref_put(&rproc->refcount, rproc_release); +} + +/** + * rproc_add_virtio_dev() - register an rproc-induced virtio device + * @rvdev: the remote vdev + * + * This function registers a virtio device. This vdev's partent is + * the rproc device. + * + * Returns 0 on success or an appropriate error value otherwise. + */ +int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) +{ + struct rproc *rproc = rvdev->rproc; + struct device *dev = rproc->dev; + struct virtio_device *vdev = &rvdev->vdev; + int ret; + + vdev->id.device = id, + vdev->config = &rproc_virtio_config_ops, + vdev->dev.parent = dev; + vdev->dev.release = rproc_vdev_release; + + /* + * We're indirectly making a non-temporary copy of the rproc pointer + * here, because drivers probed with this vdev will indirectly + * access the wrapping rproc. + * + * Therefore we must increment the rproc refcount here, and decrement + * it _only_ when the vdev is released. + */ + kref_get(&rproc->refcount); + + ret = register_virtio_device(vdev); + if (ret) { + kref_put(&rproc->refcount, rproc_release); + dev_err(dev, "failed to register vdev: %d\n", ret); + goto out; + } + + dev_info(dev, "registered %s (type %d)\n", dev_name(&vdev->dev), id); + +out: + return ret; +} + +/** + * rproc_remove_virtio_dev() - remove an rproc-induced virtio device + * @rvdev: the remote vdev + * + * This function unregisters an existing virtio device. + */ +void rproc_remove_virtio_dev(struct rproc_vdev *rvdev) +{ + unregister_virtio_device(&rvdev->vdev); +} diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig new file mode 100644 index 000000000000..32aead65735a --- /dev/null +++ b/drivers/rpmsg/Kconfig @@ -0,0 +1,10 @@ +menu "Rpmsg drivers (EXPERIMENTAL)" + +# RPMSG always gets selected by whoever wants it +config RPMSG + tristate + select VIRTIO + select VIRTIO_RING + depends on EXPERIMENTAL + +endmenu diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile new file mode 100644 index 000000000000..7617fcb8259f --- /dev/null +++ b/drivers/rpmsg/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_RPMSG) += virtio_rpmsg_bus.o diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c new file mode 100644 index 000000000000..75506ec2840e --- /dev/null +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -0,0 +1,1054 @@ +/* + * Virtio-based remote processor messaging bus + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * + * Ohad Ben-Cohen <ohad@wizery.com> + * Brian Swetland <swetland@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/virtio.h> +#include <linux/virtio_ids.h> +#include <linux/virtio_config.h> +#include <linux/scatterlist.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/idr.h> +#include <linux/jiffies.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/rpmsg.h> +#include <linux/mutex.h> + +/** + * struct virtproc_info - virtual remote processor state + * @vdev: the virtio device + * @rvq: rx virtqueue + * @svq: tx virtqueue + * @rbufs: kernel address of rx buffers + * @sbufs: kernel address of tx buffers + * @last_sbuf: index of last tx buffer used + * @bufs_dma: dma base addr of the buffers + * @tx_lock: protects svq, sbufs and sleepers, to allow concurrent senders. + * sending a message might require waking up a dozing remote + * processor, which involves sleeping, hence the mutex. + * @endpoints: idr of local endpoints, allows fast retrieval + * @endpoints_lock: lock of the endpoints set + * @sendq: wait queue of sending contexts waiting for a tx buffers + * @sleepers: number of senders that are waiting for a tx buffer + * @ns_ept: the bus's name service endpoint + * + * This structure stores the rpmsg state of a given virtio remote processor + * device (there might be several virtio proc devices for each physical + * remote processor). + */ +struct virtproc_info { + struct virtio_device *vdev; + struct virtqueue *rvq, *svq; + void *rbufs, *sbufs; + int last_sbuf; + dma_addr_t bufs_dma; + struct mutex tx_lock; + struct idr endpoints; + struct mutex endpoints_lock; + wait_queue_head_t sendq; + atomic_t sleepers; + struct rpmsg_endpoint *ns_ept; +}; + +/** + * struct rpmsg_channel_info - internal channel info representation + * @name: name of service + * @src: local address + * @dst: destination address + */ +struct rpmsg_channel_info { + char name[RPMSG_NAME_SIZE]; + u32 src; + u32 dst; +}; + +#define to_rpmsg_channel(d) container_of(d, struct rpmsg_channel, dev) +#define to_rpmsg_driver(d) container_of(d, struct rpmsg_driver, drv) + +/* + * We're allocating 512 buffers of 512 bytes for communications, and then + * using the first 256 buffers for RX, and the last 256 buffers for TX. + * + * Each buffer will have 16 bytes for the msg header and 496 bytes for + * the payload. + * + * This will require a total space of 256KB for the buffers. + * + * We might also want to add support for user-provided buffers in time. + * This will allow bigger buffer size flexibility, and can also be used + * to achieve zero-copy messaging. + * + * Note that these numbers are purely a decision of this driver - we + * can change this without changing anything in the firmware of the remote + * processor. + */ +#define RPMSG_NUM_BUFS (512) +#define RPMSG_BUF_SIZE (512) +#define RPMSG_TOTAL_BUF_SPACE (RPMSG_NUM_BUFS * RPMSG_BUF_SIZE) + +/* + * Local addresses are dynamically allocated on-demand. + * We do not dynamically assign addresses from the low 1024 range, + * in order to reserve that address range for predefined services. + */ +#define RPMSG_RESERVED_ADDRESSES (1024) + +/* Address 53 is reserved for advertising remote services */ +#define RPMSG_NS_ADDR (53) + +/* sysfs show configuration fields */ +#define rpmsg_show_attr(field, path, format_string) \ +static ssize_t \ +field##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); \ + \ + return sprintf(buf, format_string, rpdev->path); \ +} + +/* for more info, see Documentation/ABI/testing/sysfs-bus-rpmsg */ +rpmsg_show_attr(name, id.name, "%s\n"); +rpmsg_show_attr(src, src, "0x%x\n"); +rpmsg_show_attr(dst, dst, "0x%x\n"); +rpmsg_show_attr(announce, announce ? "true" : "false", "%s\n"); + +/* + * Unique (and free running) index for rpmsg devices. + * + * Yeah, we're not recycling those numbers (yet?). will be easy + * to change if/when we want to. + */ +static unsigned int rpmsg_dev_index; + +static ssize_t modalias_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + + return sprintf(buf, RPMSG_DEVICE_MODALIAS_FMT "\n", rpdev->id.name); +} + +static struct device_attribute rpmsg_dev_attrs[] = { + __ATTR_RO(name), + __ATTR_RO(modalias), + __ATTR_RO(dst), + __ATTR_RO(src), + __ATTR_RO(announce), + __ATTR_NULL +}; + +/* rpmsg devices and drivers are matched using the service name */ +static inline int rpmsg_id_match(const struct rpmsg_channel *rpdev, + const struct rpmsg_device_id *id) +{ + return strncmp(id->name, rpdev->id.name, RPMSG_NAME_SIZE) == 0; +} + +/* match rpmsg channel and rpmsg driver */ +static int rpmsg_dev_match(struct device *dev, struct device_driver *drv) +{ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + struct rpmsg_driver *rpdrv = to_rpmsg_driver(drv); + const struct rpmsg_device_id *ids = rpdrv->id_table; + unsigned int i; + + for (i = 0; ids[i].name[0]; i++) + if (rpmsg_id_match(rpdev, &ids[i])) + return 1; + + return 0; +} + +static int rpmsg_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + + return add_uevent_var(env, "MODALIAS=" RPMSG_DEVICE_MODALIAS_FMT, + rpdev->id.name); +} + +/* for more info, see below documentation of rpmsg_create_ept() */ +static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp, + struct rpmsg_channel *rpdev, rpmsg_rx_cb_t cb, + void *priv, u32 addr) +{ + int err, tmpaddr, request; + struct rpmsg_endpoint *ept; + struct device *dev = rpdev ? &rpdev->dev : &vrp->vdev->dev; + + if (!idr_pre_get(&vrp->endpoints, GFP_KERNEL)) + return NULL; + + ept = kzalloc(sizeof(*ept), GFP_KERNEL); + if (!ept) { + dev_err(dev, "failed to kzalloc a new ept\n"); + return NULL; + } + + ept->rpdev = rpdev; + ept->cb = cb; + ept->priv = priv; + + /* do we need to allocate a local address ? */ + request = addr == RPMSG_ADDR_ANY ? RPMSG_RESERVED_ADDRESSES : addr; + + mutex_lock(&vrp->endpoints_lock); + + /* bind the endpoint to an rpmsg address (and allocate one if needed) */ + err = idr_get_new_above(&vrp->endpoints, ept, request, &tmpaddr); + if (err) { + dev_err(dev, "idr_get_new_above failed: %d\n", err); + goto free_ept; + } + + /* make sure the user's address request is fulfilled, if relevant */ + if (addr != RPMSG_ADDR_ANY && tmpaddr != addr) { + dev_err(dev, "address 0x%x already in use\n", addr); + goto rem_idr; + } + + ept->addr = tmpaddr; + + mutex_unlock(&vrp->endpoints_lock); + + return ept; + +rem_idr: + idr_remove(&vrp->endpoints, request); +free_ept: + mutex_unlock(&vrp->endpoints_lock); + kfree(ept); + return NULL; +} + +/** + * rpmsg_create_ept() - create a new rpmsg_endpoint + * @rpdev: rpmsg channel device + * @cb: rx callback handler + * @priv: private data for the driver's use + * @addr: local rpmsg address to bind with @cb + * + * Every rpmsg address in the system is bound to an rx callback (so when + * inbound messages arrive, they are dispatched by the rpmsg bus using the + * appropriate callback handler) by means of an rpmsg_endpoint struct. + * + * This function allows drivers to create such an endpoint, and by that, + * bind a callback, and possibly some private data too, to an rpmsg address + * (either one that is known in advance, or one that will be dynamically + * assigned for them). + * + * Simple rpmsg drivers need not call rpmsg_create_ept, because an endpoint + * is already created for them when they are probed by the rpmsg bus + * (using the rx callback provided when they registered to the rpmsg bus). + * + * So things should just work for simple drivers: they already have an + * endpoint, their rx callback is bound to their rpmsg address, and when + * relevant inbound messages arrive (i.e. messages which their dst address + * equals to the src address of their rpmsg channel), the driver's handler + * is invoked to process it. + * + * That said, more complicated drivers might do need to allocate + * additional rpmsg addresses, and bind them to different rx callbacks. + * To accomplish that, those drivers need to call this function. + * + * Drivers should provide their @rpdev channel (so the new endpoint would belong + * to the same remote processor their channel belongs to), an rx callback + * function, an optional private data (which is provided back when the + * rx callback is invoked), and an address they want to bind with the + * callback. If @addr is RPMSG_ADDR_ANY, then rpmsg_create_ept will + * dynamically assign them an available rpmsg address (drivers should have + * a very good reason why not to always use RPMSG_ADDR_ANY here). + * + * Returns a pointer to the endpoint on success, or NULL on error. + */ +struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rpdev, + rpmsg_rx_cb_t cb, void *priv, u32 addr) +{ + return __rpmsg_create_ept(rpdev->vrp, rpdev, cb, priv, addr); +} +EXPORT_SYMBOL(rpmsg_create_ept); + +/** + * __rpmsg_destroy_ept() - destroy an existing rpmsg endpoint + * @vrp: virtproc which owns this ept + * @ept: endpoing to destroy + * + * An internal function which destroy an ept without assuming it is + * bound to an rpmsg channel. This is needed for handling the internal + * name service endpoint, which isn't bound to an rpmsg channel. + * See also __rpmsg_create_ept(). + */ +static void +__rpmsg_destroy_ept(struct virtproc_info *vrp, struct rpmsg_endpoint *ept) +{ + mutex_lock(&vrp->endpoints_lock); + idr_remove(&vrp->endpoints, ept->addr); + mutex_unlock(&vrp->endpoints_lock); + + kfree(ept); +} + +/** + * rpmsg_destroy_ept() - destroy an existing rpmsg endpoint + * @ept: endpoing to destroy + * + * Should be used by drivers to destroy an rpmsg endpoint previously + * created with rpmsg_create_ept(). + */ +void rpmsg_destroy_ept(struct rpmsg_endpoint *ept) +{ + __rpmsg_destroy_ept(ept->rpdev->vrp, ept); +} +EXPORT_SYMBOL(rpmsg_destroy_ept); + +/* + * when an rpmsg driver is probed with a channel, we seamlessly create + * it an endpoint, binding its rx callback to a unique local rpmsg + * address. + * + * if we need to, we also announce about this channel to the remote + * processor (needed in case the driver is exposing an rpmsg service). + */ +static int rpmsg_dev_probe(struct device *dev) +{ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver); + struct virtproc_info *vrp = rpdev->vrp; + struct rpmsg_endpoint *ept; + int err; + + ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, rpdev->src); + if (!ept) { + dev_err(dev, "failed to create endpoint\n"); + err = -ENOMEM; + goto out; + } + + rpdev->ept = ept; + rpdev->src = ept->addr; + + err = rpdrv->probe(rpdev); + if (err) { + dev_err(dev, "%s: failed: %d\n", __func__, err); + rpmsg_destroy_ept(ept); + goto out; + } + + /* need to tell remote processor's name service about this channel ? */ + if (rpdev->announce && + virtio_has_feature(vrp->vdev, VIRTIO_RPMSG_F_NS)) { + struct rpmsg_ns_msg nsm; + + strncpy(nsm.name, rpdev->id.name, RPMSG_NAME_SIZE); + nsm.addr = rpdev->src; + nsm.flags = RPMSG_NS_CREATE; + + err = rpmsg_sendto(rpdev, &nsm, sizeof(nsm), RPMSG_NS_ADDR); + if (err) + dev_err(dev, "failed to announce service %d\n", err); + } + +out: + return err; +} + +static int rpmsg_dev_remove(struct device *dev) +{ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver); + struct virtproc_info *vrp = rpdev->vrp; + int err = 0; + + /* tell remote processor's name service we're removing this channel */ + if (rpdev->announce && + virtio_has_feature(vrp->vdev, VIRTIO_RPMSG_F_NS)) { + struct rpmsg_ns_msg nsm; + + strncpy(nsm.name, rpdev->id.name, RPMSG_NAME_SIZE); + nsm.addr = rpdev->src; + nsm.flags = RPMSG_NS_DESTROY; + + err = rpmsg_sendto(rpdev, &nsm, sizeof(nsm), RPMSG_NS_ADDR); + if (err) + dev_err(dev, "failed to announce service %d\n", err); + } + + rpdrv->remove(rpdev); + + rpmsg_destroy_ept(rpdev->ept); + + return err; +} + +static struct bus_type rpmsg_bus = { + .name = "rpmsg", + .match = rpmsg_dev_match, + .dev_attrs = rpmsg_dev_attrs, + .uevent = rpmsg_uevent, + .probe = rpmsg_dev_probe, + .remove = rpmsg_dev_remove, +}; + +/** + * register_rpmsg_driver() - register an rpmsg driver with the rpmsg bus + * @rpdrv: pointer to a struct rpmsg_driver + * + * Returns 0 on success, and an appropriate error value on failure. + */ +int register_rpmsg_driver(struct rpmsg_driver *rpdrv) +{ + rpdrv->drv.bus = &rpmsg_bus; + return driver_register(&rpdrv->drv); +} +EXPORT_SYMBOL(register_rpmsg_driver); + +/** + * unregister_rpmsg_driver() - unregister an rpmsg driver from the rpmsg bus + * @rpdrv: pointer to a struct rpmsg_driver + * + * Returns 0 on success, and an appropriate error value on failure. + */ +void unregister_rpmsg_driver(struct rpmsg_driver *rpdrv) +{ + driver_unregister(&rpdrv->drv); +} +EXPORT_SYMBOL(unregister_rpmsg_driver); + +static void rpmsg_release_device(struct device *dev) +{ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + + kfree(rpdev); +} + +/* + * match an rpmsg channel with a channel info struct. + * this is used to make sure we're not creating rpmsg devices for channels + * that already exist. + */ +static int rpmsg_channel_match(struct device *dev, void *data) +{ + struct rpmsg_channel_info *chinfo = data; + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + + if (chinfo->src != RPMSG_ADDR_ANY && chinfo->src != rpdev->src) + return 0; + + if (chinfo->dst != RPMSG_ADDR_ANY && chinfo->dst != rpdev->dst) + return 0; + + if (strncmp(chinfo->name, rpdev->id.name, RPMSG_NAME_SIZE)) + return 0; + + /* found a match ! */ + return 1; +} + +/* + * create an rpmsg channel using its name and address info. + * this function will be used to create both static and dynamic + * channels. + */ +static struct rpmsg_channel *rpmsg_create_channel(struct virtproc_info *vrp, + struct rpmsg_channel_info *chinfo) +{ + struct rpmsg_channel *rpdev; + struct device *tmp, *dev = &vrp->vdev->dev; + int ret; + + /* make sure a similar channel doesn't already exist */ + tmp = device_find_child(dev, chinfo, rpmsg_channel_match); + if (tmp) { + /* decrement the matched device's refcount back */ + put_device(tmp); + dev_err(dev, "channel %s:%x:%x already exist\n", + chinfo->name, chinfo->src, chinfo->dst); + return NULL; + } + + rpdev = kzalloc(sizeof(struct rpmsg_channel), GFP_KERNEL); + if (!rpdev) { + pr_err("kzalloc failed\n"); + return NULL; + } + + rpdev->vrp = vrp; + rpdev->src = chinfo->src; + rpdev->dst = chinfo->dst; + + /* + * rpmsg server channels has predefined local address (for now), + * and their existence needs to be announced remotely + */ + rpdev->announce = rpdev->src != RPMSG_ADDR_ANY ? true : false; + + strncpy(rpdev->id.name, chinfo->name, RPMSG_NAME_SIZE); + + /* very simple device indexing plumbing which is enough for now */ + dev_set_name(&rpdev->dev, "rpmsg%d", rpmsg_dev_index++); + + rpdev->dev.parent = &vrp->vdev->dev; + rpdev->dev.bus = &rpmsg_bus; + rpdev->dev.release = rpmsg_release_device; + + ret = device_register(&rpdev->dev); + if (ret) { + dev_err(dev, "device_register failed: %d\n", ret); + put_device(&rpdev->dev); + return NULL; + } + + return rpdev; +} + +/* + * find an existing channel using its name + address properties, + * and destroy it + */ +static int rpmsg_destroy_channel(struct virtproc_info *vrp, + struct rpmsg_channel_info *chinfo) +{ + struct virtio_device *vdev = vrp->vdev; + struct device *dev; + + dev = device_find_child(&vdev->dev, chinfo, rpmsg_channel_match); + if (!dev) + return -EINVAL; + + device_unregister(dev); + + put_device(dev); + + return 0; +} + +/* super simple buffer "allocator" that is just enough for now */ +static void *get_a_tx_buf(struct virtproc_info *vrp) +{ + unsigned int len; + void *ret; + + /* support multiple concurrent senders */ + mutex_lock(&vrp->tx_lock); + + /* + * either pick the next unused tx buffer + * (half of our buffers are used for sending messages) + */ + if (vrp->last_sbuf < RPMSG_NUM_BUFS / 2) + ret = vrp->sbufs + RPMSG_BUF_SIZE * vrp->last_sbuf++; + /* or recycle a used one */ + else + ret = virtqueue_get_buf(vrp->svq, &len); + + mutex_unlock(&vrp->tx_lock); + + return ret; +} + +/** + * rpmsg_upref_sleepers() - enable "tx-complete" interrupts, if needed + * @vrp: virtual remote processor state + * + * This function is called before a sender is blocked, waiting for + * a tx buffer to become available. + * + * If we already have blocking senders, this function merely increases + * the "sleepers" reference count, and exits. + * + * Otherwise, if this is the first sender to block, we also enable + * virtio's tx callbacks, so we'd be immediately notified when a tx + * buffer is consumed (we rely on virtio's tx callback in order + * to wake up sleeping senders as soon as a tx buffer is used by the + * remote processor). + */ +static void rpmsg_upref_sleepers(struct virtproc_info *vrp) +{ + /* support multiple concurrent senders */ + mutex_lock(&vrp->tx_lock); + + /* are we the first sleeping context waiting for tx buffers ? */ + if (atomic_inc_return(&vrp->sleepers) == 1) + /* enable "tx-complete" interrupts before dozing off */ + virtqueue_enable_cb(vrp->svq); + + mutex_unlock(&vrp->tx_lock); +} + +/** + * rpmsg_downref_sleepers() - disable "tx-complete" interrupts, if needed + * @vrp: virtual remote processor state + * + * This function is called after a sender, that waited for a tx buffer + * to become available, is unblocked. + * + * If we still have blocking senders, this function merely decreases + * the "sleepers" reference count, and exits. + * + * Otherwise, if there are no more blocking senders, we also disable + * virtio's tx callbacks, to avoid the overhead incurred with handling + * those (now redundant) interrupts. + */ +static void rpmsg_downref_sleepers(struct virtproc_info *vrp) +{ + /* support multiple concurrent senders */ + mutex_lock(&vrp->tx_lock); + + /* are we the last sleeping context waiting for tx buffers ? */ + if (atomic_dec_and_test(&vrp->sleepers)) + /* disable "tx-complete" interrupts */ + virtqueue_disable_cb(vrp->svq); + + mutex_unlock(&vrp->tx_lock); +} + +/** + * rpmsg_send_offchannel_raw() - send a message across to the remote processor + * @rpdev: the rpmsg channel + * @src: source address + * @dst: destination address + * @data: payload of message + * @len: length of payload + * @wait: indicates whether caller should block in case no TX buffers available + * + * This function is the base implementation for all of the rpmsg sending API. + * + * It will send @data of length @len to @dst, and say it's from @src. The + * message will be sent to the remote processor which the @rpdev channel + * belongs to. + * + * The message is sent using one of the TX buffers that are available for + * communication with this remote processor. + * + * If @wait is true, the caller will be blocked until either a TX buffer is + * available, or 15 seconds elapses (we don't want callers to + * sleep indefinitely due to misbehaving remote processors), and in that + * case -ERESTARTSYS is returned. The number '15' itself was picked + * arbitrarily; there's little point in asking drivers to provide a timeout + * value themselves. + * + * Otherwise, if @wait is false, and there are no TX buffers available, + * the function will immediately fail, and -ENOMEM will be returned. + * + * Normally drivers shouldn't use this function directly; instead, drivers + * should use the appropriate rpmsg_{try}send{to, _offchannel} API + * (see include/linux/rpmsg.h). + * + * Returns 0 on success and an appropriate error value on failure. + */ +int rpmsg_send_offchannel_raw(struct rpmsg_channel *rpdev, u32 src, u32 dst, + void *data, int len, bool wait) +{ + struct virtproc_info *vrp = rpdev->vrp; + struct device *dev = &rpdev->dev; + struct scatterlist sg; + struct rpmsg_hdr *msg; + int err; + + /* bcasting isn't allowed */ + if (src == RPMSG_ADDR_ANY || dst == RPMSG_ADDR_ANY) { + dev_err(dev, "invalid addr (src 0x%x, dst 0x%x)\n", src, dst); + return -EINVAL; + } + + /* + * We currently use fixed-sized buffers, and therefore the payload + * length is limited. + * + * One of the possible improvements here is either to support + * user-provided buffers (and then we can also support zero-copy + * messaging), or to improve the buffer allocator, to support + * variable-length buffer sizes. + */ + if (len > RPMSG_BUF_SIZE - sizeof(struct rpmsg_hdr)) { + dev_err(dev, "message is too big (%d)\n", len); + return -EMSGSIZE; + } + + /* grab a buffer */ + msg = get_a_tx_buf(vrp); + if (!msg && !wait) + return -ENOMEM; + + /* no free buffer ? wait for one (but bail after 15 seconds) */ + while (!msg) { + /* enable "tx-complete" interrupts, if not already enabled */ + rpmsg_upref_sleepers(vrp); + + /* + * sleep until a free buffer is available or 15 secs elapse. + * the timeout period is not configurable because there's + * little point in asking drivers to specify that. + * if later this happens to be required, it'd be easy to add. + */ + err = wait_event_interruptible_timeout(vrp->sendq, + (msg = get_a_tx_buf(vrp)), + msecs_to_jiffies(15000)); + + /* disable "tx-complete" interrupts if we're the last sleeper */ + rpmsg_downref_sleepers(vrp); + + /* timeout ? */ + if (!err) { + dev_err(dev, "timeout waiting for a tx buffer\n"); + return -ERESTARTSYS; + } + } + + msg->len = len; + msg->flags = 0; + msg->src = src; + msg->dst = dst; + msg->reserved = 0; + memcpy(msg->data, data, len); + + dev_dbg(dev, "TX From 0x%x, To 0x%x, Len %d, Flags %d, Reserved %d\n", + msg->src, msg->dst, msg->len, + msg->flags, msg->reserved); + print_hex_dump(KERN_DEBUG, "rpmsg_virtio TX: ", DUMP_PREFIX_NONE, 16, 1, + msg, sizeof(*msg) + msg->len, true); + + sg_init_one(&sg, msg, sizeof(*msg) + len); + + mutex_lock(&vrp->tx_lock); + + /* add message to the remote processor's virtqueue */ + err = virtqueue_add_buf(vrp->svq, &sg, 1, 0, msg, GFP_KERNEL); + if (err < 0) { + /* + * need to reclaim the buffer here, otherwise it's lost + * (memory won't leak, but rpmsg won't use it again for TX). + * this will wait for a buffer management overhaul. + */ + dev_err(dev, "virtqueue_add_buf failed: %d\n", err); + goto out; + } + + /* tell the remote processor it has a pending message to read */ + virtqueue_kick(vrp->svq); + + err = 0; +out: + mutex_unlock(&vrp->tx_lock); + return err; +} +EXPORT_SYMBOL(rpmsg_send_offchannel_raw); + +/* called when an rx buffer is used, and it's time to digest a message */ +static void rpmsg_recv_done(struct virtqueue *rvq) +{ + struct rpmsg_hdr *msg; + unsigned int len; + struct rpmsg_endpoint *ept; + struct scatterlist sg; + struct virtproc_info *vrp = rvq->vdev->priv; + struct device *dev = &rvq->vdev->dev; + int err; + + msg = virtqueue_get_buf(rvq, &len); + if (!msg) { + dev_err(dev, "uhm, incoming signal, but no used buffer ?\n"); + return; + } + + dev_dbg(dev, "From: 0x%x, To: 0x%x, Len: %d, Flags: %d, Reserved: %d\n", + msg->src, msg->dst, msg->len, + msg->flags, msg->reserved); + print_hex_dump(KERN_DEBUG, "rpmsg_virtio RX: ", DUMP_PREFIX_NONE, 16, 1, + msg, sizeof(*msg) + msg->len, true); + + /* + * We currently use fixed-sized buffers, so trivially sanitize + * the reported payload length. + */ + if (len > RPMSG_BUF_SIZE || + msg->len > (len - sizeof(struct rpmsg_hdr))) { + dev_warn(dev, "inbound msg too big: (%d, %d)\n", len, msg->len); + return; + } + + /* use the dst addr to fetch the callback of the appropriate user */ + mutex_lock(&vrp->endpoints_lock); + ept = idr_find(&vrp->endpoints, msg->dst); + mutex_unlock(&vrp->endpoints_lock); + + if (ept && ept->cb) + ept->cb(ept->rpdev, msg->data, msg->len, ept->priv, msg->src); + else + dev_warn(dev, "msg received with no recepient\n"); + + /* publish the real size of the buffer */ + sg_init_one(&sg, msg, RPMSG_BUF_SIZE); + + /* add the buffer back to the remote processor's virtqueue */ + err = virtqueue_add_buf(vrp->rvq, &sg, 0, 1, msg, GFP_KERNEL); + if (err < 0) { + dev_err(dev, "failed to add a virtqueue buffer: %d\n", err); + return; + } + + /* tell the remote processor we added another available rx buffer */ + virtqueue_kick(vrp->rvq); +} + +/* + * This is invoked whenever the remote processor completed processing + * a TX msg we just sent it, and the buffer is put back to the used ring. + * + * Normally, though, we suppress this "tx complete" interrupt in order to + * avoid the incurred overhead. + */ +static void rpmsg_xmit_done(struct virtqueue *svq) +{ + struct virtproc_info *vrp = svq->vdev->priv; + + dev_dbg(&svq->vdev->dev, "%s\n", __func__); + + /* wake up potential senders that are waiting for a tx buffer */ + wake_up_interruptible(&vrp->sendq); +} + +/* invoked when a name service announcement arrives */ +static void rpmsg_ns_cb(struct rpmsg_channel *rpdev, void *data, int len, + void *priv, u32 src) +{ + struct rpmsg_ns_msg *msg = data; + struct rpmsg_channel *newch; + struct rpmsg_channel_info chinfo; + struct virtproc_info *vrp = priv; + struct device *dev = &vrp->vdev->dev; + int ret; + + print_hex_dump(KERN_DEBUG, "NS announcement: ", + DUMP_PREFIX_NONE, 16, 1, + data, len, true); + + if (len != sizeof(*msg)) { + dev_err(dev, "malformed ns msg (%d)\n", len); + return; + } + + /* + * the name service ept does _not_ belong to a real rpmsg channel, + * and is handled by the rpmsg bus itself. + * for sanity reasons, make sure a valid rpdev has _not_ sneaked + * in somehow. + */ + if (rpdev) { + dev_err(dev, "anomaly: ns ept has an rpdev handle\n"); + return; + } + + /* don't trust the remote processor for null terminating the name */ + msg->name[RPMSG_NAME_SIZE - 1] = '\0'; + + dev_info(dev, "%sing channel %s addr 0x%x\n", + msg->flags & RPMSG_NS_DESTROY ? "destroy" : "creat", + msg->name, msg->addr); + + strncpy(chinfo.name, msg->name, sizeof(chinfo.name)); + chinfo.src = RPMSG_ADDR_ANY; + chinfo.dst = msg->addr; + + if (msg->flags & RPMSG_NS_DESTROY) { + ret = rpmsg_destroy_channel(vrp, &chinfo); + if (ret) + dev_err(dev, "rpmsg_destroy_channel failed: %d\n", ret); + } else { + newch = rpmsg_create_channel(vrp, &chinfo); + if (!newch) + dev_err(dev, "rpmsg_create_channel failed\n"); + } +} + +static int rpmsg_probe(struct virtio_device *vdev) +{ + vq_callback_t *vq_cbs[] = { rpmsg_recv_done, rpmsg_xmit_done }; + const char *names[] = { "input", "output" }; + struct virtqueue *vqs[2]; + struct virtproc_info *vrp; + void *bufs_va; + int err = 0, i; + + vrp = kzalloc(sizeof(*vrp), GFP_KERNEL); + if (!vrp) + return -ENOMEM; + + vrp->vdev = vdev; + + idr_init(&vrp->endpoints); + mutex_init(&vrp->endpoints_lock); + mutex_init(&vrp->tx_lock); + init_waitqueue_head(&vrp->sendq); + + /* We expect two virtqueues, rx and tx (and in this order) */ + err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, names); + if (err) + goto free_vrp; + + vrp->rvq = vqs[0]; + vrp->svq = vqs[1]; + + /* allocate coherent memory for the buffers */ + bufs_va = dma_alloc_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE, + &vrp->bufs_dma, GFP_KERNEL); + if (!bufs_va) + goto vqs_del; + + dev_dbg(&vdev->dev, "buffers: va %p, dma 0x%llx\n", bufs_va, + (unsigned long long)vrp->bufs_dma); + + /* half of the buffers is dedicated for RX */ + vrp->rbufs = bufs_va; + + /* and half is dedicated for TX */ + vrp->sbufs = bufs_va + RPMSG_TOTAL_BUF_SPACE / 2; + + /* set up the receive buffers */ + for (i = 0; i < RPMSG_NUM_BUFS / 2; i++) { + struct scatterlist sg; + void *cpu_addr = vrp->rbufs + i * RPMSG_BUF_SIZE; + + sg_init_one(&sg, cpu_addr, RPMSG_BUF_SIZE); + + err = virtqueue_add_buf(vrp->rvq, &sg, 0, 1, cpu_addr, + GFP_KERNEL); + WARN_ON(err < 0); /* sanity check; this can't really happen */ + } + + /* suppress "tx-complete" interrupts */ + virtqueue_disable_cb(vrp->svq); + + vdev->priv = vrp; + + /* if supported by the remote processor, enable the name service */ + if (virtio_has_feature(vdev, VIRTIO_RPMSG_F_NS)) { + /* a dedicated endpoint handles the name service msgs */ + vrp->ns_ept = __rpmsg_create_ept(vrp, NULL, rpmsg_ns_cb, + vrp, RPMSG_NS_ADDR); + if (!vrp->ns_ept) { + dev_err(&vdev->dev, "failed to create the ns ept\n"); + err = -ENOMEM; + goto free_coherent; + } + } + + /* tell the remote processor it can start sending messages */ + virtqueue_kick(vrp->rvq); + + dev_info(&vdev->dev, "rpmsg host is online\n"); + + return 0; + +free_coherent: + dma_free_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE, bufs_va, + vrp->bufs_dma); +vqs_del: + vdev->config->del_vqs(vrp->vdev); +free_vrp: + kfree(vrp); + return err; +} + +static int rpmsg_remove_device(struct device *dev, void *data) +{ + device_unregister(dev); + + return 0; +} + +static void __devexit rpmsg_remove(struct virtio_device *vdev) +{ + struct virtproc_info *vrp = vdev->priv; + int ret; + + vdev->config->reset(vdev); + + ret = device_for_each_child(&vdev->dev, NULL, rpmsg_remove_device); + if (ret) + dev_warn(&vdev->dev, "can't remove rpmsg device: %d\n", ret); + + if (vrp->ns_ept) + __rpmsg_destroy_ept(vrp, vrp->ns_ept); + + idr_remove_all(&vrp->endpoints); + idr_destroy(&vrp->endpoints); + + vdev->config->del_vqs(vrp->vdev); + + dma_free_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE, + vrp->rbufs, vrp->bufs_dma); + + kfree(vrp); +} + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_RPMSG, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static unsigned int features[] = { + VIRTIO_RPMSG_F_NS, +}; + +static struct virtio_driver virtio_ipc_driver = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = rpmsg_probe, + .remove = __devexit_p(rpmsg_remove), +}; + +static int __init rpmsg_init(void) +{ + int ret; + + ret = bus_register(&rpmsg_bus); + if (ret) { + pr_err("failed to register rpmsg bus: %d\n", ret); + return ret; + } + + ret = register_virtio_driver(&virtio_ipc_driver); + if (ret) { + pr_err("failed to register virtio driver: %d\n", ret); + bus_unregister(&rpmsg_bus); + } + + return ret; +} +module_init(rpmsg_init); + +static void __exit rpmsg_fini(void) +{ + unregister_virtio_driver(&virtio_ipc_driver); + bus_unregister(&rpmsg_bus); +} +module_exit(rpmsg_fini); + +MODULE_DEVICE_TABLE(virtio, id_table); +MODULE_DESCRIPTION("Virtio-based remote processor messaging bus"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 3a125b835546..8c8377d50c4c 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -554,6 +554,13 @@ config RTC_DRV_DS1742 This driver can also be built as a module. If so, the module will be called rtc-ds1742. +config RTC_DRV_DA9052 + tristate "Dialog DA9052/DA9053 RTC" + depends on PMIC_DA9052 + help + Say y here to support the RTC driver for Dialog Semiconductor + DA9052-BC and DA9053-AA/Bx PMICs. + config RTC_DRV_EFI tristate "EFI RTC" depends on IA64 @@ -748,7 +755,7 @@ config HAVE_S3C_RTC config RTC_DRV_S3C tristate "Samsung S3C series SoC RTC" - depends on ARCH_S3C2410 || ARCH_S3C64XX || HAVE_S3C_RTC + depends on ARCH_S3C64XX || HAVE_S3C_RTC help RTC (Realtime Clock) driver for the clock inbuilt into the Samsung S3C24XX series of SoCs. This can provide periodic @@ -773,8 +780,8 @@ config RTC_DRV_EP93XX will be called rtc-ep93xx. config RTC_DRV_SA1100 - tristate "SA11x0/PXA2xx" - depends on ARCH_SA1100 || ARCH_PXA + tristate "SA11x0/PXA2xx/PXA910" + depends on ARCH_SA1100 || ARCH_PXA || ARCH_MMP help If you say Y here you will get access to the real time clock built into your SA11x0 or PXA2xx CPU. @@ -1070,4 +1077,14 @@ config RTC_DRV_PUV3 This drive can also be built as a module. If so, the module will be called rtc-puv3. +config RTC_DRV_LOONGSON1 + tristate "loongson1 RTC support" + depends on MACH_LOONGSON1 + help + This is a driver for the loongson1 on-chip Counter0 (Time-Of-Year + counter) to be used as a RTC. + + This driver can also be built as a module. If so, the module + will be called rtc-ls1x. + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 6e6982335c10..727ae7786e6c 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o +obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o obj-$(CONFIG_RTC_DRV_DAVINCI) += rtc-davinci.o obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o @@ -53,6 +54,7 @@ obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o obj-$(CONFIG_RTC_DRV_LPC32XX) += rtc-lpc32xx.o +obj-$(CONFIG_RTC_DRV_LOONGSON1) += rtc-ls1x.o obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o obj-$(CONFIG_RTC_DRV_M41T93) += rtc-m41t93.o obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c index ee3c122c0599..831868904e02 100644 --- a/drivers/rtc/rtc-at91sam9.c +++ b/drivers/rtc/rtc-at91sam9.c @@ -57,6 +57,7 @@ struct sam9_rtc { void __iomem *rtt; struct rtc_device *rtcdev; u32 imr; + void __iomem *gpbr; }; #define rtt_readl(rtc, field) \ @@ -65,9 +66,9 @@ struct sam9_rtc { __raw_writel((val), (rtc)->rtt + AT91_RTT_ ## field) #define gpbr_readl(rtc) \ - at91_sys_read(AT91_GPBR + 4 * CONFIG_RTC_DRV_AT91SAM9_GPBR) + __raw_readl((rtc)->gpbr) #define gpbr_writel(rtc, val) \ - at91_sys_write(AT91_GPBR + 4 * CONFIG_RTC_DRV_AT91SAM9_GPBR, (val)) + __raw_writel((val), (rtc)->gpbr) /* * Read current time and date in RTC @@ -287,16 +288,19 @@ static const struct rtc_class_ops at91_rtc_ops = { /* * Initialize and install RTC driver */ -static int __init at91_rtc_probe(struct platform_device *pdev) +static int __devinit at91_rtc_probe(struct platform_device *pdev) { - struct resource *r; + struct resource *r, *r_gpbr; struct sam9_rtc *rtc; int ret; u32 mr; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) + r_gpbr = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!r || !r_gpbr) { + dev_err(&pdev->dev, "need 2 ressources\n"); return -ENODEV; + } rtc = kzalloc(sizeof *rtc, GFP_KERNEL); if (!rtc) @@ -314,6 +318,13 @@ static int __init at91_rtc_probe(struct platform_device *pdev) goto fail; } + rtc->gpbr = ioremap(r_gpbr->start, resource_size(r_gpbr)); + if (!rtc->gpbr) { + dev_err(&pdev->dev, "failed to map gpbr registers, aborting.\n"); + ret = -ENOMEM; + goto fail_gpbr; + } + mr = rtt_readl(rtc, MR); /* unless RTT is counting at 1 Hz, re-initialize it */ @@ -335,12 +346,12 @@ static int __init at91_rtc_probe(struct platform_device *pdev) /* register irq handler after we know what name we'll use */ ret = request_irq(AT91_ID_SYS, at91_rtc_interrupt, - IRQF_DISABLED | IRQF_SHARED, + IRQF_SHARED, dev_name(&rtc->rtcdev->dev), rtc); if (ret) { dev_dbg(&pdev->dev, "can't share IRQ %d?\n", AT91_ID_SYS); rtc_device_unregister(rtc->rtcdev); - goto fail; + goto fail_register; } /* NOTE: sam9260 rev A silicon has a ROM bug which resets the @@ -356,6 +367,8 @@ static int __init at91_rtc_probe(struct platform_device *pdev) return 0; fail_register: + iounmap(rtc->gpbr); +fail_gpbr: iounmap(rtc->rtt); fail: platform_set_drvdata(pdev, NULL); @@ -366,7 +379,7 @@ fail: /* * Disable and remove the RTC driver */ -static int __exit at91_rtc_remove(struct platform_device *pdev) +static int __devexit at91_rtc_remove(struct platform_device *pdev) { struct sam9_rtc *rtc = platform_get_drvdata(pdev); u32 mr = rtt_readl(rtc, MR); @@ -377,6 +390,7 @@ static int __exit at91_rtc_remove(struct platform_device *pdev) rtc_device_unregister(rtc->rtcdev); + iounmap(rtc->gpbr); iounmap(rtc->rtt); platform_set_drvdata(pdev, NULL); kfree(rtc); @@ -440,63 +454,20 @@ static int at91_rtc_resume(struct platform_device *pdev) #endif static struct platform_driver at91_rtc_driver = { - .driver.name = "rtc-at91sam9", - .driver.owner = THIS_MODULE, - .remove = __exit_p(at91_rtc_remove), + .probe = at91_rtc_probe, + .remove = __devexit_p(at91_rtc_remove), .shutdown = at91_rtc_shutdown, .suspend = at91_rtc_suspend, .resume = at91_rtc_resume, + .driver = { + .name = "rtc-at91sam9", + .owner = THIS_MODULE, + }, }; -/* Chips can have more than one RTT module, and they can be used for more - * than just RTCs. So we can't just register as "the" RTT driver. - * - * A normal approach in such cases is to create a library to allocate and - * free the modules. Here we just use bus_find_device() as like such a - * library, binding directly ... no runtime "library" footprint is needed. - */ -static int __init at91_rtc_match(struct device *dev, void *v) -{ - struct platform_device *pdev = to_platform_device(dev); - int ret; - - /* continue searching if this isn't the RTT we need */ - if (strcmp("at91_rtt", pdev->name) != 0 - || pdev->id != CONFIG_RTC_DRV_AT91SAM9_RTT) - goto fail; - - /* else we found it ... but fail unless we can bind to the RTC driver */ - if (dev->driver) { - dev_dbg(dev, "busy, can't use as RTC!\n"); - goto fail; - } - dev->driver = &at91_rtc_driver.driver; - if (device_attach(dev) == 0) { - dev_dbg(dev, "can't attach RTC!\n"); - goto fail; - } - ret = at91_rtc_probe(pdev); - if (ret == 0) - return true; - - dev_dbg(dev, "RTC probe err %d!\n", ret); -fail: - return false; -} - static int __init at91_rtc_init(void) { - int status; - struct device *rtc; - - status = platform_driver_register(&at91_rtc_driver); - if (status) - return status; - rtc = bus_find_device(&platform_bus_type, NULL, - NULL, at91_rtc_match); - if (!rtc) - platform_driver_unregister(&at91_rtc_driver); - return rtc ? 0 : -ENODEV; + return platform_driver_register(&at91_rtc_driver); } module_init(at91_rtc_init); diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c index 408cc8f735be..f090159dce4a 100644 --- a/drivers/rtc/rtc-bq32k.c +++ b/drivers/rtc/rtc-bq32k.c @@ -187,17 +187,7 @@ static struct i2c_driver bq32k_driver = { .id_table = bq32k_id, }; -static __init int bq32k_init(void) -{ - return i2c_add_driver(&bq32k_driver); -} -module_init(bq32k_init); - -static __exit void bq32k_exit(void) -{ - i2c_del_driver(&bq32k_driver); -} -module_exit(bq32k_exit); +module_i2c_driver(bq32k_driver); MODULE_AUTHOR("Semihalf, Piotr Ziecik <kosmo@semihalf.com>"); MODULE_DESCRIPTION("TI BQ32000 I2C RTC driver"); diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index d7782aa09943..7d5f56edb8ef 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -714,7 +714,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) rtc_cmos_int_handler = cmos_interrupt; retval = request_irq(rtc_irq, rtc_cmos_int_handler, - IRQF_DISABLED, dev_name(&cmos_rtc.rtc->dev), + 0, dev_name(&cmos_rtc.rtc->dev), cmos_rtc.rtc); if (retval < 0) { dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq); diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c index 80f9c88214c5..a5b8a0c4ea84 100644 --- a/drivers/rtc/rtc-coh901331.c +++ b/drivers/rtc/rtc-coh901331.c @@ -199,7 +199,7 @@ static int __init coh901331_probe(struct platform_device *pdev) } rtap->irq = platform_get_irq(pdev, 0); - if (request_irq(rtap->irq, coh901331_interrupt, IRQF_DISABLED, + if (request_irq(rtap->irq, coh901331_interrupt, 0, "RTC COH 901 331 Alarm", rtap)) { ret = -EIO; goto out_no_irq; diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c new file mode 100644 index 000000000000..da6ab5291a41 --- /dev/null +++ b/drivers/rtc/rtc-da9052.c @@ -0,0 +1,293 @@ +/* + * Real time clock driver for DA9052 + * + * Copyright(c) 2012 Dialog Semiconductor Ltd. + * + * Author: Dajun Dajun Chen <dajun.chen@diasemi.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> + +#include <linux/mfd/da9052/da9052.h> +#include <linux/mfd/da9052/reg.h> + +#define rtc_err(da9052, fmt, ...) \ + dev_err(da9052->dev, "%s: " fmt, __func__, ##__VA_ARGS__) + +struct da9052_rtc { + struct rtc_device *rtc; + struct da9052 *da9052; + int irq; +}; + +static int da9052_rtc_enable_alarm(struct da9052 *da9052, bool enable) +{ + int ret; + if (enable) { + ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG, + DA9052_ALARM_Y_ALARM_ON, + DA9052_ALARM_Y_ALARM_ON); + if (ret != 0) + rtc_err(da9052, "Failed to enable ALM: %d\n", ret); + } else { + ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG, + DA9052_ALARM_Y_ALARM_ON, 0); + if (ret != 0) + rtc_err(da9052, "Write error: %d\n", ret); + } + return ret; +} + +static irqreturn_t da9052_rtc_irq(int irq, void *data) +{ + struct da9052_rtc *rtc = data; + int ret; + + ret = da9052_reg_read(rtc->da9052, DA9052_ALARM_MI_REG); + if (ret < 0) { + rtc_err(rtc->da9052, "Read error: %d\n", ret); + return IRQ_NONE; + } + + if (ret & DA9052_ALARMMI_ALARMTYPE) { + da9052_rtc_enable_alarm(rtc->da9052, 0); + rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); + } else + rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_PF); + + return IRQ_HANDLED; +} + +static int da9052_read_alarm(struct da9052 *da9052, struct rtc_time *rtc_tm) +{ + int ret; + uint8_t v[5]; + + ret = da9052_group_read(da9052, DA9052_ALARM_MI_REG, 5, v); + if (ret != 0) { + rtc_err(da9052, "Failed to group read ALM: %d\n", ret); + return ret; + } + + rtc_tm->tm_year = (v[4] & DA9052_RTC_YEAR) + 100; + rtc_tm->tm_mon = (v[3] & DA9052_RTC_MONTH) - 1; + rtc_tm->tm_mday = v[2] & DA9052_RTC_DAY; + rtc_tm->tm_hour = v[1] & DA9052_RTC_HOUR; + rtc_tm->tm_min = v[0] & DA9052_RTC_MIN; + + ret = rtc_valid_tm(rtc_tm); + if (ret != 0) + return ret; + return ret; +} + +static int da9052_set_alarm(struct da9052 *da9052, struct rtc_time *rtc_tm) +{ + int ret; + uint8_t v[3]; + + rtc_tm->tm_year -= 100; + rtc_tm->tm_mon += 1; + + ret = da9052_reg_update(da9052, DA9052_ALARM_MI_REG, + DA9052_RTC_MIN, rtc_tm->tm_min); + if (ret != 0) { + rtc_err(da9052, "Failed to write ALRM MIN: %d\n", ret); + return ret; + } + + v[0] = rtc_tm->tm_hour; + v[1] = rtc_tm->tm_mday; + v[2] = rtc_tm->tm_mon; + + ret = da9052_group_write(da9052, DA9052_ALARM_H_REG, 3, v); + if (ret < 0) + return ret; + + ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG, + DA9052_RTC_YEAR, rtc_tm->tm_year); + if (ret != 0) + rtc_err(da9052, "Failed to write ALRM YEAR: %d\n", ret); + + return ret; +} + +static int da9052_rtc_get_alarm_status(struct da9052 *da9052) +{ + int ret; + + ret = da9052_reg_read(da9052, DA9052_ALARM_Y_REG); + if (ret < 0) { + rtc_err(da9052, "Failed to read ALM: %d\n", ret); + return ret; + } + ret &= DA9052_ALARM_Y_ALARM_ON; + return (ret > 0) ? 1 : 0; +} + +static int da9052_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm) +{ + struct da9052_rtc *rtc = dev_get_drvdata(dev); + uint8_t v[6]; + int ret; + + ret = da9052_group_read(rtc->da9052, DA9052_COUNT_S_REG, 6, v); + if (ret < 0) { + rtc_err(rtc->da9052, "Failed to read RTC time : %d\n", ret); + return ret; + } + + rtc_tm->tm_year = (v[5] & DA9052_RTC_YEAR) + 100; + rtc_tm->tm_mon = (v[4] & DA9052_RTC_MONTH) - 1; + rtc_tm->tm_mday = v[3] & DA9052_RTC_DAY; + rtc_tm->tm_hour = v[2] & DA9052_RTC_HOUR; + rtc_tm->tm_min = v[1] & DA9052_RTC_MIN; + rtc_tm->tm_sec = v[0] & DA9052_RTC_SEC; + + ret = rtc_valid_tm(rtc_tm); + if (ret != 0) { + rtc_err(rtc->da9052, "rtc_valid_tm failed: %d\n", ret); + return ret; + } + + return 0; +} + +static int da9052_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct da9052_rtc *rtc; + uint8_t v[6]; + + rtc = dev_get_drvdata(dev); + + v[0] = tm->tm_sec; + v[1] = tm->tm_min; + v[2] = tm->tm_hour; + v[3] = tm->tm_mday; + v[4] = tm->tm_mon + 1; + v[5] = tm->tm_year - 100; + + return da9052_group_write(rtc->da9052, DA9052_COUNT_S_REG, 6, v); +} + +static int da9052_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + int ret; + struct rtc_time *tm = &alrm->time; + struct da9052_rtc *rtc = dev_get_drvdata(dev); + + ret = da9052_read_alarm(rtc->da9052, tm); + + if (ret) + return ret; + + alrm->enabled = da9052_rtc_get_alarm_status(rtc->da9052); + + return 0; +} + +static int da9052_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + int ret; + struct rtc_time *tm = &alrm->time; + struct da9052_rtc *rtc = dev_get_drvdata(dev); + + ret = da9052_rtc_enable_alarm(rtc->da9052, 0); + if (ret < 0) + return ret; + + ret = da9052_set_alarm(rtc->da9052, tm); + if (ret) + return ret; + + ret = da9052_rtc_enable_alarm(rtc->da9052, 1); + + return ret; +} + +static int da9052_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct da9052_rtc *rtc = dev_get_drvdata(dev); + + return da9052_rtc_enable_alarm(rtc->da9052, enabled); +} + +static const struct rtc_class_ops da9052_rtc_ops = { + .read_time = da9052_rtc_read_time, + .set_time = da9052_rtc_set_time, + .read_alarm = da9052_rtc_read_alarm, + .set_alarm = da9052_rtc_set_alarm, + .alarm_irq_enable = da9052_rtc_alarm_irq_enable, +}; + +static int __devinit da9052_rtc_probe(struct platform_device *pdev) +{ + struct da9052_rtc *rtc; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(struct da9052_rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->da9052 = dev_get_drvdata(pdev->dev.parent); + platform_set_drvdata(pdev, rtc); + rtc->irq = platform_get_irq_byname(pdev, "ALM"); + ret = request_threaded_irq(rtc->irq, NULL, da9052_rtc_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "ALM", rtc); + if (ret != 0) { + rtc_err(rtc->da9052, "irq registration failed: %d\n", ret); + goto err_mem; + } + + rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, + &da9052_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc->rtc)) { + ret = PTR_ERR(rtc->rtc); + goto err_free_irq; + } + + return 0; + +err_free_irq: + free_irq(rtc->irq, rtc); +err_mem: + devm_kfree(&pdev->dev, rtc); + return ret; +} + +static int __devexit da9052_rtc_remove(struct platform_device *pdev) +{ + struct da9052_rtc *rtc = pdev->dev.platform_data; + + rtc_device_unregister(rtc->rtc); + free_irq(rtc->irq, rtc); + platform_set_drvdata(pdev, NULL); + devm_kfree(&pdev->dev, rtc); + + return 0; +} + +static struct platform_driver da9052_rtc_driver = { + .probe = da9052_rtc_probe, + .remove = __devexit_p(da9052_rtc_remove), + .driver = { + .name = "da9052-rtc", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(da9052_rtc_driver); + +MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); +MODULE_DESCRIPTION("RTC driver for Dialog DA9052 PMIC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9052-rtc"); diff --git a/drivers/rtc/rtc-davinci.c b/drivers/rtc/rtc-davinci.c index 755e1fe914af..14c2109dbaa3 100644 --- a/drivers/rtc/rtc-davinci.c +++ b/drivers/rtc/rtc-davinci.c @@ -542,7 +542,7 @@ static int __init davinci_rtc_probe(struct platform_device *pdev) rtcss_write(davinci_rtc, 0, PRTCSS_RTC_CCTRL); ret = request_irq(davinci_rtc->irq, davinci_rtc_interrupt, - IRQF_DISABLED, "davinci_rtc", davinci_rtc); + 0, "davinci_rtc", davinci_rtc); if (ret < 0) { dev_err(dev, "unable to register davinci RTC interrupt\n"); goto fail4; diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c index 3a33b1fdbe0f..686a865913e1 100644 --- a/drivers/rtc/rtc-ds1305.c +++ b/drivers/rtc/rtc-ds1305.c @@ -814,17 +814,7 @@ static struct spi_driver ds1305_driver = { /* REVISIT add suspend/resume */ }; -static int __init ds1305_init(void) -{ - return spi_register_driver(&ds1305_driver); -} -module_init(ds1305_init); - -static void __exit ds1305_exit(void) -{ - spi_unregister_driver(&ds1305_driver); -} -module_exit(ds1305_exit); +module_spi_driver(ds1305_driver); MODULE_DESCRIPTION("RTC driver for DS1305 and DS1306 chips"); MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 62b0763b7b9a..cd188ab72f79 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -20,7 +20,8 @@ -/* We can't determine type by probing, but if we expect pre-Linux code +/* + * We can't determine type by probing, but if we expect pre-Linux code * to have set the chip up as a clock (turning on the oscillator and * setting the date and time), Linux can ignore the non-clock features. * That's a natural job for a factory or repair bench. @@ -36,7 +37,8 @@ enum ds_type { m41t00, mcp7941x, rx_8025, - // rs5c372 too? different address... + last_ds_type /* always last */ + /* rs5c372 too? different address... */ }; @@ -58,7 +60,8 @@ enum ds_type { # define DS1337_BIT_CENTURY 0x80 /* in REG_MONTH */ #define DS1307_REG_YEAR 0x06 /* 00-99 */ -/* Other registers (control, status, alarms, trickle charge, NVRAM, etc) +/* + * Other registers (control, status, alarms, trickle charge, NVRAM, etc) * start at 7, and they differ a LOT. Only control and status matter for * basic RTC date and time functionality; be careful using them. */ @@ -102,6 +105,8 @@ enum ds_type { struct ds1307 { u8 offset; /* register's offset */ u8 regs[11]; + u16 nvram_offset; + struct bin_attribute *nvram; enum ds_type type; unsigned long flags; #define HAS_NVRAM 0 /* bit 0 == sysfs file active */ @@ -116,34 +121,35 @@ struct ds1307 { }; struct chip_desc { - unsigned nvram56:1; unsigned alarm:1; + u16 nvram_offset; + u16 nvram_size; }; -static const struct chip_desc chips[] = { -[ds_1307] = { - .nvram56 = 1, -}, -[ds_1337] = { - .alarm = 1, -}, -[ds_1338] = { - .nvram56 = 1, -}, -[ds_1339] = { - .alarm = 1, -}, -[ds_1340] = { -}, -[ds_3231] = { - .alarm = 1, -}, -[m41t00] = { -}, -[mcp7941x] = { -}, -[rx_8025] = { -}, }; +static const struct chip_desc chips[last_ds_type] = { + [ds_1307] = { + .nvram_offset = 8, + .nvram_size = 56, + }, + [ds_1337] = { + .alarm = 1, + }, + [ds_1338] = { + .nvram_offset = 8, + .nvram_size = 56, + }, + [ds_1339] = { + .alarm = 1, + }, + [ds_3231] = { + .alarm = 1, + }, + [mcp7941x] = { + /* this is battery backed SRAM */ + .nvram_offset = 0x20, + .nvram_size = 0x40, + }, +}; static const struct i2c_device_id ds1307_id[] = { { "ds1307", ds_1307 }, @@ -372,6 +378,11 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) | DS1340_BIT_CENTURY; break; case mcp7941x: + /* + * these bits were cleared when preparing the date/time + * values and need to be set again before writing the + * buffer out to the device. + */ buf[DS1307_REG_SECS] |= MCP7941X_BIT_ST; buf[DS1307_REG_WDAY] |= MCP7941X_BIT_VBATEN; break; @@ -417,7 +428,8 @@ static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t) ds1307->regs[6], ds1307->regs[7], ds1307->regs[8]); - /* report alarm time (ALARM1); assume 24 hour and day-of-month modes, + /* + * report alarm time (ALARM1); assume 24 hour and day-of-month modes, * and that all four fields are checked matches */ t->time.tm_sec = bcd2bin(ds1307->regs[0] & 0x7f); @@ -445,7 +457,7 @@ static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t) static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) { - struct i2c_client *client = to_i2c_client(dev); + struct i2c_client *client = to_i2c_client(dev); struct ds1307 *ds1307 = i2c_get_clientdata(client); unsigned char *buf = ds1307->regs; u8 control, status; @@ -541,8 +553,6 @@ static const struct rtc_class_ops ds13xx_rtc_ops = { /*----------------------------------------------------------------------*/ -#define NVRAM_SIZE 56 - static ssize_t ds1307_nvram_read(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, @@ -555,14 +565,15 @@ ds1307_nvram_read(struct file *filp, struct kobject *kobj, client = kobj_to_i2c_client(kobj); ds1307 = i2c_get_clientdata(client); - if (unlikely(off >= NVRAM_SIZE)) + if (unlikely(off >= ds1307->nvram->size)) return 0; - if ((off + count) > NVRAM_SIZE) - count = NVRAM_SIZE - off; + if ((off + count) > ds1307->nvram->size) + count = ds1307->nvram->size - off; if (unlikely(!count)) return count; - result = ds1307->read_block_data(client, 8 + off, count, buf); + result = ds1307->read_block_data(client, ds1307->nvram_offset + off, + count, buf); if (result < 0) dev_err(&client->dev, "%s error %d\n", "nvram read", result); return result; @@ -580,14 +591,15 @@ ds1307_nvram_write(struct file *filp, struct kobject *kobj, client = kobj_to_i2c_client(kobj); ds1307 = i2c_get_clientdata(client); - if (unlikely(off >= NVRAM_SIZE)) + if (unlikely(off >= ds1307->nvram->size)) return -EFBIG; - if ((off + count) > NVRAM_SIZE) - count = NVRAM_SIZE - off; + if ((off + count) > ds1307->nvram->size) + count = ds1307->nvram->size - off; if (unlikely(!count)) return count; - result = ds1307->write_block_data(client, 8 + off, count, buf); + result = ds1307->write_block_data(client, ds1307->nvram_offset + off, + count, buf); if (result < 0) { dev_err(&client->dev, "%s error %d\n", "nvram write", result); return result; @@ -595,21 +607,8 @@ ds1307_nvram_write(struct file *filp, struct kobject *kobj, return count; } -static struct bin_attribute nvram = { - .attr = { - .name = "nvram", - .mode = S_IRUGO | S_IWUSR, - }, - - .read = ds1307_nvram_read, - .write = ds1307_nvram_write, - .size = NVRAM_SIZE, -}; - /*----------------------------------------------------------------------*/ -static struct i2c_driver ds1307_driver; - static int __devinit ds1307_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -630,7 +629,8 @@ static int __devinit ds1307_probe(struct i2c_client *client, && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) return -EIO; - if (!(ds1307 = kzalloc(sizeof(struct ds1307), GFP_KERNEL))) + ds1307 = kzalloc(sizeof(struct ds1307), GFP_KERNEL); + if (!ds1307) return -ENOMEM; i2c_set_clientdata(client, ds1307); @@ -652,11 +652,6 @@ static int __devinit ds1307_probe(struct i2c_client *client, case ds_1337: case ds_1339: case ds_3231: - /* has IRQ? */ - if (ds1307->client->irq > 0 && chip->alarm) { - INIT_WORK(&ds1307->work, ds1307_work); - want_irq = true; - } /* get registers that the "rtc" read below won't read... */ tmp = ds1307->read_block_data(ds1307->client, DS1337_REG_CONTROL, 2, buf); @@ -670,14 +665,19 @@ static int __devinit ds1307_probe(struct i2c_client *client, if (ds1307->regs[0] & DS1337_BIT_nEOSC) ds1307->regs[0] &= ~DS1337_BIT_nEOSC; - /* Using IRQ? Disable the square wave and both alarms. + /* + * Using IRQ? Disable the square wave and both alarms. * For some variants, be sure alarms can trigger when we're * running on Vbackup (BBSQI/BBSQW) */ - if (want_irq) { + if (ds1307->client->irq > 0 && chip->alarm) { + INIT_WORK(&ds1307->work, ds1307_work); + ds1307->regs[0] |= DS1337_BIT_INTCN | bbsqi_bitpos[ds1307->type]; ds1307->regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE); + + want_irq = true; } i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, @@ -772,7 +772,8 @@ read_rtc: goto exit_free; } - /* minimal sanity checking; some chips (like DS1340) don't + /* + * minimal sanity checking; some chips (like DS1340) don't * specify the extra bits as must-be-zero, but there are * still a few values that are clearly out-of-range. */ @@ -836,11 +837,7 @@ read_rtc: } break; - case rx_8025: - case ds_1337: - case ds_1339: - case ds_1388: - case ds_3231: + default: break; } @@ -848,7 +845,8 @@ read_rtc: switch (ds1307->type) { case ds_1340: case m41t00: - /* NOTE: ignores century bits; fix before deploying + /* + * NOTE: ignores century bits; fix before deploying * systems that will run through year 2100. */ break; @@ -858,7 +856,8 @@ read_rtc: if (!(tmp & DS1307_BIT_12HR)) break; - /* Be sure we're in 24 hour mode. Multi-master systems + /* + * Be sure we're in 24 hour mode. Multi-master systems * take note... */ tmp = bcd2bin(tmp & 0x1f); @@ -894,16 +893,31 @@ read_rtc: dev_dbg(&client->dev, "got IRQ %d\n", client->irq); } - if (chip->nvram56) { - err = sysfs_create_bin_file(&client->dev.kobj, &nvram); - if (err == 0) { - set_bit(HAS_NVRAM, &ds1307->flags); - dev_info(&client->dev, "56 bytes nvram\n"); + if (chip->nvram_size) { + ds1307->nvram = kzalloc(sizeof(struct bin_attribute), + GFP_KERNEL); + if (!ds1307->nvram) { + err = -ENOMEM; + goto exit_nvram; + } + ds1307->nvram->attr.name = "nvram"; + ds1307->nvram->attr.mode = S_IRUGO | S_IWUSR; + ds1307->nvram->read = ds1307_nvram_read, + ds1307->nvram->write = ds1307_nvram_write, + ds1307->nvram->size = chip->nvram_size; + ds1307->nvram_offset = chip->nvram_offset; + err = sysfs_create_bin_file(&client->dev.kobj, ds1307->nvram); + if (err) { + kfree(ds1307->nvram); + goto exit_nvram; } + set_bit(HAS_NVRAM, &ds1307->flags); + dev_info(&client->dev, "%zu bytes nvram\n", ds1307->nvram->size); } return 0; +exit_nvram: exit_irq: rtc_device_unregister(ds1307->rtc); exit_free: @@ -913,15 +927,17 @@ exit_free: static int __devexit ds1307_remove(struct i2c_client *client) { - struct ds1307 *ds1307 = i2c_get_clientdata(client); + struct ds1307 *ds1307 = i2c_get_clientdata(client); if (test_and_clear_bit(HAS_ALARM, &ds1307->flags)) { free_irq(client->irq, client); cancel_work_sync(&ds1307->work); } - if (test_and_clear_bit(HAS_NVRAM, &ds1307->flags)) - sysfs_remove_bin_file(&client->dev.kobj, &nvram); + if (test_and_clear_bit(HAS_NVRAM, &ds1307->flags)) { + sysfs_remove_bin_file(&client->dev.kobj, ds1307->nvram); + kfree(ds1307->nvram); + } rtc_device_unregister(ds1307->rtc); kfree(ds1307); @@ -938,17 +954,7 @@ static struct i2c_driver ds1307_driver = { .id_table = ds1307_id, }; -static int __init ds1307_init(void) -{ - return i2c_add_driver(&ds1307_driver); -} -module_init(ds1307_init); - -static void __exit ds1307_exit(void) -{ - i2c_del_driver(&ds1307_driver); -} -module_exit(ds1307_exit); +module_i2c_driver(ds1307_driver); MODULE_DESCRIPTION("RTC driver for DS1307 and similar chips"); MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c index e6e71deb188f..966316088b7f 100644 --- a/drivers/rtc/rtc-ds1374.c +++ b/drivers/rtc/rtc-ds1374.c @@ -446,18 +446,7 @@ static struct i2c_driver ds1374_driver = { .id_table = ds1374_id, }; -static int __init ds1374_init(void) -{ - return i2c_add_driver(&ds1374_driver); -} - -static void __exit ds1374_exit(void) -{ - i2c_del_driver(&ds1374_driver); -} - -module_init(ds1374_init); -module_exit(ds1374_exit); +module_i2c_driver(ds1374_driver); MODULE_AUTHOR("Scott Wood <scottwood@freescale.com>"); MODULE_DESCRIPTION("Maxim/Dallas DS1374 RTC Driver"); diff --git a/drivers/rtc/rtc-ds1390.c b/drivers/rtc/rtc-ds1390.c index b038d2cfef26..b0a99e1b25be 100644 --- a/drivers/rtc/rtc-ds1390.c +++ b/drivers/rtc/rtc-ds1390.c @@ -175,17 +175,7 @@ static struct spi_driver ds1390_driver = { .remove = __devexit_p(ds1390_remove), }; -static __init int ds1390_init(void) -{ - return spi_register_driver(&ds1390_driver); -} -module_init(ds1390_init); - -static __exit void ds1390_exit(void) -{ - spi_unregister_driver(&ds1390_driver); -} -module_exit(ds1390_exit); +module_spi_driver(ds1390_driver); MODULE_DESCRIPTION("Dallas/Maxim DS1390/93/94 SPI RTC driver"); MODULE_AUTHOR("Mark Jackson <mpfj@mimc.co.uk>"); diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c index 761f36bc83a9..1f675f5294f5 100644 --- a/drivers/rtc/rtc-ds1511.c +++ b/drivers/rtc/rtc-ds1511.c @@ -532,7 +532,7 @@ ds1511_rtc_probe(struct platform_device *pdev) if (pdata->irq > 0) { rtc_read(RTC_CMD1); if (devm_request_irq(&pdev->dev, pdata->irq, ds1511_interrupt, - IRQF_DISABLED | IRQF_SHARED, pdev->name, pdev) < 0) { + IRQF_SHARED, pdev->name, pdev) < 0) { dev_warn(&pdev->dev, "interrupt not available.\n"); pdata->irq = 0; diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c index 6f0a1b530f2e..6ccedbbf923c 100644 --- a/drivers/rtc/rtc-ds1553.c +++ b/drivers/rtc/rtc-ds1553.c @@ -320,7 +320,7 @@ static int __devinit ds1553_rtc_probe(struct platform_device *pdev) writeb(0, ioaddr + RTC_INTERRUPTS); if (devm_request_irq(&pdev->dev, pdata->irq, ds1553_rtc_interrupt, - IRQF_DISABLED, pdev->name, pdev) < 0) { + 0, pdev->name, pdev) < 0) { dev_warn(&pdev->dev, "interrupt not available.\n"); pdata->irq = 0; } diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c index a319402a5447..7fa67d0df172 100644 --- a/drivers/rtc/rtc-ds1672.c +++ b/drivers/rtc/rtc-ds1672.c @@ -202,20 +202,9 @@ static struct i2c_driver ds1672_driver = { .id_table = ds1672_id, }; -static int __init ds1672_init(void) -{ - return i2c_add_driver(&ds1672_driver); -} - -static void __exit ds1672_exit(void) -{ - i2c_del_driver(&ds1672_driver); -} +module_i2c_driver(ds1672_driver); MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); MODULE_DESCRIPTION("Dallas/Maxim DS1672 timekeeper driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); - -module_init(ds1672_init); -module_exit(ds1672_exit); diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c index 27b7bf672ac6..e1945095814e 100644 --- a/drivers/rtc/rtc-ds3232.c +++ b/drivers/rtc/rtc-ds3232.c @@ -473,18 +473,7 @@ static struct i2c_driver ds3232_driver = { .id_table = ds3232_id, }; -static int __init ds3232_init(void) -{ - return i2c_add_driver(&ds3232_driver); -} - -static void __exit ds3232_exit(void) -{ - i2c_del_driver(&ds3232_driver); -} - -module_init(ds3232_init); -module_exit(ds3232_exit); +module_i2c_driver(ds3232_driver); MODULE_AUTHOR("Srikanth Srinivasan <srikanth.srinivasan@freescale.com>"); MODULE_DESCRIPTION("Maxim/Dallas DS3232 RTC Driver"); diff --git a/drivers/rtc/rtc-ds3234.c b/drivers/rtc/rtc-ds3234.c index bbd26228f532..fda707926f02 100644 --- a/drivers/rtc/rtc-ds3234.c +++ b/drivers/rtc/rtc-ds3234.c @@ -173,17 +173,7 @@ static struct spi_driver ds3234_driver = { .remove = __devexit_p(ds3234_remove), }; -static __init int ds3234_init(void) -{ - return spi_register_driver(&ds3234_driver); -} -module_init(ds3234_init); - -static __exit void ds3234_exit(void) -{ - spi_unregister_driver(&ds3234_driver); -} -module_exit(ds3234_exit); +module_spi_driver(ds3234_driver); MODULE_DESCRIPTION("DS3234 SPI RTC driver"); MODULE_AUTHOR("Dennis Aberilla <denzzzhome@yahoo.com>"); diff --git a/drivers/rtc/rtc-em3027.c b/drivers/rtc/rtc-em3027.c index 8414dea5fb14..0104ea7ebe50 100644 --- a/drivers/rtc/rtc-em3027.c +++ b/drivers/rtc/rtc-em3027.c @@ -144,19 +144,8 @@ static struct i2c_driver em3027_driver = { .id_table = em3027_id, }; -static int __init em3027_init(void) -{ - return i2c_add_driver(&em3027_driver); -} - -static void __exit em3027_exit(void) -{ - i2c_del_driver(&em3027_driver); -} +module_i2c_driver(em3027_driver); MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); MODULE_DESCRIPTION("EM Microelectronic EM3027 RTC driver"); MODULE_LICENSE("GPL"); - -module_init(em3027_init); -module_exit(em3027_exit); diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c index 4cf2e70c5078..86b6ecce99f0 100644 --- a/drivers/rtc/rtc-fm3130.c +++ b/drivers/rtc/rtc-fm3130.c @@ -565,17 +565,7 @@ static struct i2c_driver fm3130_driver = { .id_table = fm3130_id, }; -static int __init fm3130_init(void) -{ - return i2c_add_driver(&fm3130_driver); -} -module_init(fm3130_init); - -static void __exit fm3130_exit(void) -{ - i2c_del_driver(&fm3130_driver); -} -module_exit(fm3130_exit); +module_i2c_driver(fm3130_driver); MODULE_DESCRIPTION("RTC driver for FM3130"); MODULE_AUTHOR("Sergey Lapin <slapin@ossfans.org>"); diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c index 6186833973ee..1850104705c0 100644 --- a/drivers/rtc/rtc-isl12022.c +++ b/drivers/rtc/rtc-isl12022.c @@ -309,18 +309,7 @@ static struct i2c_driver isl12022_driver = { .id_table = isl12022_id, }; -static int __init isl12022_init(void) -{ - return i2c_add_driver(&isl12022_driver); -} - -static void __exit isl12022_exit(void) -{ - i2c_del_driver(&isl12022_driver); -} - -module_init(isl12022_init); -module_exit(isl12022_exit); +module_i2c_driver(isl12022_driver); MODULE_AUTHOR("roman.fietze@telemotive.de"); MODULE_DESCRIPTION("ISL 12022 RTC driver"); diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index da8beb8cae51..dd2aeee6c66a 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -710,22 +710,9 @@ static struct i2c_driver isl1208_driver = { .id_table = isl1208_id, }; -static int __init -isl1208_init(void) -{ - return i2c_add_driver(&isl1208_driver); -} - -static void __exit -isl1208_exit(void) -{ - i2c_del_driver(&isl1208_driver); -} +module_i2c_driver(isl1208_driver); MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>"); MODULE_DESCRIPTION("Intersil ISL1208 RTC driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); - -module_init(isl1208_init); -module_exit(isl1208_exit); diff --git a/drivers/rtc/rtc-lpc32xx.c b/drivers/rtc/rtc-lpc32xx.c index ecc1713b2b4f..63c72189c64b 100644 --- a/drivers/rtc/rtc-lpc32xx.c +++ b/drivers/rtc/rtc-lpc32xx.c @@ -287,7 +287,7 @@ static int __devinit lpc32xx_rtc_probe(struct platform_device *pdev) if (rtc->irq >= 0) { if (devm_request_irq(&pdev->dev, rtc->irq, lpc32xx_rtc_alarm_interrupt, - IRQF_DISABLED, pdev->name, rtc) < 0) { + 0, pdev->name, rtc) < 0) { dev_warn(&pdev->dev, "Can't request interrupt.\n"); rtc->irq = -1; } else { diff --git a/drivers/rtc/rtc-ls1x.c b/drivers/rtc/rtc-ls1x.c new file mode 100644 index 000000000000..07e81c5f8247 --- /dev/null +++ b/drivers/rtc/rtc-ls1x.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2011 Zhao Zhang <zhzhl555@gmail.com> + * + * Derived from driver/rtc/rtc-au1xxx.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/rtc.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/types.h> +#include <linux/io.h> +#include <asm/mach-loongson1/loongson1.h> + +#define LS1X_RTC_REG_OFFSET (LS1X_RTC_BASE + 0x20) +#define LS1X_RTC_REGS(x) \ + ((void __iomem *)KSEG1ADDR(LS1X_RTC_REG_OFFSET + (x))) + +/*RTC programmable counters 0 and 1*/ +#define SYS_COUNTER_CNTRL (LS1X_RTC_REGS(0x20)) +#define SYS_CNTRL_ERS (1 << 23) +#define SYS_CNTRL_RTS (1 << 20) +#define SYS_CNTRL_RM2 (1 << 19) +#define SYS_CNTRL_RM1 (1 << 18) +#define SYS_CNTRL_RM0 (1 << 17) +#define SYS_CNTRL_RS (1 << 16) +#define SYS_CNTRL_BP (1 << 14) +#define SYS_CNTRL_REN (1 << 13) +#define SYS_CNTRL_BRT (1 << 12) +#define SYS_CNTRL_TEN (1 << 11) +#define SYS_CNTRL_BTT (1 << 10) +#define SYS_CNTRL_E0 (1 << 8) +#define SYS_CNTRL_ETS (1 << 7) +#define SYS_CNTRL_32S (1 << 5) +#define SYS_CNTRL_TTS (1 << 4) +#define SYS_CNTRL_TM2 (1 << 3) +#define SYS_CNTRL_TM1 (1 << 2) +#define SYS_CNTRL_TM0 (1 << 1) +#define SYS_CNTRL_TS (1 << 0) + +/* Programmable Counter 0 Registers */ +#define SYS_TOYTRIM (LS1X_RTC_REGS(0)) +#define SYS_TOYWRITE0 (LS1X_RTC_REGS(4)) +#define SYS_TOYWRITE1 (LS1X_RTC_REGS(8)) +#define SYS_TOYREAD0 (LS1X_RTC_REGS(0xC)) +#define SYS_TOYREAD1 (LS1X_RTC_REGS(0x10)) +#define SYS_TOYMATCH0 (LS1X_RTC_REGS(0x14)) +#define SYS_TOYMATCH1 (LS1X_RTC_REGS(0x18)) +#define SYS_TOYMATCH2 (LS1X_RTC_REGS(0x1C)) + +/* Programmable Counter 1 Registers */ +#define SYS_RTCTRIM (LS1X_RTC_REGS(0x40)) +#define SYS_RTCWRITE0 (LS1X_RTC_REGS(0x44)) +#define SYS_RTCREAD0 (LS1X_RTC_REGS(0x48)) +#define SYS_RTCMATCH0 (LS1X_RTC_REGS(0x4C)) +#define SYS_RTCMATCH1 (LS1X_RTC_REGS(0x50)) +#define SYS_RTCMATCH2 (LS1X_RTC_REGS(0x54)) + +#define LS1X_SEC_OFFSET (4) +#define LS1X_MIN_OFFSET (10) +#define LS1X_HOUR_OFFSET (16) +#define LS1X_DAY_OFFSET (21) +#define LS1X_MONTH_OFFSET (26) + + +#define LS1X_SEC_MASK (0x3f) +#define LS1X_MIN_MASK (0x3f) +#define LS1X_HOUR_MASK (0x1f) +#define LS1X_DAY_MASK (0x1f) +#define LS1X_MONTH_MASK (0x3f) +#define LS1X_YEAR_MASK (0xffffffff) + +#define ls1x_get_sec(t) (((t) >> LS1X_SEC_OFFSET) & LS1X_SEC_MASK) +#define ls1x_get_min(t) (((t) >> LS1X_MIN_OFFSET) & LS1X_MIN_MASK) +#define ls1x_get_hour(t) (((t) >> LS1X_HOUR_OFFSET) & LS1X_HOUR_MASK) +#define ls1x_get_day(t) (((t) >> LS1X_DAY_OFFSET) & LS1X_DAY_MASK) +#define ls1x_get_month(t) (((t) >> LS1X_MONTH_OFFSET) & LS1X_MONTH_MASK) + +#define RTC_CNTR_OK (SYS_CNTRL_E0 | SYS_CNTRL_32S) + +static int ls1x_rtc_read_time(struct device *dev, struct rtc_time *rtm) +{ + unsigned long v, t; + + v = readl(SYS_TOYREAD0); + t = readl(SYS_TOYREAD1); + + memset(rtm, 0, sizeof(struct rtc_time)); + t = mktime((t & LS1X_YEAR_MASK), ls1x_get_month(v), + ls1x_get_day(v), ls1x_get_hour(v), + ls1x_get_min(v), ls1x_get_sec(v)); + rtc_time_to_tm(t, rtm); + + return rtc_valid_tm(rtm); +} + +static int ls1x_rtc_set_time(struct device *dev, struct rtc_time *rtm) +{ + unsigned long v, t, c; + int ret = -ETIMEDOUT; + + v = ((rtm->tm_mon + 1) << LS1X_MONTH_OFFSET) + | (rtm->tm_mday << LS1X_DAY_OFFSET) + | (rtm->tm_hour << LS1X_HOUR_OFFSET) + | (rtm->tm_min << LS1X_MIN_OFFSET) + | (rtm->tm_sec << LS1X_SEC_OFFSET); + + writel(v, SYS_TOYWRITE0); + c = 0x10000; + /* add timeout check counter, for more safe */ + while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TS) && --c) + usleep_range(1000, 3000); + + if (!c) { + dev_err(dev, "set time timeout!\n"); + goto err; + } + + t = rtm->tm_year + 1900; + writel(t, SYS_TOYWRITE1); + c = 0x10000; + while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TS) && --c) + usleep_range(1000, 3000); + + if (!c) { + dev_err(dev, "set time timeout!\n"); + goto err; + } + return 0; +err: + return ret; +} + +static struct rtc_class_ops ls1x_rtc_ops = { + .read_time = ls1x_rtc_read_time, + .set_time = ls1x_rtc_set_time, +}; + +static int __devinit ls1x_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtcdev; + unsigned long v; + int ret; + + v = readl(SYS_COUNTER_CNTRL); + if (!(v & RTC_CNTR_OK)) { + dev_err(&pdev->dev, "rtc counters not working\n"); + ret = -ENODEV; + goto err; + } + ret = -ETIMEDOUT; + /* set to 1 HZ if needed */ + if (readl(SYS_TOYTRIM) != 32767) { + v = 0x100000; + while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS) && --v) + usleep_range(1000, 3000); + + if (!v) { + dev_err(&pdev->dev, "time out\n"); + goto err; + } + writel(32767, SYS_TOYTRIM); + } + /* this loop coundn't be endless */ + while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS) + usleep_range(1000, 3000); + + rtcdev = rtc_device_register("ls1x-rtc", &pdev->dev, + &ls1x_rtc_ops , THIS_MODULE); + if (IS_ERR(rtcdev)) { + ret = PTR_ERR(rtcdev); + goto err; + } + + platform_set_drvdata(pdev, rtcdev); + return 0; +err: + return ret; +} + +static int __devexit ls1x_rtc_remove(struct platform_device *pdev) +{ + struct rtc_device *rtcdev = platform_get_drvdata(pdev); + + rtc_device_unregister(rtcdev); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver ls1x_rtc_driver = { + .driver = { + .name = "ls1x-rtc", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(ls1x_rtc_remove), + .probe = ls1x_rtc_probe, +}; + +module_platform_driver(ls1x_rtc_driver); + +MODULE_AUTHOR("zhao zhang <zhzhl555@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 64aedd8cc095..4e0f84af99a7 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -900,20 +900,9 @@ static struct i2c_driver m41t80_driver = { .id_table = m41t80_id, }; -static int __init m41t80_rtc_init(void) -{ - return i2c_add_driver(&m41t80_driver); -} - -static void __exit m41t80_rtc_exit(void) -{ - i2c_del_driver(&m41t80_driver); -} +module_i2c_driver(m41t80_driver); MODULE_AUTHOR("Alexander Bigga <ab@mycable.de>"); MODULE_DESCRIPTION("ST Microelectronics M41T80 series RTC I2C Client Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); - -module_init(m41t80_rtc_init); -module_exit(m41t80_rtc_exit); diff --git a/drivers/rtc/rtc-m41t93.c b/drivers/rtc/rtc-m41t93.c index ef71132ff205..10f1c29436ec 100644 --- a/drivers/rtc/rtc-m41t93.c +++ b/drivers/rtc/rtc-m41t93.c @@ -206,17 +206,7 @@ static struct spi_driver m41t93_driver = { .remove = __devexit_p(m41t93_remove), }; -static __init int m41t93_init(void) -{ - return spi_register_driver(&m41t93_driver); -} -module_init(m41t93_init); - -static __exit void m41t93_exit(void) -{ - spi_unregister_driver(&m41t93_driver); -} -module_exit(m41t93_exit); +module_spi_driver(m41t93_driver); MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>"); MODULE_DESCRIPTION("Driver for ST M41T93 SPI RTC"); diff --git a/drivers/rtc/rtc-m41t94.c b/drivers/rtc/rtc-m41t94.c index 2a4721f61797..6e78193e026b 100644 --- a/drivers/rtc/rtc-m41t94.c +++ b/drivers/rtc/rtc-m41t94.c @@ -153,19 +153,7 @@ static struct spi_driver m41t94_driver = { .remove = __devexit_p(m41t94_remove), }; -static __init int m41t94_init(void) -{ - return spi_register_driver(&m41t94_driver); -} - -module_init(m41t94_init); - -static __exit void m41t94_exit(void) -{ - spi_unregister_driver(&m41t94_driver); -} - -module_exit(m41t94_exit); +module_spi_driver(m41t94_driver); MODULE_AUTHOR("Kim B. Heino <Kim.Heino@bluegiga.com>"); MODULE_DESCRIPTION("Driver for ST M41T94 SPI RTC"); diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c index 486142c2637a..a00e33204b91 100644 --- a/drivers/rtc/rtc-max6900.c +++ b/drivers/rtc/rtc-max6900.c @@ -261,20 +261,9 @@ static struct i2c_driver max6900_driver = { .id_table = max6900_id, }; -static int __init max6900_init(void) -{ - return i2c_add_driver(&max6900_driver); -} - -static void __exit max6900_exit(void) -{ - i2c_del_driver(&max6900_driver); -} +module_i2c_driver(max6900_driver); MODULE_DESCRIPTION("Maxim MAX6900 RTC driver"); MODULE_AUTHOR("Dale Farnsworth <dale@farnsworth.org>"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); - -module_init(max6900_init); -module_exit(max6900_exit); diff --git a/drivers/rtc/rtc-max6902.c b/drivers/rtc/rtc-max6902.c index 1f6b3cc58e8a..36c74d22e8b5 100644 --- a/drivers/rtc/rtc-max6902.c +++ b/drivers/rtc/rtc-max6902.c @@ -160,17 +160,7 @@ static struct spi_driver max6902_driver = { .remove = __devexit_p(max6902_remove), }; -static __init int max6902_init(void) -{ - return spi_register_driver(&max6902_driver); -} -module_init(max6902_init); - -static __exit void max6902_exit(void) -{ - spi_unregister_driver(&max6902_driver); -} -module_exit(max6902_exit); +module_spi_driver(max6902_driver); MODULE_DESCRIPTION ("max6902 spi RTC driver"); MODULE_AUTHOR ("Raphael Assenat"); diff --git a/drivers/rtc/rtc-max8925.c b/drivers/rtc/rtc-max8925.c index 2d71943bc436..1459055a83aa 100644 --- a/drivers/rtc/rtc-max8925.c +++ b/drivers/rtc/rtc-max8925.c @@ -193,10 +193,17 @@ static int max8925_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) ret = max8925_reg_read(info->rtc, MAX8925_RTC_IRQ_MASK); if (ret < 0) goto out; - if ((ret & ALARM0_IRQ) == 0) - alrm->enabled = 1; - else + if (ret & ALARM0_IRQ) { alrm->enabled = 0; + } else { + ret = max8925_reg_read(info->rtc, MAX8925_ALARM0_CNTL); + if (ret < 0) + goto out; + if (!ret) + alrm->enabled = 0; + else + alrm->enabled = 1; + } ret = max8925_reg_read(info->rtc, MAX8925_RTC_STATUS); if (ret < 0) goto out; @@ -204,6 +211,7 @@ static int max8925_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) alrm->pending = 1; else alrm->pending = 0; + return 0; out: return ret; } @@ -220,8 +228,11 @@ static int max8925_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) ret = max8925_bulk_write(info->rtc, MAX8925_ALARM0_SEC, TIME_NUM, buf); if (ret < 0) goto out; - /* only enable alarm on year/month/day/hour/min/sec */ - ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x77); + if (alrm->enabled) + /* only enable alarm on year/month/day/hour/min/sec */ + ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x77); + else + ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x0); if (ret < 0) goto out; out: diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c index 9d3caccfc250..e954a759ba85 100644 --- a/drivers/rtc/rtc-mpc5121.c +++ b/drivers/rtc/rtc-mpc5121.c @@ -327,7 +327,7 @@ static int __devinit mpc5121_rtc_probe(struct platform_device *op) dev_set_drvdata(&op->dev, rtc); rtc->irq = irq_of_parse_and_map(op->dev.of_node, 1); - err = request_irq(rtc->irq, mpc5121_rtc_handler, IRQF_DISABLED, + err = request_irq(rtc->irq, mpc5121_rtc_handler, 0, "mpc5121-rtc", &op->dev); if (err) { dev_err(&op->dev, "%s: could not request irq: %i\n", @@ -337,7 +337,7 @@ static int __devinit mpc5121_rtc_probe(struct platform_device *op) rtc->irq_periodic = irq_of_parse_and_map(op->dev.of_node, 0); err = request_irq(rtc->irq_periodic, mpc5121_rtc_handler_upd, - IRQF_DISABLED, "mpc5121-rtc_upd", &op->dev); + 0, "mpc5121-rtc_upd", &op->dev); if (err) { dev_err(&op->dev, "%s: could not request irq: %i\n", __func__, rtc->irq_periodic); diff --git a/drivers/rtc/rtc-mrst.c b/drivers/rtc/rtc-mrst.c index 6cd6c7235344..f51719bf4a75 100644 --- a/drivers/rtc/rtc-mrst.c +++ b/drivers/rtc/rtc-mrst.c @@ -366,7 +366,7 @@ vrtc_mrst_do_probe(struct device *dev, struct resource *iomem, int rtc_irq) if (rtc_irq) { retval = request_irq(rtc_irq, mrst_rtc_irq, - IRQF_DISABLED, dev_name(&mrst_rtc.rtc->dev), + 0, dev_name(&mrst_rtc.rtc->dev), mrst_rtc.rtc); if (retval < 0) { dev_dbg(dev, "IRQ %d is already in use, err %d\n", diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c index 768e2edb9678..1300962486d1 100644 --- a/drivers/rtc/rtc-mv.c +++ b/drivers/rtc/rtc-mv.c @@ -273,7 +273,7 @@ static int __devinit mv_rtc_probe(struct platform_device *pdev) if (pdata->irq >= 0) { writel(0, pdata->ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS); if (devm_request_irq(&pdev->dev, pdata->irq, mv_rtc_interrupt, - IRQF_DISABLED | IRQF_SHARED, + IRQF_SHARED, pdev->name, pdata) < 0) { dev_warn(&pdev->dev, "interrupt not available.\n"); pdata->irq = -1; diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c index 781068d62f23..b79010987d1e 100644 --- a/drivers/rtc/rtc-nuc900.c +++ b/drivers/rtc/rtc-nuc900.c @@ -269,7 +269,7 @@ static int __devinit nuc900_rtc_probe(struct platform_device *pdev) nuc900_rtc->irq_num = platform_get_irq(pdev, 0); if (request_irq(nuc900_rtc->irq_num, nuc900_rtc_interrupt, - IRQF_DISABLED, "nuc900rtc", nuc900_rtc)) { + 0, "nuc900rtc", nuc900_rtc)) { dev_err(&pdev->dev, "NUC900 RTC request irq failed\n"); err = -EBUSY; goto fail4; diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index 7789002bdd5c..0b614e32653d 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -348,14 +348,14 @@ static int __init omap_rtc_probe(struct platform_device *pdev) rtc_write(OMAP_RTC_STATUS_ALARM, OMAP_RTC_STATUS_REG); /* handle periodic and alarm irqs */ - if (request_irq(omap_rtc_timer, rtc_irq, IRQF_DISABLED, + if (request_irq(omap_rtc_timer, rtc_irq, 0, dev_name(&rtc->dev), rtc)) { pr_debug("%s: RTC timer interrupt IRQ%d already claimed\n", pdev->name, omap_rtc_timer); goto fail1; } if ((omap_rtc_timer != omap_rtc_alarm) && - (request_irq(omap_rtc_alarm, rtc_irq, IRQF_DISABLED, + (request_irq(omap_rtc_alarm, rtc_irq, 0, dev_name(&rtc->dev), rtc))) { pr_debug("%s: RTC alarm interrupt IRQ%d already claimed\n", pdev->name, omap_rtc_alarm); diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c index b46c4004d8fe..836118795c0b 100644 --- a/drivers/rtc/rtc-pcf2123.c +++ b/drivers/rtc/rtc-pcf2123.c @@ -346,20 +346,9 @@ static struct spi_driver pcf2123_driver = { .remove = __devexit_p(pcf2123_remove), }; -static int __init pcf2123_init(void) -{ - return spi_register_driver(&pcf2123_driver); -} - -static void __exit pcf2123_exit(void) -{ - spi_unregister_driver(&pcf2123_driver); -} +module_spi_driver(pcf2123_driver); MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>"); MODULE_DESCRIPTION("NXP PCF2123 RTC driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); - -module_init(pcf2123_init); -module_exit(pcf2123_exit); diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 606fdfab34e2..bc0677de1996 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -252,20 +252,9 @@ static struct i2c_driver pcf8563_driver = { .id_table = pcf8563_id, }; -static int __init pcf8563_init(void) -{ - return i2c_add_driver(&pcf8563_driver); -} - -static void __exit pcf8563_exit(void) -{ - i2c_del_driver(&pcf8563_driver); -} +module_i2c_driver(pcf8563_driver); MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); MODULE_DESCRIPTION("Philips PCF8563/Epson RTC8564 RTC driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); - -module_init(pcf8563_init); -module_exit(pcf8563_exit); diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c index 2d201afead3b..019ff3571168 100644 --- a/drivers/rtc/rtc-pcf8583.c +++ b/drivers/rtc/rtc-pcf8583.c @@ -320,18 +320,7 @@ static struct i2c_driver pcf8583_driver = { .id_table = pcf8583_id, }; -static __init int pcf8583_init(void) -{ - return i2c_add_driver(&pcf8583_driver); -} - -static __exit void pcf8583_exit(void) -{ - i2c_del_driver(&pcf8583_driver); -} - -module_init(pcf8583_init); -module_exit(pcf8583_exit); +module_i2c_driver(pcf8583_driver); MODULE_AUTHOR("Russell King"); MODULE_DESCRIPTION("PCF8583 I2C RTC driver"); diff --git a/drivers/rtc/rtc-pl030.c b/drivers/rtc/rtc-pl030.c index 02111fee077e..22bacdbf9139 100644 --- a/drivers/rtc/rtc-pl030.c +++ b/drivers/rtc/rtc-pl030.c @@ -123,7 +123,7 @@ static int pl030_probe(struct amba_device *dev, const struct amba_id *id) amba_set_drvdata(dev, rtc); - ret = request_irq(dev->irq[0], pl030_interrupt, IRQF_DISABLED, + ret = request_irq(dev->irq[0], pl030_interrupt, 0, "rtc-pl030", rtc); if (ret) goto err_irq; @@ -185,18 +185,7 @@ static struct amba_driver pl030_driver = { .id_table = pl030_ids, }; -static int __init pl030_init(void) -{ - return amba_driver_register(&pl030_driver); -} - -static void __exit pl030_exit(void) -{ - amba_driver_unregister(&pl030_driver); -} - -module_init(pl030_init); -module_exit(pl030_exit); +module_amba_driver(pl030_driver); MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); MODULE_DESCRIPTION("ARM AMBA PL030 RTC Driver"); diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index a952c8de1dd7..692de7360e94 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -352,7 +352,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) } if (request_irq(adev->irq[0], pl031_interrupt, - IRQF_DISABLED, "rtc-pl031", ldata)) { + 0, "rtc-pl031", ldata)) { ret = -EIO; goto out_no_irq; } @@ -431,18 +431,7 @@ static struct amba_driver pl031_driver = { .remove = pl031_remove, }; -static int __init pl031_init(void) -{ - return amba_driver_register(&pl031_driver); -} - -static void __exit pl031_exit(void) -{ - amba_driver_unregister(&pl031_driver); -} - -module_init(pl031_init); -module_exit(pl031_exit); +module_amba_driver(pl031_driver); MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net"); MODULE_DESCRIPTION("ARM AMBA PL031 RTC Driver"); diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c index 9f1d6bcbdf6c..d00bd24342a3 100644 --- a/drivers/rtc/rtc-pm8xxx.c +++ b/drivers/rtc/rtc-pm8xxx.c @@ -520,7 +520,7 @@ static int pm8xxx_rtc_suspend(struct device *dev) } #endif -SIMPLE_DEV_PM_OPS(pm8xxx_rtc_pm_ops, pm8xxx_rtc_suspend, pm8xxx_rtc_resume); +static SIMPLE_DEV_PM_OPS(pm8xxx_rtc_pm_ops, pm8xxx_rtc_suspend, pm8xxx_rtc_resume); static struct platform_driver pm8xxx_rtc_driver = { .probe = pm8xxx_rtc_probe, diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c index fc9f4991574b..0075c8fd93d8 100644 --- a/drivers/rtc/rtc-pxa.c +++ b/drivers/rtc/rtc-pxa.c @@ -174,14 +174,14 @@ static int pxa_rtc_open(struct device *dev) struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); int ret; - ret = request_irq(pxa_rtc->irq_1Hz, pxa_rtc_irq, IRQF_DISABLED, + ret = request_irq(pxa_rtc->irq_1Hz, pxa_rtc_irq, 0, "rtc 1Hz", dev); if (ret < 0) { dev_err(dev, "can't get irq %i, err %d\n", pxa_rtc->irq_1Hz, ret); goto err_irq_1Hz; } - ret = request_irq(pxa_rtc->irq_Alrm, pxa_rtc_irq, IRQF_DISABLED, + ret = request_irq(pxa_rtc->irq_Alrm, pxa_rtc_irq, 0, "rtc Alrm", dev); if (ret < 0) { dev_err(dev, "can't get irq %i, err %d\n", pxa_rtc->irq_Alrm, diff --git a/drivers/rtc/rtc-r9701.c b/drivers/rtc/rtc-r9701.c index 2853c2a6f10f..7f8e6c247935 100644 --- a/drivers/rtc/rtc-r9701.c +++ b/drivers/rtc/rtc-r9701.c @@ -159,17 +159,7 @@ static struct spi_driver r9701_driver = { .remove = __devexit_p(r9701_remove), }; -static __init int r9701_init(void) -{ - return spi_register_driver(&r9701_driver); -} -module_init(r9701_init); - -static __exit void r9701_exit(void) -{ - spi_unregister_driver(&r9701_driver); -} -module_exit(r9701_exit); +module_spi_driver(r9701_driver); MODULE_DESCRIPTION("r9701 spi RTC driver"); MODULE_AUTHOR("Magnus Damm <damm@opensource.se>"); diff --git a/drivers/rtc/rtc-rs5c348.c b/drivers/rtc/rtc-rs5c348.c index ce2ca8523ddd..77074ccd2850 100644 --- a/drivers/rtc/rtc-rs5c348.c +++ b/drivers/rtc/rtc-rs5c348.c @@ -235,18 +235,7 @@ static struct spi_driver rs5c348_driver = { .remove = __devexit_p(rs5c348_remove), }; -static __init int rs5c348_init(void) -{ - return spi_register_driver(&rs5c348_driver); -} - -static __exit void rs5c348_exit(void) -{ - spi_unregister_driver(&rs5c348_driver); -} - -module_init(rs5c348_init); -module_exit(rs5c348_exit); +module_spi_driver(rs5c348_driver); MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>"); MODULE_DESCRIPTION("Ricoh RS5C348 RTC driver"); diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index d29f5432c6e8..fb4842c3544e 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c @@ -689,18 +689,7 @@ static struct i2c_driver rs5c372_driver = { .id_table = rs5c372_id, }; -static __init int rs5c372_init(void) -{ - return i2c_add_driver(&rs5c372_driver); -} - -static __exit void rs5c372_exit(void) -{ - i2c_del_driver(&rs5c372_driver); -} - -module_init(rs5c372_init); -module_exit(rs5c372_exit); +module_i2c_driver(rs5c372_driver); MODULE_AUTHOR( "Pavel Mironchik <pmironchik@optifacio.net>, " diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c index ea09ff211dc6..0fbe57b2f6d2 100644 --- a/drivers/rtc/rtc-rv3029c2.c +++ b/drivers/rtc/rtc-rv3029c2.c @@ -436,18 +436,7 @@ static struct i2c_driver rv3029c2_driver = { .id_table = rv3029c2_id, }; -static int __init rv3029c2_init(void) -{ - return i2c_add_driver(&rv3029c2_driver); -} - -static void __exit rv3029c2_exit(void) -{ - i2c_del_driver(&rv3029c2_driver); -} - -module_init(rv3029c2_init); -module_exit(rv3029c2_exit); +module_i2c_driver(rv3029c2_driver); MODULE_AUTHOR("Gregory Hermant <gregory.hermant@calao-systems.com>"); MODULE_DESCRIPTION("Micro Crystal RV3029C2 RTC driver"); diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c index fde172fb2abe..0de902dc1cd5 100644 --- a/drivers/rtc/rtc-rx8025.c +++ b/drivers/rtc/rtc-rx8025.c @@ -644,19 +644,8 @@ static struct i2c_driver rx8025_driver = { .id_table = rx8025_id, }; -static int __init rx8025_init(void) -{ - return i2c_add_driver(&rx8025_driver); -} - -static void __exit rx8025_exit(void) -{ - i2c_del_driver(&rx8025_driver); -} +module_i2c_driver(rx8025_driver); MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); MODULE_DESCRIPTION("RX-8025 SA/NB RTC driver"); MODULE_LICENSE("GPL"); - -module_init(rx8025_init); -module_exit(rx8025_exit); diff --git a/drivers/rtc/rtc-rx8581.c b/drivers/rtc/rtc-rx8581.c index 600b890a3c15..d84825124a7a 100644 --- a/drivers/rtc/rtc-rx8581.c +++ b/drivers/rtc/rtc-rx8581.c @@ -276,20 +276,9 @@ static struct i2c_driver rx8581_driver = { .id_table = rx8581_id, }; -static int __init rx8581_init(void) -{ - return i2c_add_driver(&rx8581_driver); -} - -static void __exit rx8581_exit(void) -{ - i2c_del_driver(&rx8581_driver); -} +module_i2c_driver(rx8581_driver); MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com>"); MODULE_DESCRIPTION("Epson RX-8581 RTC driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); - -module_init(rx8581_init); -module_exit(rx8581_exit); diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c index f789e002c9b0..c9562ceedef3 100644 --- a/drivers/rtc/rtc-s35390a.c +++ b/drivers/rtc/rtc-s35390a.c @@ -304,19 +304,8 @@ static struct i2c_driver s35390a_driver = { .id_table = s35390a_id, }; -static int __init s35390a_rtc_init(void) -{ - return i2c_add_driver(&s35390a_driver); -} - -static void __exit s35390a_rtc_exit(void) -{ - i2c_del_driver(&s35390a_driver); -} +module_i2c_driver(s35390a_driver); MODULE_AUTHOR("Byron Bradley <byron.bbradley@gmail.com>"); MODULE_DESCRIPTION("S35390A RTC driver"); MODULE_LICENSE("GPL"); - -module_init(s35390a_rtc_init); -module_exit(s35390a_rtc_exit); diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index aef40bd2957b..9ccea134a996 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -35,6 +35,8 @@ enum s3c_cpu_type { TYPE_S3C2410, + TYPE_S3C2416, + TYPE_S3C2443, TYPE_S3C64XX, }; @@ -132,6 +134,7 @@ static int s3c_rtc_setfreq(struct device *dev, int freq) struct platform_device *pdev = to_platform_device(dev); struct rtc_device *rtc_dev = platform_get_drvdata(pdev); unsigned int tmp = 0; + int val; if (!is_power_of_2(freq)) return -EINVAL; @@ -139,12 +142,22 @@ static int s3c_rtc_setfreq(struct device *dev, int freq) clk_enable(rtc_clk); spin_lock_irq(&s3c_rtc_pie_lock); - if (s3c_rtc_cpu_type == TYPE_S3C2410) { + if (s3c_rtc_cpu_type != TYPE_S3C64XX) { tmp = readb(s3c_rtc_base + S3C2410_TICNT); tmp &= S3C2410_TICNT_ENABLE; } - tmp |= (rtc_dev->max_user_freq / freq)-1; + val = (rtc_dev->max_user_freq / freq) - 1; + + if (s3c_rtc_cpu_type == TYPE_S3C2416 || s3c_rtc_cpu_type == TYPE_S3C2443) { + tmp |= S3C2443_TICNT_PART(val); + writel(S3C2443_TICNT1_PART(val), s3c_rtc_base + S3C2443_TICNT1); + + if (s3c_rtc_cpu_type == TYPE_S3C2416) + writel(S3C2416_TICNT2_PART(val), s3c_rtc_base + S3C2416_TICNT2); + } else { + tmp |= val; + } writel(tmp, s3c_rtc_base + S3C2410_TICNT); spin_unlock_irq(&s3c_rtc_pie_lock); @@ -371,7 +384,7 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en) tmp &= ~S3C2410_RTCCON_RTCEN; writew(tmp, base + S3C2410_RTCCON); - if (s3c_rtc_cpu_type == TYPE_S3C2410) { + if (s3c_rtc_cpu_type != TYPE_S3C64XX) { tmp = readb(base + S3C2410_TICNT); tmp &= ~S3C2410_TICNT_ENABLE; writeb(tmp, base + S3C2410_TICNT); @@ -428,12 +441,27 @@ static int __devexit s3c_rtc_remove(struct platform_device *dev) return 0; } +static const struct of_device_id s3c_rtc_dt_match[]; + +static inline int s3c_rtc_get_driver_data(struct platform_device *pdev) +{ +#ifdef CONFIG_OF + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node); + return match->data; + } +#endif + return platform_get_device_id(pdev)->driver_data; +} + static int __devinit s3c_rtc_probe(struct platform_device *pdev) { struct rtc_device *rtc; struct rtc_time rtc_tm; struct resource *res; int ret; + int tmp; pr_debug("%s: probe=%p\n", __func__, pdev); @@ -508,13 +536,7 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) goto err_nortc; } -#ifdef CONFIG_OF - if (pdev->dev.of_node) - s3c_rtc_cpu_type = of_device_is_compatible(pdev->dev.of_node, - "samsung,s3c6410-rtc") ? TYPE_S3C64XX : TYPE_S3C2410; - else -#endif - s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data; + s3c_rtc_cpu_type = s3c_rtc_get_driver_data(pdev); /* Check RTC Time */ @@ -533,24 +555,30 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n"); } - if (s3c_rtc_cpu_type == TYPE_S3C64XX) + if (s3c_rtc_cpu_type != TYPE_S3C2410) rtc->max_user_freq = 32768; else rtc->max_user_freq = 128; + if (s3c_rtc_cpu_type == TYPE_S3C2416 || s3c_rtc_cpu_type == TYPE_S3C2443) { + tmp = readw(s3c_rtc_base + S3C2410_RTCCON); + tmp |= S3C2443_RTCCON_TICSEL; + writew(tmp, s3c_rtc_base + S3C2410_RTCCON); + } + platform_set_drvdata(pdev, rtc); s3c_rtc_setfreq(&pdev->dev, 1); ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq, - IRQF_DISABLED, "s3c2410-rtc alarm", rtc); + 0, "s3c2410-rtc alarm", rtc); if (ret) { dev_err(&pdev->dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret); goto err_alarm_irq; } ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq, - IRQF_DISABLED, "s3c2410-rtc tick", rtc); + 0, "s3c2410-rtc tick", rtc); if (ret) { dev_err(&pdev->dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret); free_irq(s3c_rtc_alarmno, rtc); @@ -638,8 +666,19 @@ static int s3c_rtc_resume(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id s3c_rtc_dt_match[] = { - { .compatible = "samsung,s3c2410-rtc" }, - { .compatible = "samsung,s3c6410-rtc" }, + { + .compatible = "samsung,s3c2410-rtc" + .data = TYPE_S3C2410, + }, { + .compatible = "samsung,s3c2416-rtc" + .data = TYPE_S3C2416, + }, { + .compatible = "samsung,s3c2443-rtc" + .data = TYPE_S3C2443, + }, { + .compatible = "samsung,s3c6410-rtc" + .data = TYPE_S3C64XX, + }, {}, }; MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match); @@ -652,6 +691,12 @@ static struct platform_device_id s3c_rtc_driver_ids[] = { .name = "s3c2410-rtc", .driver_data = TYPE_S3C2410, }, { + .name = "s3c2416-rtc", + .driver_data = TYPE_S3C2416, + }, { + .name = "s3c2443-rtc", + .driver_data = TYPE_S3C2443, + }, { .name = "s3c64xx-rtc", .driver_data = TYPE_S3C64XX, }, diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index cb9a585312cc..4940fa8c4e10 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -23,94 +23,44 @@ #include <linux/platform_device.h> #include <linux/module.h> +#include <linux/clk.h> #include <linux/rtc.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/interrupt.h> +#include <linux/slab.h> #include <linux/string.h> +#include <linux/of.h> #include <linux/pm.h> #include <linux/bitops.h> #include <mach/hardware.h> -#include <asm/irq.h> +#include <mach/irqs.h> -#ifdef CONFIG_ARCH_PXA +#if defined(CONFIG_ARCH_PXA) || defined(CONFIG_ARCH_MMP) #include <mach/regs-rtc.h> #endif #define RTC_DEF_DIVIDER (32768 - 1) #define RTC_DEF_TRIM 0 - -static const unsigned long RTC_FREQ = 1024; -static struct rtc_time rtc_alarm; -static DEFINE_SPINLOCK(sa1100_rtc_lock); - -static inline int rtc_periodic_alarm(struct rtc_time *tm) -{ - return (tm->tm_year == -1) || - ((unsigned)tm->tm_mon >= 12) || - ((unsigned)(tm->tm_mday - 1) >= 31) || - ((unsigned)tm->tm_hour > 23) || - ((unsigned)tm->tm_min > 59) || - ((unsigned)tm->tm_sec > 59); -} - -/* - * Calculate the next alarm time given the requested alarm time mask - * and the current time. - */ -static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, - struct rtc_time *alrm) -{ - unsigned long next_time; - unsigned long now_time; - - next->tm_year = now->tm_year; - next->tm_mon = now->tm_mon; - next->tm_mday = now->tm_mday; - next->tm_hour = alrm->tm_hour; - next->tm_min = alrm->tm_min; - next->tm_sec = alrm->tm_sec; - - rtc_tm_to_time(now, &now_time); - rtc_tm_to_time(next, &next_time); - - if (next_time < now_time) { - /* Advance one day */ - next_time += 60 * 60 * 24; - rtc_time_to_tm(next_time, next); - } -} - -static int rtc_update_alarm(struct rtc_time *alrm) -{ - struct rtc_time alarm_tm, now_tm; - unsigned long now, time; - int ret; - - do { - now = RCNR; - rtc_time_to_tm(now, &now_tm); - rtc_next_alarm_time(&alarm_tm, &now_tm, alrm); - ret = rtc_tm_to_time(&alarm_tm, &time); - if (ret != 0) - break; - - RTSR = RTSR & (RTSR_HZE|RTSR_ALE|RTSR_AL); - RTAR = time; - } while (now != RCNR); - - return ret; -} +#define RTC_FREQ 1024 + +struct sa1100_rtc { + spinlock_t lock; + int irq_1hz; + int irq_alarm; + struct rtc_device *rtc; + struct clk *clk; +}; static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id) { - struct platform_device *pdev = to_platform_device(dev_id); - struct rtc_device *rtc = platform_get_drvdata(pdev); + struct sa1100_rtc *info = dev_get_drvdata(dev_id); + struct rtc_device *rtc = info->rtc; unsigned int rtsr; unsigned long events = 0; - spin_lock(&sa1100_rtc_lock); + spin_lock(&info->lock); rtsr = RTSR; /* clear interrupt sources */ @@ -146,30 +96,28 @@ static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id) rtc_update_irq(rtc, 1, events); - if (rtsr & RTSR_AL && rtc_periodic_alarm(&rtc_alarm)) - rtc_update_alarm(&rtc_alarm); - - spin_unlock(&sa1100_rtc_lock); + spin_unlock(&info->lock); return IRQ_HANDLED; } static int sa1100_rtc_open(struct device *dev) { + struct sa1100_rtc *info = dev_get_drvdata(dev); + struct rtc_device *rtc = info->rtc; int ret; - struct platform_device *plat_dev = to_platform_device(dev); - struct rtc_device *rtc = platform_get_drvdata(plat_dev); - ret = request_irq(IRQ_RTC1Hz, sa1100_rtc_interrupt, IRQF_DISABLED, - "rtc 1Hz", dev); + ret = clk_prepare_enable(info->clk); + if (ret) + goto fail_clk; + ret = request_irq(info->irq_1hz, sa1100_rtc_interrupt, 0, "rtc 1Hz", dev); if (ret) { - dev_err(dev, "IRQ %d already in use.\n", IRQ_RTC1Hz); + dev_err(dev, "IRQ %d already in use.\n", info->irq_1hz); goto fail_ui; } - ret = request_irq(IRQ_RTCAlrm, sa1100_rtc_interrupt, IRQF_DISABLED, - "rtc Alrm", dev); + ret = request_irq(info->irq_alarm, sa1100_rtc_interrupt, 0, "rtc Alrm", dev); if (ret) { - dev_err(dev, "IRQ %d already in use.\n", IRQ_RTCAlrm); + dev_err(dev, "IRQ %d already in use.\n", info->irq_alarm); goto fail_ai; } rtc->max_user_freq = RTC_FREQ; @@ -178,29 +126,36 @@ static int sa1100_rtc_open(struct device *dev) return 0; fail_ai: - free_irq(IRQ_RTC1Hz, dev); + free_irq(info->irq_1hz, dev); fail_ui: + clk_disable_unprepare(info->clk); + fail_clk: return ret; } static void sa1100_rtc_release(struct device *dev) { - spin_lock_irq(&sa1100_rtc_lock); + struct sa1100_rtc *info = dev_get_drvdata(dev); + + spin_lock_irq(&info->lock); RTSR = 0; - spin_unlock_irq(&sa1100_rtc_lock); + spin_unlock_irq(&info->lock); - free_irq(IRQ_RTCAlrm, dev); - free_irq(IRQ_RTC1Hz, dev); + free_irq(info->irq_alarm, dev); + free_irq(info->irq_1hz, dev); + clk_disable_unprepare(info->clk); } static int sa1100_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { - spin_lock_irq(&sa1100_rtc_lock); + struct sa1100_rtc *info = dev_get_drvdata(dev); + + spin_lock_irq(&info->lock); if (enabled) RTSR |= RTSR_ALE; else RTSR &= ~RTSR_ALE; - spin_unlock_irq(&sa1100_rtc_lock); + spin_unlock_irq(&info->lock); return 0; } @@ -225,7 +180,6 @@ static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) { u32 rtsr; - memcpy(&alrm->time, &rtc_alarm, sizeof(struct rtc_time)); rtsr = RTSR; alrm->enabled = (rtsr & RTSR_ALE) ? 1 : 0; alrm->pending = (rtsr & RTSR_AL) ? 1 : 0; @@ -234,17 +188,22 @@ static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) static int sa1100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) { + struct sa1100_rtc *info = dev_get_drvdata(dev); + unsigned long time; int ret; - spin_lock_irq(&sa1100_rtc_lock); - ret = rtc_update_alarm(&alrm->time); - if (ret == 0) { - if (alrm->enabled) - RTSR |= RTSR_ALE; - else - RTSR &= ~RTSR_ALE; - } - spin_unlock_irq(&sa1100_rtc_lock); + spin_lock_irq(&info->lock); + ret = rtc_tm_to_time(&alrm->time, &time); + if (ret != 0) + goto out; + RTSR = RTSR & (RTSR_HZE|RTSR_ALE|RTSR_AL); + RTAR = time; + if (alrm->enabled) + RTSR |= RTSR_ALE; + else + RTSR &= ~RTSR_ALE; +out: + spin_unlock_irq(&info->lock); return ret; } @@ -271,6 +230,27 @@ static const struct rtc_class_ops sa1100_rtc_ops = { static int sa1100_rtc_probe(struct platform_device *pdev) { struct rtc_device *rtc; + struct sa1100_rtc *info; + int irq_1hz, irq_alarm, ret = 0; + + irq_1hz = platform_get_irq_byname(pdev, "rtc 1Hz"); + irq_alarm = platform_get_irq_byname(pdev, "rtc alarm"); + if (irq_1hz < 0 || irq_alarm < 0) + return -ENODEV; + + info = kzalloc(sizeof(struct sa1100_rtc), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(info->clk)) { + dev_err(&pdev->dev, "failed to find rtc clock source\n"); + ret = PTR_ERR(info->clk); + goto err_clk; + } + info->irq_1hz = irq_1hz; + info->irq_alarm = irq_alarm; + spin_lock_init(&info->lock); + platform_set_drvdata(pdev, info); /* * According to the manual we should be able to let RTTR be zero @@ -292,10 +272,11 @@ static int sa1100_rtc_probe(struct platform_device *pdev) rtc = rtc_device_register(pdev->name, &pdev->dev, &sa1100_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc)) - return PTR_ERR(rtc); - - platform_set_drvdata(pdev, rtc); + if (IS_ERR(rtc)) { + ret = PTR_ERR(rtc); + goto err_dev; + } + info->rtc = rtc; /* Fix for a nasty initialization problem the in SA11xx RTSR register. * See also the comments in sa1100_rtc_interrupt(). @@ -322,14 +303,24 @@ static int sa1100_rtc_probe(struct platform_device *pdev) RTSR = RTSR_AL | RTSR_HZ; return 0; +err_dev: + platform_set_drvdata(pdev, NULL); + clk_put(info->clk); +err_clk: + kfree(info); + return ret; } static int sa1100_rtc_remove(struct platform_device *pdev) { - struct rtc_device *rtc = platform_get_drvdata(pdev); + struct sa1100_rtc *info = platform_get_drvdata(pdev); - if (rtc) - rtc_device_unregister(rtc); + if (info) { + rtc_device_unregister(info->rtc); + clk_put(info->clk); + platform_set_drvdata(pdev, NULL); + kfree(info); + } return 0; } @@ -337,15 +328,17 @@ static int sa1100_rtc_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int sa1100_rtc_suspend(struct device *dev) { + struct sa1100_rtc *info = dev_get_drvdata(dev); if (device_may_wakeup(dev)) - enable_irq_wake(IRQ_RTCAlrm); + enable_irq_wake(info->irq_alarm); return 0; } static int sa1100_rtc_resume(struct device *dev) { + struct sa1100_rtc *info = dev_get_drvdata(dev); if (device_may_wakeup(dev)) - disable_irq_wake(IRQ_RTCAlrm); + disable_irq_wake(info->irq_alarm); return 0; } @@ -355,6 +348,13 @@ static const struct dev_pm_ops sa1100_rtc_pm_ops = { }; #endif +static struct of_device_id sa1100_rtc_dt_ids[] = { + { .compatible = "mrvl,sa1100-rtc", }, + { .compatible = "mrvl,mmp-rtc", }, + {} +}; +MODULE_DEVICE_TABLE(of, sa1100_rtc_dt_ids); + static struct platform_driver sa1100_rtc_driver = { .probe = sa1100_rtc_probe, .remove = sa1100_rtc_remove, @@ -363,6 +363,7 @@ static struct platform_driver sa1100_rtc_driver = { #ifdef CONFIG_PM .pm = &sa1100_rtc_pm_ops, #endif + .of_match_table = sa1100_rtc_dt_ids, }, }; diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index 6ac55fd48413..e55a7635ae5f 100644 --- a/drivers/rtc/rtc-sh.c +++ b/drivers/rtc/rtc-sh.c @@ -666,7 +666,7 @@ static int __init sh_rtc_probe(struct platform_device *pdev) if (rtc->carry_irq <= 0) { /* register shared periodic/carry/alarm irq */ ret = request_irq(rtc->periodic_irq, sh_rtc_shared, - IRQF_DISABLED, "sh-rtc", rtc); + 0, "sh-rtc", rtc); if (unlikely(ret)) { dev_err(&pdev->dev, "request IRQ failed with %d, IRQ %d\n", ret, @@ -676,7 +676,7 @@ static int __init sh_rtc_probe(struct platform_device *pdev) } else { /* register periodic/carry/alarm irqs */ ret = request_irq(rtc->periodic_irq, sh_rtc_periodic, - IRQF_DISABLED, "sh-rtc period", rtc); + 0, "sh-rtc period", rtc); if (unlikely(ret)) { dev_err(&pdev->dev, "request period IRQ failed with %d, IRQ %d\n", @@ -685,7 +685,7 @@ static int __init sh_rtc_probe(struct platform_device *pdev) } ret = request_irq(rtc->carry_irq, sh_rtc_interrupt, - IRQF_DISABLED, "sh-rtc carry", rtc); + 0, "sh-rtc carry", rtc); if (unlikely(ret)) { dev_err(&pdev->dev, "request carry IRQ failed with %d, IRQ %d\n", @@ -695,7 +695,7 @@ static int __init sh_rtc_probe(struct platform_device *pdev) } ret = request_irq(rtc->alarm_irq, sh_rtc_alarm, - IRQF_DISABLED, "sh-rtc alarm", rtc); + 0, "sh-rtc alarm", rtc); if (unlikely(ret)) { dev_err(&pdev->dev, "request alarm IRQ failed with %d, IRQ %d\n", diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c index 19a28a671a8e..e38da0dc4187 100644 --- a/drivers/rtc/rtc-spear.c +++ b/drivers/rtc/rtc-spear.c @@ -77,9 +77,11 @@ #define STATUS_FAIL (LOST_WR_TIME | LOST_WR_DATE) struct spear_rtc_config { + struct rtc_device *rtc; struct clk *clk; spinlock_t lock; void __iomem *ioaddr; + unsigned int irq_wake; }; static inline void spear_rtc_clear_interrupt(struct spear_rtc_config *config) @@ -149,8 +151,7 @@ static void rtc_wait_not_busy(struct spear_rtc_config *config) static irqreturn_t spear_rtc_irq(int irq, void *dev_id) { - struct rtc_device *rtc = (struct rtc_device *)dev_id; - struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + struct spear_rtc_config *config = dev_id; unsigned long flags, events = 0; unsigned int irq_data; @@ -161,7 +162,7 @@ static irqreturn_t spear_rtc_irq(int irq, void *dev_id) if ((irq_data & RTC_INT_MASK)) { spear_rtc_clear_interrupt(config); events = RTC_IRQF | RTC_AF; - rtc_update_irq(rtc, 1, events); + rtc_update_irq(config->rtc, 1, events); return IRQ_HANDLED; } else return IRQ_NONE; @@ -203,9 +204,7 @@ static void bcd2tm(struct rtc_time *tm) */ static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm) { - struct platform_device *pdev = to_platform_device(dev); - struct rtc_device *rtc = platform_get_drvdata(pdev); - struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + struct spear_rtc_config *config = dev_get_drvdata(dev); unsigned int time, date; /* we don't report wday/yday/isdst ... */ @@ -234,9 +233,7 @@ static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm) */ static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm) { - struct platform_device *pdev = to_platform_device(dev); - struct rtc_device *rtc = platform_get_drvdata(pdev); - struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + struct spear_rtc_config *config = dev_get_drvdata(dev); unsigned int time, date, err = 0; if (tm2bcd(tm) < 0) @@ -266,9 +263,7 @@ static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm) */ static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) { - struct platform_device *pdev = to_platform_device(dev); - struct rtc_device *rtc = platform_get_drvdata(pdev); - struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + struct spear_rtc_config *config = dev_get_drvdata(dev); unsigned int time, date; rtc_wait_not_busy(config); @@ -298,9 +293,7 @@ static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) */ static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) { - struct platform_device *pdev = to_platform_device(dev); - struct rtc_device *rtc = platform_get_drvdata(pdev); - struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + struct spear_rtc_config *config = dev_get_drvdata(dev); unsigned int time, date, err = 0; if (tm2bcd(&alm->time) < 0) @@ -326,17 +319,42 @@ static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) return 0; } + +static int spear_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct spear_rtc_config *config = dev_get_drvdata(dev); + int ret = 0; + + spear_rtc_clear_interrupt(config); + + switch (enabled) { + case 0: + /* alarm off */ + spear_rtc_disable_interrupt(config); + break; + case 1: + /* alarm on */ + spear_rtc_enable_interrupt(config); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + static struct rtc_class_ops spear_rtc_ops = { .read_time = spear_rtc_read_time, .set_time = spear_rtc_set_time, .read_alarm = spear_rtc_read_alarm, .set_alarm = spear_rtc_set_alarm, + .alarm_irq_enable = spear_alarm_irq_enable, }; static int __devinit spear_rtc_probe(struct platform_device *pdev) { struct resource *res; - struct rtc_device *rtc; struct spear_rtc_config *config; unsigned int status = 0; int irq; @@ -376,19 +394,17 @@ static int __devinit spear_rtc_probe(struct platform_device *pdev) } spin_lock_init(&config->lock); + platform_set_drvdata(pdev, config); - rtc = rtc_device_register(pdev->name, &pdev->dev, &spear_rtc_ops, - THIS_MODULE); - if (IS_ERR(rtc)) { + config->rtc = rtc_device_register(pdev->name, &pdev->dev, + &spear_rtc_ops, THIS_MODULE); + if (IS_ERR(config->rtc)) { dev_err(&pdev->dev, "can't register RTC device, err %ld\n", - PTR_ERR(rtc)); - status = PTR_ERR(rtc); + PTR_ERR(config->rtc)); + status = PTR_ERR(config->rtc); goto err_iounmap; } - platform_set_drvdata(pdev, rtc); - dev_set_drvdata(&rtc->dev, config); - /* alarm irqs */ irq = platform_get_irq(pdev, 0); if (irq < 0) { @@ -397,7 +413,7 @@ static int __devinit spear_rtc_probe(struct platform_device *pdev) goto err_clear_platdata; } - status = request_irq(irq, spear_rtc_irq, 0, pdev->name, rtc); + status = request_irq(irq, spear_rtc_irq, 0, pdev->name, config); if (status) { dev_err(&pdev->dev, "Alarm interrupt IRQ%d already \ claimed\n", irq); @@ -411,8 +427,7 @@ static int __devinit spear_rtc_probe(struct platform_device *pdev) err_clear_platdata: platform_set_drvdata(pdev, NULL); - dev_set_drvdata(&rtc->dev, NULL); - rtc_device_unregister(rtc); + rtc_device_unregister(config->rtc); err_iounmap: iounmap(config->ioaddr); err_disable_clock: @@ -429,8 +444,7 @@ err_release_region: static int __devexit spear_rtc_remove(struct platform_device *pdev) { - struct rtc_device *rtc = platform_get_drvdata(pdev); - struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + struct spear_rtc_config *config = platform_get_drvdata(pdev); int irq; struct resource *res; @@ -448,8 +462,7 @@ static int __devexit spear_rtc_remove(struct platform_device *pdev) if (res) release_mem_region(res->start, resource_size(res)); platform_set_drvdata(pdev, NULL); - dev_set_drvdata(&rtc->dev, NULL); - rtc_device_unregister(rtc); + rtc_device_unregister(config->rtc); return 0; } @@ -458,14 +471,14 @@ static int __devexit spear_rtc_remove(struct platform_device *pdev) static int spear_rtc_suspend(struct platform_device *pdev, pm_message_t state) { - struct rtc_device *rtc = platform_get_drvdata(pdev); - struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + struct spear_rtc_config *config = platform_get_drvdata(pdev); int irq; irq = platform_get_irq(pdev, 0); - if (device_may_wakeup(&pdev->dev)) - enable_irq_wake(irq); - else { + if (device_may_wakeup(&pdev->dev)) { + if (!enable_irq_wake(irq)) + config->irq_wake = 1; + } else { spear_rtc_disable_interrupt(config); clk_disable(config->clk); } @@ -475,15 +488,17 @@ static int spear_rtc_suspend(struct platform_device *pdev, pm_message_t state) static int spear_rtc_resume(struct platform_device *pdev) { - struct rtc_device *rtc = platform_get_drvdata(pdev); - struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + struct spear_rtc_config *config = platform_get_drvdata(pdev); int irq; irq = platform_get_irq(pdev, 0); - if (device_may_wakeup(&pdev->dev)) - disable_irq_wake(irq); - else { + if (device_may_wakeup(&pdev->dev)) { + if (config->irq_wake) { + disable_irq_wake(irq); + config->irq_wake = 0; + } + } else { clk_enable(config->clk); spear_rtc_enable_interrupt(config); } @@ -498,8 +513,7 @@ static int spear_rtc_resume(struct platform_device *pdev) static void spear_rtc_shutdown(struct platform_device *pdev) { - struct rtc_device *rtc = platform_get_drvdata(pdev); - struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + struct spear_rtc_config *config = platform_get_drvdata(pdev); spear_rtc_disable_interrupt(config); clk_disable(config->clk); diff --git a/drivers/rtc/rtc-stk17ta8.c b/drivers/rtc/rtc-stk17ta8.c index 7621116bd20d..279f5cfa691a 100644 --- a/drivers/rtc/rtc-stk17ta8.c +++ b/drivers/rtc/rtc-stk17ta8.c @@ -329,7 +329,7 @@ static int __devinit stk17ta8_rtc_probe(struct platform_device *pdev) writeb(0, ioaddr + RTC_INTERRUPTS); if (devm_request_irq(&pdev->dev, pdata->irq, stk17ta8_rtc_interrupt, - IRQF_DISABLED | IRQF_SHARED, + IRQF_SHARED, pdev->name, pdev) < 0) { dev_warn(&pdev->dev, "interrupt not available.\n"); pdata->irq = 0; diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c index d43b4f6eb4e4..4c2c6df2a9ef 100644 --- a/drivers/rtc/rtc-twl.c +++ b/drivers/rtc/rtc-twl.c @@ -176,6 +176,10 @@ static int set_rtc_irq_bit(unsigned char bit) unsigned char val; int ret; + /* if the bit is set, return from here */ + if (rtc_irq_bits & bit) + return 0; + val = rtc_irq_bits | bit; val &= ~BIT_RTC_INTERRUPTS_REG_EVERY_M; ret = twl_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG); @@ -193,6 +197,10 @@ static int mask_rtc_irq_bit(unsigned char bit) unsigned char val; int ret; + /* if the bit is clear, return from here */ + if (!(rtc_irq_bits & bit)) + return 0; + val = rtc_irq_bits & ~bit; ret = twl_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG); if (ret == 0) @@ -357,7 +365,7 @@ out: static irqreturn_t twl_rtc_interrupt(int irq, void *rtc) { - unsigned long events = 0; + unsigned long events; int ret = IRQ_NONE; int res; u8 rd_reg; @@ -372,11 +380,11 @@ static irqreturn_t twl_rtc_interrupt(int irq, void *rtc) * by reading RTS_INTERRUPTS_REGISTER[IT_TIMER,IT_ALARM] */ if (rd_reg & BIT_RTC_STATUS_REG_ALARM_M) - events |= RTC_IRQF | RTC_AF; + events = RTC_IRQF | RTC_AF; else - events |= RTC_IRQF | RTC_UF; + events = RTC_IRQF | RTC_PF; - res = twl_rtc_write_u8(rd_reg | BIT_RTC_STATUS_REG_ALARM_M, + res = twl_rtc_write_u8(BIT_RTC_STATUS_REG_ALARM_M, REG_RTC_STATUS_REG); if (res) goto out; @@ -449,19 +457,11 @@ static int __devinit twl_rtc_probe(struct platform_device *pdev) REG_INT_MSK_STS_A); } - /* Check RTC module status, Enable if it is off */ - ret = twl_rtc_read_u8(&rd_reg, REG_RTC_CTRL_REG); + dev_info(&pdev->dev, "Enabling TWL-RTC\n"); + ret = twl_rtc_write_u8(BIT_RTC_CTRL_REG_STOP_RTC_M, REG_RTC_CTRL_REG); if (ret < 0) goto out1; - if (!(rd_reg & BIT_RTC_CTRL_REG_STOP_RTC_M)) { - dev_info(&pdev->dev, "Enabling TWL-RTC.\n"); - rd_reg = BIT_RTC_CTRL_REG_STOP_RTC_M; - ret = twl_rtc_write_u8(rd_reg, REG_RTC_CTRL_REG); - if (ret < 0) - goto out1; - } - /* init cached IRQ enable bits */ ret = twl_rtc_read_u8(&rtc_irq_bits, REG_RTC_INTERRUPTS_REG); if (ret < 0) diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c index aac0ffed4345..a12bfac49d36 100644 --- a/drivers/rtc/rtc-tx4939.c +++ b/drivers/rtc/rtc-tx4939.c @@ -266,7 +266,7 @@ static int __init tx4939_rtc_probe(struct platform_device *pdev) spin_lock_init(&pdata->lock); tx4939_rtc_cmd(pdata->rtcreg, TX4939_RTCCTL_COMMAND_NOP); if (devm_request_irq(&pdev->dev, irq, tx4939_rtc_interrupt, - IRQF_DISABLED, pdev->name, &pdev->dev) < 0) + 0, pdev->name, &pdev->dev) < 0) return -EBUSY; rtc = rtc_device_register(pdev->name, &pdev->dev, &tx4939_rtc_ops, THIS_MODULE); diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c index fcbfdda2993b..5f60a7c6a155 100644 --- a/drivers/rtc/rtc-vr41xx.c +++ b/drivers/rtc/rtc-vr41xx.c @@ -333,7 +333,7 @@ static int __devinit rtc_probe(struct platform_device *pdev) goto err_device_unregister; } - retval = request_irq(aie_irq, elapsedtime_interrupt, IRQF_DISABLED, + retval = request_irq(aie_irq, elapsedtime_interrupt, 0, "elapsed_time", pdev); if (retval < 0) goto err_device_unregister; @@ -342,7 +342,7 @@ static int __devinit rtc_probe(struct platform_device *pdev) if (pie_irq <= 0) goto err_free_irq; - retval = request_irq(pie_irq, rtclong1_interrupt, IRQF_DISABLED, + retval = request_irq(pie_irq, rtclong1_interrupt, 0, "rtclong1", pdev); if (retval < 0) goto err_free_irq; diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c index 8c051d3179db..403b3d41d101 100644 --- a/drivers/rtc/rtc-x1205.c +++ b/drivers/rtc/rtc-x1205.c @@ -623,15 +623,7 @@ static struct i2c_driver x1205_driver = { .id_table = x1205_id, }; -static int __init x1205_init(void) -{ - return i2c_add_driver(&x1205_driver); -} - -static void __exit x1205_exit(void) -{ - i2c_del_driver(&x1205_driver); -} +module_i2c_driver(x1205_driver); MODULE_AUTHOR( "Karen Spearel <kas111 at gmail dot com>, " @@ -639,6 +631,3 @@ MODULE_AUTHOR( MODULE_DESCRIPTION("Xicor/Intersil X1205 RTC driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); - -module_init(x1205_init); -module_exit(x1205_exit); diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index 0b54a91f8dcd..168525a9c292 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -441,9 +441,8 @@ static int sclp_mem_notifier(struct notifier_block *nb, start = arg->start_pfn << PAGE_SHIFT; size = arg->nr_pages << PAGE_SHIFT; mutex_lock(&sclp_mem_mutex); - for (id = 0; id <= sclp_max_storage_id; id++) - if (!test_bit(id, sclp_storage_ids)) - sclp_attach_storage(id); + for_each_clear_bit(id, sclp_storage_ids, sclp_max_storage_id + 1) + sclp_attach_storage(id); switch (action) { case MEM_ONLINE: case MEM_GOING_OFFLINE: diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 2a0dfcb0bc42..35c685c374e9 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -1458,7 +1458,6 @@ int qdio_establish(struct qdio_initialize *init_data) } qdio_setup_ssqd_info(irq_ptr); - DBF_EVENT("qib ac:%4x", irq_ptr->qib.ac); qdio_detect_hsicq(irq_ptr); diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index 452989a7ec13..ecf12f0aca7b 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c @@ -311,7 +311,8 @@ void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr) check_and_setup_qebsm(irq_ptr, qdioac, irq_ptr->ssqd_desc.sch_token); process_ac_flags(irq_ptr, qdioac); - DBF_EVENT("qdioac:%4x", qdioac); + DBF_EVENT("ac 1:%2x 2:%4x", qdioac, irq_ptr->ssqd_desc.qdioac2); + DBF_EVENT("3:%4x qib:%4x", irq_ptr->ssqd_desc.qdioac3, irq_ptr->qib.ac); } void qdio_release_memory(struct qdio_irq *irq_ptr) diff --git a/drivers/scsi/arm/arxescsi.c b/drivers/scsi/arm/arxescsi.c index a750aa72b8ef..2a28b4ad1975 100644 --- a/drivers/scsi/arm/arxescsi.c +++ b/drivers/scsi/arm/arxescsi.c @@ -305,7 +305,7 @@ arxescsi_probe(struct expansion_card *ec, const struct ecard_id *id) info->base = base; info->info.scsi.io_base = base + 0x2000; - info->info.scsi.irq = NO_IRQ; + info->info.scsi.irq = 0; info->info.scsi.dma = NO_DMA; info->info.scsi.io_shift = 5; info->info.ifcfg.clockrate = 24; /* MHz */ diff --git a/drivers/scsi/arm/fas216.c b/drivers/scsi/arm/fas216.c index e85c40b6e19b..6206a666a8ec 100644 --- a/drivers/scsi/arm/fas216.c +++ b/drivers/scsi/arm/fas216.c @@ -2176,7 +2176,7 @@ static void fas216_done(FAS216_Info *info, unsigned int result) fn = (void (*)(FAS216_Info *, struct scsi_cmnd *, unsigned int))SCpnt->host_scribble; fn(info, SCpnt, result); - if (info->scsi.irq != NO_IRQ) { + if (info->scsi.irq) { spin_lock_irqsave(&info->host_lock, flags); if (info->scsi.phase == PHASE_IDLE) fas216_kick(info); @@ -2276,7 +2276,7 @@ static int fas216_noqueue_command_lck(struct scsi_cmnd *SCpnt, * We should only be using this if we don't have an interrupt. * Provide some "incentive" to use the queueing code. */ - BUG_ON(info->scsi.irq != NO_IRQ); + BUG_ON(info->scsi.irq); info->internal_done = 0; fas216_queue_command_lck(SCpnt, fas216_internal_done); diff --git a/drivers/scsi/arm/fas216.h b/drivers/scsi/arm/fas216.h index 84b7127c0121..df2e1b3ddfe2 100644 --- a/drivers/scsi/arm/fas216.h +++ b/drivers/scsi/arm/fas216.h @@ -12,10 +12,6 @@ #ifndef FAS216_H #define FAS216_H -#ifndef NO_IRQ -#define NO_IRQ 255 -#endif - #include <scsi/scsi_eh.h> #include "queue.h" diff --git a/drivers/scsi/bnx2fc/bnx2fc_constants.h b/drivers/scsi/bnx2fc/bnx2fc_constants.h index c12702bb16d6..dad9924abbbb 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_constants.h +++ b/drivers/scsi/bnx2fc/bnx2fc_constants.h @@ -47,6 +47,7 @@ #define FCOE_KCQE_COMPLETION_STATUS_CTX_FREE_FAILURE (0x4) #define FCOE_KCQE_COMPLETION_STATUS_NIC_ERROR (0x5) #define FCOE_KCQE_COMPLETION_STATUS_WRONG_HSI_VERSION (0x6) +#define FCOE_KCQE_COMPLETION_STATUS_PARITY_ERROR (0x81) /* CQE type */ #define FCOE_PENDING_CQE_TYPE 0 diff --git a/drivers/scsi/bnx2i/57xx_iscsi_constants.h b/drivers/scsi/bnx2i/57xx_iscsi_constants.h index 57515f1f1690..495a841645f9 100644 --- a/drivers/scsi/bnx2i/57xx_iscsi_constants.h +++ b/drivers/scsi/bnx2i/57xx_iscsi_constants.h @@ -122,6 +122,7 @@ #define ISCSI_KCQE_COMPLETION_STATUS_LOM_ISCSI_NOT_ENABLED (0x51) #define ISCSI_KCQE_COMPLETION_STATUS_CID_BUSY (0x80) +#define ISCSI_KCQE_COMPLETION_STATUS_PARITY_ERR (0x81) /* SQ/RQ/CQ DB structure sizes */ #define ISCSI_SQ_DB_SIZE (16) diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index 82fa6ce481f0..5e69f468535f 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -132,7 +132,7 @@ static int mpt2sas_remove_dead_ioc_func(void *arg) pdev = ioc->pdev; if ((pdev == NULL)) return -1; - pci_remove_bus_device(pdev); + pci_stop_and_remove_bus_device(pdev); return 0; } diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 92d314a73f69..91b6d52f74eb 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -26,7 +26,7 @@ static void sh_clk_mstp32_disable(struct clk *clk) clk->mapped_reg); } -static struct clk_ops sh_clk_mstp32_clk_ops = { +static struct sh_clk_ops sh_clk_mstp32_clk_ops = { .enable = sh_clk_mstp32_enable, .disable = sh_clk_mstp32_disable, .recalc = followparent_recalc, @@ -150,7 +150,7 @@ static void sh_clk_div6_disable(struct clk *clk) iowrite32(value, clk->mapped_reg); } -static struct clk_ops sh_clk_div6_clk_ops = { +static struct sh_clk_ops sh_clk_div6_clk_ops = { .recalc = sh_clk_div6_recalc, .round_rate = sh_clk_div_round_rate, .set_rate = sh_clk_div6_set_rate, @@ -158,7 +158,7 @@ static struct clk_ops sh_clk_div6_clk_ops = { .disable = sh_clk_div6_disable, }; -static struct clk_ops sh_clk_div6_reparent_clk_ops = { +static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { .recalc = sh_clk_div6_recalc, .round_rate = sh_clk_div_round_rate, .set_rate = sh_clk_div6_set_rate, @@ -200,7 +200,7 @@ static int __init sh_clk_init_parent(struct clk *clk) } static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, - struct clk_ops *ops) + struct sh_clk_ops *ops) { struct clk *clkp; void *freq_table; @@ -317,13 +317,13 @@ static void sh_clk_div4_disable(struct clk *clk) iowrite32(ioread32(clk->mapped_reg) | (1 << 8), clk->mapped_reg); } -static struct clk_ops sh_clk_div4_clk_ops = { +static struct sh_clk_ops sh_clk_div4_clk_ops = { .recalc = sh_clk_div4_recalc, .set_rate = sh_clk_div4_set_rate, .round_rate = sh_clk_div_round_rate, }; -static struct clk_ops sh_clk_div4_enable_clk_ops = { +static struct sh_clk_ops sh_clk_div4_enable_clk_ops = { .recalc = sh_clk_div4_recalc, .set_rate = sh_clk_div4_set_rate, .round_rate = sh_clk_div_round_rate, @@ -331,7 +331,7 @@ static struct clk_ops sh_clk_div4_enable_clk_ops = { .disable = sh_clk_div4_disable, }; -static struct clk_ops sh_clk_div4_reparent_clk_ops = { +static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = { .recalc = sh_clk_div4_recalc, .set_rate = sh_clk_div4_set_rate, .round_rate = sh_clk_div_round_rate, @@ -341,7 +341,7 @@ static struct clk_ops sh_clk_div4_reparent_clk_ops = { }; static int __init sh_clk_div4_register_ops(struct clk *clks, int nr, - struct clk_div4_table *table, struct clk_ops *ops) + struct clk_div4_table *table, struct sh_clk_ops *ops) { struct clk *clkp; void *freq_table; diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 0b06e360628a..3ed748355b98 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -293,7 +293,7 @@ config SPI_RSPI config SPI_S3C24XX tristate "Samsung S3C24XX series SPI" - depends on ARCH_S3C2410 && EXPERIMENTAL + depends on ARCH_S3C24XX && EXPERIMENTAL select SPI_BITBANG help SPI driver for Samsung S3C24XX series ARM SoCs diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index fc064535f4fc..8ee7d790ce49 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -24,10 +24,10 @@ #include <linux/spi/spi.h> #include <linux/spi/spi_bitbang.h> +#include <linux/spi/s3c24xx.h> #include <linux/module.h> #include <plat/regs-spi.h> -#include <mach/spi.h> #include <plat/fiq.h> #include <asm/fiq.h> diff --git a/drivers/staging/iio/gyro/adis16060_core.c b/drivers/staging/iio/gyro/adis16060_core.c index c0ca7093e0ed..02cc23420b90 100644 --- a/drivers/staging/iio/gyro/adis16060_core.c +++ b/drivers/staging/iio/gyro/adis16060_core.c @@ -14,7 +14,6 @@ #include <linux/spi/spi.h> #include <linux/slab.h> #include <linux/sysfs.h> -#include <linux/module.h> #include "../iio.h" #include "../sysfs.h" diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 7e5caa39ed3f..4f4b7d6281a7 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -6,7 +6,7 @@ menuconfig STAGING_MEDIA don't have the "normal" Linux kernel quality level. Most of them don't follow properly the V4L, DVB and/or RC API's, so, they won't likely work fine with the existing applications. - That also means that, one fixed, their API's will change to match + That also means that, once fixed, their API's will change to match the existing ones. If you wish to work on these drivers, to help improve them, or diff --git a/drivers/staging/media/as102/as102_drv.c b/drivers/staging/media/as102/as102_drv.c index aae0505a36c4..ea4f992de235 100644 --- a/drivers/staging/media/as102/as102_drv.c +++ b/drivers/staging/media/as102/as102_drv.c @@ -27,7 +27,7 @@ #include <linux/uaccess.h> #include <linux/usb.h> -/* header file for Usb device driver*/ +/* header file for usb device driver*/ #include "as102_drv.h" #include "as102_fw.h" #include "dvbdev.h" diff --git a/drivers/staging/media/as102/as102_drv.h b/drivers/staging/media/as102/as102_drv.h index 957f0ed0d81a..b0e5a23bd532 100644 --- a/drivers/staging/media/as102/as102_drv.h +++ b/drivers/staging/media/as102/as102_drv.h @@ -76,7 +76,7 @@ struct as102_dev_t { struct as10x_bus_adapter_t bus_adap; struct list_head device_entry; struct kref kref; - unsigned long minor; + uint8_t elna_cfg; struct dvb_adapter dvb_adap; struct dvb_frontend dvb_fe; diff --git a/drivers/staging/media/as102/as102_fe.c b/drivers/staging/media/as102/as102_fe.c index bdc5a38cddf7..5917657b9d0f 100644 --- a/drivers/staging/media/as102/as102_fe.c +++ b/drivers/staging/media/as102/as102_fe.c @@ -265,7 +265,7 @@ static int as102_fe_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) if (acquire) { if (elna_enable) - as10x_cmd_set_context(&dev->bus_adap, 1010, 0xC0); + as10x_cmd_set_context(&dev->bus_adap, CONTEXT_LNA, dev->elna_cfg); ret = as10x_cmd_turn_on(&dev->bus_adap); } else { @@ -337,7 +337,7 @@ int as102_dvb_register_fe(struct as102_dev_t *as102_dev, strncpy(dvb_fe->ops.info.name, as102_dev->name, sizeof(dvb_fe->ops.info.name)); - /* register dbvb frontend */ + /* register dvb frontend */ errno = dvb_register_frontend(dvb_adap, dvb_fe); if (errno == 0) dvb_fe->tuner_priv = as102_dev; @@ -349,7 +349,7 @@ static void as10x_fe_copy_tps_parameters(struct dtv_frontend_properties *fe_tps, struct as10x_tps *as10x_tps) { - /* extract consteallation */ + /* extract constellation */ switch (as10x_tps->modulation) { case CONST_QPSK: fe_tps->modulation = QPSK; diff --git a/drivers/staging/media/as102/as102_fw.h b/drivers/staging/media/as102/as102_fw.h index bd21f0554392..4bfc6849d95a 100644 --- a/drivers/staging/media/as102/as102_fw.h +++ b/drivers/staging/media/as102/as102_fw.h @@ -29,7 +29,7 @@ struct as10x_fw_pkt_t { union { unsigned char request[2]; unsigned char length[2]; - } u; + } __packed u; struct as10x_raw_fw_pkt raw; } __packed; diff --git a/drivers/staging/media/as102/as102_usb_drv.c b/drivers/staging/media/as102/as102_usb_drv.c index d775be0173ea..0f6bfe7eccba 100644 --- a/drivers/staging/media/as102/as102_usb_drv.c +++ b/drivers/staging/media/as102/as102_usb_drv.c @@ -57,6 +57,17 @@ static const char * const as102_device_names[] = { NULL /* Terminating entry */ }; +/* eLNA configuration: devices built on the reference design work best + with 0xA0, while custom designs seem to require 0xC0 */ +static uint8_t const as102_elna_cfg[] = { + 0xA0, + 0xC0, + 0xC0, + 0xA0, + 0xA0, + 0x00 /* Terminating entry */ +}; + struct usb_driver as102_usb_driver = { .name = DRIVER_FULL_NAME, .probe = as102_usb_probe, @@ -270,6 +281,8 @@ static int as102_alloc_usb_stream_buffer(struct as102_dev_t *dev) } urb->transfer_buffer = dev->stream + (i * AS102_USB_BUF_SIZE); + urb->transfer_dma = dev->dma_addr + (i * AS102_USB_BUF_SIZE); + urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; urb->transfer_buffer_length = AS102_USB_BUF_SIZE; dev->stream_urb[i] = urb; @@ -369,8 +382,10 @@ static int as102_usb_probe(struct usb_interface *intf, /* Assign the user-friendly device name */ for (i = 0; i < (sizeof(as102_usb_id_table) / sizeof(struct usb_device_id)); i++) { - if (id == &as102_usb_id_table[i]) + if (id == &as102_usb_id_table[i]) { as102_dev->name = as102_device_names[i]; + as102_dev->elna_cfg = as102_elna_cfg[i]; + } } if (as102_dev->name == NULL) diff --git a/drivers/staging/media/as102/as10x_cmd.h b/drivers/staging/media/as102/as10x_cmd.h index 4ea249e7adab..e21ec6c702a9 100644 --- a/drivers/staging/media/as102/as10x_cmd.h +++ b/drivers/staging/media/as102/as10x_cmd.h @@ -99,14 +99,14 @@ union as10x_turn_on { struct { /* request identifier */ uint16_t proc_id; - } req; + } __packed req; /* response */ struct { /* response identifier */ uint16_t proc_id; /* error */ uint8_t error; - } rsp; + } __packed rsp; } __packed; union as10x_turn_off { @@ -114,14 +114,14 @@ union as10x_turn_off { struct { /* request identifier */ uint16_t proc_id; - } req; + } __packed req; /* response */ struct { /* response identifier */ uint16_t proc_id; /* error */ uint8_t err; - } rsp; + } __packed rsp; } __packed; union as10x_set_tune { @@ -131,14 +131,14 @@ union as10x_set_tune { uint16_t proc_id; /* tune params */ struct as10x_tune_args args; - } req; + } __packed req; /* response */ struct { /* response identifier */ uint16_t proc_id; /* response error */ uint8_t error; - } rsp; + } __packed rsp; } __packed; union as10x_get_tune_status { @@ -146,7 +146,7 @@ union as10x_get_tune_status { struct { /* request identifier */ uint16_t proc_id; - } req; + } __packed req; /* response */ struct { /* response identifier */ @@ -155,7 +155,7 @@ union as10x_get_tune_status { uint8_t error; /* tune status */ struct as10x_tune_status sts; - } rsp; + } __packed rsp; } __packed; union as10x_get_tps { @@ -163,7 +163,7 @@ union as10x_get_tps { struct { /* request identifier */ uint16_t proc_id; - } req; + } __packed req; /* response */ struct { /* response identifier */ @@ -172,7 +172,7 @@ union as10x_get_tps { uint8_t error; /* tps details */ struct as10x_tps tps; - } rsp; + } __packed rsp; } __packed; union as10x_common { @@ -180,14 +180,14 @@ union as10x_common { struct { /* request identifier */ uint16_t proc_id; - } req; + } __packed req; /* response */ struct { /* response identifier */ uint16_t proc_id; /* response error */ uint8_t error; - } rsp; + } __packed rsp; } __packed; union as10x_add_pid_filter { @@ -201,7 +201,7 @@ union as10x_add_pid_filter { uint8_t stream_type; /* PID index in filter table */ uint8_t idx; - } req; + } __packed req; /* response */ struct { /* response identifier */ @@ -210,7 +210,7 @@ union as10x_add_pid_filter { uint8_t error; /* Filter id */ uint8_t filter_id; - } rsp; + } __packed rsp; } __packed; union as10x_del_pid_filter { @@ -220,14 +220,14 @@ union as10x_del_pid_filter { uint16_t proc_id; /* PID to remove */ uint16_t pid; - } req; + } __packed req; /* response */ struct { /* response identifier */ uint16_t proc_id; /* response error */ uint8_t error; - } rsp; + } __packed rsp; } __packed; union as10x_start_streaming { @@ -235,14 +235,14 @@ union as10x_start_streaming { struct { /* request identifier */ uint16_t proc_id; - } req; + } __packed req; /* response */ struct { /* response identifier */ uint16_t proc_id; /* error */ uint8_t error; - } rsp; + } __packed rsp; } __packed; union as10x_stop_streaming { @@ -250,14 +250,14 @@ union as10x_stop_streaming { struct { /* request identifier */ uint16_t proc_id; - } req; + } __packed req; /* response */ struct { /* response identifier */ uint16_t proc_id; /* error */ uint8_t error; - } rsp; + } __packed rsp; } __packed; union as10x_get_demod_stats { @@ -265,7 +265,7 @@ union as10x_get_demod_stats { struct { /* request identifier */ uint16_t proc_id; - } req; + } __packed req; /* response */ struct { /* response identifier */ @@ -274,7 +274,7 @@ union as10x_get_demod_stats { uint8_t error; /* demod stats */ struct as10x_demod_stats stats; - } rsp; + } __packed rsp; } __packed; union as10x_get_impulse_resp { @@ -282,7 +282,7 @@ union as10x_get_impulse_resp { struct { /* request identifier */ uint16_t proc_id; - } req; + } __packed req; /* response */ struct { /* response identifier */ @@ -291,7 +291,7 @@ union as10x_get_impulse_resp { uint8_t error; /* impulse response ready */ uint8_t is_ready; - } rsp; + } __packed rsp; } __packed; union as10x_fw_context { @@ -305,7 +305,7 @@ union as10x_fw_context { uint16_t tag; /* context request type */ uint16_t type; - } req; + } __packed req; /* response */ struct { /* response identifier */ @@ -316,7 +316,7 @@ union as10x_fw_context { uint16_t type; /* error */ uint8_t error; - } rsp; + } __packed rsp; } __packed; union as10x_set_register { @@ -328,14 +328,14 @@ union as10x_set_register { struct as10x_register_addr reg_addr; /* register content */ struct as10x_register_value reg_val; - } req; + } __packed req; /* response */ struct { /* response identifier */ uint16_t proc_id; /* error */ uint8_t error; - } rsp; + } __packed rsp; } __packed; union as10x_get_register { @@ -345,7 +345,7 @@ union as10x_get_register { uint16_t proc_id; /* register description */ struct as10x_register_addr reg_addr; - } req; + } __packed req; /* response */ struct { /* response identifier */ @@ -354,7 +354,7 @@ union as10x_get_register { uint8_t error; /* register content */ struct as10x_register_value reg_val; - } rsp; + } __packed rsp; } __packed; union as10x_cfg_change_mode { @@ -364,14 +364,14 @@ union as10x_cfg_change_mode { uint16_t proc_id; /* mode */ uint8_t mode; - } req; + } __packed req; /* response */ struct { /* response identifier */ uint16_t proc_id; /* error */ uint8_t error; - } rsp; + } __packed rsp; } __packed; struct as10x_cmd_header_t { @@ -394,7 +394,7 @@ union as10x_dump_memory { struct as10x_register_addr reg_addr; /* nb blocks to read */ uint16_t num_blocks; - } req; + } __packed req; /* response */ struct { /* response identifier */ @@ -408,8 +408,8 @@ union as10x_dump_memory { uint8_t data8[DUMP_BLOCK_SIZE]; uint16_t data16[DUMP_BLOCK_SIZE / sizeof(uint16_t)]; uint32_t data32[DUMP_BLOCK_SIZE / sizeof(uint32_t)]; - } u; - } rsp; + } __packed u; + } __packed rsp; } __packed; union as10x_dumplog_memory { @@ -418,7 +418,7 @@ union as10x_dumplog_memory { uint16_t proc_id; /* dump memory type request */ uint8_t dump_req; - } req; + } __packed req; struct { /* request identifier */ uint16_t proc_id; @@ -428,7 +428,7 @@ union as10x_dumplog_memory { uint8_t dump_rsp; /* dump data */ uint8_t data[DUMP_BLOCK_SIZE]; - } rsp; + } __packed rsp; } __packed; union as10x_raw_data { @@ -437,14 +437,14 @@ union as10x_raw_data { uint16_t proc_id; uint8_t data[64 - sizeof(struct as10x_cmd_header_t) - 2 /* proc_id */]; - } req; + } __packed req; /* response */ struct { uint16_t proc_id; uint8_t error; uint8_t data[64 - sizeof(struct as10x_cmd_header_t) - 2 /* proc_id */ - 1 /* rc */]; - } rsp; + } __packed rsp; } __packed; struct as10x_cmd_t { @@ -469,7 +469,7 @@ struct as10x_cmd_t { union as10x_dump_memory dump_memory; union as10x_dumplog_memory dumplog_memory; union as10x_raw_data raw_data; - } body; + } __packed body; } __packed; struct as10x_token_cmd_t { diff --git a/drivers/staging/media/as102/as10x_types.h b/drivers/staging/media/as102/as10x_types.h index fde8140ae88b..af26e057d9a2 100644 --- a/drivers/staging/media/as102/as10x_types.h +++ b/drivers/staging/media/as102/as10x_types.h @@ -181,7 +181,7 @@ struct as10x_register_value { uint8_t value8; /* 8 bit value */ uint16_t value16; /* 16 bit value */ uint32_t value32; /* 32 bit value */ - } u; + } __packed u; } __packed; struct as10x_register_addr { diff --git a/drivers/staging/media/easycap/easycap_main.c b/drivers/staging/media/easycap/easycap_main.c index 3d439b790cc6..d0fe34afc2e5 100644 --- a/drivers/staging/media/easycap/easycap_main.c +++ b/drivers/staging/media/easycap/easycap_main.c @@ -2849,13 +2849,11 @@ static const struct v4l2_file_operations v4l2_fops = { .poll = easycap_poll, .mmap = easycap_mmap, }; -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ + /* - * WHEN THE EasyCAP IS PHYSICALLY PLUGGED IN, THIS FUNCTION IS CALLED THREE - * TIMES, ONCE FOR EACH OF THE THREE INTERFACES. BEWARE. + * When the device is plugged, this function is called three times, + * one for each interface. */ -/*---------------------------------------------------------------------------*/ static int easycap_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -2884,7 +2882,6 @@ static int easycap_usb_probe(struct usb_interface *intf, usbdev = interface_to_usbdev(intf); -/*---------------------------------------------------------------------------*/ alt = usb_altnum_to_altsetting(intf, 0); if (!alt) { SAY("ERROR: usb_host_interface not found\n"); @@ -2896,11 +2893,8 @@ static int easycap_usb_probe(struct usb_interface *intf, SAY("ERROR: intf_descriptor is NULL\n"); return -EFAULT; } -/*---------------------------------------------------------------------------*/ -/* - * GET PROPERTIES OF PROBED INTERFACE - */ -/*---------------------------------------------------------------------------*/ + + /* Get properties of probed interface */ bInterfaceNumber = interface->bInterfaceNumber; bInterfaceClass = interface->bInterfaceClass; bInterfaceSubClass = interface->bInterfaceSubClass; @@ -2912,28 +2906,23 @@ static int easycap_usb_probe(struct usb_interface *intf, (long int)(intf->cur_altsetting - intf->altsetting)); JOT(4, "intf[%i]: bInterfaceClass=0x%02X bInterfaceSubClass=0x%02X\n", bInterfaceNumber, bInterfaceClass, bInterfaceSubClass); -/*---------------------------------------------------------------------------*/ -/* - * A NEW struct easycap IS ALWAYS ALLOCATED WHEN INTERFACE 0 IS PROBED. - * IT IS NOT POSSIBLE HERE TO FREE ANY EXISTING struct easycap. THIS - * SHOULD HAVE BEEN DONE BY easycap_delete() WHEN THE EasyCAP WAS - * PHYSICALLY UNPLUGGED. - * - * THE POINTER peasycap TO THE struct easycap IS REMEMBERED WHEN - * INTERFACES 1 AND 2 ARE PROBED. -*/ -/*---------------------------------------------------------------------------*/ + + /* + * A new struct easycap is always allocated when interface 0 is probed. + * It is not possible here to free any existing struct easycap. + * This should have been done by easycap_delete() when the device was + * physically unplugged. + * The allocated struct easycap is saved for later usage when + * interfaces 1 and 2 are probed. + */ if (0 == bInterfaceNumber) { peasycap = kzalloc(sizeof(struct easycap), GFP_KERNEL); if (!peasycap) { SAY("ERROR: Could not allocate peasycap\n"); return -ENOMEM; } -/*---------------------------------------------------------------------------*/ -/* - * PERFORM URGENT INTIALIZATIONS ... -*/ -/*---------------------------------------------------------------------------*/ + + /* Perform urgent initializations */ peasycap->minor = -1; kref_init(&peasycap->kref); JOM(8, "intf[%i]: after kref_init(..._video) " @@ -2976,11 +2965,7 @@ static int easycap_usb_probe(struct usb_interface *intf, peasycap->allocation_video_struct = sizeof(struct easycap); -/*---------------------------------------------------------------------------*/ -/* - * ... AND FURTHER INITIALIZE THE STRUCTURE -*/ -/*---------------------------------------------------------------------------*/ + /* and further initialize the structure */ peasycap->pusb_device = usbdev; peasycap->pusb_interface = intf; @@ -3002,11 +2987,7 @@ static int easycap_usb_probe(struct usb_interface *intf, peasycap->frame_buffer_many = FRAME_BUFFER_MANY; -/*---------------------------------------------------------------------------*/ -/* - * DYNAMICALLY FILL IN THE AVAILABLE FORMATS ... - */ -/*---------------------------------------------------------------------------*/ + /* Dynamically fill in the available formats */ rc = easycap_video_fillin_formats(); if (0 > rc) { SAM("ERROR: fillin_formats() rc = %i\n", rc); @@ -3014,10 +2995,8 @@ static int easycap_usb_probe(struct usb_interface *intf, } JOM(4, "%i formats available\n", rc); - /* ... AND POPULATE easycap.inputset[] */ - + /* Populate easycap.inputset[] */ inputset = peasycap->inputset; - fmtidx = peasycap->ntsc ? NTSC_M : PAL_BGHIN; m = 0; mask = 0; @@ -3030,7 +3009,6 @@ static int easycap_usb_probe(struct usb_interface *intf, mask = easycap_standard[i].mask; } } - if (1 != m) { SAM("ERROR: " "inputset->standard_offset unpopulated, %i=m\n", m); @@ -3089,14 +3067,13 @@ static int easycap_usb_probe(struct usb_interface *intf, JOM(4, "populated inputset[]\n"); JOM(4, "finished initialization\n"); } else { -/*---------------------------------------------------------------------------*/ -/* - * FIXME - * - * IDENTIFY THE APPROPRIATE POINTER peasycap FOR INTERFACES 1 AND 2. - * THE ADDRESS OF peasycap->pusb_device IS RELUCTANTLY USED FOR THIS PURPOSE. - */ -/*---------------------------------------------------------------------------*/ + + /* + * FIXME: Identify the appropriate pointer + * peasycap for interfaces 1 and 2. + * The address of peasycap->pusb_device + * is reluctantly used for this purpose. + */ for (ndong = 0; ndong < DONGLE_MANY; ndong++) { if (usbdev == easycapdc60_dongle[ndong].peasycap-> pusb_device) { @@ -3117,7 +3094,7 @@ static int easycap_usb_probe(struct usb_interface *intf, return -ENODEV; } } -/*---------------------------------------------------------------------------*/ + if ((USB_CLASS_VIDEO == bInterfaceClass) || (USB_CLASS_VENDOR_SPEC == bInterfaceClass)) { if (-1 == peasycap->video_interface) { @@ -3149,14 +3126,12 @@ static int easycap_usb_probe(struct usb_interface *intf, } } } -/*---------------------------------------------------------------------------*/ -/* - * INVESTIGATE ALL ALTSETTINGS. - * DONE IN DETAIL BECAUSE USB DEVICE 05e1:0408 HAS DISPARATE INCARNATIONS. - */ -/*---------------------------------------------------------------------------*/ - isokalt = 0; + /* + * Investigate all altsettings. This is done in detail + * because USB device 05e1:0408 has disparate incarnations. + */ + isokalt = 0; for (i = 0; i < intf->num_altsetting; i++) { alt = usb_altnum_to_altsetting(intf, i); if (!alt) { @@ -3172,7 +3147,6 @@ static int easycap_usb_probe(struct usb_interface *intf, if (0 == interface->bNumEndpoints) JOM(4, "intf[%i]alt[%i] has no endpoints\n", bInterfaceNumber, i); -/*---------------------------------------------------------------------------*/ for (j = 0; j < interface->bNumEndpoints; j++) { ep = &alt->endpoint[j].desc; if (!ep) { @@ -3312,19 +3286,12 @@ static int easycap_usb_probe(struct usb_interface *intf, } } } -/*---------------------------------------------------------------------------*/ -/* - * PERFORM INITIALIZATION OF THE PROBED INTERFACE - */ -/*---------------------------------------------------------------------------*/ + + /* Perform initialization of the probed interface */ JOM(4, "initialization begins for interface %i\n", interface->bInterfaceNumber); switch (bInterfaceNumber) { -/*---------------------------------------------------------------------------*/ -/* - * INTERFACE 0 IS THE VIDEO INTERFACE - */ -/*---------------------------------------------------------------------------*/ + /* 0: Video interface */ case 0: { if (!peasycap) { SAM("MISTAKE: peasycap is NULL\n"); @@ -3337,11 +3304,8 @@ static int easycap_usb_probe(struct usb_interface *intf, peasycap->video_altsetting_on = okalt[isokalt - 1]; JOM(4, "%i=video_altsetting_on <====\n", peasycap->video_altsetting_on); -/*---------------------------------------------------------------------------*/ -/* - * DECIDE THE VIDEO STREAMING PARAMETERS - */ -/*---------------------------------------------------------------------------*/ + + /* Decide video streaming parameters */ peasycap->video_endpointnumber = okepn[isokalt - 1]; JOM(4, "%i=video_endpointnumber\n", peasycap->video_endpointnumber); maxpacketsize = okmps[isokalt - 1]; @@ -3373,7 +3337,6 @@ static int easycap_usb_probe(struct usb_interface *intf, SAM("MISTAKE: peasycap->video_isoc_buffer_size too big\n"); return -EFAULT; } -/*---------------------------------------------------------------------------*/ if (-1 == peasycap->video_interface) { SAM("MISTAKE: video_interface is unset\n"); return -EFAULT; @@ -3398,14 +3361,13 @@ static int easycap_usb_probe(struct usb_interface *intf, SAM("MISTAKE: video_isoc_buffer_size is unset\n"); return -EFAULT; } -/*---------------------------------------------------------------------------*/ -/* - * ALLOCATE MEMORY FOR VIDEO BUFFERS. LISTS MUST BE INITIALIZED FIRST. - */ -/*---------------------------------------------------------------------------*/ + + /* + * Allocate memory for video buffers. + * Lists must be initialized first. + */ INIT_LIST_HEAD(&(peasycap->urb_video_head)); peasycap->purb_video_head = &(peasycap->urb_video_head); -/*---------------------------------------------------------------------------*/ JOM(4, "allocating %i frame buffers of size %li\n", FRAME_BUFFER_MANY, (long int)FRAME_BUFFER_SIZE); JOM(4, ".... each scattered over %li pages\n", @@ -3436,7 +3398,6 @@ static int easycap_usb_probe(struct usb_interface *intf, peasycap->frame_read = 0; JOM(4, "allocation of frame buffers done: %i pages\n", k * m); -/*---------------------------------------------------------------------------*/ JOM(4, "allocating %i field buffers of size %li\n", FIELD_BUFFER_MANY, (long int)FIELD_BUFFER_SIZE); JOM(4, ".... each scattered over %li pages\n", @@ -3468,7 +3429,6 @@ static int easycap_usb_probe(struct usb_interface *intf, peasycap->field_read = 0; JOM(4, "allocation of field buffers done: %i pages\n", k * m); -/*---------------------------------------------------------------------------*/ JOM(4, "allocating %i isoc video buffers of size %i\n", VIDEO_ISOC_BUFFER_MANY, peasycap->video_isoc_buffer_size); @@ -3492,11 +3452,8 @@ static int easycap_usb_probe(struct usb_interface *intf, } JOM(4, "allocation of isoc video buffers done: %i pages\n", k * (0x01 << VIDEO_ISOC_ORDER)); -/*---------------------------------------------------------------------------*/ -/* - * ALLOCATE AND INITIALIZE MULTIPLE struct urb ... - */ -/*---------------------------------------------------------------------------*/ + + /* Allocate and initialize multiple struct usb */ JOM(4, "allocating %i struct urb.\n", VIDEO_ISOC_BUFFER_MANY); JOM(4, "using %i=peasycap->video_isoc_framesperdesc\n", peasycap->video_isoc_framesperdesc); @@ -3515,7 +3472,6 @@ static int easycap_usb_probe(struct usb_interface *intf, } peasycap->allocation_video_urb += 1; -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL); if (!pdata_urb) { SAM("ERROR: Could not allocate struct data_urb.\n"); @@ -3530,11 +3486,8 @@ static int easycap_usb_probe(struct usb_interface *intf, pdata_urb->length = 0; list_add_tail(&(pdata_urb->list_head), peasycap->purb_video_head); -/*---------------------------------------------------------------------------*/ -/* - * ... AND INITIALIZE THEM - */ -/*---------------------------------------------------------------------------*/ + + /* Initialize allocated urbs */ if (!k) { JOM(4, "initializing video urbs thus:\n"); JOM(4, " purb->interval = 1;\n"); @@ -3582,20 +3535,16 @@ static int easycap_usb_probe(struct usb_interface *intf, } } JOM(4, "allocation of %i struct urb done.\n", k); -/*--------------------------------------------------------------------------*/ -/* - * SAVE POINTER peasycap IN THIS INTERFACE. - */ -/*--------------------------------------------------------------------------*/ + + /* Save pointer peasycap in this interface */ usb_set_intfdata(intf, peasycap); -/*---------------------------------------------------------------------------*/ -/* - * IT IS ESSENTIAL TO INITIALIZE THE HARDWARE BEFORE, RATHER THAN AFTER, - * THE DEVICE IS REGISTERED, BECAUSE SOME VERSIONS OF THE videodev MODULE - * CALL easycap_open() IMMEDIATELY AFTER REGISTRATION, CAUSING A CLASH. - * BEWARE. -*/ -/*---------------------------------------------------------------------------*/ + + /* + * It is essential to initialize the hardware before, + * rather than after, the device is registered, + * because some udev rules triggers easycap_open() + * immediately after registration, causing a clash. + */ peasycap->ntsc = easycap_ntsc; JOM(8, "defaulting initially to %s\n", easycap_ntsc ? "NTSC" : "PAL"); @@ -3604,27 +3553,20 @@ static int easycap_usb_probe(struct usb_interface *intf, SAM("ERROR: reset() rc = %i\n", rc); return -EFAULT; } -/*--------------------------------------------------------------------------*/ -/* - * THE VIDEO DEVICE CAN BE REGISTERED NOW, AS IT IS READY. - */ -/*--------------------------------------------------------------------------*/ + + /* The video device can now be registered */ if (v4l2_device_register(&intf->dev, &peasycap->v4l2_device)) { SAM("v4l2_device_register() failed\n"); return -ENODEV; } JOM(4, "registered device instance: %s\n", peasycap->v4l2_device.name); -/*---------------------------------------------------------------------------*/ -/* - * FIXME - * - * - * THIS IS BELIEVED TO BE HARMLESS, BUT MAY WELL BE UNNECESSARY OR WRONG: -*/ -/*---------------------------------------------------------------------------*/ + + /* + * FIXME: This is believed to be harmless, + * but may well be unnecessary or wrong. + */ peasycap->video_device.v4l2_dev = NULL; -/*---------------------------------------------------------------------------*/ strcpy(&peasycap->video_device.name[0], "easycapdc60"); @@ -3648,28 +3590,19 @@ static int easycap_usb_probe(struct usb_interface *intf, break; } -/*--------------------------------------------------------------------------*/ -/* - * INTERFACE 1 IS THE AUDIO CONTROL INTERFACE - * INTERFACE 2 IS THE AUDIO STREAMING INTERFACE - */ -/*--------------------------------------------------------------------------*/ + /* 1: Audio control */ case 1: { if (!peasycap) { SAM("MISTAKE: peasycap is NULL\n"); return -EFAULT; } -/*--------------------------------------------------------------------------*/ -/* - * SAVE POINTER peasycap IN INTERFACE 1 - */ -/*--------------------------------------------------------------------------*/ + /* Save pointer peasycap in this interface */ usb_set_intfdata(intf, peasycap); JOM(4, "no initialization required for interface %i\n", interface->bInterfaceNumber); break; } -/*--------------------------------------------------------------------------*/ + /* 2: Audio streaming */ case 2: { if (!peasycap) { SAM("MISTAKE: peasycap is NULL\n"); @@ -3769,15 +3702,14 @@ static int easycap_usb_probe(struct usb_interface *intf, SAM("MISTAKE: audio_isoc_buffer_size is unset\n"); return -EFAULT; } -/*---------------------------------------------------------------------------*/ -/* - * ALLOCATE MEMORY FOR AUDIO BUFFERS. LISTS MUST BE INITIALIZED FIRST. - */ -/*---------------------------------------------------------------------------*/ + + /* + * Allocate memory for audio buffers. + * Lists must be initialized first. + */ INIT_LIST_HEAD(&(peasycap->urb_audio_head)); peasycap->purb_audio_head = &(peasycap->urb_audio_head); -/*---------------------------------------------------------------------------*/ JOM(4, "allocating %i isoc audio buffers of size %i\n", AUDIO_ISOC_BUFFER_MANY, peasycap->audio_isoc_buffer_size); @@ -3800,11 +3732,8 @@ static int easycap_usb_probe(struct usb_interface *intf, peasycap->audio_isoc_buffer[k].kount = k; } JOM(4, "allocation of isoc audio buffers done.\n"); -/*---------------------------------------------------------------------------*/ -/* - * ALLOCATE AND INITIALIZE MULTIPLE struct urb ... - */ -/*---------------------------------------------------------------------------*/ + + /* Allocate and initialize urbs */ JOM(4, "allocating %i struct urb.\n", AUDIO_ISOC_BUFFER_MANY); JOM(4, "using %i=peasycap->audio_isoc_framesperdesc\n", peasycap->audio_isoc_framesperdesc); @@ -3822,7 +3751,6 @@ static int easycap_usb_probe(struct usb_interface *intf, return -ENOMEM; } peasycap->allocation_audio_urb += 1 ; -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL); if (!pdata_urb) { usb_free_urb(purb); @@ -3837,11 +3765,7 @@ static int easycap_usb_probe(struct usb_interface *intf, pdata_urb->length = 0; list_add_tail(&(pdata_urb->list_head), peasycap->purb_audio_head); -/*---------------------------------------------------------------------------*/ -/* - * ... AND INITIALIZE THEM - */ -/*---------------------------------------------------------------------------*/ + if (!k) { JOM(4, "initializing audio urbs thus:\n"); JOM(4, " purb->interval = 1;\n"); @@ -3889,17 +3813,11 @@ static int easycap_usb_probe(struct usb_interface *intf, } } JOM(4, "allocation of %i struct urb done.\n", k); -/*---------------------------------------------------------------------------*/ -/* - * SAVE POINTER peasycap IN THIS INTERFACE. - */ -/*---------------------------------------------------------------------------*/ + + /* Save pointer peasycap in this interface */ usb_set_intfdata(intf, peasycap); -/*---------------------------------------------------------------------------*/ -/* - * THE AUDIO DEVICE CAN BE REGISTERED NOW, AS IT IS READY. - */ -/*---------------------------------------------------------------------------*/ + + /* The audio device can now be registered */ JOM(4, "initializing ALSA card\n"); rc = easycap_alsa_probe(peasycap); @@ -3915,11 +3833,7 @@ static int easycap_usb_probe(struct usb_interface *intf, peasycap->registered_audio++; break; } -/*---------------------------------------------------------------------------*/ -/* - * INTERFACES OTHER THAN 0, 1 AND 2 ARE UNEXPECTED - */ -/*---------------------------------------------------------------------------*/ + /* Interfaces other than 0,1,2 are unexpected */ default: JOM(4, "ERROR: unexpected interface %i\n", bInterfaceNumber); return -EINVAL; diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index 2b27d8da70a2..f91658670e34 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -1050,15 +1050,15 @@ static int vidioc_s_parm(struct file *filp, void *priv, return 0; } -/* VIDIOC_ENUMSTD on go7007 were used for enumberating the supported fps and +/* VIDIOC_ENUMSTD on go7007 were used for enumerating the supported fps and its resolution, when the device is not connected to TV. - This were an API abuse, probably used by the lack of specific IOCTL's to - enumberate it, by the time the driver were written. + This is were an API abuse, probably used by the lack of specific IOCTL's to + enumerate it, by the time the driver was written. However, since kernel 2.6.19, two new ioctls (VIDIOC_ENUM_FRAMEINTERVALS and VIDIOC_ENUM_FRAMESIZES) were added for this purpose. - The two functions bellow implements the newer ioctls + The two functions below implement the newer ioctls */ static int vidioc_enum_framesizes(struct file *filp, void *priv, struct v4l2_frmsizeenum *fsize) diff --git a/drivers/staging/media/go7007/s2250-board.c b/drivers/staging/media/go7007/s2250-board.c index e7736a915530..014d38410c99 100644 --- a/drivers/staging/media/go7007/s2250-board.c +++ b/drivers/staging/media/go7007/s2250-board.c @@ -192,6 +192,7 @@ static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val) { struct go7007 *go = i2c_get_adapdata(client->adapter); struct go7007_usb *usb; + int rc; u8 *buf; struct s2250 *dec = i2c_get_clientdata(client); @@ -216,12 +217,13 @@ static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val) kfree(buf); return -EINTR; } - if (go7007_usb_vendor_request(go, 0x57, addr, val, buf, 16, 1) < 0) { + rc = go7007_usb_vendor_request(go, 0x57, addr, val, buf, 16, 1); + mutex_unlock(&usb->i2c_lock); + if (rc < 0) { kfree(buf); - return -EFAULT; + return rc; } - mutex_unlock(&usb->i2c_lock); if (buf[0] == 0) { unsigned int subaddr, val_read; @@ -254,6 +256,7 @@ static int read_reg_fp(struct i2c_client *client, u16 addr, u16 *val) { struct go7007 *go = i2c_get_adapdata(client->adapter); struct go7007_usb *usb; + int rc; u8 *buf; if (go == NULL) @@ -276,11 +279,12 @@ static int read_reg_fp(struct i2c_client *client, u16 addr, u16 *val) kfree(buf); return -EINTR; } - if (go7007_usb_vendor_request(go, 0x58, addr, 0, buf, 16, 1) < 0) { + rc = go7007_usb_vendor_request(go, 0x58, addr, 0, buf, 16, 1); + mutex_unlock(&usb->i2c_lock); + if (rc < 0) { kfree(buf); - return -EFAULT; + return rc; } - mutex_unlock(&usb->i2c_lock); *val = (buf[0] << 8) | buf[1]; kfree(buf); diff --git a/drivers/staging/media/lirc/lirc_serial.c b/drivers/staging/media/lirc/lirc_serial.c index 8dd8897ad860..97352cf6bd98 100644 --- a/drivers/staging/media/lirc/lirc_serial.c +++ b/drivers/staging/media/lirc/lirc_serial.c @@ -1282,7 +1282,7 @@ MODULE_PARM_DESC(iommap, "physical base for memory mapped I/O" /* * some architectures (e.g. intel xscale) align the 8bit serial registers * on 32bit word boundaries. - * See linux-kernel/serial/8250.c serial_in()/out() + * See linux-kernel/drivers/tty/serial/8250/8250.c serial_in()/out() */ module_param(ioshift, int, S_IRUGO); MODULE_PARM_DESC(ioshift, "shift I/O register offset (0 = no shift)"); diff --git a/drivers/staging/media/solo6x10/Kconfig b/drivers/staging/media/solo6x10/Kconfig index 03dcac4ea4d0..63352de5eabf 100644 --- a/drivers/staging/media/solo6x10/Kconfig +++ b/drivers/staging/media/solo6x10/Kconfig @@ -5,4 +5,4 @@ config SOLO6X10 select SND_PCM ---help--- This driver supports the Softlogic based MPEG-4 and h.264 codec - codec cards. + cards. diff --git a/drivers/staging/media/solo6x10/core.c b/drivers/staging/media/solo6x10/core.c index f974f6412ad7..d2fd842e37cf 100644 --- a/drivers/staging/media/solo6x10/core.c +++ b/drivers/staging/media/solo6x10/core.c @@ -195,28 +195,28 @@ static int __devinit solo_pci_probe(struct pci_dev *pdev, SOLO6010_SYS_CFG_OUTDIV(3); solo_reg_write(solo_dev, SOLO_SYS_CFG, reg); - if (solo_dev->flags & FLAGS_6110) { - u32 sys_clock_MHz = SOLO_CLOCK_MHZ; - u32 pll_DIVQ; - u32 pll_DIVF; - - if (sys_clock_MHz < 125) { - pll_DIVQ = 3; - pll_DIVF = (sys_clock_MHz * 4) / 3; - } else { - pll_DIVQ = 2; - pll_DIVF = (sys_clock_MHz * 2) / 3; - } - - solo_reg_write(solo_dev, SOLO6110_PLL_CONFIG, + if (solo_dev->flags & FLAGS_6110) { + u32 sys_clock_MHz = SOLO_CLOCK_MHZ; + u32 pll_DIVQ; + u32 pll_DIVF; + + if (sys_clock_MHz < 125) { + pll_DIVQ = 3; + pll_DIVF = (sys_clock_MHz * 4) / 3; + } else { + pll_DIVQ = 2; + pll_DIVF = (sys_clock_MHz * 2) / 3; + } + + solo_reg_write(solo_dev, SOLO6110_PLL_CONFIG, SOLO6110_PLL_RANGE_5_10MHZ | SOLO6110_PLL_DIVR(9) | SOLO6110_PLL_DIVQ_EXP(pll_DIVQ) | SOLO6110_PLL_DIVF(pll_DIVF) | SOLO6110_PLL_FSEN); - mdelay(1); // PLL Locking time (1ms) + mdelay(1); /* PLL Locking time (1ms) */ solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 3 << 8); /* ? */ - } else + } else solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 1 << 8); /* ? */ solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM, SOLO_CLOCK_MHZ - 1); diff --git a/drivers/staging/rtl8187se/r8180_core.c b/drivers/staging/rtl8187se/r8180_core.c index e4ade550cfe5..4fe52f6b0034 100644 --- a/drivers/staging/rtl8187se/r8180_core.c +++ b/drivers/staging/rtl8187se/r8180_core.c @@ -4159,7 +4159,7 @@ void GPIOChangeRFWorkItemCallBack(struct work_struct *work) argv[0] = RadioPowerPath; argv[2] = NULL; - call_usermodehelper(RadioPowerPath, argv, envp, 1); + call_usermodehelper(RadioPowerPath, argv, envp, UMH_WAIT_PROC); } } diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c b/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c index a7fa9aad6f2d..f026b7171f62 100644 --- a/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c @@ -208,7 +208,7 @@ static void dm_check_ac_dc_power(struct net_device *dev) if (priv->rtllib->state != RTLLIB_LINKED) return; - call_usermodehelper(ac_dc_check_script_path, argv, envp, 1); + call_usermodehelper(ac_dc_check_script_path, argv, envp, UMH_WAIT_PROC); return; }; @@ -2296,7 +2296,7 @@ void dm_CheckRfCtrlGPIO(void *data) argv[0] = RadioPowerPath; argv[2] = NULL; - call_usermodehelper(RadioPowerPath, argv, envp, 1); + call_usermodehelper(RadioPowerPath, argv, envp, UMH_WAIT_PROC); } } diff --git a/drivers/staging/telephony/ixj.c b/drivers/staging/telephony/ixj.c index d5f923bcdffe..f96027921f60 100644 --- a/drivers/staging/telephony/ixj.c +++ b/drivers/staging/telephony/ixj.c @@ -5927,7 +5927,8 @@ static void add_caps(IXJ *j) j->caplist[j->caps].cap = PHONE_VENDOR_QUICKNET; strcpy(j->caplist[j->caps].desc, "Quicknet Technologies, Inc. (www.quicknet.net)"); j->caplist[j->caps].captype = vendor; - j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].handle = j->caps; + j->caps++; j->caplist[j->caps].captype = device; switch (j->cardtype) { case QTI_PHONEJACK: @@ -5947,11 +5948,13 @@ static void add_caps(IXJ *j) break; } j->caplist[j->caps].cap = j->cardtype; - j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].handle = j->caps; + j->caps++; strcpy(j->caplist[j->caps].desc, "POTS"); j->caplist[j->caps].captype = port; j->caplist[j->caps].cap = pots; - j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].handle = j->caps; + j->caps++; /* add devices that can do speaker/mic */ switch (j->cardtype) { @@ -5962,7 +5965,8 @@ static void add_caps(IXJ *j) strcpy(j->caplist[j->caps].desc, "SPEAKER"); j->caplist[j->caps].captype = port; j->caplist[j->caps].cap = speaker; - j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].handle = j->caps; + j->caps++; default: break; } @@ -5973,7 +5977,8 @@ static void add_caps(IXJ *j) strcpy(j->caplist[j->caps].desc, "HANDSET"); j->caplist[j->caps].captype = port; j->caplist[j->caps].cap = handset; - j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].handle = j->caps; + j->caps++; break; default: break; @@ -5985,7 +5990,8 @@ static void add_caps(IXJ *j) strcpy(j->caplist[j->caps].desc, "PSTN"); j->caplist[j->caps].captype = port; j->caplist[j->caps].cap = pstn; - j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].handle = j->caps; + j->caps++; break; default: break; @@ -5995,50 +6001,59 @@ static void add_caps(IXJ *j) strcpy(j->caplist[j->caps].desc, "ULAW"); j->caplist[j->caps].captype = codec; j->caplist[j->caps].cap = ULAW; - j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].handle = j->caps; + j->caps++; strcpy(j->caplist[j->caps].desc, "LINEAR 16 bit"); j->caplist[j->caps].captype = codec; j->caplist[j->caps].cap = LINEAR16; - j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].handle = j->caps; + j->caps++; strcpy(j->caplist[j->caps].desc, "LINEAR 8 bit"); j->caplist[j->caps].captype = codec; j->caplist[j->caps].cap = LINEAR8; - j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].handle = j->caps; + j->caps++; strcpy(j->caplist[j->caps].desc, "Windows Sound System"); j->caplist[j->caps].captype = codec; j->caplist[j->caps].cap = WSS; - j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].handle = j->caps; + j->caps++; /* software ALAW codec, made from ULAW */ strcpy(j->caplist[j->caps].desc, "ALAW"); j->caplist[j->caps].captype = codec; j->caplist[j->caps].cap = ALAW; - j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].handle = j->caps; + j->caps++; /* version 12 of the 8020 does the following codecs in a broken way */ if (j->dsp.low != 0x20 || j->ver.low != 0x12) { strcpy(j->caplist[j->caps].desc, "G.723.1 6.3kbps"); j->caplist[j->caps].captype = codec; j->caplist[j->caps].cap = G723_63; - j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].handle = j->caps; + j->caps++; strcpy(j->caplist[j->caps].desc, "G.723.1 5.3kbps"); j->caplist[j->caps].captype = codec; j->caplist[j->caps].cap = G723_53; - j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].handle = j->caps; + j->caps++; strcpy(j->caplist[j->caps].desc, "TrueSpeech 4.8kbps"); j->caplist[j->caps].captype = codec; j->caplist[j->caps].cap = TS48; - j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].handle = j->caps; + j->caps++; strcpy(j->caplist[j->caps].desc, "TrueSpeech 4.1kbps"); j->caplist[j->caps].captype = codec; j->caplist[j->caps].cap = TS41; - j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].handle = j->caps; + j->caps++; } /* 8020 chips can do TS8.5 native, and 8021/8022 can load it */ @@ -6046,7 +6061,8 @@ static void add_caps(IXJ *j) strcpy(j->caplist[j->caps].desc, "TrueSpeech 8.5kbps"); j->caplist[j->caps].captype = codec; j->caplist[j->caps].cap = TS85; - j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].handle = j->caps; + j->caps++; } /* 8021 chips can do G728 */ @@ -6054,7 +6070,8 @@ static void add_caps(IXJ *j) strcpy(j->caplist[j->caps].desc, "G.728 16kbps"); j->caplist[j->caps].captype = codec; j->caplist[j->caps].cap = G728; - j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].handle = j->caps; + j->caps++; } /* 8021/8022 chips can do G729 if loaded */ @@ -6062,13 +6079,15 @@ static void add_caps(IXJ *j) strcpy(j->caplist[j->caps].desc, "G.729A 8kbps"); j->caplist[j->caps].captype = codec; j->caplist[j->caps].cap = G729; - j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].handle = j->caps; + j->caps++; } if (j->dsp.low != 0x20 && j->flags.g729_loaded) { strcpy(j->caplist[j->caps].desc, "G.729B 8kbps"); j->caplist[j->caps].captype = codec; j->caplist[j->caps].cap = G729B; - j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].handle = j->caps; + j->caps++; } } diff --git a/drivers/staging/wlags49_h2/hcf.c b/drivers/staging/wlags49_h2/hcf.c index b008773323b3..5957c3a439ac 100644 --- a/drivers/staging/wlags49_h2/hcf.c +++ b/drivers/staging/wlags49_h2/hcf.c @@ -91,6 +91,7 @@ #include "hcf.h" // HCF and MSF common include file #include "hcfdef.h" // HCF specific include file #include "mmd.h" // MoreModularDriver common include file +#include <linux/bug.h> #include <linux/kernel.h> #if ! defined offsetof diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 10605ecc99ab..f9a6be7a9bed 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1526,6 +1526,8 @@ void __init atmel_register_uart_fns(struct atmel_port_fns *fns) atmel_pops.set_wake = fns->set_wake; } +struct platform_device *atmel_default_console_device; /* the serial console device */ + #ifdef CONFIG_SERIAL_ATMEL_CONSOLE static void atmel_console_putchar(struct uart_port *port, int ch) { diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 0b7fed746b27..e7feceeebc2f 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1508,7 +1508,7 @@ static int serial_imx_probe(struct platform_device *pdev) ret = PTR_ERR(sport->clk); goto unmap; } - clk_enable(sport->clk); + clk_prepare_enable(sport->clk); sport->port.uartclk = clk_get_rate(sport->clk); @@ -1531,8 +1531,8 @@ deinit: if (pdata && pdata->exit) pdata->exit(pdev); clkput: + clk_disable_unprepare(sport->clk); clk_put(sport->clk); - clk_disable(sport->clk); unmap: iounmap(sport->port.membase); free: @@ -1552,11 +1552,10 @@ static int serial_imx_remove(struct platform_device *pdev) if (sport) { uart_remove_one_port(&imx_reg, &sport->port); + clk_disable_unprepare(sport->clk); clk_put(sport->clk); } - clk_disable(sport->clk); - if (pdata && pdata->exit) pdata->exit(pdev); diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index e2fd3d8e0ab4..5847a4b855f7 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -36,6 +36,7 @@ #include <linux/circ_buf.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/tty.h> #include <linux/tty_flip.h> @@ -44,6 +45,8 @@ #include <linux/io.h> #include <linux/slab.h> +#define PXA_NAME_LEN 8 + struct uart_pxa_port { struct uart_port port; unsigned char ier; @@ -51,7 +54,7 @@ struct uart_pxa_port { unsigned char mcr; unsigned int lsr_break_flag; struct clk *clk; - char *name; + char name[PXA_NAME_LEN]; }; static inline unsigned int serial_in(struct uart_pxa_port *up, int offset) @@ -781,6 +784,31 @@ static const struct dev_pm_ops serial_pxa_pm_ops = { }; #endif +static struct of_device_id serial_pxa_dt_ids[] = { + { .compatible = "mrvl,pxa-uart", }, + { .compatible = "mrvl,mmp-uart", }, + {} +}; +MODULE_DEVICE_TABLE(of, serial_pxa_dt_ids); + +static int serial_pxa_probe_dt(struct platform_device *pdev, + struct uart_pxa_port *sport) +{ + struct device_node *np = pdev->dev.of_node; + int ret; + + if (!np) + return 1; + + ret = of_alias_get_id(np, "serial"); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); + return ret; + } + sport->port.line = ret; + return 0; +} + static int serial_pxa_probe(struct platform_device *dev) { struct uart_pxa_port *sport; @@ -808,20 +836,16 @@ static int serial_pxa_probe(struct platform_device *dev) sport->port.irq = irqres->start; sport->port.fifosize = 64; sport->port.ops = &serial_pxa_pops; - sport->port.line = dev->id; sport->port.dev = &dev->dev; sport->port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; sport->port.uartclk = clk_get_rate(sport->clk); - switch (dev->id) { - case 0: sport->name = "FFUART"; break; - case 1: sport->name = "BTUART"; break; - case 2: sport->name = "STUART"; break; - case 3: sport->name = "HWUART"; break; - default: - sport->name = "???"; - break; - } + ret = serial_pxa_probe_dt(dev, sport); + if (ret > 0) + sport->port.line = dev->id; + else if (ret < 0) + goto err_clk; + snprintf(sport->name, PXA_NAME_LEN - 1, "UART%d", sport->port.line + 1); sport->port.membase = ioremap(mmres->start, resource_size(mmres)); if (!sport->port.membase) { @@ -829,7 +853,7 @@ static int serial_pxa_probe(struct platform_device *dev) goto err_clk; } - serial_pxa_ports[dev->id] = sport; + serial_pxa_ports[sport->port.line] = sport; uart_add_one_port(&serial_pxa_reg, &sport->port); platform_set_drvdata(dev, sport); @@ -866,6 +890,7 @@ static struct platform_driver serial_pxa_driver = { #ifdef CONFIG_PM .pm = &serial_pxa_pm_ops, #endif + .of_match_table = serial_pxa_dt_ids, }, }; diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c index ef7a21a6a01b..2ca5959ec3fa 100644 --- a/drivers/tty/serial/sa1100.c +++ b/drivers/tty/serial/sa1100.c @@ -38,6 +38,7 @@ #include <asm/irq.h> #include <mach/hardware.h> +#include <mach/irqs.h> #include <asm/mach/serial_sa1100.h> /* We've been assigned a range on the "Low-density serial ports" major */ diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index a60523fee11b..5b3eda2024fe 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -22,7 +22,7 @@ #include <linux/io.h> #include <asm/irq.h> #include <asm/mach/irq.h> -#include <linux/pinctrl/pinmux.h> +#include <linux/pinctrl/consumer.h> #include "sirfsoc_uart.h" @@ -673,12 +673,10 @@ int sirfsoc_uart_probe(struct platform_device *pdev) port->irq = res->start; if (sirfport->hw_flow_ctrl) { - sirfport->pmx = pinmux_get(&pdev->dev, NULL); - ret = IS_ERR(sirfport->pmx); + sirfport->p = pinctrl_get_select_default(&pdev->dev); + ret = IS_ERR(sirfport->p); if (ret) - goto pmx_err; - - pinmux_enable(sirfport->pmx); + goto pin_err; } port->ops = &sirfsoc_uart_ops; @@ -695,11 +693,9 @@ int sirfsoc_uart_probe(struct platform_device *pdev) port_err: platform_set_drvdata(pdev, NULL); - if (sirfport->hw_flow_ctrl) { - pinmux_disable(sirfport->pmx); - pinmux_put(sirfport->pmx); - } -pmx_err: + if (sirfport->hw_flow_ctrl) + pinctrl_put(sirfport->p); +pin_err: irq_err: devm_iounmap(&pdev->dev, port->membase); err: @@ -711,10 +707,8 @@ static int sirfsoc_uart_remove(struct platform_device *pdev) struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev); struct uart_port *port = &sirfport->port; platform_set_drvdata(pdev, NULL); - if (sirfport->hw_flow_ctrl) { - pinmux_disable(sirfport->pmx); - pinmux_put(sirfport->pmx); - } + if (sirfport->hw_flow_ctrl) + pinctrl_put(sirfport->p); devm_iounmap(&pdev->dev, port->membase); uart_remove_one_port(&sirfsoc_uart_drv, port); return 0; diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h index fc64260fa93c..6e207fdc2fed 100644 --- a/drivers/tty/serial/sirfsoc_uart.h +++ b/drivers/tty/serial/sirfsoc_uart.h @@ -162,7 +162,7 @@ struct sirfsoc_uart_port { unsigned char ms_enabled; struct uart_port port; - struct pinmux *pmx; + struct pinctrl *p; }; /* Hardware Flow Control */ diff --git a/drivers/tty/serial/sn_console.c b/drivers/tty/serial/sn_console.c index 4e1b5515f881..1c6de9f58699 100644 --- a/drivers/tty/serial/sn_console.c +++ b/drivers/tty/serial/sn_console.c @@ -743,6 +743,7 @@ static void __init sn_sal_switch_to_interrupts(struct sn_cons_port *port) spin_lock_irqsave(&port->sc_port.lock, flags); port->sc_port.irq = SGI_UART_VECTOR; port->sc_ops = &intr_ops; + irq_set_handler(port->sc_port.irq, handle_level_irq); /* turn on receive interrupts */ ia64_sn_console_intr_enable(SAL_CONSOLE_INTR_RECV); diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index e4405e088589..48ac6e781ba2 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -16,7 +16,7 @@ config USB_ARCH_HAS_OHCI # ARM: default y if SA1111 default y if ARCH_OMAP - default y if ARCH_S3C2410 + default y if ARCH_S3C24XX default y if PXA27x default y if PXA3xx default y if ARCH_EP93XX diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index c14a3972953a..26c0b75f152e 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -137,7 +137,7 @@ choice config USB_AT91 tristate "Atmel AT91 USB Device Port" - depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9 && !ARCH_AT91SAM9G45 + depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91SAM9G45 help Many Atmel AT91 processors (such as the AT91RM2000) have a full speed USB Device Port with support for five configurable @@ -150,7 +150,7 @@ config USB_AT91 config USB_ATMEL_USBA tristate "Atmel USBA" select USB_GADGET_DUALSPEED - depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 + depends on AVR32 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 help USBA is the integrated high-speed USB Device controller on the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel. @@ -284,7 +284,7 @@ config USB_IMX config USB_S3C2410 tristate "S3C2410 USB Device Controller" - depends on ARCH_S3C2410 + depends on ARCH_S3C24XX help Samsung's S3C2410 is an ARM-4 processor with an integrated full speed USB 1.1 device controller. It has 4 configurable @@ -299,7 +299,7 @@ config USB_S3C2410_DEBUG config USB_S3C_HSUDC tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller" - depends on ARCH_S3C2410 + depends on ARCH_S3C24XX select USB_GADGET_DUALSPEED help Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 15a8cdb2ded5..2db5f68f7960 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -40,6 +40,7 @@ #include <mach/board.h> #include <mach/cpu.h> #include <mach/at91sam9261_matrix.h> +#include <mach/at91_matrix.h> #include "at91_udc.h" @@ -910,9 +911,9 @@ static void pullup(struct at91_udc *udc, int is_on) } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { u32 usbpucr; - usbpucr = at91_sys_read(AT91_MATRIX_USBPUCR); + usbpucr = at91_matrix_read(AT91_MATRIX_USBPUCR); usbpucr |= AT91_MATRIX_USBPUCR_PUON; - at91_sys_write(AT91_MATRIX_USBPUCR, usbpucr); + at91_matrix_write(AT91_MATRIX_USBPUCR, usbpucr); } } else { stop_activity(udc); @@ -928,9 +929,9 @@ static void pullup(struct at91_udc *udc, int is_on) } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { u32 usbpucr; - usbpucr = at91_sys_read(AT91_MATRIX_USBPUCR); + usbpucr = at91_matrix_read(AT91_MATRIX_USBPUCR); usbpucr &= ~AT91_MATRIX_USBPUCR_PUON; - at91_sys_write(AT91_MATRIX_USBPUCR, usbpucr); + at91_matrix_write(AT91_MATRIX_USBPUCR, usbpucr); } clk_off(udc); } diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 5e10f651ad63..9f98508966d1 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -332,12 +332,12 @@ static int vbus_is_present(struct usba_udc *udc) static void toggle_bias(int is_on) { - unsigned int uckr = at91_sys_read(AT91_CKGR_UCKR); + unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR); if (is_on) - at91_sys_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN); + at91_pmc_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN); else - at91_sys_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN)); + at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN)); } #else diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index 7cdcb63b21ff..85a5cebe96b3 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -345,7 +345,7 @@ static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req) } skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, - skb->len <= 1, req->actual); + skb->len <= 1, req->actual, req->actual); page = NULL; if (req->actual < req->length) { /* Last fragment */ diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 77afabc77f9b..8e855eb0bf89 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -448,10 +448,11 @@ static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data) /* From the GPIO notifying the over-current situation, find * out the corresponding port */ - gpio = irq_to_gpio(irq); for (port = 0; port < ARRAY_SIZE(pdata->overcurrent_pin); port++) { - if (pdata->overcurrent_pin[port] == gpio) + if (gpio_to_irq(pdata->overcurrent_pin[port]) == irq) { + gpio = pdata->overcurrent_pin[port]; break; + } } if (port == ARRAY_SIZE(pdata->overcurrent_pin)) { diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index cd5e382db89c..543e90e336b8 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1000,7 +1000,7 @@ MODULE_LICENSE ("GPL"); #define SA1111_DRIVER ohci_hcd_sa1111_driver #endif -#if defined(CONFIG_ARCH_S3C2410) || defined(CONFIG_ARCH_S3C64XX) +#if defined(CONFIG_ARCH_S3C24XX) || defined(CONFIG_ARCH_S3C64XX) #include "ohci-s3c2410.c" #define PLATFORM_DRIVER ohci_hcd_s3c2410_driver #endif diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c index 4bde4f9821ba..e1004fb37bd9 100644 --- a/drivers/usb/host/ohci-sa1111.c +++ b/drivers/usb/host/ohci-sa1111.c @@ -16,29 +16,115 @@ #include <mach/hardware.h> #include <asm/mach-types.h> #include <mach/assabet.h> -#include <mach/badge4.h> #include <asm/hardware/sa1111.h> #ifndef CONFIG_SA1111 #error "This file is SA-1111 bus glue. CONFIG_SA1111 must be defined." #endif -extern int usb_disabled(void); +#define USB_STATUS 0x0118 +#define USB_RESET 0x011c +#define USB_IRQTEST 0x0120 + +#define USB_RESET_FORCEIFRESET (1 << 0) +#define USB_RESET_FORCEHCRESET (1 << 1) +#define USB_RESET_CLKGENRESET (1 << 2) +#define USB_RESET_SIMSCALEDOWN (1 << 3) +#define USB_RESET_USBINTTEST (1 << 4) +#define USB_RESET_SLEEPSTBYEN (1 << 5) +#define USB_RESET_PWRSENSELOW (1 << 6) +#define USB_RESET_PWRCTRLLOW (1 << 7) + +#define USB_STATUS_IRQHCIRMTWKUP (1 << 7) +#define USB_STATUS_IRQHCIBUFFACC (1 << 8) +#define USB_STATUS_NIRQHCIM (1 << 9) +#define USB_STATUS_NHCIMFCLR (1 << 10) +#define USB_STATUS_USBPWRSENSE (1 << 11) -/*-------------------------------------------------------------------------*/ +#if 0 +static void dump_hci_status(struct usb_hcd *hcd, const char *label) +{ + unsigned long status = sa1111_readl(hcd->regs + USB_STATUS); + + dbg("%s USB_STATUS = { %s%s%s%s%s}", label, + ((status & USB_STATUS_IRQHCIRMTWKUP) ? "IRQHCIRMTWKUP " : ""), + ((status & USB_STATUS_IRQHCIBUFFACC) ? "IRQHCIBUFFACC " : ""), + ((status & USB_STATUS_NIRQHCIM) ? "" : "IRQHCIM "), + ((status & USB_STATUS_NHCIMFCLR) ? "" : "HCIMFCLR "), + ((status & USB_STATUS_USBPWRSENSE) ? "USBPWRSENSE " : "")); +} +#endif -static void sa1111_start_hc(struct sa1111_dev *dev) +static int ohci_sa1111_reset(struct usb_hcd *hcd) { - unsigned int usb_rst = 0; + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + + ohci_hcd_init(ohci); + return ohci_init(ohci); +} - printk(KERN_DEBUG "%s: starting SA-1111 OHCI USB Controller\n", - __FILE__); +static int __devinit ohci_sa1111_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int ret; -#ifdef CONFIG_SA1100_BADGE4 - if (machine_is_badge4()) { - badge4_set_5V(BADGE4_5V_USB, 1); + ret = ohci_run(ohci); + if (ret < 0) { + ohci_err(ohci, "can't start\n"); + ohci_stop(hcd); } + return ret; +} + +static const struct hc_driver ohci_sa1111_hc_driver = { + .description = hcd_name, + .product_desc = "SA-1111 OHCI", + .hcd_priv_size = sizeof(struct ohci_hcd), + + /* + * generic hardware linkage + */ + .irq = ohci_irq, + .flags = HCD_USB11 | HCD_MEMORY, + + /* + * basic lifecycle operations + */ + .reset = ohci_sa1111_reset, + .start = ohci_sa1111_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ohci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, #endif + .start_port_reset = ohci_start_port_reset, +}; + +static int sa1111_start_hc(struct sa1111_dev *dev) +{ + unsigned int usb_rst = 0; + int ret; + + dev_dbg(&dev->dev, "starting SA-1111 OHCI USB Controller\n"); if (machine_is_xp860() || machine_has_neponset() || @@ -51,220 +137,121 @@ static void sa1111_start_hc(struct sa1111_dev *dev) * host controller in reset. */ sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET, - dev->mapbase + SA1111_USB_RESET); + dev->mapbase + USB_RESET); /* * Now, carefully enable the USB clock, and take * the USB host controller out of reset. */ - sa1111_enable_device(dev); - udelay(11); - sa1111_writel(usb_rst, dev->mapbase + SA1111_USB_RESET); + ret = sa1111_enable_device(dev); + if (ret == 0) { + udelay(11); + sa1111_writel(usb_rst, dev->mapbase + USB_RESET); + } + + return ret; } static void sa1111_stop_hc(struct sa1111_dev *dev) { unsigned int usb_rst; - printk(KERN_DEBUG "%s: stopping SA-1111 OHCI USB Controller\n", - __FILE__); + + dev_dbg(&dev->dev, "stopping SA-1111 OHCI USB Controller\n"); /* * Put the USB host controller into reset. */ - usb_rst = sa1111_readl(dev->mapbase + SA1111_USB_RESET); + usb_rst = sa1111_readl(dev->mapbase + USB_RESET); sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET, - dev->mapbase + SA1111_USB_RESET); + dev->mapbase + USB_RESET); /* * Stop the USB clock. */ sa1111_disable_device(dev); - -#ifdef CONFIG_SA1100_BADGE4 - if (machine_is_badge4()) { - /* Disable power to the USB bus */ - badge4_set_5V(BADGE4_5V_USB, 0); - } -#endif -} - - -/*-------------------------------------------------------------------------*/ - -#if 0 -static void dump_hci_status(struct usb_hcd *hcd, const char *label) -{ - unsigned long status = sa1111_readl(hcd->regs + SA1111_USB_STATUS); - - dbg ("%s USB_STATUS = { %s%s%s%s%s}", label, - ((status & USB_STATUS_IRQHCIRMTWKUP) ? "IRQHCIRMTWKUP " : ""), - ((status & USB_STATUS_IRQHCIBUFFACC) ? "IRQHCIBUFFACC " : ""), - ((status & USB_STATUS_NIRQHCIM) ? "" : "IRQHCIM "), - ((status & USB_STATUS_NHCIMFCLR) ? "" : "HCIMFCLR "), - ((status & USB_STATUS_USBPWRSENSE) ? "USBPWRSENSE " : "")); } -#endif - -/*-------------------------------------------------------------------------*/ - -/* configure so an HC device and id are always provided */ -/* always called with process context; sleeping is OK */ - /** - * usb_hcd_sa1111_probe - initialize SA-1111-based HCDs - * Context: !in_interrupt() + * ohci_hcd_sa1111_probe - initialize SA-1111-based HCDs * * Allocates basic resources for this USB host controller, and - * then invokes the start() method for the HCD associated with it - * through the hotplug entry's driver_data. - * - * Store this function in the HCD's struct pci_driver as probe(). + * then invokes the start() method for the HCD associated with it. */ -int usb_hcd_sa1111_probe (const struct hc_driver *driver, - struct sa1111_dev *dev) +static int ohci_hcd_sa1111_probe(struct sa1111_dev *dev) { struct usb_hcd *hcd; - int retval; + int ret; - hcd = usb_create_hcd (driver, &dev->dev, "sa1111"); + if (usb_disabled()) + return -ENODEV; + + hcd = usb_create_hcd(&ohci_sa1111_hc_driver, &dev->dev, "sa1111"); if (!hcd) return -ENOMEM; + hcd->rsrc_start = dev->res.start; hcd->rsrc_len = resource_size(&dev->res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { dbg("request_mem_region failed"); - retval = -EBUSY; + ret = -EBUSY; goto err1; } + hcd->regs = dev->mapbase; - sa1111_start_hc(dev); - ohci_hcd_init(hcd_to_ohci(hcd)); + ret = sa1111_start_hc(dev); + if (ret) + goto err2; - retval = usb_add_hcd(hcd, dev->irq[1], 0); - if (retval == 0) - return retval; + ret = usb_add_hcd(hcd, dev->irq[1], 0); + if (ret == 0) + return ret; sa1111_stop_hc(dev); + err2: release_mem_region(hcd->rsrc_start, hcd->rsrc_len); err1: usb_put_hcd(hcd); - return retval; + return ret; } - -/* may be called without controller electrically present */ -/* may be called with controller, bus, and devices active */ - /** - * usb_hcd_sa1111_remove - shutdown processing for SA-1111-based HCDs + * ohci_hcd_sa1111_remove - shutdown processing for SA-1111-based HCDs * @dev: USB Host Controller being removed - * Context: !in_interrupt() - * - * Reverses the effect of usb_hcd_sa1111_probe(), first invoking - * the HCD's stop() method. It is always called from a thread - * context, normally "rmmod", "apmd", or something similar. * + * Reverses the effect of ohci_hcd_sa1111_probe(), first invoking + * the HCD's stop() method. */ -void usb_hcd_sa1111_remove (struct usb_hcd *hcd, struct sa1111_dev *dev) +static int ohci_hcd_sa1111_remove(struct sa1111_dev *dev) { + struct usb_hcd *hcd = sa1111_get_drvdata(dev); + usb_remove_hcd(hcd); sa1111_stop_hc(dev); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); -} - -/*-------------------------------------------------------------------------*/ -static int __devinit -ohci_sa1111_start (struct usb_hcd *hcd) -{ - struct ohci_hcd *ohci = hcd_to_ohci (hcd); - int ret; - - if ((ret = ohci_init(ohci)) < 0) - return ret; - - if ((ret = ohci_run (ohci)) < 0) { - err ("can't start %s", hcd->self.bus_name); - ohci_stop (hcd); - return ret; - } return 0; } -/*-------------------------------------------------------------------------*/ - -static const struct hc_driver ohci_sa1111_hc_driver = { - .description = hcd_name, - .product_desc = "SA-1111 OHCI", - .hcd_priv_size = sizeof(struct ohci_hcd), - - /* - * generic hardware linkage - */ - .irq = ohci_irq, - .flags = HCD_USB11 | HCD_MEMORY, - - /* - * basic lifecycle operations - */ - .start = ohci_sa1111_start, - .stop = ohci_stop, - - /* - * managing i/o requests and associated device resources - */ - .urb_enqueue = ohci_urb_enqueue, - .urb_dequeue = ohci_urb_dequeue, - .endpoint_disable = ohci_endpoint_disable, - - /* - * scheduling support - */ - .get_frame_number = ohci_get_frame, - - /* - * root hub support - */ - .hub_status_data = ohci_hub_status_data, - .hub_control = ohci_hub_control, -#ifdef CONFIG_PM - .bus_suspend = ohci_bus_suspend, - .bus_resume = ohci_bus_resume, -#endif - .start_port_reset = ohci_start_port_reset, -}; - -/*-------------------------------------------------------------------------*/ - -static int ohci_hcd_sa1111_drv_probe(struct sa1111_dev *dev) -{ - int ret; - - if (usb_disabled()) - return -ENODEV; - - ret = usb_hcd_sa1111_probe(&ohci_sa1111_hc_driver, dev); - return ret; -} - -static int ohci_hcd_sa1111_drv_remove(struct sa1111_dev *dev) +static void ohci_hcd_sa1111_shutdown(struct sa1111_dev *dev) { struct usb_hcd *hcd = sa1111_get_drvdata(dev); - usb_hcd_sa1111_remove(hcd, dev); - return 0; + if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + hcd->driver->shutdown(hcd); + sa1111_stop_hc(dev); + } } static struct sa1111_driver ohci_hcd_sa1111_driver = { .drv = { .name = "sa1111-ohci", + .owner = THIS_MODULE, }, .devid = SA1111_DEVID_USB, - .probe = ohci_hcd_sa1111_drv_probe, - .remove = ohci_hcd_sa1111_drv_remove, + .probe = ohci_hcd_sa1111_probe, + .remove = ohci_hcd_sa1111_remove, + .shutdown = ohci_hcd_sa1111_shutdown, }; - diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 7732d69e49e0..11de5f1be981 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -893,4 +893,5 @@ static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev) quirk_usb_handoff_xhci(pdev); pci_disable_device(pdev); } -DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff); +DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_SERIAL_USB, 8, quirk_usb_early_handoff); diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 6815701cf656..836cfa9a515f 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -903,8 +903,10 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0165, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0167, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1057, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1058, 0xff, 0xff, 0xff) }, diff --git a/drivers/uwb/allocator.c b/drivers/uwb/allocator.c index e45e673b8770..6e3e713f0ef7 100644 --- a/drivers/uwb/allocator.c +++ b/drivers/uwb/allocator.c @@ -334,10 +334,8 @@ int uwb_rsv_find_best_allocation(struct uwb_rsv *rsv, struct uwb_mas_bm *availab /* fill the not available vector from the available bm */ - for (bit_index = 0; bit_index < UWB_NUM_MAS; bit_index++) { - if (!test_bit(bit_index, available->bm)) - ai->bm[bit_index] = UWB_RSV_MAS_NOT_AVAIL; - } + for_each_clear_bit(bit_index, available->bm, UWB_NUM_MAS) + ai->bm[bit_index] = UWB_RSV_MAS_NOT_AVAIL; if (ai->max_interval == 1) { get_row_descriptors(ai); diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 9dab1f51dd43..f0da2c32fbde 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -588,7 +588,7 @@ static int vhost_net_release(struct inode *inode, struct file *f) vhost_net_stop(n, &tx_sock, &rx_sock); vhost_net_flush(n); - vhost_dev_cleanup(&n->dev); + vhost_dev_cleanup(&n->dev, false); if (tx_sock) fput(tx_sock->file); if (rx_sock) diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index bdb2d6436b2b..947f00d8e091 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -222,6 +222,8 @@ static int vhost_worker(void *data) if (work) { __set_current_state(TASK_RUNNING); work->fn(work); + if (need_resched()) + schedule(); } else schedule(); @@ -403,7 +405,7 @@ long vhost_dev_reset_owner(struct vhost_dev *dev) if (!memory) return -ENOMEM; - vhost_dev_cleanup(dev); + vhost_dev_cleanup(dev, true); memory->nregions = 0; RCU_INIT_POINTER(dev->memory, memory); @@ -434,8 +436,8 @@ int vhost_zerocopy_signal_used(struct vhost_virtqueue *vq) return j; } -/* Caller should have device mutex */ -void vhost_dev_cleanup(struct vhost_dev *dev) +/* Caller should have device mutex if and only if locked is set */ +void vhost_dev_cleanup(struct vhost_dev *dev, bool locked) { int i; @@ -472,7 +474,8 @@ void vhost_dev_cleanup(struct vhost_dev *dev) dev->log_file = NULL; /* No one will access memory at this point */ kfree(rcu_dereference_protected(dev->memory, - lockdep_is_held(&dev->mutex))); + locked == + lockdep_is_held(&dev->mutex))); RCU_INIT_POINTER(dev->memory, NULL); WARN_ON(!list_empty(&dev->work_list)); if (dev->worker) { diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h index a801e2821d03..8dcf4cca6bf2 100644 --- a/drivers/vhost/vhost.h +++ b/drivers/vhost/vhost.h @@ -163,7 +163,7 @@ struct vhost_dev { long vhost_dev_init(struct vhost_dev *, struct vhost_virtqueue *vqs, int nvqs); long vhost_dev_check_owner(struct vhost_dev *); long vhost_dev_reset_owner(struct vhost_dev *); -void vhost_dev_cleanup(struct vhost_dev *); +void vhost_dev_cleanup(struct vhost_dev *, bool locked); long vhost_dev_ioctl(struct vhost_dev *, unsigned int ioctl, unsigned long arg); int vhost_vq_access_ok(struct vhost_virtqueue *vq); int vhost_log_access_ok(struct vhost_dev *); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index eca60c73ef1f..a290be51a1f4 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1123,6 +1123,18 @@ config FB_RIVA_BACKLIGHT help Say Y here if you want to control the backlight of your display. +config FB_I740 + tristate "Intel740 support (EXPERIMENTAL)" + depends on EXPERIMENTAL && FB && PCI + select FB_MODE_HELPERS + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + select VGASTATE + select FB_DDC + help + This driver supports graphics cards based on Intel740 chip. + config FB_I810 tristate "Intel 810/815 support (EXPERIMENTAL)" depends on EXPERIMENTAL && FB && PCI && X86_32 && AGP_INTEL @@ -2001,18 +2013,6 @@ config FB_SH_MOBILE_HDMI ---help--- Driver for the on-chip SH-Mobile HDMI controller. -config FB_SH_MOBILE_MERAM - tristate "SuperH Mobile MERAM read ahead support for LCDC" - depends on FB_SH_MOBILE_LCDC - default y - ---help--- - Enable MERAM support for the SH-Mobile LCD controller. - - This will allow for caching of the framebuffer to provide more - reliable access under heavy main memory bus traffic situations. - Up to 4 memory channels can be configured, allowing 4 RGB or - 2 YCbCr framebuffers to be configured. - config FB_TMIO tristate "Toshiba Mobile IO FrameBuffer support" depends on FB && MFD_CORE @@ -2061,7 +2061,7 @@ config FB_S3C_DEBUG_REGWRITE config FB_S3C2410 tristate "S3C2410 LCD framebuffer support" - depends on FB && ARCH_S3C2410 + depends on FB && ARCH_S3C24XX select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -2233,6 +2233,7 @@ config FB_DA8XX select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT + select FB_CFB_REV_PIXELS_IN_BYTE ---help--- This is the frame buffer device driver for the TI LCD controller found on DA8xx/OMAP-L1xx SoCs. @@ -2412,7 +2413,7 @@ config FB_PUV3_UNIGFX source "drivers/video/omap/Kconfig" source "drivers/video/omap2/Kconfig" - +source "drivers/video/exynos/Kconfig" source "drivers/video/backlight/Kconfig" if VT @@ -2423,4 +2424,16 @@ if FB || SGI_NEWPORT_CONSOLE source "drivers/video/logo/Kconfig" endif +config FB_SH_MOBILE_MERAM + tristate "SuperH Mobile MERAM read ahead support" + depends on (SUPERH || ARCH_SHMOBILE) + select GENERIC_ALLOCATOR + ---help--- + Enable MERAM support for the SuperH controller. + + This will allow for caching of the framebuffer to provide more + reliable access under heavy main memory bus traffic situations. + Up to 4 memory channels can be configured, allowing 4 RGB or + 2 YCbCr framebuffers to be configured. + endmenu diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 142606814d98..9356add945b3 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -15,6 +15,8 @@ obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ obj-y += backlight/ +obj-$(CONFIG_EXYNOS_VIDEO) += exynos/ + obj-$(CONFIG_FB_CFB_FILLRECT) += cfbfillrect.o obj-$(CONFIG_FB_CFB_COPYAREA) += cfbcopyarea.o obj-$(CONFIG_FB_CFB_IMAGEBLIT) += cfbimgblt.o @@ -37,6 +39,7 @@ obj-$(CONFIG_FB_GRVGA) += grvga.o obj-$(CONFIG_FB_PM2) += pm2fb.o obj-$(CONFIG_FB_PM3) += pm3fb.o +obj-$(CONFIG_FB_I740) += i740fb.o obj-$(CONFIG_FB_MATROX) += matrox/ obj-$(CONFIG_FB_RIVA) += riva/ obj-$(CONFIG_FB_NVIDIA) += nvidia/ diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index e40c00f2c2ba..d99505b16374 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -421,24 +421,18 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, var->red.length = var->green.length = var->blue.length = var->bits_per_pixel; break; - case 15: case 16: if (sinfo->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB) { /* RGB:565 mode */ var->red.offset = 11; var->blue.offset = 0; - var->green.length = 6; - } else if (sinfo->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB555) { - var->red.offset = 10; - var->blue.offset = 0; - var->green.length = 5; } else { - /* BGR:555 mode */ + /* BGR:565 mode */ var->red.offset = 0; - var->blue.offset = 10; - var->green.length = 5; + var->blue.offset = 11; } var->green.offset = 5; + var->green.length = 6; var->red.length = var->blue.length = 5; break; case 32: diff --git a/drivers/video/au1100fb.c b/drivers/video/au1100fb.c index de9da6774fd9..befcbd8ef019 100644 --- a/drivers/video/au1100fb.c +++ b/drivers/video/au1100fb.c @@ -477,7 +477,8 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev) u32 sys_clksrc; /* Allocate new device private */ - fbdev = kzalloc(sizeof(struct au1100fb_device), GFP_KERNEL); + fbdev = devm_kzalloc(&dev->dev, sizeof(struct au1100fb_device), + GFP_KERNEL); if (!fbdev) { print_err("fail to allocate device private record"); return -ENOMEM; @@ -498,8 +499,9 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev) au1100fb_fix.mmio_start = regs_res->start; au1100fb_fix.mmio_len = resource_size(regs_res); - if (!request_mem_region(au1100fb_fix.mmio_start, au1100fb_fix.mmio_len, - DRIVER_NAME)) { + if (!devm_request_mem_region(au1100fb_fix.mmio_start, + au1100fb_fix.mmio_len, + DRIVER_NAME)) { print_err("fail to lock memory region at 0x%08lx", au1100fb_fix.mmio_start); return -EBUSY; @@ -514,8 +516,9 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev) fbdev->fb_len = fbdev->panel->xres * fbdev->panel->yres * (fbdev->panel->bpp >> 3) * AU1100FB_NBR_VIDEO_BUFFERS; - fbdev->fb_mem = dma_alloc_coherent(&dev->dev, PAGE_ALIGN(fbdev->fb_len), - &fbdev->fb_phys, GFP_KERNEL); + fbdev->fb_mem = dmam_alloc_coherent(&dev->dev, &dev->dev, + PAGE_ALIGN(fbdev->fb_len), + &fbdev->fb_phys, GFP_KERNEL); if (!fbdev->fb_mem) { print_err("fail to allocate frambuffer (size: %dK))", fbdev->fb_len / 1024); @@ -557,14 +560,14 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev) fbdev->info.fbops = &au1100fb_ops; fbdev->info.fix = au1100fb_fix; - if (!(fbdev->info.pseudo_palette = kzalloc(sizeof(u32) * 16, GFP_KERNEL))) { + fbdev->info.pseudo_palette = + devm_kzalloc(&dev->dev, sizeof(u32) * 16, GFP_KERNEL); + if (!fbdev->info.pseudo_palette) return -ENOMEM; - } if (fb_alloc_cmap(&fbdev->info.cmap, AU1100_LCD_NBR_PALETTE_ENTRIES, 0) < 0) { print_err("Fail to allocate colormap (%d entries)", AU1100_LCD_NBR_PALETTE_ENTRIES); - kfree(fbdev->info.pseudo_palette); return -EFAULT; } @@ -582,9 +585,6 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev) return 0; failed: - if (fbdev->regs) { - release_mem_region(fbdev->regs_phys, fbdev->regs_len); - } if (fbdev->fb_mem) { dma_free_noncoherent(&dev->dev, fbdev->fb_len, fbdev->fb_mem, fbdev->fb_phys); @@ -592,10 +592,9 @@ failed: if (fbdev->info.cmap.len != 0) { fb_dealloc_cmap(&fbdev->info.cmap); } - kfree(fbdev); platform_set_drvdata(dev, NULL); - return 0; + return -ENODEV; } int au1100fb_drv_remove(struct platform_device *dev) @@ -615,14 +614,7 @@ int au1100fb_drv_remove(struct platform_device *dev) /* Clean up all probe data */ unregister_framebuffer(&fbdev->info); - release_mem_region(fbdev->regs_phys, fbdev->regs_len); - - dma_free_coherent(&dev->dev, PAGE_ALIGN(fbdev->fb_len), fbdev->fb_mem, - fbdev->fb_phys); - fb_dealloc_cmap(&fbdev->info.cmap); - kfree(fbdev->info.pseudo_palette); - kfree((void*)fbdev); return 0; } diff --git a/drivers/video/au1200fb.c b/drivers/video/au1200fb.c index 04e4479d5afd..3e9a773db09f 100644 --- a/drivers/video/au1200fb.c +++ b/drivers/video/au1200fb.c @@ -1724,7 +1724,7 @@ static int __devinit au1200fb_drv_probe(struct platform_device *dev) /* Allocate the framebuffer to the maximum screen size */ fbdev->fb_len = (win->w[plane].xres * win->w[plane].yres * bpp) / 8; - fbdev->fb_mem = dma_alloc_noncoherent(&dev->dev, + fbdev->fb_mem = dmam_alloc_noncoherent(&dev->dev, &dev->dev, PAGE_ALIGN(fbdev->fb_len), &fbdev->fb_phys, GFP_KERNEL); if (!fbdev->fb_mem) { @@ -1788,9 +1788,6 @@ static int __devinit au1200fb_drv_probe(struct platform_device *dev) failed: /* NOTE: This only does the current plane/window that failed; others are still active */ - if (fbdev->fb_mem) - dma_free_noncoherent(&dev->dev, PAGE_ALIGN(fbdev->fb_len), - fbdev->fb_mem, fbdev->fb_phys); if (fbi) { if (fbi->cmap.len != 0) fb_dealloc_cmap(&fbi->cmap); @@ -1817,10 +1814,6 @@ static int __devexit au1200fb_drv_remove(struct platform_device *dev) /* Clean up all probe data */ unregister_framebuffer(fbi); - if (fbdev->fb_mem) - dma_free_noncoherent(&dev->dev, - PAGE_ALIGN(fbdev->fb_len), - fbdev->fb_mem, fbdev->fb_phys); if (fbi->cmap.len != 0) fb_dealloc_cmap(&fbi->cmap); kfree(fbi->pseudo_palette); diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c index a1376dc73d71..915943af3f21 100644 --- a/drivers/video/backlight/88pm860x_bl.c +++ b/drivers/video/backlight/88pm860x_bl.c @@ -187,7 +187,8 @@ static int pm860x_backlight_probe(struct platform_device *pdev) return -EINVAL; } - data = kzalloc(sizeof(struct pm860x_backlight_data), GFP_KERNEL); + data = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_backlight_data), + GFP_KERNEL); if (data == NULL) return -ENOMEM; strncpy(name, res->name, MFD_NAME_SIZE); @@ -200,7 +201,6 @@ static int pm860x_backlight_probe(struct platform_device *pdev) data->port = pdata->flags; if (data->port < 0) { dev_err(&pdev->dev, "wrong platform data is assigned"); - kfree(data); return -EINVAL; } @@ -211,7 +211,6 @@ static int pm860x_backlight_probe(struct platform_device *pdev) &pm860x_backlight_ops, &props); if (IS_ERR(bl)) { dev_err(&pdev->dev, "failed to register backlight\n"); - kfree(data); return PTR_ERR(bl); } bl->props.brightness = MAX_BRIGHTNESS; @@ -247,17 +246,14 @@ static int pm860x_backlight_probe(struct platform_device *pdev) return 0; out: backlight_device_unregister(bl); - kfree(data); return ret; } static int pm860x_backlight_remove(struct platform_device *pdev) { struct backlight_device *bl = platform_get_drvdata(pdev); - struct pm860x_backlight_data *data = bl_get_data(bl); backlight_device_unregister(bl); - kfree(data); return 0; } diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 681b36929fe4..7ed9991fa747 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -334,6 +334,27 @@ config BACKLIGHT_AAT2870 If you have a AnalogicTech AAT2870 say Y to enable the backlight driver. +config BACKLIGHT_LP855X + tristate "Backlight driver for TI LP855X" + depends on BACKLIGHT_CLASS_DEVICE && I2C + help + This supports TI LP8550, LP8551, LP8552, LP8553 and LP8556 + backlight driver. + +config BACKLIGHT_OT200 + tristate "Backlight driver for ot200 visualisation device" + depends on BACKLIGHT_CLASS_DEVICE && CS5535_MFGPT && GPIO_CS5535 + help + To compile this driver as a module, choose M here: the module will be + called ot200_bl. + +config BACKLIGHT_PANDORA + tristate "Backlight driver for Pandora console" + depends on TWL4030_CORE + help + If you have a Pandora console, say Y to enable the + backlight driver. + endif # BACKLIGHT_CLASS_DEVICE endif # BACKLIGHT_LCD_SUPPORT diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index af5cf654ec7c..8071eb656147 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -22,7 +22,9 @@ obj-$(CONFIG_BACKLIGHT_GENERIC) += generic_bl.o obj-$(CONFIG_BACKLIGHT_HP700) += jornada720_bl.o obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o +obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o +obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o @@ -38,4 +40,4 @@ obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o obj-$(CONFIG_BACKLIGHT_AAT2870) += aat2870_bl.o - +obj-$(CONFIG_BACKLIGHT_OT200) += ot200_bl.o diff --git a/drivers/video/backlight/aat2870_bl.c b/drivers/video/backlight/aat2870_bl.c index 331f1ef1dad5..7ff752288b92 100644 --- a/drivers/video/backlight/aat2870_bl.c +++ b/drivers/video/backlight/aat2870_bl.c @@ -145,7 +145,9 @@ static int aat2870_bl_probe(struct platform_device *pdev) goto out; } - aat2870_bl = kzalloc(sizeof(struct aat2870_bl_driver_data), GFP_KERNEL); + aat2870_bl = devm_kzalloc(&pdev->dev, + sizeof(struct aat2870_bl_driver_data), + GFP_KERNEL); if (!aat2870_bl) { dev_err(&pdev->dev, "Failed to allocate memory for aat2870 backlight\n"); @@ -162,7 +164,7 @@ static int aat2870_bl_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed allocate memory for backlight device\n"); ret = PTR_ERR(bd); - goto out_kfree; + goto out; } aat2870_bl->pdev = pdev; @@ -199,8 +201,6 @@ static int aat2870_bl_probe(struct platform_device *pdev) out_bl_dev_unregister: backlight_device_unregister(bd); -out_kfree: - kfree(aat2870_bl); out: return ret; } @@ -215,7 +215,6 @@ static int aat2870_bl_remove(struct platform_device *pdev) backlight_update_status(bd); backlight_device_unregister(bd); - kfree(aat2870_bl); return 0; } diff --git a/drivers/video/backlight/adp5520_bl.c b/drivers/video/backlight/adp5520_bl.c index 2e630bf1164c..4911ea7989c8 100644 --- a/drivers/video/backlight/adp5520_bl.c +++ b/drivers/video/backlight/adp5520_bl.c @@ -289,7 +289,7 @@ static int __devinit adp5520_bl_probe(struct platform_device *pdev) struct adp5520_bl *data; int ret = 0; - data = kzalloc(sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (data == NULL) return -ENOMEM; @@ -298,7 +298,6 @@ static int __devinit adp5520_bl_probe(struct platform_device *pdev) if (data->pdata == NULL) { dev_err(&pdev->dev, "missing platform data\n"); - kfree(data); return -ENODEV; } @@ -314,7 +313,6 @@ static int __devinit adp5520_bl_probe(struct platform_device *pdev) &adp5520_bl_ops, &props); if (IS_ERR(bl)) { dev_err(&pdev->dev, "failed to register backlight\n"); - kfree(data); return PTR_ERR(bl); } @@ -326,7 +324,6 @@ static int __devinit adp5520_bl_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "failed to register sysfs\n"); backlight_device_unregister(bl); - kfree(data); } platform_set_drvdata(pdev, bl); @@ -348,7 +345,6 @@ static int __devexit adp5520_bl_remove(struct platform_device *pdev) &adp5520_bl_attr_group); backlight_device_unregister(bl); - kfree(data); return 0; } diff --git a/drivers/video/backlight/adp8860_bl.c b/drivers/video/backlight/adp8860_bl.c index 378276c9d3cf..550dbf0bb896 100644 --- a/drivers/video/backlight/adp8860_bl.c +++ b/drivers/video/backlight/adp8860_bl.c @@ -819,17 +819,7 @@ static struct i2c_driver adp8860_driver = { .id_table = adp8860_id, }; -static int __init adp8860_init(void) -{ - return i2c_add_driver(&adp8860_driver); -} -module_init(adp8860_init); - -static void __exit adp8860_exit(void) -{ - i2c_del_driver(&adp8860_driver); -} -module_exit(adp8860_exit); +module_i2c_driver(adp8860_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); diff --git a/drivers/video/backlight/adp8870_bl.c b/drivers/video/backlight/adp8870_bl.c index 6735059376d6..9be58c6f18f1 100644 --- a/drivers/video/backlight/adp8870_bl.c +++ b/drivers/video/backlight/adp8870_bl.c @@ -991,17 +991,7 @@ static struct i2c_driver adp8870_driver = { .id_table = adp8870_id, }; -static int __init adp8870_init(void) -{ - return i2c_add_driver(&adp8870_driver); -} -module_init(adp8870_init); - -static void __exit adp8870_exit(void) -{ - i2c_del_driver(&adp8870_driver); -} -module_exit(adp8870_exit); +module_i2c_driver(adp8870_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); diff --git a/drivers/video/backlight/ams369fg06.c b/drivers/video/backlight/ams369fg06.c index 7838a23fbdd1..7bdadc790117 100644 --- a/drivers/video/backlight/ams369fg06.c +++ b/drivers/video/backlight/ams369fg06.c @@ -629,18 +629,7 @@ static struct spi_driver ams369fg06_driver = { .resume = ams369fg06_resume, }; -static int __init ams369fg06_init(void) -{ - return spi_register_driver(&ams369fg06_driver); -} - -static void __exit ams369fg06_exit(void) -{ - spi_unregister_driver(&ams369fg06_driver); -} - -module_init(ams369fg06_init); -module_exit(ams369fg06_exit); +module_spi_driver(ams369fg06_driver); MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); MODULE_DESCRIPTION("ams369fg06 LCD Driver"); diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c index c6533bad26f8..6dab13fe562e 100644 --- a/drivers/video/backlight/corgi_lcd.c +++ b/drivers/video/backlight/corgi_lcd.c @@ -629,17 +629,7 @@ static struct spi_driver corgi_lcd_driver = { .resume = corgi_lcd_resume, }; -static int __init corgi_lcd_init(void) -{ - return spi_register_driver(&corgi_lcd_driver); -} -module_init(corgi_lcd_init); - -static void __exit corgi_lcd_exit(void) -{ - spi_unregister_driver(&corgi_lcd_driver); -} -module_exit(corgi_lcd_exit); +module_spi_driver(corgi_lcd_driver); MODULE_DESCRIPTION("LCD and backlight driver for SHARP C7x0/Cxx00"); MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); diff --git a/drivers/video/backlight/cr_bllcd.c b/drivers/video/backlight/cr_bllcd.c index 6c8c54041fae..22489eb5f3e0 100644 --- a/drivers/video/backlight/cr_bllcd.c +++ b/drivers/video/backlight/cr_bllcd.c @@ -212,7 +212,7 @@ static int cr_backlight_probe(struct platform_device *pdev) &gpio_bar); gpio_bar &= ~0x3F; - crp = kzalloc(sizeof(*crp), GFP_KERNEL); + crp = devm_kzalloc(&pdev->dev, sizeof(*crp), GFP_KERNEL); if (!crp) { lcd_device_unregister(ldp); backlight_device_unregister(bdp); @@ -243,7 +243,6 @@ static int cr_backlight_remove(struct platform_device *pdev) backlight_device_unregister(crp->cr_backlight_device); lcd_device_unregister(crp->cr_lcd_device); pci_dev_put(lpc_dev); - kfree(crp); return 0; } diff --git a/drivers/video/backlight/da903x_bl.c b/drivers/video/backlight/da903x_bl.c index abb4a06268f1..30e19681a30b 100644 --- a/drivers/video/backlight/da903x_bl.c +++ b/drivers/video/backlight/da903x_bl.c @@ -110,7 +110,7 @@ static int da903x_backlight_probe(struct platform_device *pdev) struct backlight_properties props; int max_brightness; - data = kzalloc(sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (data == NULL) return -ENOMEM; @@ -124,7 +124,6 @@ static int da903x_backlight_probe(struct platform_device *pdev) default: dev_err(&pdev->dev, "invalid backlight device ID(%d)\n", pdev->id); - kfree(data); return -EINVAL; } @@ -143,7 +142,6 @@ static int da903x_backlight_probe(struct platform_device *pdev) &da903x_backlight_ops, &props); if (IS_ERR(bl)) { dev_err(&pdev->dev, "failed to register backlight\n"); - kfree(data); return PTR_ERR(bl); } @@ -157,10 +155,8 @@ static int da903x_backlight_probe(struct platform_device *pdev) static int da903x_backlight_remove(struct platform_device *pdev) { struct backlight_device *bl = platform_get_drvdata(pdev); - struct da903x_backlight_data *data = bl_get_data(bl); backlight_device_unregister(bl); - kfree(data); return 0; } diff --git a/drivers/video/backlight/ep93xx_bl.c b/drivers/video/backlight/ep93xx_bl.c index b62b8b9063b5..08214e1f0958 100644 --- a/drivers/video/backlight/ep93xx_bl.c +++ b/drivers/video/backlight/ep93xx_bl.c @@ -17,11 +17,6 @@ #include <linux/fb.h> #include <linux/backlight.h> -#include <mach/hardware.h> - -#define EP93XX_RASTER_REG(x) (EP93XX_RASTER_BASE + (x)) -#define EP93XX_RASTER_BRIGHTNESS EP93XX_RASTER_REG(0x20) - #define EP93XX_MAX_COUNT 255 #define EP93XX_MAX_BRIGHT 255 #define EP93XX_DEF_BRIGHT 128 @@ -35,7 +30,7 @@ static int ep93xxbl_set(struct backlight_device *bl, int brightness) { struct ep93xxbl *ep93xxbl = bl_get_data(bl); - __raw_writel((brightness << 8) | EP93XX_MAX_COUNT, ep93xxbl->mmio); + writel((brightness << 8) | EP93XX_MAX_COUNT, ep93xxbl->mmio); ep93xxbl->brightness = brightness; @@ -70,21 +65,29 @@ static int __init ep93xxbl_probe(struct platform_device *dev) struct ep93xxbl *ep93xxbl; struct backlight_device *bl; struct backlight_properties props; + struct resource *res; ep93xxbl = devm_kzalloc(&dev->dev, sizeof(*ep93xxbl), GFP_KERNEL); if (!ep93xxbl) return -ENOMEM; + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + /* - * This register is located in the range already ioremap'ed by - * the framebuffer driver. A MFD driver seems a bit of overkill - * to handle this so use the static I/O mapping; this address - * is already virtual. + * FIXME - We don't do a request_mem_region here because we are + * sharing the register space with the framebuffer driver (see + * drivers/video/ep93xx-fb.c) and doing so will cause the second + * loaded driver to return -EBUSY. * * NOTE: No locking is required; the framebuffer does not touch * this register. */ - ep93xxbl->mmio = EP93XX_RASTER_BRIGHTNESS; + ep93xxbl->mmio = devm_ioremap(&dev->dev, res->start, + resource_size(res)); + if (!ep93xxbl->mmio) + return -ENXIO; memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; diff --git a/drivers/video/backlight/l4f00242t03.c b/drivers/video/backlight/l4f00242t03.c index 27d1d7a29c77..6022b67285ec 100644 --- a/drivers/video/backlight/l4f00242t03.c +++ b/drivers/video/backlight/l4f00242t03.c @@ -274,18 +274,7 @@ static struct spi_driver l4f00242t03_driver = { .shutdown = l4f00242t03_shutdown, }; -static __init int l4f00242t03_init(void) -{ - return spi_register_driver(&l4f00242t03_driver); -} - -static __exit void l4f00242t03_exit(void) -{ - spi_unregister_driver(&l4f00242t03_driver); -} - -module_init(l4f00242t03_init); -module_exit(l4f00242t03_exit); +module_spi_driver(l4f00242t03_driver); MODULE_AUTHOR("Alberto Panizzo <maramaopercheseimorto@gmail.com>"); MODULE_DESCRIPTION("EPSON L4F00242T03 LCD"); diff --git a/drivers/video/backlight/ld9040.c b/drivers/video/backlight/ld9040.c index 78dafc0c8fc5..efd352be21ae 100644 --- a/drivers/video/backlight/ld9040.c +++ b/drivers/video/backlight/ld9040.c @@ -856,18 +856,7 @@ static struct spi_driver ld9040_driver = { .resume = ld9040_resume, }; -static int __init ld9040_init(void) -{ - return spi_register_driver(&ld9040_driver); -} - -static void __exit ld9040_exit(void) -{ - spi_unregister_driver(&ld9040_driver); -} - -module_init(ld9040_init); -module_exit(ld9040_exit); +module_spi_driver(ld9040_driver); MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>"); MODULE_DESCRIPTION("ld9040 LCD Driver"); diff --git a/drivers/video/backlight/lms283gf05.c b/drivers/video/backlight/lms283gf05.c index 4ec78cfe26ea..4161f9e3982a 100644 --- a/drivers/video/backlight/lms283gf05.c +++ b/drivers/video/backlight/lms283gf05.c @@ -226,18 +226,7 @@ static struct spi_driver lms283gf05_driver = { .remove = __devexit_p(lms283gf05_remove), }; -static __init int lms283gf05_init(void) -{ - return spi_register_driver(&lms283gf05_driver); -} - -static __exit void lms283gf05_exit(void) -{ - spi_unregister_driver(&lms283gf05_driver); -} - -module_init(lms283gf05_init); -module_exit(lms283gf05_exit); +module_spi_driver(lms283gf05_driver); MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); MODULE_DESCRIPTION("LCD283GF05 LCD"); diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c new file mode 100644 index 000000000000..72a0e0c917cf --- /dev/null +++ b/drivers/video/backlight/lp855x_bl.c @@ -0,0 +1,331 @@ +/* + * TI LP855x Backlight Driver + * + * Copyright (C) 2011 Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/backlight.h> +#include <linux/err.h> +#include <linux/lp855x.h> + +/* Registers */ +#define BRIGHTNESS_CTRL (0x00) +#define DEVICE_CTRL (0x01) + +#define BUF_SIZE 20 +#define DEFAULT_BL_NAME "lcd-backlight" +#define MAX_BRIGHTNESS 255 + +struct lp855x { + const char *chipname; + enum lp855x_chip_id chip_id; + struct i2c_client *client; + struct backlight_device *bl; + struct device *dev; + struct mutex xfer_lock; + struct lp855x_platform_data *pdata; +}; + +static int lp855x_read_byte(struct lp855x *lp, u8 reg, u8 *data) +{ + int ret; + + mutex_lock(&lp->xfer_lock); + ret = i2c_smbus_read_byte_data(lp->client, reg); + if (ret < 0) { + mutex_unlock(&lp->xfer_lock); + dev_err(lp->dev, "failed to read 0x%.2x\n", reg); + return ret; + } + mutex_unlock(&lp->xfer_lock); + + *data = (u8)ret; + return 0; +} + +static int lp855x_write_byte(struct lp855x *lp, u8 reg, u8 data) +{ + int ret; + + mutex_lock(&lp->xfer_lock); + ret = i2c_smbus_write_byte_data(lp->client, reg, data); + mutex_unlock(&lp->xfer_lock); + + return ret; +} + +static bool lp855x_is_valid_rom_area(struct lp855x *lp, u8 addr) +{ + u8 start, end; + + switch (lp->chip_id) { + case LP8550: + case LP8551: + case LP8552: + case LP8553: + start = EEPROM_START; + end = EEPROM_END; + break; + case LP8556: + start = EPROM_START; + end = EPROM_END; + break; + default: + return false; + } + + return (addr >= start && addr <= end); +} + +static int lp855x_init_registers(struct lp855x *lp) +{ + u8 val, addr; + int i, ret; + struct lp855x_platform_data *pd = lp->pdata; + + val = pd->initial_brightness; + ret = lp855x_write_byte(lp, BRIGHTNESS_CTRL, val); + if (ret) + return ret; + + val = pd->device_control; + ret = lp855x_write_byte(lp, DEVICE_CTRL, val); + if (ret) + return ret; + + if (pd->load_new_rom_data && pd->size_program) { + for (i = 0; i < pd->size_program; i++) { + addr = pd->rom_data[i].addr; + val = pd->rom_data[i].val; + if (!lp855x_is_valid_rom_area(lp, addr)) + continue; + + ret = lp855x_write_byte(lp, addr, val); + if (ret) + return ret; + } + } + + return ret; +} + +static int lp855x_bl_update_status(struct backlight_device *bl) +{ + struct lp855x *lp = bl_get_data(bl); + enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode; + + if (bl->props.state & BL_CORE_SUSPENDED) + bl->props.brightness = 0; + + if (mode == PWM_BASED) { + struct lp855x_pwm_data *pd = &lp->pdata->pwm_data; + int br = bl->props.brightness; + int max_br = bl->props.max_brightness; + + if (pd->pwm_set_intensity) + pd->pwm_set_intensity(br, max_br); + + } else if (mode == REGISTER_BASED) { + u8 val = bl->props.brightness; + lp855x_write_byte(lp, BRIGHTNESS_CTRL, val); + } + + return 0; +} + +static int lp855x_bl_get_brightness(struct backlight_device *bl) +{ + struct lp855x *lp = bl_get_data(bl); + enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode; + + if (mode == PWM_BASED) { + struct lp855x_pwm_data *pd = &lp->pdata->pwm_data; + int max_br = bl->props.max_brightness; + + if (pd->pwm_get_intensity) + bl->props.brightness = pd->pwm_get_intensity(max_br); + + } else if (mode == REGISTER_BASED) { + u8 val = 0; + + lp855x_read_byte(lp, BRIGHTNESS_CTRL, &val); + bl->props.brightness = val; + } + + return bl->props.brightness; +} + +static const struct backlight_ops lp855x_bl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = lp855x_bl_update_status, + .get_brightness = lp855x_bl_get_brightness, +}; + +static int lp855x_backlight_register(struct lp855x *lp) +{ + struct backlight_device *bl; + struct backlight_properties props; + struct lp855x_platform_data *pdata = lp->pdata; + char *name = pdata->name ? : DEFAULT_BL_NAME; + + props.type = BACKLIGHT_PLATFORM; + props.max_brightness = MAX_BRIGHTNESS; + + if (pdata->initial_brightness > props.max_brightness) + pdata->initial_brightness = props.max_brightness; + + props.brightness = pdata->initial_brightness; + + bl = backlight_device_register(name, lp->dev, lp, + &lp855x_bl_ops, &props); + if (IS_ERR(bl)) + return PTR_ERR(bl); + + lp->bl = bl; + + return 0; +} + +static void lp855x_backlight_unregister(struct lp855x *lp) +{ + if (lp->bl) + backlight_device_unregister(lp->bl); +} + +static ssize_t lp855x_get_chip_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lp855x *lp = dev_get_drvdata(dev); + return scnprintf(buf, BUF_SIZE, "%s\n", lp->chipname); +} + +static ssize_t lp855x_get_bl_ctl_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lp855x *lp = dev_get_drvdata(dev); + enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode; + char *strmode = NULL; + + if (mode == PWM_BASED) + strmode = "pwm based"; + else if (mode == REGISTER_BASED) + strmode = "register based"; + + return scnprintf(buf, BUF_SIZE, "%s\n", strmode); +} + +static DEVICE_ATTR(chip_id, S_IRUGO, lp855x_get_chip_id, NULL); +static DEVICE_ATTR(bl_ctl_mode, S_IRUGO, lp855x_get_bl_ctl_mode, NULL); + +static struct attribute *lp855x_attributes[] = { + &dev_attr_chip_id.attr, + &dev_attr_bl_ctl_mode.attr, + NULL, +}; + +static const struct attribute_group lp855x_attr_group = { + .attrs = lp855x_attributes, +}; + +static int lp855x_probe(struct i2c_client *cl, const struct i2c_device_id *id) +{ + struct lp855x *lp; + struct lp855x_platform_data *pdata = cl->dev.platform_data; + enum lp855x_brightness_ctrl_mode mode; + int ret; + + if (!pdata) { + dev_err(&cl->dev, "no platform data supplied\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) + return -EIO; + + lp = devm_kzalloc(&cl->dev, sizeof(struct lp855x), GFP_KERNEL); + if (!lp) + return -ENOMEM; + + mode = pdata->mode; + lp->client = cl; + lp->dev = &cl->dev; + lp->pdata = pdata; + lp->chipname = id->name; + lp->chip_id = id->driver_data; + i2c_set_clientdata(cl, lp); + + mutex_init(&lp->xfer_lock); + + ret = lp855x_init_registers(lp); + if (ret) { + dev_err(lp->dev, "i2c communication err: %d", ret); + if (mode == REGISTER_BASED) + goto err_dev; + } + + ret = lp855x_backlight_register(lp); + if (ret) { + dev_err(lp->dev, + "failed to register backlight. err: %d\n", ret); + goto err_dev; + } + + ret = sysfs_create_group(&lp->dev->kobj, &lp855x_attr_group); + if (ret) { + dev_err(lp->dev, "failed to register sysfs. err: %d\n", ret); + goto err_sysfs; + } + + backlight_update_status(lp->bl); + return 0; + +err_sysfs: + lp855x_backlight_unregister(lp); +err_dev: + return ret; +} + +static int __devexit lp855x_remove(struct i2c_client *cl) +{ + struct lp855x *lp = i2c_get_clientdata(cl); + + lp->bl->props.brightness = 0; + backlight_update_status(lp->bl); + sysfs_remove_group(&lp->dev->kobj, &lp855x_attr_group); + lp855x_backlight_unregister(lp); + + return 0; +} + +static const struct i2c_device_id lp855x_ids[] = { + {"lp8550", LP8550}, + {"lp8551", LP8551}, + {"lp8552", LP8552}, + {"lp8553", LP8553}, + {"lp8556", LP8556}, + { } +}; +MODULE_DEVICE_TABLE(i2c, lp855x_ids); + +static struct i2c_driver lp855x_driver = { + .driver = { + .name = "lp855x", + }, + .probe = lp855x_probe, + .remove = __devexit_p(lp855x_remove), + .id_table = lp855x_ids, +}; + +module_i2c_driver(lp855x_driver); + +MODULE_DESCRIPTION("Texas Instruments LP855x Backlight driver"); +MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/ltv350qv.c b/drivers/video/backlight/ltv350qv.c index cca43c06d3c8..333949ff3265 100644 --- a/drivers/video/backlight/ltv350qv.c +++ b/drivers/video/backlight/ltv350qv.c @@ -321,17 +321,7 @@ static struct spi_driver ltv350qv_driver = { .resume = ltv350qv_resume, }; -static int __init ltv350qv_init(void) -{ - return spi_register_driver(<v350qv_driver); -} - -static void __exit ltv350qv_exit(void) -{ - spi_unregister_driver(<v350qv_driver); -} -module_init(ltv350qv_init); -module_exit(ltv350qv_exit); +module_spi_driver(ltv350qv_driver); MODULE_AUTHOR("Haavard Skinnemoen (Atmel)"); MODULE_DESCRIPTION("Samsung LTV350QV LCD Driver"); diff --git a/drivers/video/backlight/max8925_bl.c b/drivers/video/backlight/max8925_bl.c index c915e3b53886..e833ac72e063 100644 --- a/drivers/video/backlight/max8925_bl.c +++ b/drivers/video/backlight/max8925_bl.c @@ -129,7 +129,8 @@ static int __devinit max8925_backlight_probe(struct platform_device *pdev) return -EINVAL; } - data = kzalloc(sizeof(struct max8925_backlight_data), GFP_KERNEL); + data = devm_kzalloc(&pdev->dev, sizeof(struct max8925_backlight_data), + GFP_KERNEL); if (data == NULL) return -ENOMEM; strncpy(name, res->name, MAX8925_NAME_SIZE); @@ -143,7 +144,6 @@ static int __devinit max8925_backlight_probe(struct platform_device *pdev) &max8925_backlight_ops, &props); if (IS_ERR(bl)) { dev_err(&pdev->dev, "failed to register backlight\n"); - kfree(data); return PTR_ERR(bl); } bl->props.brightness = MAX_BRIGHTNESS; @@ -165,17 +165,14 @@ static int __devinit max8925_backlight_probe(struct platform_device *pdev) return 0; out: backlight_device_unregister(bl); - kfree(data); return ret; } static int __devexit max8925_backlight_remove(struct platform_device *pdev) { struct backlight_device *bl = platform_get_drvdata(pdev); - struct max8925_backlight_data *data = bl_get_data(bl); backlight_device_unregister(bl); - kfree(data); return 0; } diff --git a/drivers/video/backlight/omap1_bl.c b/drivers/video/backlight/omap1_bl.c index d8cde277ec83..0175bfb08a1c 100644 --- a/drivers/video/backlight/omap1_bl.c +++ b/drivers/video/backlight/omap1_bl.c @@ -141,7 +141,8 @@ static int omapbl_probe(struct platform_device *pdev) if (!pdata) return -ENXIO; - bl = kzalloc(sizeof(struct omap_backlight), GFP_KERNEL); + bl = devm_kzalloc(&pdev->dev, sizeof(struct omap_backlight), + GFP_KERNEL); if (unlikely(!bl)) return -ENOMEM; @@ -150,10 +151,8 @@ static int omapbl_probe(struct platform_device *pdev) props.max_brightness = OMAPBL_MAX_INTENSITY; dev = backlight_device_register("omap-bl", &pdev->dev, bl, &omapbl_ops, &props); - if (IS_ERR(dev)) { - kfree(bl); + if (IS_ERR(dev)) return PTR_ERR(dev); - } bl->powermode = FB_BLANK_POWERDOWN; bl->current_intensity = 0; @@ -177,10 +176,8 @@ static int omapbl_probe(struct platform_device *pdev) static int omapbl_remove(struct platform_device *pdev) { struct backlight_device *dev = platform_get_drvdata(pdev); - struct omap_backlight *bl = dev_get_drvdata(&dev->dev); backlight_device_unregister(dev); - kfree(bl); return 0; } diff --git a/drivers/video/backlight/ot200_bl.c b/drivers/video/backlight/ot200_bl.c new file mode 100644 index 000000000000..f519d55a294c --- /dev/null +++ b/drivers/video/backlight/ot200_bl.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2012 Bachmann electronic GmbH + * Christian Gmeiner <christian.gmeiner@gmail.com> + * + * Backlight driver for ot200 visualisation device from + * Bachmann electronic GmbH. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/gpio.h> +#include <linux/cs5535.h> + +static struct cs5535_mfgpt_timer *pwm_timer; + +/* this array defines the mapping of brightness in % to pwm frequency */ +static const u8 dim_table[101] = {0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, + 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9, 9, + 10, 10, 11, 11, 12, 12, 13, 14, 15, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 26, 27, 28, + 30, 31, 33, 35, 37, 39, 41, 43, 45, 47, 50, + 53, 55, 58, 61, 65, 68, 72, 75, 79, 84, 88, + 93, 97, 103, 108, 114, 120, 126, 133, 140, + 147, 155, 163}; + +struct ot200_backlight_data { + int current_brightness; +}; + +#define GPIO_DIMM 27 +#define SCALE 1 +#define CMP1MODE 0x2 /* compare on GE; output high on compare + * greater than or equal */ +#define PWM_SETUP (SCALE | CMP1MODE << 6 | MFGPT_SETUP_CNTEN) +#define MAX_COMP2 163 + +static int ot200_backlight_update_status(struct backlight_device *bl) +{ + struct ot200_backlight_data *data = bl_get_data(bl); + int brightness = bl->props.brightness; + + if (bl->props.state & BL_CORE_FBBLANK) + brightness = 0; + + /* enable or disable PWM timer */ + if (brightness == 0) + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_SETUP, 0); + else if (data->current_brightness == 0) { + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_COUNTER, 0); + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_SETUP, + MFGPT_SETUP_CNTEN); + } + + /* apply new brightness value */ + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_CMP1, + MAX_COMP2 - dim_table[brightness]); + data->current_brightness = brightness; + + return 0; +} + +static int ot200_backlight_get_brightness(struct backlight_device *bl) +{ + struct ot200_backlight_data *data = bl_get_data(bl); + return data->current_brightness; +} + +static const struct backlight_ops ot200_backlight_ops = { + .update_status = ot200_backlight_update_status, + .get_brightness = ot200_backlight_get_brightness, +}; + +static int ot200_backlight_probe(struct platform_device *pdev) +{ + struct backlight_device *bl; + struct ot200_backlight_data *data; + struct backlight_properties props; + int retval = 0; + + /* request gpio */ + if (gpio_request(GPIO_DIMM, "ot200 backlight dimmer") < 0) { + dev_err(&pdev->dev, "failed to request GPIO %d\n", GPIO_DIMM); + return -ENODEV; + } + + /* request timer */ + pwm_timer = cs5535_mfgpt_alloc_timer(7, MFGPT_DOMAIN_ANY); + if (!pwm_timer) { + dev_err(&pdev->dev, "MFGPT 7 not available\n"); + retval = -ENODEV; + goto error_mfgpt_alloc; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + retval = -ENOMEM; + goto error_kzalloc; + } + + /* setup gpio */ + cs5535_gpio_set(GPIO_DIMM, GPIO_OUTPUT_ENABLE); + cs5535_gpio_set(GPIO_DIMM, GPIO_OUTPUT_AUX1); + + /* setup timer */ + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_CMP1, 0); + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_CMP2, MAX_COMP2); + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_SETUP, PWM_SETUP); + + data->current_brightness = 100; + props.max_brightness = 100; + props.brightness = 100; + props.type = BACKLIGHT_RAW; + + bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, data, + &ot200_backlight_ops, &props); + if (IS_ERR(bl)) { + dev_err(&pdev->dev, "failed to register backlight\n"); + retval = PTR_ERR(bl); + goto error_backlight_device_register; + } + + platform_set_drvdata(pdev, bl); + + return 0; + +error_backlight_device_register: + kfree(data); +error_kzalloc: + cs5535_mfgpt_free_timer(pwm_timer); +error_mfgpt_alloc: + gpio_free(GPIO_DIMM); + return retval; +} + +static int ot200_backlight_remove(struct platform_device *pdev) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + struct ot200_backlight_data *data = bl_get_data(bl); + + backlight_device_unregister(bl); + + /* on module unload set brightness to 100% */ + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_COUNTER, 0); + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN); + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_CMP1, + MAX_COMP2 - dim_table[100]); + + cs5535_mfgpt_free_timer(pwm_timer); + gpio_free(GPIO_DIMM); + + kfree(data); + return 0; +} + +static struct platform_driver ot200_backlight_driver = { + .driver = { + .name = "ot200-backlight", + .owner = THIS_MODULE, + }, + .probe = ot200_backlight_probe, + .remove = ot200_backlight_remove, +}; + +module_platform_driver(ot200_backlight_driver); + +MODULE_DESCRIPTION("backlight driver for ot200 visualisation device"); +MODULE_AUTHOR("Christian Gmeiner <christian.gmeiner@gmail.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ot200-backlight"); diff --git a/drivers/video/backlight/pandora_bl.c b/drivers/video/backlight/pandora_bl.c new file mode 100644 index 000000000000..4ec30748b447 --- /dev/null +++ b/drivers/video/backlight/pandora_bl.c @@ -0,0 +1,171 @@ +/* + * Backlight driver for Pandora handheld. + * Pandora uses TWL4030 PWM0 -> TPS61161 combo for control backlight. + * Based on pwm_bl.c + * + * Copyright 2009,2012 Gražvydas Ignotas <notasas@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/i2c/twl.h> +#include <linux/err.h> + +#define TWL_PWM0_ON 0x00 +#define TWL_PWM0_OFF 0x01 + +#define TWL_INTBR_GPBR1 0x0c +#define TWL_INTBR_PMBR1 0x0d + +#define TWL_PMBR1_PWM0_MUXMASK 0x0c +#define TWL_PMBR1_PWM0 0x04 +#define PWM0_CLK_ENABLE BIT(0) +#define PWM0_ENABLE BIT(2) + +/* range accepted by hardware */ +#define MIN_VALUE 9 +#define MAX_VALUE 63 +#define MAX_USER_VALUE (MAX_VALUE - MIN_VALUE) + +#define PANDORABL_WAS_OFF BL_CORE_DRIVER1 + +static int pandora_backlight_update_status(struct backlight_device *bl) +{ + int brightness = bl->props.brightness; + u8 r; + + if (bl->props.power != FB_BLANK_UNBLANK) + brightness = 0; + if (bl->props.state & BL_CORE_FBBLANK) + brightness = 0; + if (bl->props.state & BL_CORE_SUSPENDED) + brightness = 0; + + if ((unsigned int)brightness > MAX_USER_VALUE) + brightness = MAX_USER_VALUE; + + if (brightness == 0) { + if (bl->props.state & PANDORABL_WAS_OFF) + goto done; + + /* first disable PWM0 output, then clock */ + twl_i2c_read_u8(TWL4030_MODULE_INTBR, &r, TWL_INTBR_GPBR1); + r &= ~PWM0_ENABLE; + twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1); + r &= ~PWM0_CLK_ENABLE; + twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1); + + goto done; + } + + if (bl->props.state & PANDORABL_WAS_OFF) { + /* + * set PWM duty cycle to max. TPS61161 seems to use this + * to calibrate it's PWM sensitivity when it starts. + */ + twl_i2c_write_u8(TWL4030_MODULE_PWM0, MAX_VALUE, + TWL_PWM0_OFF); + + /* first enable clock, then PWM0 out */ + twl_i2c_read_u8(TWL4030_MODULE_INTBR, &r, TWL_INTBR_GPBR1); + r &= ~PWM0_ENABLE; + r |= PWM0_CLK_ENABLE; + twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1); + r |= PWM0_ENABLE; + twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1); + + /* + * TI made it very easy to enable digital control, so easy that + * it often triggers unintentionally and disabes PWM control, + * so wait until 1 wire mode detection window ends. + */ + usleep_range(2000, 10000); + } + + twl_i2c_write_u8(TWL4030_MODULE_PWM0, MIN_VALUE + brightness, + TWL_PWM0_OFF); + +done: + if (brightness != 0) + bl->props.state &= ~PANDORABL_WAS_OFF; + else + bl->props.state |= PANDORABL_WAS_OFF; + + return 0; +} + +static int pandora_backlight_get_brightness(struct backlight_device *bl) +{ + return bl->props.brightness; +} + +static const struct backlight_ops pandora_backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = pandora_backlight_update_status, + .get_brightness = pandora_backlight_get_brightness, +}; + +static int pandora_backlight_probe(struct platform_device *pdev) +{ + struct backlight_properties props; + struct backlight_device *bl; + u8 r; + + memset(&props, 0, sizeof(props)); + props.max_brightness = MAX_USER_VALUE; + props.type = BACKLIGHT_RAW; + bl = backlight_device_register(pdev->name, &pdev->dev, + NULL, &pandora_backlight_ops, &props); + if (IS_ERR(bl)) { + dev_err(&pdev->dev, "failed to register backlight\n"); + return PTR_ERR(bl); + } + + platform_set_drvdata(pdev, bl); + + /* 64 cycle period, ON position 0 */ + twl_i2c_write_u8(TWL4030_MODULE_PWM0, 0x80, TWL_PWM0_ON); + + bl->props.state |= PANDORABL_WAS_OFF; + bl->props.brightness = MAX_USER_VALUE; + backlight_update_status(bl); + + /* enable PWM function in pin mux */ + twl_i2c_read_u8(TWL4030_MODULE_INTBR, &r, TWL_INTBR_PMBR1); + r &= ~TWL_PMBR1_PWM0_MUXMASK; + r |= TWL_PMBR1_PWM0; + twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_PMBR1); + + return 0; +} + +static int pandora_backlight_remove(struct platform_device *pdev) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + backlight_device_unregister(bl); + return 0; +} + +static struct platform_driver pandora_backlight_driver = { + .driver = { + .name = "pandora-backlight", + .owner = THIS_MODULE, + }, + .probe = pandora_backlight_probe, + .remove = pandora_backlight_remove, +}; + +module_platform_driver(pandora_backlight_driver); + +MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>"); +MODULE_DESCRIPTION("Pandora Backlight Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pandora-backlight"); diff --git a/drivers/video/backlight/pcf50633-backlight.c b/drivers/video/backlight/pcf50633-backlight.c index 13e88b71daec..c65853cb9740 100644 --- a/drivers/video/backlight/pcf50633-backlight.c +++ b/drivers/video/backlight/pcf50633-backlight.c @@ -101,14 +101,13 @@ static const struct backlight_ops pcf50633_bl_ops = { static int __devinit pcf50633_bl_probe(struct platform_device *pdev) { - int ret; struct pcf50633_bl *pcf_bl; struct device *parent = pdev->dev.parent; struct pcf50633_platform_data *pcf50633_data = parent->platform_data; struct pcf50633_bl_platform_data *pdata = pcf50633_data->backlight_data; struct backlight_properties bl_props; - pcf_bl = kzalloc(sizeof(*pcf_bl), GFP_KERNEL); + pcf_bl = devm_kzalloc(&pdev->dev, sizeof(*pcf_bl), GFP_KERNEL); if (!pcf_bl) return -ENOMEM; @@ -129,10 +128,8 @@ static int __devinit pcf50633_bl_probe(struct platform_device *pdev) pcf_bl->bl = backlight_device_register(pdev->name, &pdev->dev, pcf_bl, &pcf50633_bl_ops, &bl_props); - if (IS_ERR(pcf_bl->bl)) { - ret = PTR_ERR(pcf_bl->bl); - goto err_free; - } + if (IS_ERR(pcf_bl->bl)) + return PTR_ERR(pcf_bl->bl); platform_set_drvdata(pdev, pcf_bl); @@ -145,11 +142,6 @@ static int __devinit pcf50633_bl_probe(struct platform_device *pdev) backlight_update_status(pcf_bl->bl); return 0; - -err_free: - kfree(pcf_bl); - - return ret; } static int __devexit pcf50633_bl_remove(struct platform_device *pdev) @@ -160,8 +152,6 @@ static int __devexit pcf50633_bl_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - kfree(pcf_bl); - return 0; } diff --git a/drivers/video/backlight/platform_lcd.c b/drivers/video/backlight/platform_lcd.c index f0bf491ed087..b6672340d6c7 100644 --- a/drivers/video/backlight/platform_lcd.c +++ b/drivers/video/backlight/platform_lcd.c @@ -121,9 +121,9 @@ static int __devexit platform_lcd_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int platform_lcd_suspend(struct platform_device *pdev, pm_message_t st) +static int platform_lcd_suspend(struct device *dev) { - struct platform_lcd *plcd = platform_get_drvdata(pdev); + struct platform_lcd *plcd = dev_get_drvdata(dev); plcd->suspended = 1; platform_lcd_set_power(plcd->lcd, plcd->power); @@ -131,29 +131,30 @@ static int platform_lcd_suspend(struct platform_device *pdev, pm_message_t st) return 0; } -static int platform_lcd_resume(struct platform_device *pdev) +static int platform_lcd_resume(struct device *dev) { - struct platform_lcd *plcd = platform_get_drvdata(pdev); + struct platform_lcd *plcd = dev_get_drvdata(dev); plcd->suspended = 0; platform_lcd_set_power(plcd->lcd, plcd->power); return 0; } -#else -#define platform_lcd_suspend NULL -#define platform_lcd_resume NULL + +static SIMPLE_DEV_PM_OPS(platform_lcd_pm_ops, platform_lcd_suspend, + platform_lcd_resume); #endif static struct platform_driver platform_lcd_driver = { .driver = { .name = "platform-lcd", .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &platform_lcd_pm_ops, +#endif }, .probe = platform_lcd_probe, .remove = __devexit_p(platform_lcd_remove), - .suspend = platform_lcd_suspend, - .resume = platform_lcd_resume, }; module_platform_driver(platform_lcd_driver); diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 7496d04e1d3c..342b7d7cbb63 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -102,7 +102,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) return ret; } - pb = kzalloc(sizeof(*pb), GFP_KERNEL); + pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL); if (!pb) { dev_err(&pdev->dev, "no memory for state\n"); ret = -ENOMEM; @@ -121,7 +121,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) if (IS_ERR(pb->pwm)) { dev_err(&pdev->dev, "unable to request PWM for backlight\n"); ret = PTR_ERR(pb->pwm); - goto err_pwm; + goto err_alloc; } else dev_dbg(&pdev->dev, "got pwm for backlight\n"); @@ -144,8 +144,6 @@ static int pwm_backlight_probe(struct platform_device *pdev) err_bl: pwm_free(pb->pwm); -err_pwm: - kfree(pb); err_alloc: if (data->exit) data->exit(&pdev->dev); @@ -162,7 +160,6 @@ static int pwm_backlight_remove(struct platform_device *pdev) pwm_config(pb->pwm, 0, pb->period); pwm_disable(pb->pwm); pwm_free(pb->pwm); - kfree(pb); if (data->exit) data->exit(&pdev->dev); return 0; diff --git a/drivers/video/backlight/s6e63m0.c b/drivers/video/backlight/s6e63m0.c index 516db703dd24..e264f55b2574 100644 --- a/drivers/video/backlight/s6e63m0.c +++ b/drivers/video/backlight/s6e63m0.c @@ -909,18 +909,7 @@ static struct spi_driver s6e63m0_driver = { .resume = s6e63m0_resume, }; -static int __init s6e63m0_init(void) -{ - return spi_register_driver(&s6e63m0_driver); -} - -static void __exit s6e63m0_exit(void) -{ - spi_unregister_driver(&s6e63m0_driver); -} - -module_init(s6e63m0_init); -module_exit(s6e63m0_exit); +module_spi_driver(s6e63m0_driver); MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>"); MODULE_DESCRIPTION("S6E63M0 LCD Driver"); diff --git a/drivers/video/backlight/tdo24m.c b/drivers/video/backlight/tdo24m.c index 1997e12a1057..2368b8e5f89e 100644 --- a/drivers/video/backlight/tdo24m.c +++ b/drivers/video/backlight/tdo24m.c @@ -459,17 +459,7 @@ static struct spi_driver tdo24m_driver = { .resume = tdo24m_resume, }; -static int __init tdo24m_init(void) -{ - return spi_register_driver(&tdo24m_driver); -} -module_init(tdo24m_init); - -static void __exit tdo24m_exit(void) -{ - spi_unregister_driver(&tdo24m_driver); -} -module_exit(tdo24m_exit); +module_spi_driver(tdo24m_driver); MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); MODULE_DESCRIPTION("Driver for Toppoly TDO24M LCD Panel"); diff --git a/drivers/video/backlight/tosa_bl.c b/drivers/video/backlight/tosa_bl.c index 425a7365470b..2b241abced43 100644 --- a/drivers/video/backlight/tosa_bl.c +++ b/drivers/video/backlight/tosa_bl.c @@ -181,18 +181,7 @@ static struct i2c_driver tosa_bl_driver = { .id_table = tosa_bl_id, }; -static int __init tosa_bl_init(void) -{ - return i2c_add_driver(&tosa_bl_driver); -} - -static void __exit tosa_bl_exit(void) -{ - i2c_del_driver(&tosa_bl_driver); -} - -module_init(tosa_bl_init); -module_exit(tosa_bl_exit); +module_i2c_driver(tosa_bl_driver); MODULE_AUTHOR("Dmitry Baryshkov"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/backlight/tosa_lcd.c b/drivers/video/backlight/tosa_lcd.c index 772f6015219a..a2161f631a83 100644 --- a/drivers/video/backlight/tosa_lcd.c +++ b/drivers/video/backlight/tosa_lcd.c @@ -285,18 +285,7 @@ static struct spi_driver tosa_lcd_driver = { .resume = tosa_lcd_resume, }; -static int __init tosa_lcd_init(void) -{ - return spi_register_driver(&tosa_lcd_driver); -} - -static void __exit tosa_lcd_exit(void) -{ - spi_unregister_driver(&tosa_lcd_driver); -} - -module_init(tosa_lcd_init); -module_exit(tosa_lcd_exit); +module_spi_driver(tosa_lcd_driver); MODULE_AUTHOR("Dmitry Baryshkov"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/backlight/vgg2432a4.c b/drivers/video/backlight/vgg2432a4.c index b49063c831e7..b617fae9aa26 100644 --- a/drivers/video/backlight/vgg2432a4.c +++ b/drivers/video/backlight/vgg2432a4.c @@ -262,20 +262,7 @@ static struct spi_driver vgg2432a4_driver = { .resume = vgg2432a4_resume, }; -/* Device driver initialisation */ - -static int __init vgg2432a4_init(void) -{ - return spi_register_driver(&vgg2432a4_driver); -} - -static void __exit vgg2432a4_exit(void) -{ - spi_unregister_driver(&vgg2432a4_driver); -} - -module_init(vgg2432a4_init); -module_exit(vgg2432a4_exit); +module_spi_driver(vgg2432a4_driver); MODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>"); MODULE_DESCRIPTION("VGG2432A4 LCD Driver"); diff --git a/drivers/video/backlight/wm831x_bl.c b/drivers/video/backlight/wm831x_bl.c index 4e915f5eca99..5d365deb5f82 100644 --- a/drivers/video/backlight/wm831x_bl.c +++ b/drivers/video/backlight/wm831x_bl.c @@ -186,7 +186,7 @@ static int wm831x_backlight_probe(struct platform_device *pdev) if (ret < 0) return ret; - data = kzalloc(sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (data == NULL) return -ENOMEM; @@ -200,7 +200,6 @@ static int wm831x_backlight_probe(struct platform_device *pdev) &wm831x_backlight_ops, &props); if (IS_ERR(bl)) { dev_err(&pdev->dev, "failed to register backlight\n"); - kfree(data); return PTR_ERR(bl); } @@ -211,7 +210,6 @@ static int wm831x_backlight_probe(struct platform_device *pdev) /* Disable the DCDC if it was started so we can bootstrap */ wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, WM831X_DC4_ENA, 0); - backlight_update_status(bl); return 0; @@ -220,10 +218,8 @@ static int wm831x_backlight_probe(struct platform_device *pdev) static int wm831x_backlight_remove(struct platform_device *pdev) { struct backlight_device *bl = platform_get_drvdata(pdev); - struct wm831x_backlight_data *data = bl_get_data(bl); backlight_device_unregister(bl); - kfree(data); return 0; } diff --git a/drivers/video/bf537-lq035.c b/drivers/video/bf537-lq035.c index bea53c1a4950..befbc80d11fc 100644 --- a/drivers/video/bf537-lq035.c +++ b/drivers/video/bf537-lq035.c @@ -383,23 +383,19 @@ static int __devinit request_ports(void) } #if (defined(UD) && defined(LBR)) - if (gpio_request(UD, KBUILD_MODNAME)) { + if (gpio_request_one(UD, GPIOF_OUT_INIT_LOW, KBUILD_MODNAME)) { pr_err("requesting GPIO %d failed\n", UD); return -EBUSY; } - if (gpio_request(LBR, KBUILD_MODNAME)) { + if (gpio_request_one(LBR, GPIOF_OUT_INIT_HIGH, KBUILD_MODNAME)) { pr_err("requesting GPIO %d failed\n", LBR); gpio_free(UD); return -EBUSY; } - - gpio_direction_output(UD, 0); - gpio_direction_output(LBR, 1); - #endif - if (gpio_request(MOD, KBUILD_MODNAME)) { + if (gpio_request_one(MOD, GPIOF_OUT_INIT_HIGH, KBUILD_MODNAME)) { pr_err("requesting GPIO %d failed\n", MOD); #if (defined(UD) && defined(LBR)) gpio_free(LBR); @@ -408,8 +404,6 @@ static int __devinit request_ports(void) return -EBUSY; } - gpio_direction_output(MOD, 1); - SSYNC(); return 0; } diff --git a/drivers/video/bf54x-lq043fb.c b/drivers/video/bf54x-lq043fb.c index 46b03f53985f..dc2f0047769b 100644 --- a/drivers/video/bf54x-lq043fb.c +++ b/drivers/video/bf54x-lq043fb.c @@ -240,7 +240,7 @@ static int request_ports(struct bfin_bf54xfb_info *fbi) u16 eppi_req_18[] = EPPI0_18; u16 disp = fbi->mach_info->disp; - if (gpio_request(disp, DRIVER_NAME)) { + if (gpio_request_one(disp, GPIOF_OUT_INIT_HIGH, DRIVER_NAME)) { printk(KERN_ERR "Requesting GPIO %d failed\n", disp); return -EFAULT; } @@ -263,8 +263,6 @@ static int request_ports(struct bfin_bf54xfb_info *fbi) } } - gpio_direction_output(disp, 1); - return 0; } diff --git a/drivers/video/bfin-lq035q1-fb.c b/drivers/video/bfin-lq035q1-fb.c index c633068372c9..86922ac84412 100644 --- a/drivers/video/bfin-lq035q1-fb.c +++ b/drivers/video/bfin-lq035q1-fb.c @@ -365,10 +365,10 @@ static int __devinit bfin_lq035q1_request_ports(struct platform_device *pdev, * Drive PPI_FS3 Low */ if (ANOMALY_05000400) { - int ret = gpio_request(P_IDENT(P_PPI0_FS3), "PPI_FS3"); + int ret = gpio_request_one(P_IDENT(P_PPI0_FS3), + GPIOF_OUT_INIT_LOW, "PPI_FS3"); if (ret) return ret; - gpio_direction_output(P_IDENT(P_PPI0_FS3), 0); } if (ppi16) @@ -716,14 +716,14 @@ static int __devinit bfin_lq035q1_probe(struct platform_device *pdev) } if (info->disp_info->use_bl) { - ret = gpio_request(info->disp_info->gpio_bl, "LQ035 Backlight"); + ret = gpio_request_one(info->disp_info->gpio_bl, + GPIOF_OUT_INIT_LOW, "LQ035 Backlight"); if (ret) { dev_err(&pdev->dev, "failed to request GPIO %d\n", info->disp_info->gpio_bl); goto out9; } - gpio_direction_output(info->disp_info->gpio_bl, 0); } ret = register_framebuffer(fbinfo); diff --git a/drivers/video/bfin_adv7393fb.c b/drivers/video/bfin_adv7393fb.c index 811dd7f6aa41..1a268a294478 100644 --- a/drivers/video/bfin_adv7393fb.c +++ b/drivers/video/bfin_adv7393fb.c @@ -36,9 +36,7 @@ #include <linux/dma-mapping.h> #include <linux/proc_fs.h> #include <linux/platform_device.h> - #include <linux/i2c.h> -#include <linux/i2c-dev.h> #include "bfin_adv7393fb.h" @@ -411,12 +409,13 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, /* Workaround "PPI Does Not Start Properly In Specific Mode" */ if (ANOMALY_05000400) { - if (gpio_request(P_IDENT(P_PPI0_FS3), "PPI0_FS3")) { + ret = gpio_request_one(P_IDENT(P_PPI0_FS3), GPIOF_OUT_INIT_LOW, + "PPI0_FS3") + if (ret) { dev_err(&client->dev, "PPI0_FS3 GPIO request failed\n"); ret = -EBUSY; goto out_8; } - gpio_direction_output(P_IDENT(P_PPI0_FS3), 0); } if (peripheral_request_list(ppi_pins, DRIVER_NAME)) { diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c index 29577bf1f559..47118c75a4c0 100644 --- a/drivers/video/da8xx-fb.c +++ b/drivers/video/da8xx-fb.c @@ -32,6 +32,7 @@ #include <linux/console.h> #include <linux/slab.h> #include <video/da8xx-fb.h> +#include <asm/div64.h> #define DRIVER_NAME "da8xx_lcdc" @@ -161,6 +162,7 @@ struct da8xx_fb_par { int vsync_timeout; #ifdef CONFIG_CPU_FREQ struct notifier_block freq_transition; + unsigned int lcd_fck_rate; #endif void (*panel_power_ctrl)(int); }; @@ -174,7 +176,6 @@ static struct fb_var_screeninfo da8xx_fb_var __devinitdata = { .activate = 0, .height = -1, .width = -1, - .pixclock = 46666, /* 46us - AUO display */ .accel_flags = 0, .left_margin = LEFT_MARGIN, .right_margin = RIGHT_MARGIN, @@ -238,6 +239,20 @@ static struct da8xx_panel known_lcd_panels[] = { .pxl_clk = 7833600, .invert_pxl_clk = 0, }, + [2] = { + /* Hitachi SP10Q010 */ + .name = "SP10Q010", + .width = 320, + .height = 240, + .hfp = 10, + .hbp = 10, + .hsw = 10, + .vfp = 10, + .vbp = 10, + .vsw = 10, + .pxl_clk = 7833600, + .invert_pxl_clk = 0, + }, }; /* Enable the Raster Engine of the LCD Controller */ @@ -546,7 +561,26 @@ static int fb_setcolreg(unsigned regno, unsigned red, unsigned green, if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) return 1; - if (info->var.bits_per_pixel == 8) { + if (info->var.bits_per_pixel == 4) { + if (regno > 15) + return 1; + + if (info->var.grayscale) { + pal = regno; + } else { + red >>= 4; + green >>= 8; + blue >>= 12; + + pal = (red & 0x0f00); + pal |= (green & 0x00f0); + pal |= (blue & 0x000f); + } + if (regno == 0) + pal |= 0x2000; + palette[regno] = pal; + + } else if (info->var.bits_per_pixel == 8) { red >>= 4; green >>= 8; blue >>= 12; @@ -801,6 +835,7 @@ static int fb_check_var(struct fb_var_screeninfo *var, var->blue.length = 8; var->transp.offset = 0; var->transp.length = 0; + var->nonstd = 0; break; case 4: var->red.offset = 0; @@ -811,6 +846,7 @@ static int fb_check_var(struct fb_var_screeninfo *var, var->blue.length = 4; var->transp.offset = 0; var->transp.length = 0; + var->nonstd = FB_NONSTD_REV_PIX_IN_B; break; case 16: /* RGB 565 */ var->red.offset = 11; @@ -821,6 +857,7 @@ static int fb_check_var(struct fb_var_screeninfo *var, var->blue.length = 5; var->transp.offset = 0; var->transp.length = 0; + var->nonstd = 0; break; default: err = -EINVAL; @@ -840,11 +877,13 @@ static int lcd_da8xx_cpufreq_transition(struct notifier_block *nb, struct da8xx_fb_par *par; par = container_of(nb, struct da8xx_fb_par, freq_transition); - if (val == CPUFREQ_PRECHANGE) { - lcd_disable_raster(); - } else if (val == CPUFREQ_POSTCHANGE) { - lcd_calc_clk_divider(par); - lcd_enable_raster(); + if (val == CPUFREQ_POSTCHANGE) { + if (par->lcd_fck_rate != clk_get_rate(par->lcdc_clk)) { + par->lcd_fck_rate = clk_get_rate(par->lcdc_clk); + lcd_disable_raster(); + lcd_calc_clk_divider(par); + lcd_enable_raster(); + } } return 0; @@ -1048,6 +1087,22 @@ static struct fb_ops da8xx_fb_ops = { .fb_blank = cfb_blank, }; +/* Calculate and return pixel clock period in pico seconds */ +static unsigned int da8xxfb_pixel_clk_period(struct da8xx_fb_par *par) +{ + unsigned int lcd_clk, div; + unsigned int configured_pix_clk; + unsigned long long pix_clk_period_picosec = 1000000000000ULL; + + lcd_clk = clk_get_rate(par->lcdc_clk); + div = lcd_clk / par->pxl_clk; + configured_pix_clk = (lcd_clk / div); + + do_div(pix_clk_period_picosec, configured_pix_clk); + + return pix_clk_period_picosec; +} + static int __devinit fb_probe(struct platform_device *device) { struct da8xx_lcdc_platform_data *fb_pdata = @@ -1137,6 +1192,9 @@ static int __devinit fb_probe(struct platform_device *device) par = da8xx_fb_info->par; par->lcdc_clk = fb_clk; +#ifdef CONFIG_CPU_FREQ + par->lcd_fck_rate = clk_get_rate(fb_clk); +#endif par->pxl_clk = lcdc_info->pxl_clk; if (fb_pdata->panel_power_ctrl) { par->panel_power_ctrl = fb_pdata->panel_power_ctrl; @@ -1209,6 +1267,11 @@ static int __devinit fb_probe(struct platform_device *device) da8xx_fb_var.hsync_len = lcdc_info->hsw; da8xx_fb_var.vsync_len = lcdc_info->vsw; + da8xx_fb_var.right_margin = lcdc_info->hfp; + da8xx_fb_var.left_margin = lcdc_info->hbp; + da8xx_fb_var.lower_margin = lcdc_info->vfp; + da8xx_fb_var.upper_margin = lcdc_info->vbp; + da8xx_fb_var.pixclock = da8xxfb_pixel_clk_period(par); /* Initialize fbinfo */ da8xx_fb_info->flags = FBINFO_FLAG_DEFAULT; @@ -1264,8 +1327,8 @@ static int __devinit fb_probe(struct platform_device *device) irq_freq: #ifdef CONFIG_CPU_FREQ lcd_da8xx_cpufreq_deregister(par); -#endif err_cpu_freq: +#endif unregister_framebuffer(da8xx_fb_info); err_dealloc_cmap: diff --git a/drivers/video/ep93xx-fb.c b/drivers/video/ep93xx-fb.c index 2e830ec52a5a..f8babbeee275 100644 --- a/drivers/video/ep93xx-fb.c +++ b/drivers/video/ep93xx-fb.c @@ -519,12 +519,15 @@ static int __devinit ep93xxfb_probe(struct platform_device *pdev) goto failed; } - res = request_mem_region(res->start, resource_size(res), pdev->name); - if (!res) { - err = -EBUSY; - goto failed; - } - + /* + * FIXME - We don't do a request_mem_region here because we are + * sharing the register space with the backlight driver (see + * drivers/video/backlight/ep93xx_bl.c) and doing so will cause + * the second loaded driver to return -EBUSY. + * + * NOTE: No locking is required; the backlight does not touch + * any of the framebuffer registers. + */ fbi->res = res; fbi->mmio_base = ioremap(res->start, resource_size(res)); if (!fbi->mmio_base) { @@ -586,8 +589,6 @@ failed: clk_put(fbi->clk); if (fbi->mmio_base) iounmap(fbi->mmio_base); - if (fbi->res) - release_mem_region(fbi->res->start, resource_size(fbi->res)); ep93xxfb_dealloc_videomem(info); if (&info->cmap) fb_dealloc_cmap(&info->cmap); @@ -608,7 +609,6 @@ static int __devexit ep93xxfb_remove(struct platform_device *pdev) clk_disable(fbi->clk); clk_put(fbi->clk); iounmap(fbi->mmio_base); - release_mem_region(fbi->res->start, resource_size(fbi->res)); ep93xxfb_dealloc_videomem(info); fb_dealloc_cmap(&info->cmap); diff --git a/drivers/video/exynos/Kconfig b/drivers/video/exynos/Kconfig new file mode 100644 index 000000000000..1b035b2eb6b6 --- /dev/null +++ b/drivers/video/exynos/Kconfig @@ -0,0 +1,37 @@ +# +# Exynos Video configuration +# + +menuconfig EXYNOS_VIDEO + bool "Exynos Video driver support" + help + This enables support for EXYNOS Video device. + +if EXYNOS_VIDEO + +# +# MIPI DSI driver +# + +config EXYNOS_MIPI_DSI + bool "EXYNOS MIPI DSI driver support." + depends on ARCH_S5PV210 || ARCH_EXYNOS + help + This enables support for MIPI-DSI device. + +config EXYNOS_LCD_S6E8AX0 + bool "S6E8AX0 MIPI AMOLED LCD Driver" + depends on (EXYNOS_MIPI_DSI && BACKLIGHT_CLASS_DEVICE && LCD_CLASS_DEVICE) + default n + help + If you have an S6E8AX0 MIPI AMOLED LCD Panel, say Y to enable its + LCD control driver. + +config EXYNOS_DP + bool "EXYNOS DP driver support" + depends on ARCH_EXYNOS + default n + help + This enables support for DP device. + +endif # EXYNOS_VIDEO diff --git a/drivers/video/exynos/Makefile b/drivers/video/exynos/Makefile new file mode 100644 index 000000000000..ec7772e452a9 --- /dev/null +++ b/drivers/video/exynos/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the exynos video drivers. +# + +obj-$(CONFIG_EXYNOS_MIPI_DSI) += exynos_mipi_dsi.o exynos_mipi_dsi_common.o \ + exynos_mipi_dsi_lowlevel.o +obj-$(CONFIG_EXYNOS_LCD_S6E8AX0) += s6e8ax0.o +obj-$(CONFIG_EXYNOS_DP) += exynos_dp_core.o exynos_dp_reg.o diff --git a/drivers/video/exynos/exynos_dp_core.c b/drivers/video/exynos/exynos_dp_core.c new file mode 100644 index 000000000000..2a4481cf260c --- /dev/null +++ b/drivers/video/exynos/exynos_dp_core.c @@ -0,0 +1,1058 @@ +/* + * Samsung SoC DP (Display Port) interface driver. + * + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * Author: Jingoo Han <jg1.han@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/delay.h> + +#include <video/exynos_dp.h> + +#include <plat/cpu.h> + +#include "exynos_dp_core.h" + +static int exynos_dp_init_dp(struct exynos_dp_device *dp) +{ + exynos_dp_reset(dp); + + /* SW defined function Normal operation */ + exynos_dp_enable_sw_function(dp); + + exynos_dp_config_interrupt(dp); + exynos_dp_init_analog_func(dp); + + exynos_dp_init_hpd(dp); + exynos_dp_init_aux(dp); + + return 0; +} + +static int exynos_dp_detect_hpd(struct exynos_dp_device *dp) +{ + int timeout_loop = 0; + + exynos_dp_init_hpd(dp); + + udelay(200); + + while (exynos_dp_get_plug_in_status(dp) != 0) { + timeout_loop++; + if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) { + dev_err(dp->dev, "failed to get hpd plug status\n"); + return -ETIMEDOUT; + } + udelay(10); + } + + return 0; +} + +static unsigned char exynos_dp_calc_edid_check_sum(unsigned char *edid_data) +{ + int i; + unsigned char sum = 0; + + for (i = 0; i < EDID_BLOCK_LENGTH; i++) + sum = sum + edid_data[i]; + + return sum; +} + +static int exynos_dp_read_edid(struct exynos_dp_device *dp) +{ + unsigned char edid[EDID_BLOCK_LENGTH * 2]; + unsigned int extend_block = 0; + unsigned char sum; + unsigned char test_vector; + int retval; + + /* + * EDID device address is 0x50. + * However, if necessary, you must have set upper address + * into E-EDID in I2C device, 0x30. + */ + + /* Read Extension Flag, Number of 128-byte EDID extension blocks */ + exynos_dp_read_byte_from_i2c(dp, I2C_EDID_DEVICE_ADDR, + EDID_EXTENSION_FLAG, + &extend_block); + + if (extend_block > 0) { + dev_dbg(dp->dev, "EDID data includes a single extension!\n"); + + /* Read EDID data */ + retval = exynos_dp_read_bytes_from_i2c(dp, I2C_EDID_DEVICE_ADDR, + EDID_HEADER_PATTERN, + EDID_BLOCK_LENGTH, + &edid[EDID_HEADER_PATTERN]); + if (retval != 0) { + dev_err(dp->dev, "EDID Read failed!\n"); + return -EIO; + } + sum = exynos_dp_calc_edid_check_sum(edid); + if (sum != 0) { + dev_err(dp->dev, "EDID bad checksum!\n"); + return -EIO; + } + + /* Read additional EDID data */ + retval = exynos_dp_read_bytes_from_i2c(dp, + I2C_EDID_DEVICE_ADDR, + EDID_BLOCK_LENGTH, + EDID_BLOCK_LENGTH, + &edid[EDID_BLOCK_LENGTH]); + if (retval != 0) { + dev_err(dp->dev, "EDID Read failed!\n"); + return -EIO; + } + sum = exynos_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]); + if (sum != 0) { + dev_err(dp->dev, "EDID bad checksum!\n"); + return -EIO; + } + + exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_TEST_REQUEST, + &test_vector); + if (test_vector & DPCD_TEST_EDID_READ) { + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TEST_EDID_CHECKSUM, + edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]); + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TEST_RESPONSE, + DPCD_TEST_EDID_CHECKSUM_WRITE); + } + } else { + dev_info(dp->dev, "EDID data does not include any extensions.\n"); + + /* Read EDID data */ + retval = exynos_dp_read_bytes_from_i2c(dp, + I2C_EDID_DEVICE_ADDR, + EDID_HEADER_PATTERN, + EDID_BLOCK_LENGTH, + &edid[EDID_HEADER_PATTERN]); + if (retval != 0) { + dev_err(dp->dev, "EDID Read failed!\n"); + return -EIO; + } + sum = exynos_dp_calc_edid_check_sum(edid); + if (sum != 0) { + dev_err(dp->dev, "EDID bad checksum!\n"); + return -EIO; + } + + exynos_dp_read_byte_from_dpcd(dp, + DPCD_ADDR_TEST_REQUEST, + &test_vector); + if (test_vector & DPCD_TEST_EDID_READ) { + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TEST_EDID_CHECKSUM, + edid[EDID_CHECKSUM]); + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TEST_RESPONSE, + DPCD_TEST_EDID_CHECKSUM_WRITE); + } + } + + dev_err(dp->dev, "EDID Read success!\n"); + return 0; +} + +static int exynos_dp_handle_edid(struct exynos_dp_device *dp) +{ + u8 buf[12]; + int i; + int retval; + + /* Read DPCD DPCD_ADDR_DPCD_REV~RECEIVE_PORT1_CAP_1 */ + exynos_dp_read_bytes_from_dpcd(dp, + DPCD_ADDR_DPCD_REV, + 12, buf); + + /* Read EDID */ + for (i = 0; i < 3; i++) { + retval = exynos_dp_read_edid(dp); + if (retval == 0) + break; + } + + return retval; +} + +static void exynos_dp_enable_rx_to_enhanced_mode(struct exynos_dp_device *dp, + bool enable) +{ + u8 data; + + exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, &data); + + if (enable) + exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, + DPCD_ENHANCED_FRAME_EN | + DPCD_LANE_COUNT_SET(data)); + else + exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, + DPCD_LANE_COUNT_SET(data)); +} + +static int exynos_dp_is_enhanced_mode_available(struct exynos_dp_device *dp) +{ + u8 data; + int retval; + + exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LANE_COUNT, &data); + retval = DPCD_ENHANCED_FRAME_CAP(data); + + return retval; +} + +static void exynos_dp_set_enhanced_mode(struct exynos_dp_device *dp) +{ + u8 data; + + data = exynos_dp_is_enhanced_mode_available(dp); + exynos_dp_enable_rx_to_enhanced_mode(dp, data); + exynos_dp_enable_enhanced_mode(dp, data); +} + +static void exynos_dp_training_pattern_dis(struct exynos_dp_device *dp) +{ + exynos_dp_set_training_pattern(dp, DP_NONE); + + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TRAINING_PATTERN_SET, + DPCD_TRAINING_PATTERN_DISABLED); +} + +static void exynos_dp_set_lane_lane_pre_emphasis(struct exynos_dp_device *dp, + int pre_emphasis, int lane) +{ + switch (lane) { + case 0: + exynos_dp_set_lane0_pre_emphasis(dp, pre_emphasis); + break; + case 1: + exynos_dp_set_lane1_pre_emphasis(dp, pre_emphasis); + break; + + case 2: + exynos_dp_set_lane2_pre_emphasis(dp, pre_emphasis); + break; + + case 3: + exynos_dp_set_lane3_pre_emphasis(dp, pre_emphasis); + break; + } +} + +static void exynos_dp_link_start(struct exynos_dp_device *dp) +{ + u8 buf[5]; + int lane; + int lane_count; + + lane_count = dp->link_train.lane_count; + + dp->link_train.lt_state = CLOCK_RECOVERY; + dp->link_train.eq_loop = 0; + + for (lane = 0; lane < lane_count; lane++) + dp->link_train.cr_loop[lane] = 0; + + /* Set sink to D0 (Sink Not Ready) mode. */ + exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_SINK_POWER_STATE, + DPCD_SET_POWER_STATE_D0); + + /* Set link rate and count as you want to establish*/ + exynos_dp_set_link_bandwidth(dp, dp->link_train.link_rate); + exynos_dp_set_lane_count(dp, dp->link_train.lane_count); + + /* Setup RX configuration */ + buf[0] = dp->link_train.link_rate; + buf[1] = dp->link_train.lane_count; + exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_LINK_BW_SET, + 2, buf); + + /* Set TX pre-emphasis to minimum */ + for (lane = 0; lane < lane_count; lane++) + exynos_dp_set_lane_lane_pre_emphasis(dp, + PRE_EMPHASIS_LEVEL_0, lane); + + /* Set training pattern 1 */ + exynos_dp_set_training_pattern(dp, TRAINING_PTN1); + + /* Set RX training pattern */ + buf[0] = DPCD_SCRAMBLING_DISABLED | + DPCD_TRAINING_PATTERN_1; + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TRAINING_PATTERN_SET, buf[0]); + + for (lane = 0; lane < lane_count; lane++) + buf[lane] = DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 | + DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0; + exynos_dp_write_bytes_to_dpcd(dp, + DPCD_ADDR_TRAINING_PATTERN_SET, + lane_count, buf); +} + +static unsigned char exynos_dp_get_lane_status(u8 link_status[6], int lane) +{ + int shift = (lane & 1) * 4; + u8 link_value = link_status[lane>>1]; + + return (link_value >> shift) & 0xf; +} + +static int exynos_dp_clock_recovery_ok(u8 link_status[6], int lane_count) +{ + int lane; + u8 lane_status; + + for (lane = 0; lane < lane_count; lane++) { + lane_status = exynos_dp_get_lane_status(link_status, lane); + if ((lane_status & DPCD_LANE_CR_DONE) == 0) + return -EINVAL; + } + return 0; +} + +static int exynos_dp_channel_eq_ok(u8 link_status[6], int lane_count) +{ + int lane; + u8 lane_align; + u8 lane_status; + + lane_align = link_status[2]; + if ((lane_align == DPCD_INTERLANE_ALIGN_DONE) == 0) + return -EINVAL; + + for (lane = 0; lane < lane_count; lane++) { + lane_status = exynos_dp_get_lane_status(link_status, lane); + lane_status &= DPCD_CHANNEL_EQ_BITS; + if (lane_status != DPCD_CHANNEL_EQ_BITS) + return -EINVAL; + } + return 0; +} + +static unsigned char exynos_dp_get_adjust_request_voltage(u8 adjust_request[2], + int lane) +{ + int shift = (lane & 1) * 4; + u8 link_value = adjust_request[lane>>1]; + + return (link_value >> shift) & 0x3; +} + +static unsigned char exynos_dp_get_adjust_request_pre_emphasis( + u8 adjust_request[2], + int lane) +{ + int shift = (lane & 1) * 4; + u8 link_value = adjust_request[lane>>1]; + + return ((link_value >> shift) & 0xc) >> 2; +} + +static void exynos_dp_set_lane_link_training(struct exynos_dp_device *dp, + u8 training_lane_set, int lane) +{ + switch (lane) { + case 0: + exynos_dp_set_lane0_link_training(dp, training_lane_set); + break; + case 1: + exynos_dp_set_lane1_link_training(dp, training_lane_set); + break; + + case 2: + exynos_dp_set_lane2_link_training(dp, training_lane_set); + break; + + case 3: + exynos_dp_set_lane3_link_training(dp, training_lane_set); + break; + } +} + +static unsigned int exynos_dp_get_lane_link_training( + struct exynos_dp_device *dp, + int lane) +{ + u32 reg; + + switch (lane) { + case 0: + reg = exynos_dp_get_lane0_link_training(dp); + break; + case 1: + reg = exynos_dp_get_lane1_link_training(dp); + break; + case 2: + reg = exynos_dp_get_lane2_link_training(dp); + break; + case 3: + reg = exynos_dp_get_lane3_link_training(dp); + break; + } + + return reg; +} + +static void exynos_dp_reduce_link_rate(struct exynos_dp_device *dp) +{ + if (dp->link_train.link_rate == LINK_RATE_2_70GBPS) { + /* set to reduced bit rate */ + dp->link_train.link_rate = LINK_RATE_1_62GBPS; + dev_err(dp->dev, "set to bandwidth %.2x\n", + dp->link_train.link_rate); + dp->link_train.lt_state = START; + } else { + exynos_dp_training_pattern_dis(dp); + /* set enhanced mode if available */ + exynos_dp_set_enhanced_mode(dp); + dp->link_train.lt_state = FAILED; + } +} + +static void exynos_dp_get_adjust_train(struct exynos_dp_device *dp, + u8 adjust_request[2]) +{ + int lane; + int lane_count; + u8 voltage_swing; + u8 pre_emphasis; + u8 training_lane; + + lane_count = dp->link_train.lane_count; + for (lane = 0; lane < lane_count; lane++) { + voltage_swing = exynos_dp_get_adjust_request_voltage( + adjust_request, lane); + pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( + adjust_request, lane); + training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | + DPCD_PRE_EMPHASIS_SET(pre_emphasis); + + if (voltage_swing == VOLTAGE_LEVEL_3 || + pre_emphasis == PRE_EMPHASIS_LEVEL_3) { + training_lane |= DPCD_MAX_SWING_REACHED; + training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED; + } + dp->link_train.training_lane[lane] = training_lane; + } +} + +static int exynos_dp_check_max_cr_loop(struct exynos_dp_device *dp, + u8 voltage_swing) +{ + int lane; + int lane_count; + + lane_count = dp->link_train.lane_count; + for (lane = 0; lane < lane_count; lane++) { + if (voltage_swing == VOLTAGE_LEVEL_3 || + dp->link_train.cr_loop[lane] == MAX_CR_LOOP) + return -EINVAL; + } + return 0; +} + +static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) +{ + u8 data; + u8 link_status[6]; + int lane; + int lane_count; + u8 buf[5]; + + u8 *adjust_request; + u8 voltage_swing; + u8 pre_emphasis; + u8 training_lane; + + udelay(100); + + exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS, + 6, link_status); + lane_count = dp->link_train.lane_count; + + if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) { + /* set training pattern 2 for EQ */ + exynos_dp_set_training_pattern(dp, TRAINING_PTN2); + + adjust_request = link_status + (DPCD_ADDR_ADJUST_REQUEST_LANE0_1 + - DPCD_ADDR_LANE0_1_STATUS); + + exynos_dp_get_adjust_train(dp, adjust_request); + + buf[0] = DPCD_SCRAMBLING_DISABLED | + DPCD_TRAINING_PATTERN_2; + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TRAINING_LANE0_SET, + buf[0]); + + for (lane = 0; lane < lane_count; lane++) { + exynos_dp_set_lane_link_training(dp, + dp->link_train.training_lane[lane], + lane); + buf[lane] = dp->link_train.training_lane[lane]; + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TRAINING_LANE0_SET + lane, + buf[lane]); + } + dp->link_train.lt_state = EQUALIZER_TRAINING; + } else { + exynos_dp_read_byte_from_dpcd(dp, + DPCD_ADDR_ADJUST_REQUEST_LANE0_1, + &data); + adjust_request[0] = data; + + exynos_dp_read_byte_from_dpcd(dp, + DPCD_ADDR_ADJUST_REQUEST_LANE2_3, + &data); + adjust_request[1] = data; + + for (lane = 0; lane < lane_count; lane++) { + training_lane = exynos_dp_get_lane_link_training( + dp, lane); + voltage_swing = exynos_dp_get_adjust_request_voltage( + adjust_request, lane); + pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( + adjust_request, lane); + if ((DPCD_VOLTAGE_SWING_GET(training_lane) == voltage_swing) && + (DPCD_PRE_EMPHASIS_GET(training_lane) == pre_emphasis)) + dp->link_train.cr_loop[lane]++; + dp->link_train.training_lane[lane] = training_lane; + } + + if (exynos_dp_check_max_cr_loop(dp, voltage_swing) != 0) { + exynos_dp_reduce_link_rate(dp); + } else { + exynos_dp_get_adjust_train(dp, adjust_request); + + for (lane = 0; lane < lane_count; lane++) { + exynos_dp_set_lane_link_training(dp, + dp->link_train.training_lane[lane], + lane); + buf[lane] = dp->link_train.training_lane[lane]; + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TRAINING_LANE0_SET + lane, + buf[lane]); + } + } + } + + return 0; +} + +static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp) +{ + u8 link_status[6]; + int lane; + int lane_count; + u8 buf[5]; + u32 reg; + + u8 *adjust_request; + + udelay(400); + + exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS, + 6, link_status); + lane_count = dp->link_train.lane_count; + + if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) { + adjust_request = link_status + (DPCD_ADDR_ADJUST_REQUEST_LANE0_1 + - DPCD_ADDR_LANE0_1_STATUS); + + if (exynos_dp_channel_eq_ok(link_status, lane_count) == 0) { + /* traing pattern Set to Normal */ + exynos_dp_training_pattern_dis(dp); + + dev_info(dp->dev, "Link Training success!\n"); + + exynos_dp_get_link_bandwidth(dp, ®); + dp->link_train.link_rate = reg; + dev_dbg(dp->dev, "final bandwidth = %.2x\n", + dp->link_train.link_rate); + + exynos_dp_get_lane_count(dp, ®); + dp->link_train.lane_count = reg; + dev_dbg(dp->dev, "final lane count = %.2x\n", + dp->link_train.lane_count); + /* set enhanced mode if available */ + exynos_dp_set_enhanced_mode(dp); + + dp->link_train.lt_state = FINISHED; + } else { + /* not all locked */ + dp->link_train.eq_loop++; + + if (dp->link_train.eq_loop > MAX_EQ_LOOP) { + exynos_dp_reduce_link_rate(dp); + } else { + exynos_dp_get_adjust_train(dp, adjust_request); + + for (lane = 0; lane < lane_count; lane++) { + exynos_dp_set_lane_link_training(dp, + dp->link_train.training_lane[lane], + lane); + buf[lane] = dp->link_train.training_lane[lane]; + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TRAINING_LANE0_SET + lane, + buf[lane]); + } + } + } + } else { + exynos_dp_reduce_link_rate(dp); + } + + return 0; +} + +static void exynos_dp_get_max_rx_bandwidth(struct exynos_dp_device *dp, + u8 *bandwidth) +{ + u8 data; + + /* + * For DP rev.1.1, Maximum link rate of Main Link lanes + * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps + */ + exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LINK_RATE, &data); + *bandwidth = data; +} + +static void exynos_dp_get_max_rx_lane_count(struct exynos_dp_device *dp, + u8 *lane_count) +{ + u8 data; + + /* + * For DP rev.1.1, Maximum number of Main Link lanes + * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes + */ + exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LANE_COUNT, &data); + *lane_count = DPCD_MAX_LANE_COUNT(data); +} + +static void exynos_dp_init_training(struct exynos_dp_device *dp, + enum link_lane_count_type max_lane, + enum link_rate_type max_rate) +{ + /* + * MACRO_RST must be applied after the PLL_LOCK to avoid + * the DP inter pair skew issue for at least 10 us + */ + exynos_dp_reset_macro(dp); + + /* Initialize by reading RX's DPCD */ + exynos_dp_get_max_rx_bandwidth(dp, &dp->link_train.link_rate); + exynos_dp_get_max_rx_lane_count(dp, &dp->link_train.lane_count); + + if ((dp->link_train.link_rate != LINK_RATE_1_62GBPS) && + (dp->link_train.link_rate != LINK_RATE_2_70GBPS)) { + dev_err(dp->dev, "Rx Max Link Rate is abnormal :%x !\n", + dp->link_train.link_rate); + dp->link_train.link_rate = LINK_RATE_1_62GBPS; + } + + if (dp->link_train.lane_count == 0) { + dev_err(dp->dev, "Rx Max Lane count is abnormal :%x !\n", + dp->link_train.lane_count); + dp->link_train.lane_count = (u8)LANE_COUNT1; + } + + /* Setup TX lane count & rate */ + if (dp->link_train.lane_count > max_lane) + dp->link_train.lane_count = max_lane; + if (dp->link_train.link_rate > max_rate) + dp->link_train.link_rate = max_rate; + + /* All DP analog module power up */ + exynos_dp_set_analog_power_down(dp, POWER_ALL, 0); +} + +static int exynos_dp_sw_link_training(struct exynos_dp_device *dp) +{ + int retval = 0; + int training_finished; + + /* Turn off unnecessary lane */ + if (dp->link_train.lane_count == 1) + exynos_dp_set_analog_power_down(dp, CH1_BLOCK, 1); + + training_finished = 0; + + dp->link_train.lt_state = START; + + /* Process here */ + while (!training_finished) { + switch (dp->link_train.lt_state) { + case START: + exynos_dp_link_start(dp); + break; + case CLOCK_RECOVERY: + exynos_dp_process_clock_recovery(dp); + break; + case EQUALIZER_TRAINING: + exynos_dp_process_equalizer_training(dp); + break; + case FINISHED: + training_finished = 1; + break; + case FAILED: + return -EREMOTEIO; + } + } + + return retval; +} + +static int exynos_dp_set_link_train(struct exynos_dp_device *dp, + u32 count, + u32 bwtype) +{ + int i; + int retval; + + for (i = 0; i < DP_TIMEOUT_LOOP_COUNT; i++) { + exynos_dp_init_training(dp, count, bwtype); + retval = exynos_dp_sw_link_training(dp); + if (retval == 0) + break; + + udelay(100); + } + + return retval; +} + +static int exynos_dp_config_video(struct exynos_dp_device *dp, + struct video_info *video_info) +{ + int retval = 0; + int timeout_loop = 0; + int done_count = 0; + + exynos_dp_config_video_slave_mode(dp, video_info); + + exynos_dp_set_video_color_format(dp, video_info->color_depth, + video_info->color_space, + video_info->dynamic_range, + video_info->ycbcr_coeff); + + if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { + dev_err(dp->dev, "PLL is not locked yet.\n"); + return -EINVAL; + } + + for (;;) { + timeout_loop++; + if (exynos_dp_is_slave_video_stream_clock_on(dp) == 0) + break; + if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) { + dev_err(dp->dev, "Timeout of video streamclk ok\n"); + return -ETIMEDOUT; + } + + mdelay(100); + } + + /* Set to use the register calculated M/N video */ + exynos_dp_set_video_cr_mn(dp, CALCULATED_M, 0, 0); + + /* For video bist, Video timing must be generated by register */ + exynos_dp_set_video_timing_mode(dp, VIDEO_TIMING_FROM_CAPTURE); + + /* Disable video mute */ + exynos_dp_enable_video_mute(dp, 0); + + /* Configure video slave mode */ + exynos_dp_enable_video_master(dp, 0); + + /* Enable video */ + exynos_dp_start_video(dp); + + timeout_loop = 0; + + for (;;) { + timeout_loop++; + if (exynos_dp_is_video_stream_on(dp) == 0) { + done_count++; + if (done_count > 10) + break; + } else if (done_count) { + done_count = 0; + } + if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) { + dev_err(dp->dev, "Timeout of video streamclk ok\n"); + return -ETIMEDOUT; + } + + mdelay(100); + } + + if (retval != 0) + dev_err(dp->dev, "Video stream is not detected!\n"); + + return retval; +} + +static void exynos_dp_enable_scramble(struct exynos_dp_device *dp, bool enable) +{ + u8 data; + + if (enable) { + exynos_dp_enable_scrambling(dp); + + exynos_dp_read_byte_from_dpcd(dp, + DPCD_ADDR_TRAINING_PATTERN_SET, + &data); + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TRAINING_PATTERN_SET, + (u8)(data & ~DPCD_SCRAMBLING_DISABLED)); + } else { + exynos_dp_disable_scrambling(dp); + + exynos_dp_read_byte_from_dpcd(dp, + DPCD_ADDR_TRAINING_PATTERN_SET, + &data); + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TRAINING_PATTERN_SET, + (u8)(data | DPCD_SCRAMBLING_DISABLED)); + } +} + +static irqreturn_t exynos_dp_irq_handler(int irq, void *arg) +{ + struct exynos_dp_device *dp = arg; + + dev_err(dp->dev, "exynos_dp_irq_handler\n"); + return IRQ_HANDLED; +} + +static int __devinit exynos_dp_probe(struct platform_device *pdev) +{ + struct resource *res; + struct exynos_dp_device *dp; + struct exynos_dp_platdata *pdata; + + int ret = 0; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data\n"); + return -EINVAL; + } + + dp = kzalloc(sizeof(struct exynos_dp_device), GFP_KERNEL); + if (!dp) { + dev_err(&pdev->dev, "no memory for device data\n"); + return -ENOMEM; + } + + dp->dev = &pdev->dev; + + dp->clock = clk_get(&pdev->dev, "dp"); + if (IS_ERR(dp->clock)) { + dev_err(&pdev->dev, "failed to get clock\n"); + ret = PTR_ERR(dp->clock); + goto err_dp; + } + + clk_enable(dp->clock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get registers\n"); + ret = -EINVAL; + goto err_clock; + } + + res = request_mem_region(res->start, resource_size(res), + dev_name(&pdev->dev)); + if (!res) { + dev_err(&pdev->dev, "failed to request registers region\n"); + ret = -EINVAL; + goto err_clock; + } + + dp->res = res; + + dp->reg_base = ioremap(res->start, resource_size(res)); + if (!dp->reg_base) { + dev_err(&pdev->dev, "failed to ioremap\n"); + ret = -ENOMEM; + goto err_req_region; + } + + dp->irq = platform_get_irq(pdev, 0); + if (!dp->irq) { + dev_err(&pdev->dev, "failed to get irq\n"); + ret = -ENODEV; + goto err_ioremap; + } + + ret = request_irq(dp->irq, exynos_dp_irq_handler, 0, + "exynos-dp", dp); + if (ret) { + dev_err(&pdev->dev, "failed to request irq\n"); + goto err_ioremap; + } + + dp->video_info = pdata->video_info; + if (pdata->phy_init) + pdata->phy_init(); + + exynos_dp_init_dp(dp); + + ret = exynos_dp_detect_hpd(dp); + if (ret) { + dev_err(&pdev->dev, "unable to detect hpd\n"); + goto err_irq; + } + + exynos_dp_handle_edid(dp); + + ret = exynos_dp_set_link_train(dp, dp->video_info->lane_count, + dp->video_info->link_rate); + if (ret) { + dev_err(&pdev->dev, "unable to do link train\n"); + goto err_irq; + } + + exynos_dp_enable_scramble(dp, 1); + exynos_dp_enable_rx_to_enhanced_mode(dp, 1); + exynos_dp_enable_enhanced_mode(dp, 1); + + exynos_dp_set_lane_count(dp, dp->video_info->lane_count); + exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate); + + exynos_dp_init_video(dp); + ret = exynos_dp_config_video(dp, dp->video_info); + if (ret) { + dev_err(&pdev->dev, "unable to config video\n"); + goto err_irq; + } + + platform_set_drvdata(pdev, dp); + + return 0; + +err_irq: + free_irq(dp->irq, dp); +err_ioremap: + iounmap(dp->reg_base); +err_req_region: + release_mem_region(res->start, resource_size(res)); +err_clock: + clk_put(dp->clock); +err_dp: + kfree(dp); + + return ret; +} + +static int __devexit exynos_dp_remove(struct platform_device *pdev) +{ + struct exynos_dp_platdata *pdata = pdev->dev.platform_data; + struct exynos_dp_device *dp = platform_get_drvdata(pdev); + + if (pdata && pdata->phy_exit) + pdata->phy_exit(); + + free_irq(dp->irq, dp); + iounmap(dp->reg_base); + + clk_disable(dp->clock); + clk_put(dp->clock); + + release_mem_region(dp->res->start, resource_size(dp->res)); + + kfree(dp); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int exynos_dp_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct exynos_dp_platdata *pdata = pdev->dev.platform_data; + struct exynos_dp_device *dp = platform_get_drvdata(pdev); + + if (pdata && pdata->phy_exit) + pdata->phy_exit(); + + clk_disable(dp->clock); + + return 0; +} + +static int exynos_dp_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct exynos_dp_platdata *pdata = pdev->dev.platform_data; + struct exynos_dp_device *dp = platform_get_drvdata(pdev); + + if (pdata && pdata->phy_init) + pdata->phy_init(); + + clk_enable(dp->clock); + + exynos_dp_init_dp(dp); + + exynos_dp_detect_hpd(dp); + exynos_dp_handle_edid(dp); + + exynos_dp_set_link_train(dp, dp->video_info->lane_count, + dp->video_info->link_rate); + + exynos_dp_enable_scramble(dp, 1); + exynos_dp_enable_rx_to_enhanced_mode(dp, 1); + exynos_dp_enable_enhanced_mode(dp, 1); + + exynos_dp_set_lane_count(dp, dp->video_info->lane_count); + exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate); + + exynos_dp_init_video(dp); + exynos_dp_config_video(dp, dp->video_info); + + return 0; +} +#endif + +static const struct dev_pm_ops exynos_dp_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(exynos_dp_suspend, exynos_dp_resume) +}; + +static struct platform_driver exynos_dp_driver = { + .probe = exynos_dp_probe, + .remove = __devexit_p(exynos_dp_remove), + .driver = { + .name = "exynos-dp", + .owner = THIS_MODULE, + .pm = &exynos_dp_pm_ops, + }, +}; + +module_platform_driver(exynos_dp_driver); + +MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); +MODULE_DESCRIPTION("Samsung SoC DP Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/exynos/exynos_dp_core.h b/drivers/video/exynos/exynos_dp_core.h new file mode 100644 index 000000000000..90ceaca0fa24 --- /dev/null +++ b/drivers/video/exynos/exynos_dp_core.h @@ -0,0 +1,206 @@ +/* + * Header file for Samsung DP (Display Port) interface driver. + * + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * Author: Jingoo Han <jg1.han@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef _EXYNOS_DP_CORE_H +#define _EXYNOS_DP_CORE_H + +struct link_train { + int eq_loop; + int cr_loop[4]; + + u8 link_rate; + u8 lane_count; + u8 training_lane[4]; + + enum link_training_state lt_state; +}; + +struct exynos_dp_device { + struct device *dev; + struct resource *res; + struct clk *clock; + unsigned int irq; + void __iomem *reg_base; + + struct video_info *video_info; + struct link_train link_train; +}; + +/* exynos_dp_reg.c */ +void exynos_dp_enable_video_mute(struct exynos_dp_device *dp, bool enable); +void exynos_dp_stop_video(struct exynos_dp_device *dp); +void exynos_dp_lane_swap(struct exynos_dp_device *dp, bool enable); +void exynos_dp_init_interrupt(struct exynos_dp_device *dp); +void exynos_dp_reset(struct exynos_dp_device *dp); +void exynos_dp_config_interrupt(struct exynos_dp_device *dp); +u32 exynos_dp_get_pll_lock_status(struct exynos_dp_device *dp); +void exynos_dp_set_pll_power_down(struct exynos_dp_device *dp, bool enable); +void exynos_dp_set_analog_power_down(struct exynos_dp_device *dp, + enum analog_power_block block, + bool enable); +void exynos_dp_init_analog_func(struct exynos_dp_device *dp); +void exynos_dp_init_hpd(struct exynos_dp_device *dp); +void exynos_dp_reset_aux(struct exynos_dp_device *dp); +void exynos_dp_init_aux(struct exynos_dp_device *dp); +int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp); +void exynos_dp_enable_sw_function(struct exynos_dp_device *dp); +int exynos_dp_start_aux_transaction(struct exynos_dp_device *dp); +int exynos_dp_write_byte_to_dpcd(struct exynos_dp_device *dp, + unsigned int reg_addr, + unsigned char data); +int exynos_dp_read_byte_from_dpcd(struct exynos_dp_device *dp, + unsigned int reg_addr, + unsigned char *data); +int exynos_dp_write_bytes_to_dpcd(struct exynos_dp_device *dp, + unsigned int reg_addr, + unsigned int count, + unsigned char data[]); +int exynos_dp_read_bytes_from_dpcd(struct exynos_dp_device *dp, + unsigned int reg_addr, + unsigned int count, + unsigned char data[]); +int exynos_dp_select_i2c_device(struct exynos_dp_device *dp, + unsigned int device_addr, + unsigned int reg_addr); +int exynos_dp_read_byte_from_i2c(struct exynos_dp_device *dp, + unsigned int device_addr, + unsigned int reg_addr, + unsigned int *data); +int exynos_dp_read_bytes_from_i2c(struct exynos_dp_device *dp, + unsigned int device_addr, + unsigned int reg_addr, + unsigned int count, + unsigned char edid[]); +void exynos_dp_set_link_bandwidth(struct exynos_dp_device *dp, u32 bwtype); +void exynos_dp_get_link_bandwidth(struct exynos_dp_device *dp, u32 *bwtype); +void exynos_dp_set_lane_count(struct exynos_dp_device *dp, u32 count); +void exynos_dp_get_lane_count(struct exynos_dp_device *dp, u32 *count); +void exynos_dp_set_link_bandwidth(struct exynos_dp_device *dp, u32 bwtype); +void exynos_dp_get_link_bandwidth(struct exynos_dp_device *dp, u32 *bwtype); +void exynos_dp_set_lane_count(struct exynos_dp_device *dp, u32 count); +void exynos_dp_get_lane_count(struct exynos_dp_device *dp, u32 *count); +void exynos_dp_enable_enhanced_mode(struct exynos_dp_device *dp, bool enable); +void exynos_dp_set_training_pattern(struct exynos_dp_device *dp, + enum pattern_set pattern); +void exynos_dp_set_lane0_pre_emphasis(struct exynos_dp_device *dp, u32 level); +void exynos_dp_set_lane1_pre_emphasis(struct exynos_dp_device *dp, u32 level); +void exynos_dp_set_lane2_pre_emphasis(struct exynos_dp_device *dp, u32 level); +void exynos_dp_set_lane3_pre_emphasis(struct exynos_dp_device *dp, u32 level); +void exynos_dp_set_lane0_link_training(struct exynos_dp_device *dp, + u32 training_lane); +void exynos_dp_set_lane1_link_training(struct exynos_dp_device *dp, + u32 training_lane); +void exynos_dp_set_lane2_link_training(struct exynos_dp_device *dp, + u32 training_lane); +void exynos_dp_set_lane3_link_training(struct exynos_dp_device *dp, + u32 training_lane); +u32 exynos_dp_get_lane0_link_training(struct exynos_dp_device *dp); +u32 exynos_dp_get_lane1_link_training(struct exynos_dp_device *dp); +u32 exynos_dp_get_lane2_link_training(struct exynos_dp_device *dp); +u32 exynos_dp_get_lane3_link_training(struct exynos_dp_device *dp); +void exynos_dp_reset_macro(struct exynos_dp_device *dp); +int exynos_dp_init_video(struct exynos_dp_device *dp); + +void exynos_dp_set_video_color_format(struct exynos_dp_device *dp, + u32 color_depth, + u32 color_space, + u32 dynamic_range, + u32 ycbcr_coeff); +int exynos_dp_is_slave_video_stream_clock_on(struct exynos_dp_device *dp); +void exynos_dp_set_video_cr_mn(struct exynos_dp_device *dp, + enum clock_recovery_m_value_type type, + u32 m_value, + u32 n_value); +void exynos_dp_set_video_timing_mode(struct exynos_dp_device *dp, u32 type); +void exynos_dp_enable_video_master(struct exynos_dp_device *dp, bool enable); +void exynos_dp_start_video(struct exynos_dp_device *dp); +int exynos_dp_is_video_stream_on(struct exynos_dp_device *dp); +void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp, + struct video_info *video_info); +void exynos_dp_enable_scrambling(struct exynos_dp_device *dp); +void exynos_dp_disable_scrambling(struct exynos_dp_device *dp); + +/* I2C EDID Chip ID, Slave Address */ +#define I2C_EDID_DEVICE_ADDR 0x50 +#define I2C_E_EDID_DEVICE_ADDR 0x30 + +#define EDID_BLOCK_LENGTH 0x80 +#define EDID_HEADER_PATTERN 0x00 +#define EDID_EXTENSION_FLAG 0x7e +#define EDID_CHECKSUM 0x7f + +/* Definition for DPCD Register */ +#define DPCD_ADDR_DPCD_REV 0x0000 +#define DPCD_ADDR_MAX_LINK_RATE 0x0001 +#define DPCD_ADDR_MAX_LANE_COUNT 0x0002 +#define DPCD_ADDR_LINK_BW_SET 0x0100 +#define DPCD_ADDR_LANE_COUNT_SET 0x0101 +#define DPCD_ADDR_TRAINING_PATTERN_SET 0x0102 +#define DPCD_ADDR_TRAINING_LANE0_SET 0x0103 +#define DPCD_ADDR_LANE0_1_STATUS 0x0202 +#define DPCD_ADDR_LANE_ALIGN__STATUS_UPDATED 0x0204 +#define DPCD_ADDR_ADJUST_REQUEST_LANE0_1 0x0206 +#define DPCD_ADDR_ADJUST_REQUEST_LANE2_3 0x0207 +#define DPCD_ADDR_TEST_REQUEST 0x0218 +#define DPCD_ADDR_TEST_RESPONSE 0x0260 +#define DPCD_ADDR_TEST_EDID_CHECKSUM 0x0261 +#define DPCD_ADDR_SINK_POWER_STATE 0x0600 + +/* DPCD_ADDR_MAX_LANE_COUNT */ +#define DPCD_ENHANCED_FRAME_CAP(x) (((x) >> 7) & 0x1) +#define DPCD_MAX_LANE_COUNT(x) ((x) & 0x1f) + +/* DPCD_ADDR_LANE_COUNT_SET */ +#define DPCD_ENHANCED_FRAME_EN (0x1 << 7) +#define DPCD_LANE_COUNT_SET(x) ((x) & 0x1f) + +/* DPCD_ADDR_TRAINING_PATTERN_SET */ +#define DPCD_SCRAMBLING_DISABLED (0x1 << 5) +#define DPCD_SCRAMBLING_ENABLED (0x0 << 5) +#define DPCD_TRAINING_PATTERN_2 (0x2 << 0) +#define DPCD_TRAINING_PATTERN_1 (0x1 << 0) +#define DPCD_TRAINING_PATTERN_DISABLED (0x0 << 0) + +/* DPCD_ADDR_TRAINING_LANE0_SET */ +#define DPCD_MAX_PRE_EMPHASIS_REACHED (0x1 << 5) +#define DPCD_PRE_EMPHASIS_SET(x) (((x) & 0x3) << 3) +#define DPCD_PRE_EMPHASIS_GET(x) (((x) >> 3) & 0x3) +#define DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 (0x0 << 3) +#define DPCD_MAX_SWING_REACHED (0x1 << 2) +#define DPCD_VOLTAGE_SWING_SET(x) (((x) & 0x3) << 0) +#define DPCD_VOLTAGE_SWING_GET(x) (((x) >> 0) & 0x3) +#define DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0 (0x0 << 0) + +/* DPCD_ADDR_LANE0_1_STATUS */ +#define DPCD_LANE_SYMBOL_LOCKED (0x1 << 2) +#define DPCD_LANE_CHANNEL_EQ_DONE (0x1 << 1) +#define DPCD_LANE_CR_DONE (0x1 << 0) +#define DPCD_CHANNEL_EQ_BITS (DPCD_LANE_CR_DONE| \ + DPCD_LANE_CHANNEL_EQ_DONE|\ + DPCD_LANE_SYMBOL_LOCKED) + +/* DPCD_ADDR_LANE_ALIGN__STATUS_UPDATED */ +#define DPCD_LINK_STATUS_UPDATED (0x1 << 7) +#define DPCD_DOWNSTREAM_PORT_STATUS_CHANGED (0x1 << 6) +#define DPCD_INTERLANE_ALIGN_DONE (0x1 << 0) + +/* DPCD_ADDR_TEST_REQUEST */ +#define DPCD_TEST_EDID_READ (0x1 << 2) + +/* DPCD_ADDR_TEST_RESPONSE */ +#define DPCD_TEST_EDID_CHECKSUM_WRITE (0x1 << 2) + +/* DPCD_ADDR_SINK_POWER_STATE */ +#define DPCD_SET_POWER_STATE_D0 (0x1 << 0) +#define DPCD_SET_POWER_STATE_D4 (0x2 << 0) + +#endif /* _EXYNOS_DP_CORE_H */ diff --git a/drivers/video/exynos/exynos_dp_reg.c b/drivers/video/exynos/exynos_dp_reg.c new file mode 100644 index 000000000000..6548afa0e3d2 --- /dev/null +++ b/drivers/video/exynos/exynos_dp_reg.c @@ -0,0 +1,1173 @@ +/* + * Samsung DP (Display port) register interface driver. + * + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * Author: Jingoo Han <jg1.han@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/device.h> +#include <linux/io.h> +#include <linux/delay.h> + +#include <video/exynos_dp.h> + +#include <plat/cpu.h> + +#include "exynos_dp_core.h" +#include "exynos_dp_reg.h" + +#define COMMON_INT_MASK_1 (0) +#define COMMON_INT_MASK_2 (0) +#define COMMON_INT_MASK_3 (0) +#define COMMON_INT_MASK_4 (0) +#define INT_STA_MASK (0) + +void exynos_dp_enable_video_mute(struct exynos_dp_device *dp, bool enable) +{ + u32 reg; + + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1); + reg |= HDCP_VIDEO_MUTE; + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1); + reg &= ~HDCP_VIDEO_MUTE; + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1); + } +} + +void exynos_dp_stop_video(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1); + reg &= ~VIDEO_EN; + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1); +} + +void exynos_dp_lane_swap(struct exynos_dp_device *dp, bool enable) +{ + u32 reg; + + if (enable) + reg = LANE3_MAP_LOGIC_LANE_0 | LANE2_MAP_LOGIC_LANE_1 | + LANE1_MAP_LOGIC_LANE_2 | LANE0_MAP_LOGIC_LANE_3; + else + reg = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 | + LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0; + + writel(reg, dp->reg_base + EXYNOS_DP_LANE_MAP); +} + +void exynos_dp_init_interrupt(struct exynos_dp_device *dp) +{ + /* Set interrupt pin assertion polarity as high */ + writel(INT_POL, dp->reg_base + EXYNOS_DP_INT_CTL); + + /* Clear pending regisers */ + writel(0xff, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_1); + writel(0x4f, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_2); + writel(0xe0, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_3); + writel(0xe7, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4); + writel(0x63, dp->reg_base + EXYNOS_DP_INT_STA); + + /* 0:mask,1: unmask */ + writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_1); + writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_2); + writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_3); + writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_4); + writel(0x00, dp->reg_base + EXYNOS_DP_INT_STA_MASK); +} + +void exynos_dp_reset(struct exynos_dp_device *dp) +{ + u32 reg; + + writel(RESET_DP_TX, dp->reg_base + EXYNOS_DP_TX_SW_RESET); + + exynos_dp_stop_video(dp); + exynos_dp_enable_video_mute(dp, 0); + + reg = MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N | + AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N | + HDCP_FUNC_EN_N | SW_FUNC_EN_N; + writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_1); + + reg = SSC_FUNC_EN_N | AUX_FUNC_EN_N | + SERDES_FIFO_FUNC_EN_N | + LS_CLK_DOMAIN_FUNC_EN_N; + writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2); + + udelay(20); + + exynos_dp_lane_swap(dp, 0); + + writel(0x0, dp->reg_base + EXYNOS_DP_SYS_CTL_1); + writel(0x40, dp->reg_base + EXYNOS_DP_SYS_CTL_2); + writel(0x0, dp->reg_base + EXYNOS_DP_SYS_CTL_3); + writel(0x0, dp->reg_base + EXYNOS_DP_SYS_CTL_4); + + writel(0x0, dp->reg_base + EXYNOS_DP_PKT_SEND_CTL); + writel(0x0, dp->reg_base + EXYNOS_DP_HDCP_CTL); + + writel(0x5e, dp->reg_base + EXYNOS_DP_HPD_DEGLITCH_L); + writel(0x1a, dp->reg_base + EXYNOS_DP_HPD_DEGLITCH_H); + + writel(0x10, dp->reg_base + EXYNOS_DP_LINK_DEBUG_CTL); + + writel(0x0, dp->reg_base + EXYNOS_DP_PHY_TEST); + + writel(0x0, dp->reg_base + EXYNOS_DP_VIDEO_FIFO_THRD); + writel(0x20, dp->reg_base + EXYNOS_DP_AUDIO_MARGIN); + + writel(0x4, dp->reg_base + EXYNOS_DP_M_VID_GEN_FILTER_TH); + writel(0x2, dp->reg_base + EXYNOS_DP_M_AUD_GEN_FILTER_TH); + + writel(0x00000101, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL); + + exynos_dp_init_interrupt(dp); +} + +void exynos_dp_config_interrupt(struct exynos_dp_device *dp) +{ + u32 reg; + + /* 0: mask, 1: unmask */ + reg = COMMON_INT_MASK_1; + writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_1); + + reg = COMMON_INT_MASK_2; + writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_2); + + reg = COMMON_INT_MASK_3; + writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_3); + + reg = COMMON_INT_MASK_4; + writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_4); + + reg = INT_STA_MASK; + writel(reg, dp->reg_base + EXYNOS_DP_INT_STA_MASK); +} + +u32 exynos_dp_get_pll_lock_status(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_DEBUG_CTL); + if (reg & PLL_LOCK) + return PLL_LOCKED; + else + return PLL_UNLOCKED; +} + +void exynos_dp_set_pll_power_down(struct exynos_dp_device *dp, bool enable) +{ + u32 reg; + + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_PLL_CTL); + reg |= DP_PLL_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PLL_CTL); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_PLL_CTL); + reg &= ~DP_PLL_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PLL_CTL); + } +} + +void exynos_dp_set_analog_power_down(struct exynos_dp_device *dp, + enum analog_power_block block, + bool enable) +{ + u32 reg; + + switch (block) { + case AUX_BLOCK: + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg |= AUX_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg &= ~AUX_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } + break; + case CH0_BLOCK: + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg |= CH0_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg &= ~CH0_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } + break; + case CH1_BLOCK: + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg |= CH1_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg &= ~CH1_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } + break; + case CH2_BLOCK: + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg |= CH2_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg &= ~CH2_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } + break; + case CH3_BLOCK: + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg |= CH3_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg &= ~CH3_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } + break; + case ANALOG_TOTAL: + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg |= DP_PHY_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg &= ~DP_PHY_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } + break; + case POWER_ALL: + if (enable) { + reg = DP_PHY_PD | AUX_PD | CH3_PD | CH2_PD | + CH1_PD | CH0_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } else { + writel(0x00, dp->reg_base + EXYNOS_DP_PHY_PD); + } + break; + default: + break; + } +} + +void exynos_dp_init_analog_func(struct exynos_dp_device *dp) +{ + u32 reg; + + exynos_dp_set_analog_power_down(dp, POWER_ALL, 0); + + reg = PLL_LOCK_CHG; + writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_1); + + reg = readl(dp->reg_base + EXYNOS_DP_DEBUG_CTL); + reg &= ~(F_PLL_LOCK | PLL_LOCK_CTRL); + writel(reg, dp->reg_base + EXYNOS_DP_DEBUG_CTL); + + /* Power up PLL */ + if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) + exynos_dp_set_pll_power_down(dp, 0); + + /* Enable Serdes FIFO function and Link symbol clock domain module */ + reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_2); + reg &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N + | AUX_FUNC_EN_N); + writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2); +} + +void exynos_dp_init_hpd(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = HOTPLUG_CHG | HPD_LOST | PLUG; + writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4); + + reg = INT_HPD; + writel(reg, dp->reg_base + EXYNOS_DP_INT_STA); + + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3); + reg &= ~(F_HPD | HPD_CTRL); + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_3); +} + +void exynos_dp_reset_aux(struct exynos_dp_device *dp) +{ + u32 reg; + + /* Disable AUX channel module */ + reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_2); + reg |= AUX_FUNC_EN_N; + writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2); +} + +void exynos_dp_init_aux(struct exynos_dp_device *dp) +{ + u32 reg; + + /* Clear inerrupts related to AUX channel */ + reg = RPLY_RECEIV | AUX_ERR; + writel(reg, dp->reg_base + EXYNOS_DP_INT_STA); + + exynos_dp_reset_aux(dp); + + /* Disable AUX transaction H/W retry */ + reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(0)| + AUX_HW_RETRY_INTERVAL_600_MICROSECONDS; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_HW_RETRY_CTL) ; + + /* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */ + reg = DEFER_CTRL_EN | DEFER_COUNT(1); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_DEFER_CTL); + + /* Enable AUX channel module */ + reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_2); + reg &= ~AUX_FUNC_EN_N; + writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2); +} + +int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3); + if (reg & HPD_STATUS) + return 0; + + return -EINVAL; +} + +void exynos_dp_enable_sw_function(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_1); + reg &= ~SW_FUNC_EN_N; + writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_1); +} + +int exynos_dp_start_aux_transaction(struct exynos_dp_device *dp) +{ + int reg; + int retval = 0; + + /* Enable AUX CH operation */ + reg = readl(dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2); + reg |= AUX_EN; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2); + + /* Is AUX CH command reply received? */ + reg = readl(dp->reg_base + EXYNOS_DP_INT_STA); + while (!(reg & RPLY_RECEIV)) + reg = readl(dp->reg_base + EXYNOS_DP_INT_STA); + + /* Clear interrupt source for AUX CH command reply */ + writel(RPLY_RECEIV, dp->reg_base + EXYNOS_DP_INT_STA); + + /* Clear interrupt source for AUX CH access error */ + reg = readl(dp->reg_base + EXYNOS_DP_INT_STA); + if (reg & AUX_ERR) { + writel(AUX_ERR, dp->reg_base + EXYNOS_DP_INT_STA); + return -EREMOTEIO; + } + + /* Check AUX CH error access status */ + reg = readl(dp->reg_base + EXYNOS_DP_AUX_CH_STA); + if ((reg & AUX_STATUS_MASK) != 0) { + dev_err(dp->dev, "AUX CH error happens: %d\n\n", + reg & AUX_STATUS_MASK); + return -EREMOTEIO; + } + + return retval; +} + +int exynos_dp_write_byte_to_dpcd(struct exynos_dp_device *dp, + unsigned int reg_addr, + unsigned char data) +{ + u32 reg; + int i; + int retval; + + for (i = 0; i < 3; i++) { + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); + + /* Select DPCD device address */ + reg = AUX_ADDR_7_0(reg_addr); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0); + reg = AUX_ADDR_15_8(reg_addr); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8); + reg = AUX_ADDR_19_16(reg_addr); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16); + + /* Write data buffer */ + reg = (unsigned int)data; + writel(reg, dp->reg_base + EXYNOS_DP_BUF_DATA_0); + + /* + * Set DisplayPort transaction and write 1 byte + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(dp); + if (retval == 0) + break; + else + dev_err(dp->dev, "Aux Transaction fail!\n"); + } + + return retval; +} + +int exynos_dp_read_byte_from_dpcd(struct exynos_dp_device *dp, + unsigned int reg_addr, + unsigned char *data) +{ + u32 reg; + int i; + int retval; + + for (i = 0; i < 10; i++) { + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); + + /* Select DPCD device address */ + reg = AUX_ADDR_7_0(reg_addr); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0); + reg = AUX_ADDR_15_8(reg_addr); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8); + reg = AUX_ADDR_19_16(reg_addr); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16); + + /* + * Set DisplayPort transaction and read 1 byte + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(dp); + if (retval == 0) + break; + else + dev_err(dp->dev, "Aux Transaction fail!\n"); + } + + /* Read data buffer */ + reg = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0); + *data = (unsigned char)(reg & 0xff); + + return retval; +} + +int exynos_dp_write_bytes_to_dpcd(struct exynos_dp_device *dp, + unsigned int reg_addr, + unsigned int count, + unsigned char data[]) +{ + u32 reg; + unsigned int start_offset; + unsigned int cur_data_count; + unsigned int cur_data_idx; + int i; + int retval = 0; + + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); + + start_offset = 0; + while (start_offset < count) { + /* Buffer size of AUX CH is 16 * 4bytes */ + if ((count - start_offset) > 16) + cur_data_count = 16; + else + cur_data_count = count - start_offset; + + for (i = 0; i < 10; i++) { + /* Select DPCD device address */ + reg = AUX_ADDR_7_0(reg_addr + start_offset); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0); + reg = AUX_ADDR_15_8(reg_addr + start_offset); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8); + reg = AUX_ADDR_19_16(reg_addr + start_offset); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16); + + for (cur_data_idx = 0; cur_data_idx < cur_data_count; + cur_data_idx++) { + reg = data[start_offset + cur_data_idx]; + writel(reg, dp->reg_base + EXYNOS_DP_BUF_DATA_0 + + 4 * cur_data_idx); + } + + /* + * Set DisplayPort transaction and write + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_LENGTH(cur_data_count) | + AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(dp); + if (retval == 0) + break; + else + dev_err(dp->dev, "Aux Transaction fail!\n"); + } + + start_offset += cur_data_count; + } + + return retval; +} + +int exynos_dp_read_bytes_from_dpcd(struct exynos_dp_device *dp, + unsigned int reg_addr, + unsigned int count, + unsigned char data[]) +{ + u32 reg; + unsigned int start_offset; + unsigned int cur_data_count; + unsigned int cur_data_idx; + int i; + int retval = 0; + + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); + + start_offset = 0; + while (start_offset < count) { + /* Buffer size of AUX CH is 16 * 4bytes */ + if ((count - start_offset) > 16) + cur_data_count = 16; + else + cur_data_count = count - start_offset; + + /* AUX CH Request Transaction process */ + for (i = 0; i < 10; i++) { + /* Select DPCD device address */ + reg = AUX_ADDR_7_0(reg_addr + start_offset); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0); + reg = AUX_ADDR_15_8(reg_addr + start_offset); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8); + reg = AUX_ADDR_19_16(reg_addr + start_offset); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16); + + /* + * Set DisplayPort transaction and read + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_LENGTH(cur_data_count) | + AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(dp); + if (retval == 0) + break; + else + dev_err(dp->dev, "Aux Transaction fail!\n"); + } + + for (cur_data_idx = 0; cur_data_idx < cur_data_count; + cur_data_idx++) { + reg = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0 + + 4 * cur_data_idx); + data[start_offset + cur_data_idx] = + (unsigned char)reg; + } + + start_offset += cur_data_count; + } + + return retval; +} + +int exynos_dp_select_i2c_device(struct exynos_dp_device *dp, + unsigned int device_addr, + unsigned int reg_addr) +{ + u32 reg; + int retval; + + /* Set EDID device address */ + reg = device_addr; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0); + writel(0x0, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8); + writel(0x0, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16); + + /* Set offset from base address of EDID device */ + writel(reg_addr, dp->reg_base + EXYNOS_DP_BUF_DATA_0); + + /* + * Set I2C transaction and write address + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT | + AUX_TX_COMM_WRITE; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(dp); + if (retval != 0) + dev_err(dp->dev, "Aux Transaction fail!\n"); + + return retval; +} + +int exynos_dp_read_byte_from_i2c(struct exynos_dp_device *dp, + unsigned int device_addr, + unsigned int reg_addr, + unsigned int *data) +{ + u32 reg; + int i; + int retval; + + for (i = 0; i < 10; i++) { + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); + + /* Select EDID device */ + retval = exynos_dp_select_i2c_device(dp, device_addr, reg_addr); + if (retval != 0) { + dev_err(dp->dev, "Select EDID device fail!\n"); + continue; + } + + /* + * Set I2C transaction and read data + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_TX_COMM_I2C_TRANSACTION | + AUX_TX_COMM_READ; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(dp); + if (retval == 0) + break; + else + dev_err(dp->dev, "Aux Transaction fail!\n"); + } + + /* Read data */ + if (retval == 0) + *data = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0); + + return retval; +} + +int exynos_dp_read_bytes_from_i2c(struct exynos_dp_device *dp, + unsigned int device_addr, + unsigned int reg_addr, + unsigned int count, + unsigned char edid[]) +{ + u32 reg; + unsigned int i, j; + unsigned int cur_data_idx; + unsigned int defer = 0; + int retval = 0; + + for (i = 0; i < count; i += 16) { + for (j = 0; j < 100; j++) { + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); + + /* Set normal AUX CH command */ + reg = readl(dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2); + reg &= ~ADDR_ONLY; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2); + + /* + * If Rx sends defer, Tx sends only reads + * request without sending addres + */ + if (!defer) + retval = exynos_dp_select_i2c_device(dp, + device_addr, reg_addr + i); + else + defer = 0; + + if (retval == 0) { + /* + * Set I2C transaction and write data + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_LENGTH(16) | + AUX_TX_COMM_I2C_TRANSACTION | + AUX_TX_COMM_READ; + writel(reg, dp->reg_base + + EXYNOS_DP_AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(dp); + if (retval == 0) + break; + else + dev_err(dp->dev, "Aux Transaction fail!\n"); + } + /* Check if Rx sends defer */ + reg = readl(dp->reg_base + EXYNOS_DP_AUX_RX_COMM); + if (reg == AUX_RX_COMM_AUX_DEFER || + reg == AUX_RX_COMM_I2C_DEFER) { + dev_err(dp->dev, "Defer: %d\n\n", reg); + defer = 1; + } + } + + for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) { + reg = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0 + + 4 * cur_data_idx); + edid[i + cur_data_idx] = (unsigned char)reg; + } + } + + return retval; +} + +void exynos_dp_set_link_bandwidth(struct exynos_dp_device *dp, u32 bwtype) +{ + u32 reg; + + reg = bwtype; + if ((bwtype == LINK_RATE_2_70GBPS) || (bwtype == LINK_RATE_1_62GBPS)) + writel(reg, dp->reg_base + EXYNOS_DP_LINK_BW_SET); +} + +void exynos_dp_get_link_bandwidth(struct exynos_dp_device *dp, u32 *bwtype) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_LINK_BW_SET); + *bwtype = reg; +} + +void exynos_dp_set_lane_count(struct exynos_dp_device *dp, u32 count) +{ + u32 reg; + + reg = count; + writel(reg, dp->reg_base + EXYNOS_DP_LANE_COUNT_SET); +} + +void exynos_dp_get_lane_count(struct exynos_dp_device *dp, u32 *count) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_LANE_COUNT_SET); + *count = reg; +} + +void exynos_dp_enable_enhanced_mode(struct exynos_dp_device *dp, bool enable) +{ + u32 reg; + + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4); + reg |= ENHANCED; + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4); + reg &= ~ENHANCED; + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4); + } +} + +void exynos_dp_set_training_pattern(struct exynos_dp_device *dp, + enum pattern_set pattern) +{ + u32 reg; + + switch (pattern) { + case PRBS7: + reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7; + writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET); + break; + case D10_2: + reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2; + writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET); + break; + case TRAINING_PTN1: + reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1; + writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET); + break; + case TRAINING_PTN2: + reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2; + writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET); + break; + case DP_NONE: + reg = SCRAMBLING_ENABLE | + LINK_QUAL_PATTERN_SET_DISABLE | + SW_TRAINING_PATTERN_SET_NORMAL; + writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET); + break; + default: + break; + } +} + +void exynos_dp_set_lane0_pre_emphasis(struct exynos_dp_device *dp, u32 level) +{ + u32 reg; + + reg = level << PRE_EMPHASIS_SET_SHIFT; + writel(reg, dp->reg_base + EXYNOS_DP_LN0_LINK_TRAINING_CTL); +} + +void exynos_dp_set_lane1_pre_emphasis(struct exynos_dp_device *dp, u32 level) +{ + u32 reg; + + reg = level << PRE_EMPHASIS_SET_SHIFT; + writel(reg, dp->reg_base + EXYNOS_DP_LN1_LINK_TRAINING_CTL); +} + +void exynos_dp_set_lane2_pre_emphasis(struct exynos_dp_device *dp, u32 level) +{ + u32 reg; + + reg = level << PRE_EMPHASIS_SET_SHIFT; + writel(reg, dp->reg_base + EXYNOS_DP_LN2_LINK_TRAINING_CTL); +} + +void exynos_dp_set_lane3_pre_emphasis(struct exynos_dp_device *dp, u32 level) +{ + u32 reg; + + reg = level << PRE_EMPHASIS_SET_SHIFT; + writel(reg, dp->reg_base + EXYNOS_DP_LN3_LINK_TRAINING_CTL); +} + +void exynos_dp_set_lane0_link_training(struct exynos_dp_device *dp, + u32 training_lane) +{ + u32 reg; + + reg = training_lane; + writel(reg, dp->reg_base + EXYNOS_DP_LN0_LINK_TRAINING_CTL); +} + +void exynos_dp_set_lane1_link_training(struct exynos_dp_device *dp, + u32 training_lane) +{ + u32 reg; + + reg = training_lane; + writel(reg, dp->reg_base + EXYNOS_DP_LN1_LINK_TRAINING_CTL); +} + +void exynos_dp_set_lane2_link_training(struct exynos_dp_device *dp, + u32 training_lane) +{ + u32 reg; + + reg = training_lane; + writel(reg, dp->reg_base + EXYNOS_DP_LN2_LINK_TRAINING_CTL); +} + +void exynos_dp_set_lane3_link_training(struct exynos_dp_device *dp, + u32 training_lane) +{ + u32 reg; + + reg = training_lane; + writel(reg, dp->reg_base + EXYNOS_DP_LN3_LINK_TRAINING_CTL); +} + +u32 exynos_dp_get_lane0_link_training(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_LN0_LINK_TRAINING_CTL); + return reg; +} + +u32 exynos_dp_get_lane1_link_training(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_LN1_LINK_TRAINING_CTL); + return reg; +} + +u32 exynos_dp_get_lane2_link_training(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_LN2_LINK_TRAINING_CTL); + return reg; +} + +u32 exynos_dp_get_lane3_link_training(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_LN3_LINK_TRAINING_CTL); + return reg; +} + +void exynos_dp_reset_macro(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_PHY_TEST); + reg |= MACRO_RST; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_TEST); + + /* 10 us is the minimum reset time. */ + udelay(10); + + reg &= ~MACRO_RST; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_TEST); +} + +int exynos_dp_init_video(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG; + writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_1); + + reg = 0x0; + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_1); + + reg = CHA_CRI(4) | CHA_CTRL; + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_2); + + reg = 0x0; + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_3); + + reg = VID_HRES_TH(2) | VID_VRES_TH(0); + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_8); + + return 0; +} + +void exynos_dp_set_video_color_format(struct exynos_dp_device *dp, + u32 color_depth, + u32 color_space, + u32 dynamic_range, + u32 ycbcr_coeff) +{ + u32 reg; + + /* Configure the input color depth, color space, dynamic range */ + reg = (dynamic_range << IN_D_RANGE_SHIFT) | + (color_depth << IN_BPC_SHIFT) | + (color_space << IN_COLOR_F_SHIFT); + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_2); + + /* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */ + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_3); + reg &= ~IN_YC_COEFFI_MASK; + if (ycbcr_coeff) + reg |= IN_YC_COEFFI_ITU709; + else + reg |= IN_YC_COEFFI_ITU601; + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_3); +} + +int exynos_dp_is_slave_video_stream_clock_on(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_1); + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_1); + + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_1); + + if (!(reg & DET_STA)) { + dev_dbg(dp->dev, "Input stream clock not detected.\n"); + return -EINVAL; + } + + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_2); + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_2); + + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_2); + dev_dbg(dp->dev, "wait SYS_CTL_2.\n"); + + if (reg & CHA_STA) { + dev_dbg(dp->dev, "Input stream clk is changing\n"); + return -EINVAL; + } + + return 0; +} + +void exynos_dp_set_video_cr_mn(struct exynos_dp_device *dp, + enum clock_recovery_m_value_type type, + u32 m_value, + u32 n_value) +{ + u32 reg; + + if (type == REGISTER_M) { + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4); + reg |= FIX_M_VID; + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4); + reg = m_value & 0xff; + writel(reg, dp->reg_base + EXYNOS_DP_M_VID_0); + reg = (m_value >> 8) & 0xff; + writel(reg, dp->reg_base + EXYNOS_DP_M_VID_1); + reg = (m_value >> 16) & 0xff; + writel(reg, dp->reg_base + EXYNOS_DP_M_VID_2); + + reg = n_value & 0xff; + writel(reg, dp->reg_base + EXYNOS_DP_N_VID_0); + reg = (n_value >> 8) & 0xff; + writel(reg, dp->reg_base + EXYNOS_DP_N_VID_1); + reg = (n_value >> 16) & 0xff; + writel(reg, dp->reg_base + EXYNOS_DP_N_VID_2); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4); + reg &= ~FIX_M_VID; + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4); + + writel(0x00, dp->reg_base + EXYNOS_DP_N_VID_0); + writel(0x80, dp->reg_base + EXYNOS_DP_N_VID_1); + writel(0x00, dp->reg_base + EXYNOS_DP_N_VID_2); + } +} + +void exynos_dp_set_video_timing_mode(struct exynos_dp_device *dp, u32 type) +{ + u32 reg; + + if (type == VIDEO_TIMING_FROM_CAPTURE) { + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + reg &= ~FORMAT_SEL; + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + reg |= FORMAT_SEL; + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + } +} + +void exynos_dp_enable_video_master(struct exynos_dp_device *dp, bool enable) +{ + u32 reg; + + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL); + reg &= ~VIDEO_MODE_MASK; + reg |= VIDEO_MASTER_MODE_EN | VIDEO_MODE_MASTER_MODE; + writel(reg, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL); + reg &= ~VIDEO_MODE_MASK; + reg |= VIDEO_MODE_SLAVE_MODE; + writel(reg, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL); + } +} + +void exynos_dp_start_video(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1); + reg |= VIDEO_EN; + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1); +} + +int exynos_dp_is_video_stream_on(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3); + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_3); + + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3); + if (!(reg & STRM_VALID)) { + dev_dbg(dp->dev, "Input video stream is not detected.\n"); + return -EINVAL; + } + + return 0; +} + +void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp, + struct video_info *video_info) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_1); + reg &= ~(MASTER_VID_FUNC_EN_N|SLAVE_VID_FUNC_EN_N); + reg |= MASTER_VID_FUNC_EN_N; + writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_1); + + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + reg &= ~INTERACE_SCAN_CFG; + reg |= (video_info->interlaced << 2); + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + reg &= ~VSYNC_POLARITY_CFG; + reg |= (video_info->v_sync_polarity << 1); + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + reg &= ~HSYNC_POLARITY_CFG; + reg |= (video_info->h_sync_polarity << 0); + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + + reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE; + writel(reg, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL); +} + +void exynos_dp_enable_scrambling(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET); + reg &= ~SCRAMBLING_DISABLE; + writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET); +} + +void exynos_dp_disable_scrambling(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET); + reg |= SCRAMBLING_DISABLE; + writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET); +} diff --git a/drivers/video/exynos/exynos_dp_reg.h b/drivers/video/exynos/exynos_dp_reg.h new file mode 100644 index 000000000000..42f608e2a43e --- /dev/null +++ b/drivers/video/exynos/exynos_dp_reg.h @@ -0,0 +1,335 @@ +/* + * Register definition file for Samsung DP driver + * + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * Author: Jingoo Han <jg1.han@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _EXYNOS_DP_REG_H +#define _EXYNOS_DP_REG_H + +#define EXYNOS_DP_TX_SW_RESET 0x14 +#define EXYNOS_DP_FUNC_EN_1 0x18 +#define EXYNOS_DP_FUNC_EN_2 0x1C +#define EXYNOS_DP_VIDEO_CTL_1 0x20 +#define EXYNOS_DP_VIDEO_CTL_2 0x24 +#define EXYNOS_DP_VIDEO_CTL_3 0x28 + +#define EXYNOS_DP_VIDEO_CTL_8 0x3C +#define EXYNOS_DP_VIDEO_CTL_10 0x44 + +#define EXYNOS_DP_LANE_MAP 0x35C + +#define EXYNOS_DP_AUX_HW_RETRY_CTL 0x390 + +#define EXYNOS_DP_COMMON_INT_STA_1 0x3C4 +#define EXYNOS_DP_COMMON_INT_STA_2 0x3C8 +#define EXYNOS_DP_COMMON_INT_STA_3 0x3CC +#define EXYNOS_DP_COMMON_INT_STA_4 0x3D0 +#define EXYNOS_DP_INT_STA 0x3DC +#define EXYNOS_DP_COMMON_INT_MASK_1 0x3E0 +#define EXYNOS_DP_COMMON_INT_MASK_2 0x3E4 +#define EXYNOS_DP_COMMON_INT_MASK_3 0x3E8 +#define EXYNOS_DP_COMMON_INT_MASK_4 0x3EC +#define EXYNOS_DP_INT_STA_MASK 0x3F8 +#define EXYNOS_DP_INT_CTL 0x3FC + +#define EXYNOS_DP_SYS_CTL_1 0x600 +#define EXYNOS_DP_SYS_CTL_2 0x604 +#define EXYNOS_DP_SYS_CTL_3 0x608 +#define EXYNOS_DP_SYS_CTL_4 0x60C + +#define EXYNOS_DP_PKT_SEND_CTL 0x640 +#define EXYNOS_DP_HDCP_CTL 0x648 + +#define EXYNOS_DP_LINK_BW_SET 0x680 +#define EXYNOS_DP_LANE_COUNT_SET 0x684 +#define EXYNOS_DP_TRAINING_PTN_SET 0x688 +#define EXYNOS_DP_LN0_LINK_TRAINING_CTL 0x68C +#define EXYNOS_DP_LN1_LINK_TRAINING_CTL 0x690 +#define EXYNOS_DP_LN2_LINK_TRAINING_CTL 0x694 +#define EXYNOS_DP_LN3_LINK_TRAINING_CTL 0x698 + +#define EXYNOS_DP_DEBUG_CTL 0x6C0 +#define EXYNOS_DP_HPD_DEGLITCH_L 0x6C4 +#define EXYNOS_DP_HPD_DEGLITCH_H 0x6C8 +#define EXYNOS_DP_LINK_DEBUG_CTL 0x6E0 + +#define EXYNOS_DP_M_VID_0 0x700 +#define EXYNOS_DP_M_VID_1 0x704 +#define EXYNOS_DP_M_VID_2 0x708 +#define EXYNOS_DP_N_VID_0 0x70C +#define EXYNOS_DP_N_VID_1 0x710 +#define EXYNOS_DP_N_VID_2 0x714 + +#define EXYNOS_DP_PLL_CTL 0x71C +#define EXYNOS_DP_PHY_PD 0x720 +#define EXYNOS_DP_PHY_TEST 0x724 + +#define EXYNOS_DP_VIDEO_FIFO_THRD 0x730 +#define EXYNOS_DP_AUDIO_MARGIN 0x73C + +#define EXYNOS_DP_M_VID_GEN_FILTER_TH 0x764 +#define EXYNOS_DP_M_AUD_GEN_FILTER_TH 0x778 +#define EXYNOS_DP_AUX_CH_STA 0x780 +#define EXYNOS_DP_AUX_CH_DEFER_CTL 0x788 +#define EXYNOS_DP_AUX_RX_COMM 0x78C +#define EXYNOS_DP_BUFFER_DATA_CTL 0x790 +#define EXYNOS_DP_AUX_CH_CTL_1 0x794 +#define EXYNOS_DP_AUX_ADDR_7_0 0x798 +#define EXYNOS_DP_AUX_ADDR_15_8 0x79C +#define EXYNOS_DP_AUX_ADDR_19_16 0x7A0 +#define EXYNOS_DP_AUX_CH_CTL_2 0x7A4 + +#define EXYNOS_DP_BUF_DATA_0 0x7C0 + +#define EXYNOS_DP_SOC_GENERAL_CTL 0x800 + +/* EXYNOS_DP_TX_SW_RESET */ +#define RESET_DP_TX (0x1 << 0) + +/* EXYNOS_DP_FUNC_EN_1 */ +#define MASTER_VID_FUNC_EN_N (0x1 << 7) +#define SLAVE_VID_FUNC_EN_N (0x1 << 5) +#define AUD_FIFO_FUNC_EN_N (0x1 << 4) +#define AUD_FUNC_EN_N (0x1 << 3) +#define HDCP_FUNC_EN_N (0x1 << 2) +#define CRC_FUNC_EN_N (0x1 << 1) +#define SW_FUNC_EN_N (0x1 << 0) + +/* EXYNOS_DP_FUNC_EN_2 */ +#define SSC_FUNC_EN_N (0x1 << 7) +#define AUX_FUNC_EN_N (0x1 << 2) +#define SERDES_FIFO_FUNC_EN_N (0x1 << 1) +#define LS_CLK_DOMAIN_FUNC_EN_N (0x1 << 0) + +/* EXYNOS_DP_VIDEO_CTL_1 */ +#define VIDEO_EN (0x1 << 7) +#define HDCP_VIDEO_MUTE (0x1 << 6) + +/* EXYNOS_DP_VIDEO_CTL_1 */ +#define IN_D_RANGE_MASK (0x1 << 7) +#define IN_D_RANGE_SHIFT (7) +#define IN_D_RANGE_CEA (0x1 << 7) +#define IN_D_RANGE_VESA (0x0 << 7) +#define IN_BPC_MASK (0x7 << 4) +#define IN_BPC_SHIFT (4) +#define IN_BPC_12_BITS (0x3 << 4) +#define IN_BPC_10_BITS (0x2 << 4) +#define IN_BPC_8_BITS (0x1 << 4) +#define IN_BPC_6_BITS (0x0 << 4) +#define IN_COLOR_F_MASK (0x3 << 0) +#define IN_COLOR_F_SHIFT (0) +#define IN_COLOR_F_YCBCR444 (0x2 << 0) +#define IN_COLOR_F_YCBCR422 (0x1 << 0) +#define IN_COLOR_F_RGB (0x0 << 0) + +/* EXYNOS_DP_VIDEO_CTL_3 */ +#define IN_YC_COEFFI_MASK (0x1 << 7) +#define IN_YC_COEFFI_SHIFT (7) +#define IN_YC_COEFFI_ITU709 (0x1 << 7) +#define IN_YC_COEFFI_ITU601 (0x0 << 7) +#define VID_CHK_UPDATE_TYPE_MASK (0x1 << 4) +#define VID_CHK_UPDATE_TYPE_SHIFT (4) +#define VID_CHK_UPDATE_TYPE_1 (0x1 << 4) +#define VID_CHK_UPDATE_TYPE_0 (0x0 << 4) + +/* EXYNOS_DP_VIDEO_CTL_8 */ +#define VID_HRES_TH(x) (((x) & 0xf) << 4) +#define VID_VRES_TH(x) (((x) & 0xf) << 0) + +/* EXYNOS_DP_VIDEO_CTL_10 */ +#define FORMAT_SEL (0x1 << 4) +#define INTERACE_SCAN_CFG (0x1 << 2) +#define VSYNC_POLARITY_CFG (0x1 << 1) +#define HSYNC_POLARITY_CFG (0x1 << 0) + +/* EXYNOS_DP_LANE_MAP */ +#define LANE3_MAP_LOGIC_LANE_0 (0x0 << 6) +#define LANE3_MAP_LOGIC_LANE_1 (0x1 << 6) +#define LANE3_MAP_LOGIC_LANE_2 (0x2 << 6) +#define LANE3_MAP_LOGIC_LANE_3 (0x3 << 6) +#define LANE2_MAP_LOGIC_LANE_0 (0x0 << 4) +#define LANE2_MAP_LOGIC_LANE_1 (0x1 << 4) +#define LANE2_MAP_LOGIC_LANE_2 (0x2 << 4) +#define LANE2_MAP_LOGIC_LANE_3 (0x3 << 4) +#define LANE1_MAP_LOGIC_LANE_0 (0x0 << 2) +#define LANE1_MAP_LOGIC_LANE_1 (0x1 << 2) +#define LANE1_MAP_LOGIC_LANE_2 (0x2 << 2) +#define LANE1_MAP_LOGIC_LANE_3 (0x3 << 2) +#define LANE0_MAP_LOGIC_LANE_0 (0x0 << 0) +#define LANE0_MAP_LOGIC_LANE_1 (0x1 << 0) +#define LANE0_MAP_LOGIC_LANE_2 (0x2 << 0) +#define LANE0_MAP_LOGIC_LANE_3 (0x3 << 0) + +/* EXYNOS_DP_AUX_HW_RETRY_CTL */ +#define AUX_BIT_PERIOD_EXPECTED_DELAY(x) (((x) & 0x7) << 8) +#define AUX_HW_RETRY_INTERVAL_MASK (0x3 << 3) +#define AUX_HW_RETRY_INTERVAL_600_MICROSECONDS (0x0 << 3) +#define AUX_HW_RETRY_INTERVAL_800_MICROSECONDS (0x1 << 3) +#define AUX_HW_RETRY_INTERVAL_1000_MICROSECONDS (0x2 << 3) +#define AUX_HW_RETRY_INTERVAL_1800_MICROSECONDS (0x3 << 3) +#define AUX_HW_RETRY_COUNT_SEL(x) (((x) & 0x7) << 0) + +/* EXYNOS_DP_COMMON_INT_STA_1 */ +#define VSYNC_DET (0x1 << 7) +#define PLL_LOCK_CHG (0x1 << 6) +#define SPDIF_ERR (0x1 << 5) +#define SPDIF_UNSTBL (0x1 << 4) +#define VID_FORMAT_CHG (0x1 << 3) +#define AUD_CLK_CHG (0x1 << 2) +#define VID_CLK_CHG (0x1 << 1) +#define SW_INT (0x1 << 0) + +/* EXYNOS_DP_COMMON_INT_STA_2 */ +#define ENC_EN_CHG (0x1 << 6) +#define HW_BKSV_RDY (0x1 << 3) +#define HW_SHA_DONE (0x1 << 2) +#define HW_AUTH_STATE_CHG (0x1 << 1) +#define HW_AUTH_DONE (0x1 << 0) + +/* EXYNOS_DP_COMMON_INT_STA_3 */ +#define AFIFO_UNDER (0x1 << 7) +#define AFIFO_OVER (0x1 << 6) +#define R0_CHK_FLAG (0x1 << 5) + +/* EXYNOS_DP_COMMON_INT_STA_4 */ +#define PSR_ACTIVE (0x1 << 7) +#define PSR_INACTIVE (0x1 << 6) +#define SPDIF_BI_PHASE_ERR (0x1 << 5) +#define HOTPLUG_CHG (0x1 << 2) +#define HPD_LOST (0x1 << 1) +#define PLUG (0x1 << 0) + +/* EXYNOS_DP_INT_STA */ +#define INT_HPD (0x1 << 6) +#define HW_TRAINING_FINISH (0x1 << 5) +#define RPLY_RECEIV (0x1 << 1) +#define AUX_ERR (0x1 << 0) + +/* EXYNOS_DP_INT_CTL */ +#define SOFT_INT_CTRL (0x1 << 2) +#define INT_POL (0x1 << 0) + +/* EXYNOS_DP_SYS_CTL_1 */ +#define DET_STA (0x1 << 2) +#define FORCE_DET (0x1 << 1) +#define DET_CTRL (0x1 << 0) + +/* EXYNOS_DP_SYS_CTL_2 */ +#define CHA_CRI(x) (((x) & 0xf) << 4) +#define CHA_STA (0x1 << 2) +#define FORCE_CHA (0x1 << 1) +#define CHA_CTRL (0x1 << 0) + +/* EXYNOS_DP_SYS_CTL_3 */ +#define HPD_STATUS (0x1 << 6) +#define F_HPD (0x1 << 5) +#define HPD_CTRL (0x1 << 4) +#define HDCP_RDY (0x1 << 3) +#define STRM_VALID (0x1 << 2) +#define F_VALID (0x1 << 1) +#define VALID_CTRL (0x1 << 0) + +/* EXYNOS_DP_SYS_CTL_4 */ +#define FIX_M_AUD (0x1 << 4) +#define ENHANCED (0x1 << 3) +#define FIX_M_VID (0x1 << 2) +#define M_VID_UPDATE_CTRL (0x3 << 0) + +/* EXYNOS_DP_TRAINING_PTN_SET */ +#define SCRAMBLER_TYPE (0x1 << 9) +#define HW_LINK_TRAINING_PATTERN (0x1 << 8) +#define SCRAMBLING_DISABLE (0x1 << 5) +#define SCRAMBLING_ENABLE (0x0 << 5) +#define LINK_QUAL_PATTERN_SET_MASK (0x3 << 2) +#define LINK_QUAL_PATTERN_SET_PRBS7 (0x3 << 2) +#define LINK_QUAL_PATTERN_SET_D10_2 (0x1 << 2) +#define LINK_QUAL_PATTERN_SET_DISABLE (0x0 << 2) +#define SW_TRAINING_PATTERN_SET_MASK (0x3 << 0) +#define SW_TRAINING_PATTERN_SET_PTN2 (0x2 << 0) +#define SW_TRAINING_PATTERN_SET_PTN1 (0x1 << 0) +#define SW_TRAINING_PATTERN_SET_NORMAL (0x0 << 0) + +/* EXYNOS_DP_LN0_LINK_TRAINING_CTL */ +#define PRE_EMPHASIS_SET_SHIFT (3) + +/* EXYNOS_DP_DEBUG_CTL */ +#define PLL_LOCK (0x1 << 4) +#define F_PLL_LOCK (0x1 << 3) +#define PLL_LOCK_CTRL (0x1 << 2) +#define PN_INV (0x1 << 0) + +/* EXYNOS_DP_PLL_CTL */ +#define DP_PLL_PD (0x1 << 7) +#define DP_PLL_RESET (0x1 << 6) +#define DP_PLL_LOOP_BIT_DEFAULT (0x1 << 4) +#define DP_PLL_REF_BIT_1_1250V (0x5 << 0) +#define DP_PLL_REF_BIT_1_2500V (0x7 << 0) + +/* EXYNOS_DP_PHY_PD */ +#define DP_PHY_PD (0x1 << 5) +#define AUX_PD (0x1 << 4) +#define CH3_PD (0x1 << 3) +#define CH2_PD (0x1 << 2) +#define CH1_PD (0x1 << 1) +#define CH0_PD (0x1 << 0) + +/* EXYNOS_DP_PHY_TEST */ +#define MACRO_RST (0x1 << 5) +#define CH1_TEST (0x1 << 1) +#define CH0_TEST (0x1 << 0) + +/* EXYNOS_DP_AUX_CH_STA */ +#define AUX_BUSY (0x1 << 4) +#define AUX_STATUS_MASK (0xf << 0) + +/* EXYNOS_DP_AUX_CH_DEFER_CTL */ +#define DEFER_CTRL_EN (0x1 << 7) +#define DEFER_COUNT(x) (((x) & 0x7f) << 0) + +/* EXYNOS_DP_AUX_RX_COMM */ +#define AUX_RX_COMM_I2C_DEFER (0x2 << 2) +#define AUX_RX_COMM_AUX_DEFER (0x2 << 0) + +/* EXYNOS_DP_BUFFER_DATA_CTL */ +#define BUF_CLR (0x1 << 7) +#define BUF_DATA_COUNT(x) (((x) & 0x1f) << 0) + +/* EXYNOS_DP_AUX_CH_CTL_1 */ +#define AUX_LENGTH(x) (((x - 1) & 0xf) << 4) +#define AUX_TX_COMM_MASK (0xf << 0) +#define AUX_TX_COMM_DP_TRANSACTION (0x1 << 3) +#define AUX_TX_COMM_I2C_TRANSACTION (0x0 << 3) +#define AUX_TX_COMM_MOT (0x1 << 2) +#define AUX_TX_COMM_WRITE (0x0 << 0) +#define AUX_TX_COMM_READ (0x1 << 0) + +/* EXYNOS_DP_AUX_ADDR_7_0 */ +#define AUX_ADDR_7_0(x) (((x) >> 0) & 0xff) + +/* EXYNOS_DP_AUX_ADDR_15_8 */ +#define AUX_ADDR_15_8(x) (((x) >> 8) & 0xff) + +/* EXYNOS_DP_AUX_ADDR_19_16 */ +#define AUX_ADDR_19_16(x) (((x) >> 16) & 0x0f) + +/* EXYNOS_DP_AUX_CH_CTL_2 */ +#define ADDR_ONLY (0x1 << 1) +#define AUX_EN (0x1 << 0) + +/* EXYNOS_DP_SOC_GENERAL_CTL */ +#define AUDIO_MODE_SPDIF_MODE (0x1 << 8) +#define AUDIO_MODE_MASTER_MODE (0x0 << 8) +#define MASTER_VIDEO_INTERLACE_EN (0x1 << 4) +#define VIDEO_MASTER_CLK_SEL (0x1 << 2) +#define VIDEO_MASTER_MODE_EN (0x1 << 1) +#define VIDEO_MODE_MASK (0x1 << 0) +#define VIDEO_MODE_SLAVE_MODE (0x1 << 0) +#define VIDEO_MODE_MASTER_MODE (0x0 << 0) + +#endif /* _EXYNOS_DP_REG_H */ diff --git a/drivers/video/exynos/exynos_mipi_dsi.c b/drivers/video/exynos/exynos_mipi_dsi.c new file mode 100644 index 000000000000..557091dc0e97 --- /dev/null +++ b/drivers/video/exynos/exynos_mipi_dsi.c @@ -0,0 +1,600 @@ +/* linux/drivers/video/exynos/exynos_mipi_dsi.c + * + * Samsung SoC MIPI-DSIM driver. + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd + * + * InKi Dae, <inki.dae@samsung.com> + * Donghwa Lee, <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/clk.h> +#include <linux/mutex.h> +#include <linux/wait.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/fb.h> +#include <linux/ctype.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/memory.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/kthread.h> +#include <linux/notifier.h> +#include <linux/regulator/consumer.h> +#include <linux/pm_runtime.h> + +#include <video/exynos_mipi_dsim.h> + +#include <plat/fb.h> + +#include "exynos_mipi_dsi_common.h" +#include "exynos_mipi_dsi_lowlevel.h" + +struct mipi_dsim_ddi { + int bus_id; + struct list_head list; + struct mipi_dsim_lcd_device *dsim_lcd_dev; + struct mipi_dsim_lcd_driver *dsim_lcd_drv; +}; + +static LIST_HEAD(dsim_ddi_list); + +static DEFINE_MUTEX(mipi_dsim_lock); + +static struct mipi_dsim_platform_data *to_dsim_plat(struct platform_device + *pdev) +{ + return pdev->dev.platform_data; +} + +static struct regulator_bulk_data supplies[] = { + { .supply = "vdd10", }, + { .supply = "vdd18", }, +}; + +static int exynos_mipi_regulator_enable(struct mipi_dsim_device *dsim) +{ + int ret; + + mutex_lock(&dsim->lock); + ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies); + mutex_unlock(&dsim->lock); + + return ret; +} + +static int exynos_mipi_regulator_disable(struct mipi_dsim_device *dsim) +{ + int ret; + + mutex_lock(&dsim->lock); + ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies); + mutex_unlock(&dsim->lock); + + return ret; +} + +/* update all register settings to MIPI DSI controller. */ +static void exynos_mipi_update_cfg(struct mipi_dsim_device *dsim) +{ + /* + * data from Display controller(FIMD) is not transferred in video mode + * but in case of command mode, all settings is not updated to + * registers. + */ + exynos_mipi_dsi_stand_by(dsim, 0); + + exynos_mipi_dsi_init_dsim(dsim); + exynos_mipi_dsi_init_link(dsim); + + exynos_mipi_dsi_set_hs_enable(dsim); + + /* set display timing. */ + exynos_mipi_dsi_set_display_mode(dsim, dsim->dsim_config); + + /* + * data from Display controller(FIMD) is transferred in video mode + * but in case of command mode, all settigs is updated to registers. + */ + exynos_mipi_dsi_stand_by(dsim, 1); +} + +static int exynos_mipi_dsi_early_blank_mode(struct mipi_dsim_device *dsim, + int power) +{ + struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; + struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; + + switch (power) { + case FB_BLANK_POWERDOWN: + if (dsim->suspended) + return 0; + + if (client_drv && client_drv->suspend) + client_drv->suspend(client_dev); + + clk_disable(dsim->clock); + + exynos_mipi_regulator_disable(dsim); + + dsim->suspended = true; + + break; + default: + break; + } + + return 0; +} + +static int exynos_mipi_dsi_blank_mode(struct mipi_dsim_device *dsim, int power) +{ + struct platform_device *pdev = to_platform_device(dsim->dev); + struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; + struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; + + switch (power) { + case FB_BLANK_UNBLANK: + if (!dsim->suspended) + return 0; + + /* lcd panel power on. */ + if (client_drv && client_drv->power_on) + client_drv->power_on(client_dev, 1); + + exynos_mipi_regulator_disable(dsim); + + /* enable MIPI-DSI PHY. */ + if (dsim->pd->phy_enable) + dsim->pd->phy_enable(pdev, true); + + clk_enable(dsim->clock); + + exynos_mipi_update_cfg(dsim); + + /* set lcd panel sequence commands. */ + if (client_drv && client_drv->set_sequence) + client_drv->set_sequence(client_dev); + + dsim->suspended = false; + + break; + case FB_BLANK_NORMAL: + /* TODO. */ + break; + default: + break; + } + + return 0; +} + +int exynos_mipi_dsi_register_lcd_device(struct mipi_dsim_lcd_device *lcd_dev) +{ + struct mipi_dsim_ddi *dsim_ddi; + + if (!lcd_dev->name) { + pr_err("dsim_lcd_device name is NULL.\n"); + return -EFAULT; + } + + dsim_ddi = kzalloc(sizeof(struct mipi_dsim_ddi), GFP_KERNEL); + if (!dsim_ddi) { + pr_err("failed to allocate dsim_ddi object.\n"); + return -ENOMEM; + } + + dsim_ddi->dsim_lcd_dev = lcd_dev; + + mutex_lock(&mipi_dsim_lock); + list_add_tail(&dsim_ddi->list, &dsim_ddi_list); + mutex_unlock(&mipi_dsim_lock); + + return 0; +} + +struct mipi_dsim_ddi *exynos_mipi_dsi_find_lcd_device(struct mipi_dsim_lcd_driver *lcd_drv) +{ + struct mipi_dsim_ddi *dsim_ddi, *next; + struct mipi_dsim_lcd_device *lcd_dev; + + mutex_lock(&mipi_dsim_lock); + + list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) { + if (!dsim_ddi) + goto out; + + lcd_dev = dsim_ddi->dsim_lcd_dev; + if (!lcd_dev) + continue; + + if ((strcmp(lcd_drv->name, lcd_dev->name)) == 0) { + /** + * bus_id would be used to identify + * connected bus. + */ + dsim_ddi->bus_id = lcd_dev->bus_id; + mutex_unlock(&mipi_dsim_lock); + + return dsim_ddi; + } + + list_del(&dsim_ddi->list); + kfree(dsim_ddi); + } + +out: + mutex_unlock(&mipi_dsim_lock); + + return NULL; +} + +int exynos_mipi_dsi_register_lcd_driver(struct mipi_dsim_lcd_driver *lcd_drv) +{ + struct mipi_dsim_ddi *dsim_ddi; + + if (!lcd_drv->name) { + pr_err("dsim_lcd_driver name is NULL.\n"); + return -EFAULT; + } + + dsim_ddi = exynos_mipi_dsi_find_lcd_device(lcd_drv); + if (!dsim_ddi) { + pr_err("mipi_dsim_ddi object not found.\n"); + return -EFAULT; + } + + dsim_ddi->dsim_lcd_drv = lcd_drv; + + pr_info("registered panel driver(%s) to mipi-dsi driver.\n", + lcd_drv->name); + + return 0; + +} + +struct mipi_dsim_ddi *exynos_mipi_dsi_bind_lcd_ddi(struct mipi_dsim_device *dsim, + const char *name) +{ + struct mipi_dsim_ddi *dsim_ddi, *next; + struct mipi_dsim_lcd_driver *lcd_drv; + struct mipi_dsim_lcd_device *lcd_dev; + int ret; + + mutex_lock(&dsim->lock); + + list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) { + lcd_drv = dsim_ddi->dsim_lcd_drv; + lcd_dev = dsim_ddi->dsim_lcd_dev; + if (!lcd_drv || !lcd_dev || + (dsim->id != dsim_ddi->bus_id)) + continue; + + dev_dbg(dsim->dev, "lcd_drv->id = %d, lcd_dev->id = %d\n", + lcd_drv->id, lcd_dev->id); + dev_dbg(dsim->dev, "lcd_dev->bus_id = %d, dsim->id = %d\n", + lcd_dev->bus_id, dsim->id); + + if ((strcmp(lcd_drv->name, name) == 0)) { + lcd_dev->master = dsim; + + lcd_dev->dev.parent = dsim->dev; + dev_set_name(&lcd_dev->dev, "%s", lcd_drv->name); + + ret = device_register(&lcd_dev->dev); + if (ret < 0) { + dev_err(dsim->dev, + "can't register %s, status %d\n", + dev_name(&lcd_dev->dev), ret); + mutex_unlock(&dsim->lock); + + return NULL; + } + + dsim->dsim_lcd_dev = lcd_dev; + dsim->dsim_lcd_drv = lcd_drv; + + mutex_unlock(&dsim->lock); + + return dsim_ddi; + } + } + + mutex_unlock(&dsim->lock); + + return NULL; +} + +/* define MIPI-DSI Master operations. */ +static struct mipi_dsim_master_ops master_ops = { + .cmd_read = exynos_mipi_dsi_rd_data, + .cmd_write = exynos_mipi_dsi_wr_data, + .get_dsim_frame_done = exynos_mipi_dsi_get_frame_done_status, + .clear_dsim_frame_done = exynos_mipi_dsi_clear_frame_done, + .set_early_blank_mode = exynos_mipi_dsi_early_blank_mode, + .set_blank_mode = exynos_mipi_dsi_blank_mode, +}; + +static int exynos_mipi_dsi_probe(struct platform_device *pdev) +{ + struct resource *res; + struct mipi_dsim_device *dsim; + struct mipi_dsim_config *dsim_config; + struct mipi_dsim_platform_data *dsim_pd; + struct mipi_dsim_ddi *dsim_ddi; + int ret = -EINVAL; + + dsim = kzalloc(sizeof(struct mipi_dsim_device), GFP_KERNEL); + if (!dsim) { + dev_err(&pdev->dev, "failed to allocate dsim object.\n"); + return -ENOMEM; + } + + dsim->pd = to_dsim_plat(pdev); + dsim->dev = &pdev->dev; + dsim->id = pdev->id; + + /* get mipi_dsim_platform_data. */ + dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd; + if (dsim_pd == NULL) { + dev_err(&pdev->dev, "failed to get platform data for dsim.\n"); + goto err_clock_get; + } + /* get mipi_dsim_config. */ + dsim_config = dsim_pd->dsim_config; + if (dsim_config == NULL) { + dev_err(&pdev->dev, "failed to get dsim config data.\n"); + goto err_clock_get; + } + + dsim->dsim_config = dsim_config; + dsim->master_ops = &master_ops; + + mutex_init(&dsim->lock); + + ret = regulator_bulk_get(&pdev->dev, ARRAY_SIZE(supplies), supplies); + if (ret) { + dev_err(&pdev->dev, "Failed to get regulators: %d\n", ret); + goto err_clock_get; + } + + dsim->clock = clk_get(&pdev->dev, "dsim0"); + if (IS_ERR(dsim->clock)) { + dev_err(&pdev->dev, "failed to get dsim clock source\n"); + goto err_clock_get; + } + + clk_enable(dsim->clock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get io memory region\n"); + goto err_platform_get; + } + + dsim->res = request_mem_region(res->start, resource_size(res), + dev_name(&pdev->dev)); + if (!dsim->res) { + dev_err(&pdev->dev, "failed to request io memory region\n"); + ret = -ENOMEM; + goto err_mem_region; + } + + dsim->reg_base = ioremap(res->start, resource_size(res)); + if (!dsim->reg_base) { + dev_err(&pdev->dev, "failed to remap io region\n"); + ret = -ENOMEM; + goto err_ioremap; + } + + mutex_init(&dsim->lock); + + /* bind lcd ddi matched with panel name. */ + dsim_ddi = exynos_mipi_dsi_bind_lcd_ddi(dsim, dsim_pd->lcd_panel_name); + if (!dsim_ddi) { + dev_err(&pdev->dev, "mipi_dsim_ddi object not found.\n"); + goto err_bind; + } + + dsim->irq = platform_get_irq(pdev, 0); + if (dsim->irq < 0) { + dev_err(&pdev->dev, "failed to request dsim irq resource\n"); + ret = -EINVAL; + goto err_platform_get_irq; + } + + ret = request_irq(dsim->irq, exynos_mipi_dsi_interrupt_handler, + IRQF_SHARED, pdev->name, dsim); + if (ret != 0) { + dev_err(&pdev->dev, "failed to request dsim irq\n"); + ret = -EINVAL; + goto err_bind; + } + + init_completion(&dsim_wr_comp); + init_completion(&dsim_rd_comp); + + /* enable interrupt */ + exynos_mipi_dsi_init_interrupt(dsim); + + /* initialize mipi-dsi client(lcd panel). */ + if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->probe) + dsim_ddi->dsim_lcd_drv->probe(dsim_ddi->dsim_lcd_dev); + + /* in case that mipi got enabled at bootloader. */ + if (dsim_pd->enabled) + goto out; + + /* lcd panel power on. */ + if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->power_on) + dsim_ddi->dsim_lcd_drv->power_on(dsim_ddi->dsim_lcd_dev, 1); + + exynos_mipi_regulator_enable(dsim); + + /* enable MIPI-DSI PHY. */ + if (dsim->pd->phy_enable) + dsim->pd->phy_enable(pdev, true); + + exynos_mipi_update_cfg(dsim); + + /* set lcd panel sequence commands. */ + if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->set_sequence) + dsim_ddi->dsim_lcd_drv->set_sequence(dsim_ddi->dsim_lcd_dev); + + dsim->suspended = false; + +out: + platform_set_drvdata(pdev, dsim); + + dev_dbg(&pdev->dev, "mipi-dsi driver(%s mode) has been probed.\n", + (dsim_config->e_interface == DSIM_COMMAND) ? + "CPU" : "RGB"); + + return 0; + +err_bind: + iounmap(dsim->reg_base); + +err_ioremap: + release_mem_region(dsim->res->start, resource_size(dsim->res)); + +err_mem_region: + release_resource(dsim->res); + +err_platform_get: + clk_disable(dsim->clock); + clk_put(dsim->clock); +err_clock_get: + kfree(dsim); + +err_platform_get_irq: + return ret; +} + +static int __devexit exynos_mipi_dsi_remove(struct platform_device *pdev) +{ + struct mipi_dsim_device *dsim = platform_get_drvdata(pdev); + struct mipi_dsim_ddi *dsim_ddi, *next; + struct mipi_dsim_lcd_driver *dsim_lcd_drv; + + iounmap(dsim->reg_base); + + clk_disable(dsim->clock); + clk_put(dsim->clock); + + release_resource(dsim->res); + release_mem_region(dsim->res->start, resource_size(dsim->res)); + + list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) { + if (dsim_ddi) { + if (dsim->id != dsim_ddi->bus_id) + continue; + + dsim_lcd_drv = dsim_ddi->dsim_lcd_drv; + + if (dsim_lcd_drv->remove) + dsim_lcd_drv->remove(dsim_ddi->dsim_lcd_dev); + + kfree(dsim_ddi); + } + } + + regulator_bulk_free(ARRAY_SIZE(supplies), supplies); + kfree(dsim); + + return 0; +} + +#ifdef CONFIG_PM +static int exynos_mipi_dsi_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct mipi_dsim_device *dsim = platform_get_drvdata(pdev); + struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; + struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; + + disable_irq(dsim->irq); + + if (dsim->suspended) + return 0; + + if (client_drv && client_drv->suspend) + client_drv->suspend(client_dev); + + /* enable MIPI-DSI PHY. */ + if (dsim->pd->phy_enable) + dsim->pd->phy_enable(pdev, false); + + clk_disable(dsim->clock); + + exynos_mipi_regulator_disable(dsim); + + dsim->suspended = true; + + return 0; +} + +static int exynos_mipi_dsi_resume(struct platform_device *pdev) +{ + struct mipi_dsim_device *dsim = platform_get_drvdata(pdev); + struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; + struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; + + enable_irq(dsim->irq); + + if (!dsim->suspended) + return 0; + + /* lcd panel power on. */ + if (client_drv && client_drv->power_on) + client_drv->power_on(client_dev, 1); + + exynos_mipi_regulator_enable(dsim); + + /* enable MIPI-DSI PHY. */ + if (dsim->pd->phy_enable) + dsim->pd->phy_enable(pdev, true); + + clk_enable(dsim->clock); + + exynos_mipi_update_cfg(dsim); + + /* set lcd panel sequence commands. */ + if (client_drv && client_drv->set_sequence) + client_drv->set_sequence(client_dev); + + dsim->suspended = false; + + return 0; +} +#else +#define exynos_mipi_dsi_suspend NULL +#define exynos_mipi_dsi_resume NULL +#endif + +static struct platform_driver exynos_mipi_dsi_driver = { + .probe = exynos_mipi_dsi_probe, + .remove = __devexit_p(exynos_mipi_dsi_remove), + .suspend = exynos_mipi_dsi_suspend, + .resume = exynos_mipi_dsi_resume, + .driver = { + .name = "exynos-mipi-dsim", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(exynos_mipi_dsi_driver); + +MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>"); +MODULE_DESCRIPTION("Samusung SoC MIPI-DSI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/exynos/exynos_mipi_dsi_common.c b/drivers/video/exynos/exynos_mipi_dsi_common.c new file mode 100644 index 000000000000..14909c1d3832 --- /dev/null +++ b/drivers/video/exynos/exynos_mipi_dsi_common.c @@ -0,0 +1,896 @@ +/* linux/drivers/video/exynos/exynos_mipi_dsi_common.c + * + * Samsung SoC MIPI-DSI common driver. + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd + * + * InKi Dae, <inki.dae@samsung.com> + * Donghwa Lee, <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/wait.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/fb.h> +#include <linux/ctype.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/memory.h> +#include <linux/delay.h> +#include <linux/kthread.h> + +#include <video/mipi_display.h> +#include <video/exynos_mipi_dsim.h> + +#include <mach/map.h> + +#include "exynos_mipi_dsi_regs.h" +#include "exynos_mipi_dsi_lowlevel.h" +#include "exynos_mipi_dsi_common.h" + +#define MIPI_FIFO_TIMEOUT msecs_to_jiffies(250) +#define MIPI_RX_FIFO_READ_DONE 0x30800002 +#define MIPI_MAX_RX_FIFO 20 +#define MHZ (1000 * 1000) +#define FIN_HZ (24 * MHZ) + +#define DFIN_PLL_MIN_HZ (6 * MHZ) +#define DFIN_PLL_MAX_HZ (12 * MHZ) + +#define DFVCO_MIN_HZ (500 * MHZ) +#define DFVCO_MAX_HZ (1000 * MHZ) + +#define TRY_GET_FIFO_TIMEOUT (5000 * 2) +#define TRY_FIFO_CLEAR (10) + +/* MIPI-DSIM status types. */ +enum { + DSIM_STATE_INIT, /* should be initialized. */ + DSIM_STATE_STOP, /* CPU and LCDC are LP mode. */ + DSIM_STATE_HSCLKEN, /* HS clock was enabled. */ + DSIM_STATE_ULPS +}; + +/* define DSI lane types. */ +enum { + DSIM_LANE_CLOCK = (1 << 0), + DSIM_LANE_DATA0 = (1 << 1), + DSIM_LANE_DATA1 = (1 << 2), + DSIM_LANE_DATA2 = (1 << 3), + DSIM_LANE_DATA3 = (1 << 4) +}; + +static unsigned int dpll_table[15] = { + 100, 120, 170, 220, 270, + 320, 390, 450, 510, 560, + 640, 690, 770, 870, 950 +}; + +irqreturn_t exynos_mipi_dsi_interrupt_handler(int irq, void *dev_id) +{ + unsigned int intsrc = 0; + unsigned int intmsk = 0; + struct mipi_dsim_device *dsim = NULL; + + dsim = dev_id; + if (!dsim) { + dev_dbg(dsim->dev, KERN_ERR "%s:error: wrong parameter\n", + __func__); + return IRQ_HANDLED; + } + + intsrc = exynos_mipi_dsi_read_interrupt(dsim); + intmsk = exynos_mipi_dsi_read_interrupt_mask(dsim); + + intmsk = ~(intmsk) & intsrc; + + switch (intmsk) { + case INTMSK_RX_DONE: + complete(&dsim_rd_comp); + dev_dbg(dsim->dev, "MIPI INTMSK_RX_DONE\n"); + break; + case INTMSK_FIFO_EMPTY: + complete(&dsim_wr_comp); + dev_dbg(dsim->dev, "MIPI INTMSK_FIFO_EMPTY\n"); + break; + default: + break; + } + + exynos_mipi_dsi_clear_interrupt(dsim, intmsk); + + return IRQ_HANDLED; +} + +/* + * write long packet to mipi dsi slave + * @dsim: mipi dsim device structure. + * @data0: packet data to send. + * @data1: size of packet data + */ +static void exynos_mipi_dsi_long_data_wr(struct mipi_dsim_device *dsim, + const unsigned char *data0, unsigned int data_size) +{ + unsigned int data_cnt = 0, payload = 0; + + /* in case that data count is more then 4 */ + for (data_cnt = 0; data_cnt < data_size; data_cnt += 4) { + /* + * after sending 4bytes per one time, + * send remainder data less then 4. + */ + if ((data_size - data_cnt) < 4) { + if ((data_size - data_cnt) == 3) { + payload = data0[data_cnt] | + data0[data_cnt + 1] << 8 | + data0[data_cnt + 2] << 16; + dev_dbg(dsim->dev, "count = 3 payload = %x, %x %x %x\n", + payload, data0[data_cnt], + data0[data_cnt + 1], + data0[data_cnt + 2]); + } else if ((data_size - data_cnt) == 2) { + payload = data0[data_cnt] | + data0[data_cnt + 1] << 8; + dev_dbg(dsim->dev, + "count = 2 payload = %x, %x %x\n", payload, + data0[data_cnt], + data0[data_cnt + 1]); + } else if ((data_size - data_cnt) == 1) { + payload = data0[data_cnt]; + } + + exynos_mipi_dsi_wr_tx_data(dsim, payload); + /* send 4bytes per one time. */ + } else { + payload = data0[data_cnt] | + data0[data_cnt + 1] << 8 | + data0[data_cnt + 2] << 16 | + data0[data_cnt + 3] << 24; + + dev_dbg(dsim->dev, + "count = 4 payload = %x, %x %x %x %x\n", + payload, *(u8 *)(data0 + data_cnt), + data0[data_cnt + 1], + data0[data_cnt + 2], + data0[data_cnt + 3]); + + exynos_mipi_dsi_wr_tx_data(dsim, payload); + } + } +} + +int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id, + const unsigned char *data0, unsigned int data_size) +{ + unsigned int check_rx_ack = 0; + + if (dsim->state == DSIM_STATE_ULPS) { + dev_err(dsim->dev, "state is ULPS.\n"); + + return -EINVAL; + } + + /* FIXME!!! why does it need this delay? */ + msleep(20); + + mutex_lock(&dsim->lock); + + switch (data_id) { + /* short packet types of packet types for command. */ + case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM: + case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM: + case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM: + case MIPI_DSI_DCS_SHORT_WRITE: + case MIPI_DSI_DCS_SHORT_WRITE_PARAM: + case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE: + exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]); + if (check_rx_ack) { + /* process response func should be implemented */ + mutex_unlock(&dsim->lock); + return 0; + } else { + mutex_unlock(&dsim->lock); + return -EINVAL; + } + + /* general command */ + case MIPI_DSI_COLOR_MODE_OFF: + case MIPI_DSI_COLOR_MODE_ON: + case MIPI_DSI_SHUTDOWN_PERIPHERAL: + case MIPI_DSI_TURN_ON_PERIPHERAL: + exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]); + if (check_rx_ack) { + /* process response func should be implemented. */ + mutex_unlock(&dsim->lock); + return 0; + } else { + mutex_unlock(&dsim->lock); + return -EINVAL; + } + + /* packet types for video data */ + case MIPI_DSI_V_SYNC_START: + case MIPI_DSI_V_SYNC_END: + case MIPI_DSI_H_SYNC_START: + case MIPI_DSI_H_SYNC_END: + case MIPI_DSI_END_OF_TRANSMISSION: + mutex_unlock(&dsim->lock); + return 0; + + /* long packet type and null packet */ + case MIPI_DSI_NULL_PACKET: + case MIPI_DSI_BLANKING_PACKET: + mutex_unlock(&dsim->lock); + return 0; + case MIPI_DSI_GENERIC_LONG_WRITE: + case MIPI_DSI_DCS_LONG_WRITE: + { + unsigned int size, payload = 0; + INIT_COMPLETION(dsim_wr_comp); + + size = data_size * 4; + + /* if data count is less then 4, then send 3bytes data. */ + if (data_size < 4) { + payload = data0[0] | + data0[1] << 8 | + data0[2] << 16; + + exynos_mipi_dsi_wr_tx_data(dsim, payload); + + dev_dbg(dsim->dev, "count = %d payload = %x,%x %x %x\n", + data_size, payload, data0[0], + data0[1], data0[2]); + + /* in case that data count is more then 4 */ + } else + exynos_mipi_dsi_long_data_wr(dsim, data0, data_size); + + /* put data into header fifo */ + exynos_mipi_dsi_wr_tx_header(dsim, data_id, data_size & 0xff, + (data_size & 0xff00) >> 8); + + if (!wait_for_completion_interruptible_timeout(&dsim_wr_comp, + MIPI_FIFO_TIMEOUT)) { + dev_warn(dsim->dev, "command write timeout.\n"); + mutex_unlock(&dsim->lock); + return -EAGAIN; + } + + if (check_rx_ack) { + /* process response func should be implemented. */ + mutex_unlock(&dsim->lock); + return 0; + } else { + mutex_unlock(&dsim->lock); + return -EINVAL; + } + } + + /* packet typo for video data */ + case MIPI_DSI_PACKED_PIXEL_STREAM_16: + case MIPI_DSI_PACKED_PIXEL_STREAM_18: + case MIPI_DSI_PIXEL_STREAM_3BYTE_18: + case MIPI_DSI_PACKED_PIXEL_STREAM_24: + if (check_rx_ack) { + /* process response func should be implemented. */ + mutex_unlock(&dsim->lock); + return 0; + } else { + mutex_unlock(&dsim->lock); + return -EINVAL; + } + default: + dev_warn(dsim->dev, + "data id %x is not supported current DSI spec.\n", + data_id); + + mutex_unlock(&dsim->lock); + return -EINVAL; + } + + mutex_unlock(&dsim->lock); + return 0; +} + +static unsigned int exynos_mipi_dsi_long_data_rd(struct mipi_dsim_device *dsim, + unsigned int req_size, unsigned int rx_data, u8 *rx_buf) +{ + unsigned int rcv_pkt, i, j; + u16 rxsize; + + /* for long packet */ + rxsize = (u16)((rx_data & 0x00ffff00) >> 8); + dev_dbg(dsim->dev, "mipi dsi rx size : %d\n", rxsize); + if (rxsize != req_size) { + dev_dbg(dsim->dev, + "received size mismatch received: %d, requested: %d\n", + rxsize, req_size); + goto err; + } + + for (i = 0; i < (rxsize >> 2); i++) { + rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim); + dev_dbg(dsim->dev, "received pkt : %08x\n", rcv_pkt); + for (j = 0; j < 4; j++) { + rx_buf[(i * 4) + j] = + (u8)(rcv_pkt >> (j * 8)) & 0xff; + dev_dbg(dsim->dev, "received value : %02x\n", + (rcv_pkt >> (j * 8)) & 0xff); + } + } + if (rxsize % 4) { + rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim); + dev_dbg(dsim->dev, "received pkt : %08x\n", rcv_pkt); + for (j = 0; j < (rxsize % 4); j++) { + rx_buf[(i * 4) + j] = + (u8)(rcv_pkt >> (j * 8)) & 0xff; + dev_dbg(dsim->dev, "received value : %02x\n", + (rcv_pkt >> (j * 8)) & 0xff); + } + } + + return rxsize; + +err: + return -EINVAL; +} + +static unsigned int exynos_mipi_dsi_response_size(unsigned int req_size) +{ + switch (req_size) { + case 1: + return MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE; + case 2: + return MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE; + default: + return MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE; + } +} + +int exynos_mipi_dsi_rd_data(struct mipi_dsim_device *dsim, unsigned int data_id, + unsigned int data0, unsigned int req_size, u8 *rx_buf) +{ + unsigned int rx_data, rcv_pkt, i; + u8 response = 0; + u16 rxsize; + + if (dsim->state == DSIM_STATE_ULPS) { + dev_err(dsim->dev, "state is ULPS.\n"); + + return -EINVAL; + } + + /* FIXME!!! */ + msleep(20); + + mutex_lock(&dsim->lock); + INIT_COMPLETION(dsim_rd_comp); + exynos_mipi_dsi_rd_tx_header(dsim, + MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, req_size); + + response = exynos_mipi_dsi_response_size(req_size); + + switch (data_id) { + case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM: + case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM: + case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM: + case MIPI_DSI_DCS_READ: + exynos_mipi_dsi_rd_tx_header(dsim, + data_id, data0); + /* process response func should be implemented. */ + break; + default: + dev_warn(dsim->dev, + "data id %x is not supported current DSI spec.\n", + data_id); + + return -EINVAL; + } + + if (!wait_for_completion_interruptible_timeout(&dsim_rd_comp, + MIPI_FIFO_TIMEOUT)) { + pr_err("RX done interrupt timeout\n"); + mutex_unlock(&dsim->lock); + return 0; + } + + msleep(20); + + rx_data = exynos_mipi_dsi_rd_rx_fifo(dsim); + + if ((u8)(rx_data & 0xff) != response) { + printk(KERN_ERR + "mipi dsi wrong response rx_data : %x, response:%x\n", + rx_data, response); + goto clear_rx_fifo; + } + + if (req_size <= 2) { + /* for short packet */ + for (i = 0; i < req_size; i++) + rx_buf[i] = (rx_data >> (8 + (i * 8))) & 0xff; + rxsize = req_size; + } else { + /* for long packet */ + rxsize = exynos_mipi_dsi_long_data_rd(dsim, req_size, rx_data, + rx_buf); + if (rxsize != req_size) + goto clear_rx_fifo; + } + + rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim); + + msleep(20); + + if (rcv_pkt != MIPI_RX_FIFO_READ_DONE) { + dev_info(dsim->dev, + "Can't found RX FIFO READ DONE FLAG : %x\n", rcv_pkt); + goto clear_rx_fifo; + } + + mutex_unlock(&dsim->lock); + + return rxsize; + +clear_rx_fifo: + i = 0; + while (1) { + rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim); + if ((rcv_pkt == MIPI_RX_FIFO_READ_DONE) + || (i > MIPI_MAX_RX_FIFO)) + break; + dev_dbg(dsim->dev, + "mipi dsi clear rx fifo : %08x\n", rcv_pkt); + i++; + } + dev_info(dsim->dev, + "mipi dsi rx done count : %d, rcv_pkt : %08x\n", i, rcv_pkt); + + mutex_unlock(&dsim->lock); + + return 0; +} + +static int exynos_mipi_dsi_pll_on(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + int sw_timeout; + + if (enable) { + sw_timeout = 1000; + + exynos_mipi_dsi_enable_pll(dsim, 1); + while (1) { + sw_timeout--; + if (exynos_mipi_dsi_is_pll_stable(dsim)) + return 0; + if (sw_timeout == 0) + return -EINVAL; + } + } else + exynos_mipi_dsi_enable_pll(dsim, 0); + + return 0; +} + +static unsigned long exynos_mipi_dsi_change_pll(struct mipi_dsim_device *dsim, + unsigned int pre_divider, unsigned int main_divider, + unsigned int scaler) +{ + unsigned long dfin_pll, dfvco, dpll_out; + unsigned int i, freq_band = 0xf; + + dfin_pll = (FIN_HZ / pre_divider); + + /****************************************************** + * Serial Clock(=ByteClk X 8) FreqBand[3:0] * + ****************************************************** + * ~ 99.99 MHz 0000 + * 100 ~ 119.99 MHz 0001 + * 120 ~ 159.99 MHz 0010 + * 160 ~ 199.99 MHz 0011 + * 200 ~ 239.99 MHz 0100 + * 140 ~ 319.99 MHz 0101 + * 320 ~ 389.99 MHz 0110 + * 390 ~ 449.99 MHz 0111 + * 450 ~ 509.99 MHz 1000 + * 510 ~ 559.99 MHz 1001 + * 560 ~ 639.99 MHz 1010 + * 640 ~ 689.99 MHz 1011 + * 690 ~ 769.99 MHz 1100 + * 770 ~ 869.99 MHz 1101 + * 870 ~ 949.99 MHz 1110 + * 950 ~ 1000 MHz 1111 + ******************************************************/ + if (dfin_pll < DFIN_PLL_MIN_HZ || dfin_pll > DFIN_PLL_MAX_HZ) { + dev_warn(dsim->dev, "fin_pll range should be 6MHz ~ 12MHz\n"); + exynos_mipi_dsi_enable_afc(dsim, 0, 0); + } else { + if (dfin_pll < 7 * MHZ) + exynos_mipi_dsi_enable_afc(dsim, 1, 0x1); + else if (dfin_pll < 8 * MHZ) + exynos_mipi_dsi_enable_afc(dsim, 1, 0x0); + else if (dfin_pll < 9 * MHZ) + exynos_mipi_dsi_enable_afc(dsim, 1, 0x3); + else if (dfin_pll < 10 * MHZ) + exynos_mipi_dsi_enable_afc(dsim, 1, 0x2); + else if (dfin_pll < 11 * MHZ) + exynos_mipi_dsi_enable_afc(dsim, 1, 0x5); + else + exynos_mipi_dsi_enable_afc(dsim, 1, 0x4); + } + + dfvco = dfin_pll * main_divider; + dev_dbg(dsim->dev, "dfvco = %lu, dfin_pll = %lu, main_divider = %d\n", + dfvco, dfin_pll, main_divider); + if (dfvco < DFVCO_MIN_HZ || dfvco > DFVCO_MAX_HZ) + dev_warn(dsim->dev, "fvco range should be 500MHz ~ 1000MHz\n"); + + dpll_out = dfvco / (1 << scaler); + dev_dbg(dsim->dev, "dpll_out = %lu, dfvco = %lu, scaler = %d\n", + dpll_out, dfvco, scaler); + + for (i = 0; i < ARRAY_SIZE(dpll_table); i++) { + if (dpll_out < dpll_table[i] * MHZ) { + freq_band = i; + break; + } + } + + dev_dbg(dsim->dev, "freq_band = %d\n", freq_band); + + exynos_mipi_dsi_pll_freq(dsim, pre_divider, main_divider, scaler); + + exynos_mipi_dsi_hs_zero_ctrl(dsim, 0); + exynos_mipi_dsi_prep_ctrl(dsim, 0); + + /* Freq Band */ + exynos_mipi_dsi_pll_freq_band(dsim, freq_band); + + /* Stable time */ + exynos_mipi_dsi_pll_stable_time(dsim, dsim->dsim_config->pll_stable_time); + + /* Enable PLL */ + dev_dbg(dsim->dev, "FOUT of mipi dphy pll is %luMHz\n", + (dpll_out / MHZ)); + + return dpll_out; +} + +static int exynos_mipi_dsi_set_clock(struct mipi_dsim_device *dsim, + unsigned int byte_clk_sel, unsigned int enable) +{ + unsigned int esc_div; + unsigned long esc_clk_error_rate; + unsigned long hs_clk = 0, byte_clk = 0, escape_clk = 0; + + if (enable) { + dsim->e_clk_src = byte_clk_sel; + + /* Escape mode clock and byte clock source */ + exynos_mipi_dsi_set_byte_clock_src(dsim, byte_clk_sel); + + /* DPHY, DSIM Link : D-PHY clock out */ + if (byte_clk_sel == DSIM_PLL_OUT_DIV8) { + hs_clk = exynos_mipi_dsi_change_pll(dsim, + dsim->dsim_config->p, dsim->dsim_config->m, + dsim->dsim_config->s); + if (hs_clk == 0) { + dev_err(dsim->dev, + "failed to get hs clock.\n"); + return -EINVAL; + } + + byte_clk = hs_clk / 8; + exynos_mipi_dsi_enable_pll_bypass(dsim, 0); + exynos_mipi_dsi_pll_on(dsim, 1); + /* DPHY : D-PHY clock out, DSIM link : external clock out */ + } else if (byte_clk_sel == DSIM_EXT_CLK_DIV8) { + dev_warn(dsim->dev, "this project is not support\n"); + dev_warn(dsim->dev, + "external clock source for MIPI DSIM.\n"); + } else if (byte_clk_sel == DSIM_EXT_CLK_BYPASS) { + dev_warn(dsim->dev, "this project is not support\n"); + dev_warn(dsim->dev, + "external clock source for MIPI DSIM\n"); + } + + /* escape clock divider */ + esc_div = byte_clk / (dsim->dsim_config->esc_clk); + dev_dbg(dsim->dev, + "esc_div = %d, byte_clk = %lu, esc_clk = %lu\n", + esc_div, byte_clk, dsim->dsim_config->esc_clk); + if ((byte_clk / esc_div) >= (20 * MHZ) || + (byte_clk / esc_div) > + dsim->dsim_config->esc_clk) + esc_div += 1; + + escape_clk = byte_clk / esc_div; + dev_dbg(dsim->dev, + "escape_clk = %lu, byte_clk = %lu, esc_div = %d\n", + escape_clk, byte_clk, esc_div); + + /* enable escape clock. */ + exynos_mipi_dsi_enable_byte_clock(dsim, 1); + + /* enable byte clk and escape clock */ + exynos_mipi_dsi_set_esc_clk_prs(dsim, 1, esc_div); + /* escape clock on lane */ + exynos_mipi_dsi_enable_esc_clk_on_lane(dsim, + (DSIM_LANE_CLOCK | dsim->data_lane), 1); + + dev_dbg(dsim->dev, "byte clock is %luMHz\n", + (byte_clk / MHZ)); + dev_dbg(dsim->dev, "escape clock that user's need is %lu\n", + (dsim->dsim_config->esc_clk / MHZ)); + dev_dbg(dsim->dev, "escape clock divider is %x\n", esc_div); + dev_dbg(dsim->dev, "escape clock is %luMHz\n", + ((byte_clk / esc_div) / MHZ)); + + if ((byte_clk / esc_div) > escape_clk) { + esc_clk_error_rate = escape_clk / + (byte_clk / esc_div); + dev_warn(dsim->dev, "error rate is %lu over.\n", + (esc_clk_error_rate / 100)); + } else if ((byte_clk / esc_div) < (escape_clk)) { + esc_clk_error_rate = (byte_clk / esc_div) / + escape_clk; + dev_warn(dsim->dev, "error rate is %lu under.\n", + (esc_clk_error_rate / 100)); + } + } else { + exynos_mipi_dsi_enable_esc_clk_on_lane(dsim, + (DSIM_LANE_CLOCK | dsim->data_lane), 0); + exynos_mipi_dsi_set_esc_clk_prs(dsim, 0, 0); + + /* disable escape clock. */ + exynos_mipi_dsi_enable_byte_clock(dsim, 0); + + if (byte_clk_sel == DSIM_PLL_OUT_DIV8) + exynos_mipi_dsi_pll_on(dsim, 0); + } + + return 0; +} + +int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim) +{ + dsim->state = DSIM_STATE_INIT; + + switch (dsim->dsim_config->e_no_data_lane) { + case DSIM_DATA_LANE_1: + dsim->data_lane = DSIM_LANE_DATA0; + break; + case DSIM_DATA_LANE_2: + dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1; + break; + case DSIM_DATA_LANE_3: + dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 | + DSIM_LANE_DATA2; + break; + case DSIM_DATA_LANE_4: + dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 | + DSIM_LANE_DATA2 | DSIM_LANE_DATA3; + break; + default: + dev_info(dsim->dev, "data lane is invalid.\n"); + return -EINVAL; + }; + + exynos_mipi_dsi_sw_reset(dsim); + exynos_mipi_dsi_func_reset(dsim); + + exynos_mipi_dsi_dp_dn_swap(dsim, 0); + + return 0; +} + +void exynos_mipi_dsi_init_interrupt(struct mipi_dsim_device *dsim) +{ + unsigned int src = 0; + + src = (INTSRC_SFR_FIFO_EMPTY | INTSRC_RX_DATA_DONE); + exynos_mipi_dsi_set_interrupt(dsim, src, 1); + + src = 0; + src = ~(INTMSK_RX_DONE | INTMSK_FIFO_EMPTY); + exynos_mipi_dsi_set_interrupt_mask(dsim, src, 1); +} + +int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + /* enable only frame done interrupt */ + exynos_mipi_dsi_set_interrupt_mask(dsim, INTMSK_FRAME_DONE, enable); + + return 0; +} + +void exynos_mipi_dsi_stand_by(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + + /* consider Main display and Sub display. */ + + exynos_mipi_dsi_set_main_stand_by(dsim, enable); +} + +int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim, + struct mipi_dsim_config *dsim_config) +{ + struct mipi_dsim_platform_data *dsim_pd; + struct fb_videomode *timing; + + dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd; + timing = (struct fb_videomode *)dsim_pd->lcd_panel_info; + + /* in case of VIDEO MODE (RGB INTERFACE), it sets polarities. */ + if (dsim_config->e_interface == (u32) DSIM_VIDEO) { + if (dsim_config->auto_vertical_cnt == 0) { + exynos_mipi_dsi_set_main_disp_vporch(dsim, + dsim_config->cmd_allow, + timing->upper_margin, + timing->lower_margin); + exynos_mipi_dsi_set_main_disp_hporch(dsim, + timing->left_margin, + timing->right_margin); + exynos_mipi_dsi_set_main_disp_sync_area(dsim, + timing->vsync_len, + timing->hsync_len); + } + } + + exynos_mipi_dsi_set_main_disp_resol(dsim, timing->xres, + timing->yres); + + exynos_mipi_dsi_display_config(dsim, dsim_config); + + dev_info(dsim->dev, "lcd panel ==> width = %d, height = %d\n", + timing->xres, timing->yres); + + return 0; +} + +int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim) +{ + unsigned int time_out = 100; + + switch (dsim->state) { + case DSIM_STATE_INIT: + exynos_mipi_dsi_init_fifo_pointer(dsim, 0x1f); + + /* dsi configuration */ + exynos_mipi_dsi_init_config(dsim); + exynos_mipi_dsi_enable_lane(dsim, DSIM_LANE_CLOCK, 1); + exynos_mipi_dsi_enable_lane(dsim, dsim->data_lane, 1); + + /* set clock configuration */ + exynos_mipi_dsi_set_clock(dsim, dsim->dsim_config->e_byte_clk, 1); + + /* check clock and data lane state are stop state */ + while (!(exynos_mipi_dsi_is_lane_state(dsim))) { + time_out--; + if (time_out == 0) { + dev_err(dsim->dev, + "DSI Master is not stop state.\n"); + dev_err(dsim->dev, + "Check initialization process\n"); + + return -EINVAL; + } + } + if (time_out != 0) { + dev_info(dsim->dev, + "DSI Master driver has been completed.\n"); + dev_info(dsim->dev, "DSI Master state is stop state\n"); + } + + dsim->state = DSIM_STATE_STOP; + + /* BTA sequence counters */ + exynos_mipi_dsi_set_stop_state_counter(dsim, + dsim->dsim_config->stop_holding_cnt); + exynos_mipi_dsi_set_bta_timeout(dsim, + dsim->dsim_config->bta_timeout); + exynos_mipi_dsi_set_lpdr_timeout(dsim, + dsim->dsim_config->rx_timeout); + + return 0; + default: + dev_info(dsim->dev, "DSI Master is already init.\n"); + return 0; + } + + return 0; +} + +int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim) +{ + if (dsim->state != DSIM_STATE_STOP) { + dev_warn(dsim->dev, "DSIM is not in stop state.\n"); + return 0; + } + + if (dsim->e_clk_src == DSIM_EXT_CLK_BYPASS) { + dev_warn(dsim->dev, "clock source is external bypass.\n"); + return 0; + } + + dsim->state = DSIM_STATE_HSCLKEN; + + /* set LCDC and CPU transfer mode to HS. */ + exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0); + exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0); + exynos_mipi_dsi_enable_hs_clock(dsim, 1); + + return 0; +} + +int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim, + unsigned int mode) +{ + if (mode) { + if (dsim->state != DSIM_STATE_HSCLKEN) { + dev_err(dsim->dev, "HS Clock lane is not enabled.\n"); + return -EINVAL; + } + + exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0); + } else { + if (dsim->state == DSIM_STATE_INIT || dsim->state == + DSIM_STATE_ULPS) { + dev_err(dsim->dev, + "DSI Master is not STOP or HSDT state.\n"); + return -EINVAL; + } + + exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0); + } + + return 0; +} + +int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim) +{ + return _exynos_mipi_dsi_get_frame_done_status(dsim); +} + +int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim) +{ + _exynos_mipi_dsi_clear_frame_done(dsim); + + return 0; +} + +int exynos_mipi_dsi_fifo_clear(struct mipi_dsim_device *dsim, + unsigned int val) +{ + int try = TRY_FIFO_CLEAR; + + exynos_mipi_dsi_sw_reset_release(dsim); + exynos_mipi_dsi_func_reset(dsim); + + do { + if (exynos_mipi_dsi_get_sw_reset_release(dsim)) { + exynos_mipi_dsi_init_interrupt(dsim); + dev_dbg(dsim->dev, "reset release done.\n"); + return 0; + } + } while (--try); + + dev_err(dsim->dev, "failed to clear dsim fifo.\n"); + return -EAGAIN; +} + +MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>"); +MODULE_DESCRIPTION("Samusung SoC MIPI-DSI common driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/exynos/exynos_mipi_dsi_common.h b/drivers/video/exynos/exynos_mipi_dsi_common.h new file mode 100644 index 000000000000..412552274df3 --- /dev/null +++ b/drivers/video/exynos/exynos_mipi_dsi_common.h @@ -0,0 +1,46 @@ +/* linux/drivers/video/exynos_mipi_dsi_common.h + * + * Header file for Samsung SoC MIPI-DSI common driver. + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd + * + * InKi Dae <inki.dae@samsung.com> + * Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef _EXYNOS_MIPI_DSI_COMMON_H +#define _EXYNOS_MIPI_DSI_COMMON_H + +static DECLARE_COMPLETION(dsim_rd_comp); +static DECLARE_COMPLETION(dsim_wr_comp); + +int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id, + const unsigned char *data0, unsigned int data_size); +int exynos_mipi_dsi_rd_data(struct mipi_dsim_device *dsim, unsigned int data_id, + unsigned int data0, unsigned int req_size, u8 *rx_buf); +irqreturn_t exynos_mipi_dsi_interrupt_handler(int irq, void *dev_id); +void exynos_mipi_dsi_init_interrupt(struct mipi_dsim_device *dsim); +int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_stand_by(struct mipi_dsim_device *dsim, + unsigned int enable); +int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim, + struct mipi_dsim_config *dsim_info); +int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim); +int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim); +int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim, + unsigned int mode); +int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim, + unsigned int enable); +int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim); +int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim); + +extern struct fb_info *registered_fb[FB_MAX] __read_mostly; + +int exynos_mipi_dsi_fifo_clear(struct mipi_dsim_device *dsim, + unsigned int val); + +#endif /* _EXYNOS_MIPI_DSI_COMMON_H */ diff --git a/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c b/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c new file mode 100644 index 000000000000..0ef38ce72af6 --- /dev/null +++ b/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c @@ -0,0 +1,618 @@ +/* linux/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c + * + * Samsung SoC MIPI-DSI lowlevel driver. + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd + * + * InKi Dae, <inki.dae@samsung.com> + * Donghwa Lee, <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/wait.h> +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/ctype.h> +#include <linux/io.h> + +#include <video/exynos_mipi_dsim.h> + +#include <mach/map.h> + +#include "exynos_mipi_dsi_regs.h" + +void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim) +{ + unsigned int reg; + + reg = readl(dsim->reg_base + EXYNOS_DSIM_SWRST); + + reg |= DSIM_FUNCRST; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_SWRST); +} + +void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim) +{ + unsigned int reg; + + reg = readl(dsim->reg_base + EXYNOS_DSIM_SWRST); + + reg |= DSIM_SWRST; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_SWRST); +} + +void exynos_mipi_dsi_sw_reset_release(struct mipi_dsim_device *dsim) +{ + unsigned int reg; + + reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); + + reg |= INTSRC_SW_RST_RELEASE; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC); +} + +int exynos_mipi_dsi_get_sw_reset_release(struct mipi_dsim_device *dsim) +{ + return (readl(dsim->reg_base + EXYNOS_DSIM_INTSRC)) & + INTSRC_SW_RST_RELEASE; +} + +unsigned int exynos_mipi_dsi_read_interrupt_mask(struct mipi_dsim_device *dsim) +{ + unsigned int reg; + + reg = readl(dsim->reg_base + EXYNOS_DSIM_INTMSK); + + return reg; +} + +void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim, + unsigned int mode, unsigned int mask) +{ + unsigned int reg = 0; + + if (mask) + reg |= mode; + else + reg &= ~mode; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_INTMSK); +} + +void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim, + unsigned int cfg) +{ + unsigned int reg; + + reg = readl(dsim->reg_base + EXYNOS_DSIM_FIFOCTRL); + + writel(reg & ~(cfg), dsim->reg_base + EXYNOS_DSIM_FIFOCTRL); + mdelay(10); + reg |= cfg; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_FIFOCTRL); +} + +/* + * this function set PLL P, M and S value in D-PHY + */ +void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim, + unsigned int value) +{ + writel(DSIM_AFC_CTL(value), dsim->reg_base + EXYNOS_DSIM_PHYACCHR); +} + +void exynos_mipi_dsi_set_main_stand_by(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + unsigned int reg; + + reg = readl(dsim->reg_base + EXYNOS_DSIM_MDRESOL); + + reg &= ~DSIM_MAIN_STAND_BY; + + if (enable) + reg |= DSIM_MAIN_STAND_BY; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL); +} + +void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim, + unsigned int width_resol, unsigned int height_resol) +{ + unsigned int reg; + + /* standby should be set after configuration so set to not ready*/ + reg = (readl(dsim->reg_base + EXYNOS_DSIM_MDRESOL)) & + ~(DSIM_MAIN_STAND_BY); + writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL); + + reg &= ~((0x7ff << 16) | (0x7ff << 0)); + reg |= DSIM_MAIN_VRESOL(height_resol) | DSIM_MAIN_HRESOL(width_resol); + + reg |= DSIM_MAIN_STAND_BY; + writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL); +} + +void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim, + unsigned int cmd_allow, unsigned int vfront, unsigned int vback) +{ + unsigned int reg; + + reg = (readl(dsim->reg_base + EXYNOS_DSIM_MVPORCH)) & + ~((DSIM_CMD_ALLOW_MASK) | (DSIM_STABLE_VFP_MASK) | + (DSIM_MAIN_VBP_MASK)); + + reg |= (DSIM_CMD_ALLOW_SHIFT(cmd_allow & 0xf) | + DSIM_STABLE_VFP_SHIFT(vfront & 0x7ff) | + DSIM_MAIN_VBP_SHIFT(vback & 0x7ff)); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_MVPORCH); +} + +void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim, + unsigned int front, unsigned int back) +{ + unsigned int reg; + + reg = (readl(dsim->reg_base + EXYNOS_DSIM_MHPORCH)) & + ~((DSIM_MAIN_HFP_MASK) | (DSIM_MAIN_HBP_MASK)); + + reg |= DSIM_MAIN_HFP_SHIFT(front) | DSIM_MAIN_HBP_SHIFT(back); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_MHPORCH); +} + +void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim, + unsigned int vert, unsigned int hori) +{ + unsigned int reg; + + reg = (readl(dsim->reg_base + EXYNOS_DSIM_MSYNC)) & + ~((DSIM_MAIN_VSA_MASK) | (DSIM_MAIN_HSA_MASK)); + + reg |= (DSIM_MAIN_VSA_SHIFT(vert & 0x3ff) | + DSIM_MAIN_HSA_SHIFT(hori)); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_MSYNC); +} + +void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim, + unsigned int vert, unsigned int hori) +{ + unsigned int reg; + + reg = (readl(dsim->reg_base + EXYNOS_DSIM_SDRESOL)) & + ~(DSIM_SUB_STANDY_MASK); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL); + + reg &= ~(DSIM_SUB_VRESOL_MASK) | ~(DSIM_SUB_HRESOL_MASK); + reg |= (DSIM_SUB_VRESOL_SHIFT(vert & 0x7ff) | + DSIM_SUB_HRESOL_SHIFT(hori & 0x7ff)); + writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL); + + reg |= DSIM_SUB_STANDY_SHIFT(1); + writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL); +} + +void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim) +{ + struct mipi_dsim_config *dsim_config = dsim->dsim_config; + + unsigned int cfg = (readl(dsim->reg_base + EXYNOS_DSIM_CONFIG)) & + ~((1 << 28) | (0x1f << 20) | (0x3 << 5)); + + cfg = ((DSIM_AUTO_FLUSH(dsim_config->auto_flush)) | + (DSIM_EOT_DISABLE(dsim_config->eot_disable)) | + (DSIM_AUTO_MODE_SHIFT(dsim_config->auto_vertical_cnt)) | + (DSIM_HSE_MODE_SHIFT(dsim_config->hse)) | + (DSIM_HFP_MODE_SHIFT(dsim_config->hfp)) | + (DSIM_HBP_MODE_SHIFT(dsim_config->hbp)) | + (DSIM_HSA_MODE_SHIFT(dsim_config->hsa)) | + (DSIM_NUM_OF_DATALANE_SHIFT(dsim_config->e_no_data_lane))); + + writel(cfg, dsim->reg_base + EXYNOS_DSIM_CONFIG); +} + +void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim, + struct mipi_dsim_config *dsim_config) +{ + u32 reg = (readl(dsim->reg_base + EXYNOS_DSIM_CONFIG)) & + ~((0x3 << 26) | (1 << 25) | (0x3 << 18) | (0x7 << 12) | + (0x3 << 16) | (0x7 << 8)); + + if (dsim_config->e_interface == DSIM_VIDEO) + reg |= (1 << 25); + else if (dsim_config->e_interface == DSIM_COMMAND) + reg &= ~(1 << 25); + else { + dev_err(dsim->dev, "unknown lcd type.\n"); + return; + } + + /* main lcd */ + reg |= ((u8) (dsim_config->e_burst_mode) & 0x3) << 26 | + ((u8) (dsim_config->e_virtual_ch) & 0x3) << 18 | + ((u8) (dsim_config->e_pixel_format) & 0x7) << 12; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_CONFIG); +} + +void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim, unsigned int lane, + unsigned int enable) +{ + unsigned int reg; + + reg = readl(dsim->reg_base + EXYNOS_DSIM_CONFIG); + + if (enable) + reg |= DSIM_LANE_ENx(lane); + else + reg &= ~DSIM_LANE_ENx(lane); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_CONFIG); +} + + +void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim, + unsigned int count) +{ + unsigned int cfg; + + /* get the data lane number. */ + cfg = DSIM_NUM_OF_DATALANE_SHIFT(count); + + writel(cfg, dsim->reg_base + EXYNOS_DSIM_CONFIG); +} + +void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim, unsigned int enable, + unsigned int afc_code) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PHYACCHR); + + if (enable) { + reg |= (1 << 14); + reg &= ~(0x7 << 5); + reg |= (afc_code & 0x7) << 5; + } else + reg &= ~(1 << 14); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PHYACCHR); +} + +void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & + ~(DSIM_PLL_BYPASS_SHIFT(0x1)); + + reg |= DSIM_PLL_BYPASS_SHIFT(enable); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); +} + +void exynos_mipi_dsi_set_pll_pms(struct mipi_dsim_device *dsim, unsigned int p, + unsigned int m, unsigned int s) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL); + + reg |= ((p & 0x3f) << 13) | ((m & 0x1ff) << 4) | ((s & 0x7) << 1); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); +} + +void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim, + unsigned int freq_band) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & + ~(DSIM_FREQ_BAND_SHIFT(0x1f)); + + reg |= DSIM_FREQ_BAND_SHIFT(freq_band & 0x1f); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); +} + +void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim, + unsigned int pre_divider, unsigned int main_divider, + unsigned int scaler) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & + ~(0x7ffff << 1); + + reg |= (pre_divider & 0x3f) << 13 | (main_divider & 0x1ff) << 4 | + (scaler & 0x7) << 1; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); +} + +void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim, + unsigned int lock_time) +{ + writel(lock_time, dsim->reg_base + EXYNOS_DSIM_PLLTMR); +} + +void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim, unsigned int enable) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & + ~(DSIM_PLL_EN_SHIFT(0x1)); + + reg |= DSIM_PLL_EN_SHIFT(enable & 0x1); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); +} + +void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim, + unsigned int src) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & + ~(DSIM_BYTE_CLK_SRC_SHIFT(0x3)); + + reg |= (DSIM_BYTE_CLK_SRC_SHIFT(src)); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); +} + +void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & + ~(DSIM_BYTE_CLKEN_SHIFT(0x1)); + + reg |= DSIM_BYTE_CLKEN_SHIFT(enable); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); +} + +void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim, + unsigned int enable, unsigned int prs_val) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & + ~(DSIM_ESC_CLKEN_SHIFT(0x1) | 0xffff); + + reg |= DSIM_ESC_CLKEN_SHIFT(enable); + if (enable) + reg |= prs_val; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); +} + +void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim, + unsigned int lane_sel, unsigned int enable) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL); + + if (enable) + reg |= DSIM_LANE_ESC_CLKEN(lane_sel); + else + + reg &= ~DSIM_LANE_ESC_CLKEN(lane_sel); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); +} + +void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE)) & + ~(DSIM_FORCE_STOP_STATE_SHIFT(0x1)); + + reg |= (DSIM_FORCE_STOP_STATE_SHIFT(enable & 0x1)); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE); +} + +unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_STATUS); + + /** + * check clock and data lane states. + * if MIPI-DSI controller was enabled at bootloader then + * TX_READY_HS_CLK is enabled otherwise STOP_STATE_CLK. + * so it should be checked for two case. + */ + if ((reg & DSIM_STOP_STATE_DAT(0xf)) && + ((reg & DSIM_STOP_STATE_CLK) || + (reg & DSIM_TX_READY_HS_CLK))) + return 1; + + return 0; +} + +void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim, + unsigned int cnt_val) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE)) & + ~(DSIM_STOP_STATE_CNT_SHIFT(0x7ff)); + + reg |= (DSIM_STOP_STATE_CNT_SHIFT(cnt_val & 0x7ff)); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE); +} + +void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim, + unsigned int timeout) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_TIMEOUT)) & + ~(DSIM_BTA_TOUT_SHIFT(0xff)); + + reg |= (DSIM_BTA_TOUT_SHIFT(timeout)); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_TIMEOUT); +} + +void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim, + unsigned int timeout) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_TIMEOUT)) & + ~(DSIM_LPDR_TOUT_SHIFT(0xffff)); + + reg |= (DSIM_LPDR_TOUT_SHIFT(timeout)); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_TIMEOUT); +} + +void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim, + unsigned int lp) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE); + + reg &= ~DSIM_CMD_LPDT_LP; + + if (lp) + reg |= DSIM_CMD_LPDT_LP; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE); +} + +void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim, + unsigned int lp) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE); + + reg &= ~DSIM_TX_LPDT_LP; + + if (lp) + reg |= DSIM_TX_LPDT_LP; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE); +} + +void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & + ~(DSIM_TX_REQUEST_HSCLK_SHIFT(0x1)); + + reg |= DSIM_TX_REQUEST_HSCLK_SHIFT(enable); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); +} + +void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim, + unsigned int swap_en) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PHYACCHR1); + + reg &= ~(0x3 << 0); + reg |= (swap_en & 0x3) << 0; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PHYACCHR1); +} + +void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim, + unsigned int hs_zero) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & + ~(0xf << 28); + + reg |= ((hs_zero & 0xf) << 28); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); +} + +void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, unsigned int prep) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & + ~(0x7 << 20); + + reg |= ((prep & 0x7) << 20); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); +} + +unsigned int exynos_mipi_dsi_read_interrupt(struct mipi_dsim_device *dsim) +{ + return readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); +} + +void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim, + unsigned int src) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); + + reg |= src; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC); +} + +void exynos_mipi_dsi_set_interrupt(struct mipi_dsim_device *dsim, + unsigned int src, unsigned int enable) +{ + unsigned int reg = 0; + + if (enable) + reg |= src; + else + reg &= ~src; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC); +} + +unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim) +{ + unsigned int reg; + + reg = readl(dsim->reg_base + EXYNOS_DSIM_STATUS); + + return reg & (1 << 31) ? 1 : 0; +} + +unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim) +{ + return readl(dsim->reg_base + EXYNOS_DSIM_FIFOCTRL) & ~(0x1f); +} + +void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim, + unsigned int di, unsigned int data0, unsigned int data1) +{ + unsigned int reg = (data1 << 16) | (data0 << 8) | ((di & 0x3f) << 0); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PKTHDR); +} + +void exynos_mipi_dsi_rd_tx_header(struct mipi_dsim_device *dsim, + unsigned int di, unsigned int data0) +{ + unsigned int reg = (data0 << 8) | (di << 0); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PKTHDR); +} + +unsigned int exynos_mipi_dsi_rd_rx_fifo(struct mipi_dsim_device *dsim) +{ + return readl(dsim->reg_base + EXYNOS_DSIM_RXFIFO); +} + +unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); + + return (reg & INTSRC_FRAME_DONE) ? 1 : 0; +} + +void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); + + writel(reg | INTSRC_FRAME_DONE, dsim->reg_base + + EXYNOS_DSIM_INTSRC); +} + +void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim, + unsigned int tx_data) +{ + writel(tx_data, dsim->reg_base + EXYNOS_DSIM_PAYLOAD); +} diff --git a/drivers/video/exynos/exynos_mipi_dsi_lowlevel.h b/drivers/video/exynos/exynos_mipi_dsi_lowlevel.h new file mode 100644 index 000000000000..85460701c7ea --- /dev/null +++ b/drivers/video/exynos/exynos_mipi_dsi_lowlevel.h @@ -0,0 +1,112 @@ +/* linux/drivers/video/exynos/exynos_mipi_dsi_lowlevel.h + * + * Header file for Samsung SoC MIPI-DSI lowlevel driver. + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd + * + * InKi Dae <inki.dae@samsung.com> + * Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef _EXYNOS_MIPI_DSI_LOWLEVEL_H +#define _EXYNOS_MIPI_DSI_LOWLEVEL_H + +void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_sw_reset_release(struct mipi_dsim_device *dsim); +int exynos_mipi_dsi_get_sw_reset_release(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim, + unsigned int mode, unsigned int mask); +void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim, + unsigned int count); +void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim, + unsigned int cfg); +void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim, + unsigned int value); +void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim, + unsigned int value); +void exynos_mipi_dsi_set_main_stand_by(struct mipi_dsim_device *dsim, + unsigned int enable); +void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim, + unsigned int width_resol, unsigned int height_resol); +void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim, + unsigned int cmd_allow, unsigned int vfront, unsigned int vback); +void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim, + unsigned int front, unsigned int back); +void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim, + unsigned int vert, unsigned int hori); +void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim, + unsigned int vert, unsigned int hori); +void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim, + struct mipi_dsim_config *dsim_config); +void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim, + unsigned int count); +void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim, unsigned int lane, + unsigned int enable); +void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim, unsigned int enable, + unsigned int afc_code); +void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim, + unsigned int enable); +void exynos_mipi_dsi_set_pll_pms(struct mipi_dsim_device *dsim, unsigned int p, + unsigned int m, unsigned int s); +void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim, + unsigned int freq_band); +void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim, + unsigned int pre_divider, unsigned int main_divider, + unsigned int scaler); +void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim, + unsigned int lock_time); +void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim, + unsigned int enable); +void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim, + unsigned int src); +void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim, + unsigned int enable); +void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim, + unsigned int enable, unsigned int prs_val); +void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim, + unsigned int lane_sel, unsigned int enable); +void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim, + unsigned int enable); +unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim, + unsigned int cnt_val); +void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim, + unsigned int timeout); +void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim, + unsigned int timeout); +void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim, + unsigned int lp); +void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim, + unsigned int lp); +void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim, + unsigned int enable); +void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim, + unsigned int swap_en); +void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim, + unsigned int hs_zero); +void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, unsigned int prep); +unsigned int exynos_mipi_dsi_read_interrupt(struct mipi_dsim_device *dsim); +unsigned int exynos_mipi_dsi_read_interrupt_mask(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim, + unsigned int src); +void exynos_mipi_dsi_set_interrupt(struct mipi_dsim_device *dsim, + unsigned int src, unsigned int enable); +unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim); +unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim); +unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim); +void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim, unsigned int di, + unsigned int data0, unsigned int data1); +void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim, + unsigned int tx_data); +void exynos_mipi_dsi_rd_tx_header(struct mipi_dsim_device *dsim, + unsigned int data0, unsigned int data1); +unsigned int exynos_mipi_dsi_rd_rx_fifo(struct mipi_dsim_device *dsim); + +#endif /* _EXYNOS_MIPI_DSI_LOWLEVEL_H */ diff --git a/drivers/video/exynos/exynos_mipi_dsi_regs.h b/drivers/video/exynos/exynos_mipi_dsi_regs.h new file mode 100644 index 000000000000..4227106d3fd0 --- /dev/null +++ b/drivers/video/exynos/exynos_mipi_dsi_regs.h @@ -0,0 +1,149 @@ +/* linux/driver/video/exynos/exynos_mipi_dsi_regs.h + * + * Register definition file for Samsung MIPI-DSIM driver + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd + * + * InKi Dae <inki.dae@samsung.com> + * Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef _EXYNOS_MIPI_DSI_REGS_H +#define _EXYNOS_MIPI_DSI_REGS_H + +#define EXYNOS_DSIM_STATUS 0x0 /* Status register */ +#define EXYNOS_DSIM_SWRST 0x4 /* Software reset register */ +#define EXYNOS_DSIM_CLKCTRL 0x8 /* Clock control register */ +#define EXYNOS_DSIM_TIMEOUT 0xc /* Time out register */ +#define EXYNOS_DSIM_CONFIG 0x10 /* Configuration register */ +#define EXYNOS_DSIM_ESCMODE 0x14 /* Escape mode register */ + +/* Main display image resolution register */ +#define EXYNOS_DSIM_MDRESOL 0x18 +#define EXYNOS_DSIM_MVPORCH 0x1c /* Main display Vporch register */ +#define EXYNOS_DSIM_MHPORCH 0x20 /* Main display Hporch register */ +#define EXYNOS_DSIM_MSYNC 0x24 /* Main display sync area register */ + +/* Sub display image resolution register */ +#define EXYNOS_DSIM_SDRESOL 0x28 +#define EXYNOS_DSIM_INTSRC 0x2c /* Interrupt source register */ +#define EXYNOS_DSIM_INTMSK 0x30 /* Interrupt mask register */ +#define EXYNOS_DSIM_PKTHDR 0x34 /* Packet Header FIFO register */ +#define EXYNOS_DSIM_PAYLOAD 0x38 /* Payload FIFO register */ +#define EXYNOS_DSIM_RXFIFO 0x3c /* Read FIFO register */ +#define EXYNOS_DSIM_FIFOTHLD 0x40 /* FIFO threshold level register */ +#define EXYNOS_DSIM_FIFOCTRL 0x44 /* FIFO status and control register */ + +/* FIFO memory AC characteristic register */ +#define EXYNOS_DSIM_PLLCTRL 0x4c /* PLL control register */ +#define EXYNOS_DSIM_PLLTMR 0x50 /* PLL timer register */ +#define EXYNOS_DSIM_PHYACCHR 0x54 /* D-PHY AC characteristic register */ +#define EXYNOS_DSIM_PHYACCHR1 0x58 /* D-PHY AC characteristic register1 */ + +/* DSIM_STATUS */ +#define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0) +#define DSIM_STOP_STATE_CLK (1 << 8) +#define DSIM_TX_READY_HS_CLK (1 << 10) + +/* DSIM_SWRST */ +#define DSIM_FUNCRST (1 << 16) +#define DSIM_SWRST (1 << 0) + +/* EXYNOS_DSIM_TIMEOUT */ +#define DSIM_LPDR_TOUT_SHIFT(x) ((x) << 0) +#define DSIM_BTA_TOUT_SHIFT(x) ((x) << 16) + +/* EXYNOS_DSIM_CLKCTRL */ +#define DSIM_LANE_ESC_CLKEN(x) (((x) & 0x1f) << 19) +#define DSIM_BYTE_CLKEN_SHIFT(x) ((x) << 24) +#define DSIM_BYTE_CLK_SRC_SHIFT(x) ((x) << 25) +#define DSIM_PLL_BYPASS_SHIFT(x) ((x) << 27) +#define DSIM_ESC_CLKEN_SHIFT(x) ((x) << 28) +#define DSIM_TX_REQUEST_HSCLK_SHIFT(x) ((x) << 31) + +/* EXYNOS_DSIM_CONFIG */ +#define DSIM_LANE_ENx(x) (((x) & 0x1f) << 0) +#define DSIM_NUM_OF_DATALANE_SHIFT(x) ((x) << 5) +#define DSIM_HSA_MODE_SHIFT(x) ((x) << 20) +#define DSIM_HBP_MODE_SHIFT(x) ((x) << 21) +#define DSIM_HFP_MODE_SHIFT(x) ((x) << 22) +#define DSIM_HSE_MODE_SHIFT(x) ((x) << 23) +#define DSIM_AUTO_MODE_SHIFT(x) ((x) << 24) +#define DSIM_EOT_DISABLE(x) ((x) << 28) +#define DSIM_AUTO_FLUSH(x) ((x) << 29) + +#define DSIM_NUM_OF_DATA_LANE(x) ((x) << DSIM_NUM_OF_DATALANE_SHIFT) + +/* EXYNOS_DSIM_ESCMODE */ +#define DSIM_TX_LPDT_LP (1 << 6) +#define DSIM_CMD_LPDT_LP (1 << 7) +#define DSIM_FORCE_STOP_STATE_SHIFT(x) ((x) << 20) +#define DSIM_STOP_STATE_CNT_SHIFT(x) ((x) << 21) + +/* EXYNOS_DSIM_MDRESOL */ +#define DSIM_MAIN_STAND_BY (1 << 31) +#define DSIM_MAIN_VRESOL(x) (((x) & 0x7ff) << 16) +#define DSIM_MAIN_HRESOL(x) (((x) & 0X7ff) << 0) + +/* EXYNOS_DSIM_MVPORCH */ +#define DSIM_CMD_ALLOW_SHIFT(x) ((x) << 28) +#define DSIM_STABLE_VFP_SHIFT(x) ((x) << 16) +#define DSIM_MAIN_VBP_SHIFT(x) ((x) << 0) +#define DSIM_CMD_ALLOW_MASK (0xf << 28) +#define DSIM_STABLE_VFP_MASK (0x7ff << 16) +#define DSIM_MAIN_VBP_MASK (0x7ff << 0) + +/* EXYNOS_DSIM_MHPORCH */ +#define DSIM_MAIN_HFP_SHIFT(x) ((x) << 16) +#define DSIM_MAIN_HBP_SHIFT(x) ((x) << 0) +#define DSIM_MAIN_HFP_MASK ((0xffff) << 16) +#define DSIM_MAIN_HBP_MASK ((0xffff) << 0) + +/* EXYNOS_DSIM_MSYNC */ +#define DSIM_MAIN_VSA_SHIFT(x) ((x) << 22) +#define DSIM_MAIN_HSA_SHIFT(x) ((x) << 0) +#define DSIM_MAIN_VSA_MASK ((0x3ff) << 22) +#define DSIM_MAIN_HSA_MASK ((0xffff) << 0) + +/* EXYNOS_DSIM_SDRESOL */ +#define DSIM_SUB_STANDY_SHIFT(x) ((x) << 31) +#define DSIM_SUB_VRESOL_SHIFT(x) ((x) << 16) +#define DSIM_SUB_HRESOL_SHIFT(x) ((x) << 0) +#define DSIM_SUB_STANDY_MASK ((0x1) << 31) +#define DSIM_SUB_VRESOL_MASK ((0x7ff) << 16) +#define DSIM_SUB_HRESOL_MASK ((0x7ff) << 0) + +/* EXYNOS_DSIM_INTSRC */ +#define INTSRC_PLL_STABLE (1 << 31) +#define INTSRC_SW_RST_RELEASE (1 << 30) +#define INTSRC_SFR_FIFO_EMPTY (1 << 29) +#define INTSRC_FRAME_DONE (1 << 24) +#define INTSRC_RX_DATA_DONE (1 << 18) + +/* EXYNOS_DSIM_INTMSK */ +#define INTMSK_FIFO_EMPTY (1 << 29) +#define INTMSK_BTA (1 << 25) +#define INTMSK_FRAME_DONE (1 << 24) +#define INTMSK_RX_TIMEOUT (1 << 21) +#define INTMSK_BTA_TIMEOUT (1 << 20) +#define INTMSK_RX_DONE (1 << 18) +#define INTMSK_RX_TE (1 << 17) +#define INTMSK_RX_ACK (1 << 16) +#define INTMSK_RX_ECC_ERR (1 << 15) +#define INTMSK_RX_CRC_ERR (1 << 14) + +/* EXYNOS_DSIM_FIFOCTRL */ +#define SFR_HEADER_EMPTY (1 << 22) + +/* EXYNOS_DSIM_PHYACCHR */ +#define DSIM_AFC_CTL(x) (((x) & 0x7) << 5) + +/* EXYNOS_DSIM_PLLCTRL */ +#define DSIM_PLL_EN_SHIFT(x) ((x) << 23) +#define DSIM_FREQ_BAND_SHIFT(x) ((x) << 24) + +#endif /* _EXYNOS_MIPI_DSI_REGS_H */ diff --git a/drivers/video/exynos/s6e8ax0.c b/drivers/video/exynos/s6e8ax0.c new file mode 100644 index 000000000000..4aa9ac6218bf --- /dev/null +++ b/drivers/video/exynos/s6e8ax0.c @@ -0,0 +1,898 @@ +/* linux/drivers/video/exynos/s6e8ax0.c + * + * MIPI-DSI based s6e8ax0 AMOLED lcd 4.65 inch panel driver. + * + * Inki Dae, <inki.dae@samsung.com> + * Donghwa Lee, <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/wait.h> +#include <linux/ctype.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/lcd.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> +#include <video/exynos_mipi_dsim.h> + +#define LDI_MTP_LENGTH 24 +#define DSIM_PM_STABLE_TIME 10 +#define MIN_BRIGHTNESS 0 +#define MAX_BRIGHTNESS 24 +#define GAMMA_TABLE_COUNT 26 + +#define POWER_IS_ON(pwr) ((pwr) == FB_BLANK_UNBLANK) +#define POWER_IS_OFF(pwr) ((pwr) == FB_BLANK_POWERDOWN) +#define POWER_IS_NRM(pwr) ((pwr) == FB_BLANK_NORMAL) + +#define lcd_to_master(a) (a->dsim_dev->master) +#define lcd_to_master_ops(a) ((lcd_to_master(a))->master_ops) + +enum { + DSIM_NONE_STATE = 0, + DSIM_RESUME_COMPLETE = 1, + DSIM_FRAME_DONE = 2, +}; + +struct s6e8ax0 { + struct device *dev; + unsigned int power; + unsigned int id; + unsigned int gamma; + unsigned int acl_enable; + unsigned int cur_acl; + + struct lcd_device *ld; + struct backlight_device *bd; + + struct mipi_dsim_lcd_device *dsim_dev; + struct lcd_platform_data *ddi_pd; + struct mutex lock; + bool enabled; +}; + + +static struct regulator_bulk_data supplies[] = { + { .supply = "vdd3", }, + { .supply = "vci", }, +}; + +static void s6e8ax0_regulator_enable(struct s6e8ax0 *lcd) +{ + int ret = 0; + struct lcd_platform_data *pd = NULL; + + pd = lcd->ddi_pd; + mutex_lock(&lcd->lock); + if (!lcd->enabled) { + ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies); + if (ret) + goto out; + + lcd->enabled = true; + } + msleep(pd->power_on_delay); +out: + mutex_unlock(&lcd->lock); +} + +static void s6e8ax0_regulator_disable(struct s6e8ax0 *lcd) +{ + int ret = 0; + + mutex_lock(&lcd->lock); + if (lcd->enabled) { + ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies); + if (ret) + goto out; + + lcd->enabled = false; + } +out: + mutex_unlock(&lcd->lock); +} + +static const unsigned char s6e8ax0_22_gamma_30[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xf5, 0x00, 0xff, 0xad, 0xaf, + 0xbA, 0xc3, 0xd8, 0xc5, 0x9f, 0xc6, 0x9e, 0xc1, 0xdc, 0xc0, + 0x00, 0x61, 0x00, 0x5a, 0x00, 0x74, +}; + +static const unsigned char s6e8ax0_22_gamma_50[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xe8, 0x1f, 0xf7, 0xad, 0xc0, + 0xb5, 0xc4, 0xdc, 0xc4, 0x9e, 0xc6, 0x9c, 0xbb, 0xd8, 0xbb, + 0x00, 0x70, 0x00, 0x68, 0x00, 0x86, +}; + +static const unsigned char s6e8ax0_22_gamma_60[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xde, 0x1f, 0xef, 0xad, 0xc4, + 0xb3, 0xc3, 0xdd, 0xc4, 0x9e, 0xc6, 0x9c, 0xbc, 0xd6, 0xba, + 0x00, 0x75, 0x00, 0x6e, 0x00, 0x8d, +}; + +static const unsigned char s6e8ax0_22_gamma_70[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xd8, 0x1f, 0xe7, 0xaf, 0xc8, + 0xb4, 0xc4, 0xdd, 0xc3, 0x9d, 0xc6, 0x9c, 0xbb, 0xd6, 0xb9, + 0x00, 0x7a, 0x00, 0x72, 0x00, 0x93, +}; + +static const unsigned char s6e8ax0_22_gamma_80[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xc9, 0x1f, 0xde, 0xae, 0xc9, + 0xb1, 0xc3, 0xdd, 0xc2, 0x9d, 0xc5, 0x9b, 0xbc, 0xd6, 0xbb, + 0x00, 0x7f, 0x00, 0x77, 0x00, 0x99, +}; + +static const unsigned char s6e8ax0_22_gamma_90[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xc7, 0x1f, 0xd9, 0xb0, 0xcc, + 0xb2, 0xc3, 0xdc, 0xc1, 0x9c, 0xc6, 0x9c, 0xbc, 0xd4, 0xb9, + 0x00, 0x83, 0x00, 0x7b, 0x00, 0x9e, +}; + +static const unsigned char s6e8ax0_22_gamma_100[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xbd, 0x80, 0xcd, 0xba, 0xce, + 0xb3, 0xc4, 0xde, 0xc3, 0x9c, 0xc4, 0x9, 0xb8, 0xd3, 0xb6, + 0x00, 0x88, 0x00, 0x80, 0x00, 0xa5, +}; + +static const unsigned char s6e8ax0_22_gamma_120[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb9, 0x95, 0xc8, 0xb1, 0xcf, + 0xb2, 0xc6, 0xdf, 0xc5, 0x9b, 0xc3, 0x99, 0xb6, 0xd2, 0xb6, + 0x00, 0x8f, 0x00, 0x86, 0x00, 0xac, +}; + +static const unsigned char s6e8ax0_22_gamma_130[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc7, 0xb1, 0xd0, + 0xb2, 0xc4, 0xdd, 0xc3, 0x9a, 0xc3, 0x98, 0xb6, 0xd0, 0xb4, + 0x00, 0x92, 0x00, 0x8a, 0x00, 0xb1, +}; + +static const unsigned char s6e8ax0_22_gamma_140[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc5, 0xb2, 0xd0, + 0xb3, 0xc3, 0xde, 0xc3, 0x9b, 0xc2, 0x98, 0xb6, 0xd0, 0xb4, + 0x00, 0x95, 0x00, 0x8d, 0x00, 0xb5, +}; + +static const unsigned char s6e8ax0_22_gamma_150[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xa0, 0xc2, 0xb2, 0xd0, + 0xb2, 0xc1, 0xdd, 0xc2, 0x9b, 0xc2, 0x98, 0xb4, 0xcf, 0xb1, + 0x00, 0x99, 0x00, 0x90, 0x00, 0xba, +}; + +static const unsigned char s6e8ax0_22_gamma_160[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xa5, 0xbf, 0xb0, 0xd0, + 0xb1, 0xc3, 0xde, 0xc2, 0x99, 0xc1, 0x97, 0xb4, 0xce, 0xb1, + 0x00, 0x9c, 0x00, 0x93, 0x00, 0xbe, +}; + +static const unsigned char s6e8ax0_22_gamma_170[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb5, 0xbf, 0xb1, 0xd1, + 0xb1, 0xc3, 0xde, 0xc3, 0x99, 0xc0, 0x96, 0xb4, 0xce, 0xb1, + 0x00, 0x9f, 0x00, 0x96, 0x00, 0xc2, +}; + +static const unsigned char s6e8ax0_22_gamma_180[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb7, 0xbe, 0xb3, 0xd2, + 0xb3, 0xc3, 0xde, 0xc2, 0x97, 0xbf, 0x95, 0xb4, 0xcd, 0xb1, + 0x00, 0xa2, 0x00, 0x99, 0x00, 0xc5, +}; + +static const unsigned char s6e8ax0_22_gamma_190[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbe, 0xb2, 0xd2, + 0xb2, 0xc3, 0xdd, 0xc3, 0x98, 0xbf, 0x95, 0xb2, 0xcc, 0xaf, + 0x00, 0xa5, 0x00, 0x9c, 0x00, 0xc9, +}; + +static const unsigned char s6e8ax0_22_gamma_200[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbc, 0xb2, 0xd2, + 0xb1, 0xc4, 0xdd, 0xc3, 0x97, 0xbe, 0x95, 0xb1, 0xcb, 0xae, + 0x00, 0xa8, 0x00, 0x9f, 0x00, 0xcd, +}; + +static const unsigned char s6e8ax0_22_gamma_210[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc1, 0xbd, 0xb1, 0xd1, + 0xb1, 0xc2, 0xde, 0xc2, 0x97, 0xbe, 0x94, 0xB0, 0xc9, 0xad, + 0x00, 0xae, 0x00, 0xa4, 0x00, 0xd4, +}; + +static const unsigned char s6e8ax0_22_gamma_220[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc7, 0xbd, 0xb1, 0xd1, + 0xb1, 0xc2, 0xdd, 0xc2, 0x97, 0xbd, 0x94, 0xb0, 0xc9, 0xad, + 0x00, 0xad, 0x00, 0xa2, 0x00, 0xd3, +}; + +static const unsigned char s6e8ax0_22_gamma_230[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc3, 0xbd, 0xb2, 0xd1, + 0xb1, 0xc3, 0xdd, 0xc1, 0x96, 0xbd, 0x94, 0xb0, 0xc9, 0xad, + 0x00, 0xb0, 0x00, 0xa7, 0x00, 0xd7, +}; + +static const unsigned char s6e8ax0_22_gamma_240[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xcb, 0xbd, 0xb1, 0xd2, + 0xb1, 0xc3, 0xdD, 0xc2, 0x95, 0xbd, 0x93, 0xaf, 0xc8, 0xab, + 0x00, 0xb3, 0x00, 0xa9, 0x00, 0xdb, +}; + +static const unsigned char s6e8ax0_22_gamma_250[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xcc, 0xbe, 0xb0, 0xd2, + 0xb0, 0xc3, 0xdD, 0xc2, 0x94, 0xbc, 0x92, 0xae, 0xc8, 0xab, + 0x00, 0xb6, 0x00, 0xab, 0x00, 0xde, +}; + +static const unsigned char s6e8ax0_22_gamma_260[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xd0, 0xbe, 0xaf, 0xd1, + 0xaf, 0xc2, 0xdd, 0xc1, 0x96, 0xbc, 0x93, 0xaf, 0xc8, 0xac, + 0x00, 0xb7, 0x00, 0xad, 0x00, 0xe0, +}; + +static const unsigned char s6e8ax0_22_gamma_270[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xcF, 0xbd, 0xb0, 0xd2, + 0xaf, 0xc2, 0xdc, 0xc1, 0x95, 0xbd, 0x93, 0xae, 0xc6, 0xaa, + 0x00, 0xba, 0x00, 0xb0, 0x00, 0xe4, +}; + +static const unsigned char s6e8ax0_22_gamma_280[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xd0, 0xbd, 0xaf, 0xd0, + 0xad, 0xc4, 0xdd, 0xc3, 0x95, 0xbd, 0x93, 0xac, 0xc5, 0xa9, + 0x00, 0xbd, 0x00, 0xb2, 0x00, 0xe7, +}; + +static const unsigned char s6e8ax0_22_gamma_300[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb5, 0xd3, 0xbd, 0xb1, 0xd2, + 0xb0, 0xc0, 0xdc, 0xc0, 0x94, 0xba, 0x91, 0xac, 0xc5, 0xa9, + 0x00, 0xc2, 0x00, 0xb7, 0x00, 0xed, +}; + +static const unsigned char *s6e8ax0_22_gamma_table[] = { + s6e8ax0_22_gamma_30, + s6e8ax0_22_gamma_50, + s6e8ax0_22_gamma_60, + s6e8ax0_22_gamma_70, + s6e8ax0_22_gamma_80, + s6e8ax0_22_gamma_90, + s6e8ax0_22_gamma_100, + s6e8ax0_22_gamma_120, + s6e8ax0_22_gamma_130, + s6e8ax0_22_gamma_140, + s6e8ax0_22_gamma_150, + s6e8ax0_22_gamma_160, + s6e8ax0_22_gamma_170, + s6e8ax0_22_gamma_180, + s6e8ax0_22_gamma_190, + s6e8ax0_22_gamma_200, + s6e8ax0_22_gamma_210, + s6e8ax0_22_gamma_220, + s6e8ax0_22_gamma_230, + s6e8ax0_22_gamma_240, + s6e8ax0_22_gamma_250, + s6e8ax0_22_gamma_260, + s6e8ax0_22_gamma_270, + s6e8ax0_22_gamma_280, + s6e8ax0_22_gamma_300, +}; + +static void s6e8ax0_panel_cond(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + static const unsigned char data_to_send[] = { + 0xf8, 0x3d, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d, + 0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08, + 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0, + 0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, 0xff, 0xff, 0xc8 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_display_cond(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xf2, 0x80, 0x03, 0x0d + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +/* Gamma 2.2 Setting (200cd, 7500K, 10MPCD) */ +static void s6e8ax0_gamma_cond(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + unsigned int gamma = lcd->bd->props.brightness; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + s6e8ax0_22_gamma_table[gamma], + GAMMA_TABLE_COUNT); +} + +static void s6e8ax0_gamma_update(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xf7, 0x03 + }; + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, data_to_send, + ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_cond1(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xd1, 0xfe, 0x80, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x40, + 0x0d, 0x00, 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_cond2(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0, + 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_cond3(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_cond4(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_cond5(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x19, 0x33, 0x02 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} +static void s6e8ax0_etc_cond6(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xe3, 0x40 + }; + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_cond7(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_elvss_set(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xb1, 0x04, 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_elvss_nvm_set(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xd9, 0x5c, 0x20, 0x0c, 0x0f, 0x41, 0x00, 0x10, 0x11, + 0x12, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcb, 0xed, + 0x64, 0xaf + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_sleep_in(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0x10, 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_sleep_out(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0x11, 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_display_on(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0x29, 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_display_off(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0x28, 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_apply_level2_key(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xf0, 0x5a, 0x5a + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_acl_on(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xc0, 0x01 + }; + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_acl_off(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xc0, 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +/* Full white 50% reducing setting */ +static void s6e8ax0_acl_ctrl_set(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + /* Full white 50% reducing setting */ + static const unsigned char cutoff_50[] = { + 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, + 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x08, 0x0f, 0x16, 0x1d, 0x24, 0x2a, 0x31, 0x38, + 0x3f, 0x46 + }; + /* Full white 45% reducing setting */ + static const unsigned char cutoff_45[] = { + 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, + 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x07, 0x0d, 0x13, 0x19, 0x1f, 0x25, 0x2b, 0x31, + 0x37, 0x3d + }; + /* Full white 40% reducing setting */ + static const unsigned char cutoff_40[] = { + 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, + 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x06, 0x0c, 0x11, 0x16, 0x1c, 0x21, 0x26, 0x2b, + 0x31, 0x36 + }; + + if (lcd->acl_enable) { + if (lcd->cur_acl == 0) { + if (lcd->gamma == 0 || lcd->gamma == 1) { + s6e8ax0_acl_off(lcd); + dev_dbg(&lcd->ld->dev, + "cur_acl=%d\n", lcd->cur_acl); + } else + s6e8ax0_acl_on(lcd); + } + switch (lcd->gamma) { + case 0: /* 30cd */ + s6e8ax0_acl_off(lcd); + lcd->cur_acl = 0; + break; + case 1 ... 3: /* 50cd ~ 90cd */ + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_LONG_WRITE, + cutoff_40, + ARRAY_SIZE(cutoff_40)); + lcd->cur_acl = 40; + break; + case 4 ... 7: /* 120cd ~ 210cd */ + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_LONG_WRITE, + cutoff_45, + ARRAY_SIZE(cutoff_45)); + lcd->cur_acl = 45; + break; + case 8 ... 10: /* 220cd ~ 300cd */ + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_LONG_WRITE, + cutoff_50, + ARRAY_SIZE(cutoff_50)); + lcd->cur_acl = 50; + break; + default: + break; + } + } else { + s6e8ax0_acl_off(lcd); + lcd->cur_acl = 0; + dev_dbg(&lcd->ld->dev, "cur_acl = %d\n", lcd->cur_acl); + } +} + +static void s6e8ax0_read_id(struct s6e8ax0 *lcd, u8 *mtp_id) +{ + unsigned int ret; + unsigned int addr = 0xd1; /* MTP ID */ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + ret = ops->cmd_read(lcd_to_master(lcd), + MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM, + addr, 3, mtp_id); +} + +static int s6e8ax0_panel_init(struct s6e8ax0 *lcd) +{ + s6e8ax0_apply_level2_key(lcd); + s6e8ax0_sleep_out(lcd); + msleep(1); + s6e8ax0_panel_cond(lcd); + s6e8ax0_display_cond(lcd); + s6e8ax0_gamma_cond(lcd); + s6e8ax0_gamma_update(lcd); + + s6e8ax0_etc_cond1(lcd); + s6e8ax0_etc_cond2(lcd); + s6e8ax0_etc_cond3(lcd); + s6e8ax0_etc_cond4(lcd); + s6e8ax0_etc_cond5(lcd); + s6e8ax0_etc_cond6(lcd); + s6e8ax0_etc_cond7(lcd); + + s6e8ax0_elvss_nvm_set(lcd); + s6e8ax0_elvss_set(lcd); + + s6e8ax0_acl_ctrl_set(lcd); + s6e8ax0_acl_on(lcd); + + /* if ID3 value is not 33h, branch private elvss mode */ + msleep(lcd->ddi_pd->power_on_delay); + + return 0; +} + +static int s6e8ax0_update_gamma_ctrl(struct s6e8ax0 *lcd, int brightness) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + s6e8ax0_22_gamma_table[brightness], + ARRAY_SIZE(s6e8ax0_22_gamma_table)); + + /* update gamma table. */ + s6e8ax0_gamma_update(lcd); + lcd->gamma = brightness; + + return 0; +} + +static int s6e8ax0_gamma_ctrl(struct s6e8ax0 *lcd, int gamma) +{ + s6e8ax0_update_gamma_ctrl(lcd, gamma); + + return 0; +} + +static int s6e8ax0_set_power(struct lcd_device *ld, int power) +{ + struct s6e8ax0 *lcd = lcd_get_data(ld); + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + int ret = 0; + + if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN && + power != FB_BLANK_NORMAL) { + dev_err(lcd->dev, "power value should be 0, 1 or 4.\n"); + return -EINVAL; + } + + if ((power == FB_BLANK_UNBLANK) && ops->set_blank_mode) { + /* LCD power on */ + if ((POWER_IS_ON(power) && POWER_IS_OFF(lcd->power)) + || (POWER_IS_ON(power) && POWER_IS_NRM(lcd->power))) { + ret = ops->set_blank_mode(lcd_to_master(lcd), power); + if (!ret && lcd->power != power) + lcd->power = power; + } + } else if ((power == FB_BLANK_POWERDOWN) && ops->set_early_blank_mode) { + /* LCD power off */ + if ((POWER_IS_OFF(power) && POWER_IS_ON(lcd->power)) || + (POWER_IS_ON(lcd->power) && POWER_IS_NRM(power))) { + ret = ops->set_early_blank_mode(lcd_to_master(lcd), + power); + if (!ret && lcd->power != power) + lcd->power = power; + } + } + + return ret; +} + +static int s6e8ax0_get_power(struct lcd_device *ld) +{ + struct s6e8ax0 *lcd = lcd_get_data(ld); + + return lcd->power; +} + +static int s6e8ax0_get_brightness(struct backlight_device *bd) +{ + return bd->props.brightness; +} + +static int s6e8ax0_set_brightness(struct backlight_device *bd) +{ + int ret = 0, brightness = bd->props.brightness; + struct s6e8ax0 *lcd = bl_get_data(bd); + + if (brightness < MIN_BRIGHTNESS || + brightness > bd->props.max_brightness) { + dev_err(lcd->dev, "lcd brightness should be %d to %d.\n", + MIN_BRIGHTNESS, MAX_BRIGHTNESS); + return -EINVAL; + } + + ret = s6e8ax0_gamma_ctrl(lcd, brightness); + if (ret) { + dev_err(&bd->dev, "lcd brightness setting failed.\n"); + return -EIO; + } + + return ret; +} + +static struct lcd_ops s6e8ax0_lcd_ops = { + .set_power = s6e8ax0_set_power, + .get_power = s6e8ax0_get_power, +}; + +static const struct backlight_ops s6e8ax0_backlight_ops = { + .get_brightness = s6e8ax0_get_brightness, + .update_status = s6e8ax0_set_brightness, +}; + +static void s6e8ax0_power_on(struct mipi_dsim_lcd_device *dsim_dev, int power) +{ + struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); + + msleep(lcd->ddi_pd->power_on_delay); + + /* lcd power on */ + if (power) + s6e8ax0_regulator_enable(lcd); + else + s6e8ax0_regulator_disable(lcd); + + msleep(lcd->ddi_pd->reset_delay); + + /* lcd reset */ + if (lcd->ddi_pd->reset) + lcd->ddi_pd->reset(lcd->ld); + msleep(5); +} + +static void s6e8ax0_set_sequence(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); + + s6e8ax0_panel_init(lcd); + s6e8ax0_display_on(lcd); + + lcd->power = FB_BLANK_UNBLANK; +} + +static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e8ax0 *lcd; + int ret; + u8 mtp_id[3] = {0, }; + + lcd = kzalloc(sizeof(struct s6e8ax0), GFP_KERNEL); + if (!lcd) { + dev_err(&dsim_dev->dev, "failed to allocate s6e8ax0 structure.\n"); + return -ENOMEM; + } + + lcd->dsim_dev = dsim_dev; + lcd->ddi_pd = (struct lcd_platform_data *)dsim_dev->platform_data; + lcd->dev = &dsim_dev->dev; + + mutex_init(&lcd->lock); + + ret = regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies); + if (ret) { + dev_err(lcd->dev, "Failed to get regulators: %d\n", ret); + goto err_lcd_register; + } + + lcd->ld = lcd_device_register("s6e8ax0", lcd->dev, lcd, + &s6e8ax0_lcd_ops); + if (IS_ERR(lcd->ld)) { + dev_err(lcd->dev, "failed to register lcd ops.\n"); + ret = PTR_ERR(lcd->ld); + goto err_lcd_register; + } + + lcd->bd = backlight_device_register("s6e8ax0-bl", lcd->dev, lcd, + &s6e8ax0_backlight_ops, NULL); + if (IS_ERR(lcd->bd)) { + dev_err(lcd->dev, "failed to register backlight ops.\n"); + ret = PTR_ERR(lcd->bd); + goto err_backlight_register; + } + + lcd->bd->props.max_brightness = MAX_BRIGHTNESS; + lcd->bd->props.brightness = MAX_BRIGHTNESS; + + s6e8ax0_read_id(lcd, mtp_id); + if (mtp_id[0] == 0x00) + dev_err(lcd->dev, "read id failed\n"); + + dev_info(lcd->dev, "Read ID : %x, %x, %x\n", + mtp_id[0], mtp_id[1], mtp_id[2]); + + if (mtp_id[2] == 0x33) + dev_info(lcd->dev, + "ID-3 is 0xff does not support dynamic elvss\n"); + else + dev_info(lcd->dev, + "ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]); + + lcd->acl_enable = 1; + lcd->cur_acl = 0; + + dev_set_drvdata(&dsim_dev->dev, lcd); + + dev_dbg(lcd->dev, "probed s6e8ax0 panel driver.\n"); + + return 0; + +err_backlight_register: + lcd_device_unregister(lcd->ld); + +err_lcd_register: + regulator_bulk_free(ARRAY_SIZE(supplies), supplies); + kfree(lcd); + + return ret; +} + +#ifdef CONFIG_PM +static int s6e8ax0_suspend(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); + + s6e8ax0_sleep_in(lcd); + msleep(lcd->ddi_pd->power_off_delay); + s6e8ax0_display_off(lcd); + + s6e8ax0_regulator_disable(lcd); + + return 0; +} + +static int s6e8ax0_resume(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); + + s6e8ax0_sleep_out(lcd); + msleep(lcd->ddi_pd->power_on_delay); + + s6e8ax0_regulator_enable(lcd); + s6e8ax0_set_sequence(dsim_dev); + + return 0; +} +#else +#define s6e8ax0_suspend NULL +#define s6e8ax0_resume NULL +#endif + +static struct mipi_dsim_lcd_driver s6e8ax0_dsim_ddi_driver = { + .name = "s6e8ax0", + .id = -1, + + .power_on = s6e8ax0_power_on, + .set_sequence = s6e8ax0_set_sequence, + .probe = s6e8ax0_probe, + .suspend = s6e8ax0_suspend, + .resume = s6e8ax0_resume, +}; + +static int s6e8ax0_init(void) +{ + exynos_mipi_dsi_register_lcd_driver(&s6e8ax0_dsim_ddi_driver); + + return 0; +} + +static void s6e8ax0_exit(void) +{ + return; +} + +module_init(s6e8ax0_init); +module_exit(s6e8ax0_exit); + +MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>"); +MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); +MODULE_DESCRIPTION("MIPI-DSI based s6e8ax0 AMOLED LCD Panel Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/exynos/s6e8ax0.h b/drivers/video/exynos/s6e8ax0.h new file mode 100644 index 000000000000..1f1b270484b0 --- /dev/null +++ b/drivers/video/exynos/s6e8ax0.h @@ -0,0 +1,21 @@ +/* linux/drivers/video/backlight/s6e8ax0.h + * + * MIPI-DSI based s6e8ax0 AMOLED LCD Panel definitions. + * + * Copyright (c) 2011 Samsung Electronics + * + * Inki Dae, <inki.dae@samsung.com> + * Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef _S6E8AX0_H +#define _S6E8AX0_H + +extern void s6e8ax0_init(void); + +#endif + diff --git a/drivers/video/i740_reg.h b/drivers/video/i740_reg.h new file mode 100644 index 000000000000..91bac76549d7 --- /dev/null +++ b/drivers/video/i740_reg.h @@ -0,0 +1,309 @@ +/************************************************************************** + +Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas. +All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sub license, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice (including the +next paragraph) shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +**************************************************************************/ + +/* + * Authors: + * Kevin E. Martin <kevin@precisioninsight.com> + */ + +/* I/O register offsets */ +#define SRX VGA_SEQ_I +#define GRX VGA_GFX_I +#define ARX VGA_ATT_IW +#define XRX 0x3D6 +#define MRX 0x3D2 + +/* VGA Color Palette Registers */ +#define DACMASK 0x3C6 +#define DACSTATE 0x3C7 +#define DACRX 0x3C7 +#define DACWX 0x3C8 +#define DACDATA 0x3C9 + +/* CRT Controller Registers (CRX) */ +#define START_ADDR_HI 0x0C +#define START_ADDR_LO 0x0D +#define VERT_SYNC_END 0x11 +#define EXT_VERT_TOTAL 0x30 +#define EXT_VERT_DISPLAY 0x31 +#define EXT_VERT_SYNC_START 0x32 +#define EXT_VERT_BLANK_START 0x33 +#define EXT_HORIZ_TOTAL 0x35 +#define EXT_HORIZ_BLANK 0x39 +#define EXT_START_ADDR 0x40 +#define EXT_START_ADDR_ENABLE 0x80 +#define EXT_OFFSET 0x41 +#define EXT_START_ADDR_HI 0x42 +#define INTERLACE_CNTL 0x70 +#define INTERLACE_ENABLE 0x80 +#define INTERLACE_DISABLE 0x00 + +/* Miscellaneous Output Register */ +#define MSR_R 0x3CC +#define MSR_W 0x3C2 +#define IO_ADDR_SELECT 0x01 + +#define MDA_BASE 0x3B0 +#define CGA_BASE 0x3D0 + +/* System Configuration Extension Registers (XRX) */ +#define IO_CTNL 0x09 +#define EXTENDED_ATTR_CNTL 0x02 +#define EXTENDED_CRTC_CNTL 0x01 + +#define ADDRESS_MAPPING 0x0A +#define PACKED_MODE_ENABLE 0x04 +#define LINEAR_MODE_ENABLE 0x02 +#define PAGE_MAPPING_ENABLE 0x01 + +#define BITBLT_CNTL 0x20 +#define COLEXP_MODE 0x30 +#define COLEXP_8BPP 0x00 +#define COLEXP_16BPP 0x10 +#define COLEXP_24BPP 0x20 +#define COLEXP_RESERVED 0x30 +#define CHIP_RESET 0x02 +#define BITBLT_STATUS 0x01 + +#define DISPLAY_CNTL 0x40 +#define VGA_WRAP_MODE 0x02 +#define VGA_WRAP_AT_256KB 0x00 +#define VGA_NO_WRAP 0x02 +#define GUI_MODE 0x01 +#define STANDARD_VGA_MODE 0x00 +#define HIRES_MODE 0x01 + +#define DRAM_ROW_TYPE 0x50 +#define DRAM_ROW_0 0x07 +#define DRAM_ROW_0_SDRAM 0x00 +#define DRAM_ROW_0_EMPTY 0x07 +#define DRAM_ROW_1 0x38 +#define DRAM_ROW_1_SDRAM 0x00 +#define DRAM_ROW_1_EMPTY 0x38 +#define DRAM_ROW_CNTL_LO 0x51 +#define DRAM_CAS_LATENCY 0x10 +#define DRAM_RAS_TIMING 0x08 +#define DRAM_RAS_PRECHARGE 0x04 +#define DRAM_ROW_CNTL_HI 0x52 +#define DRAM_EXT_CNTL 0x53 +#define DRAM_REFRESH_RATE 0x03 +#define DRAM_REFRESH_DISABLE 0x00 +#define DRAM_REFRESH_60HZ 0x01 +#define DRAM_REFRESH_FAST_TEST 0x02 +#define DRAM_REFRESH_RESERVED 0x03 +#define DRAM_TIMING 0x54 +#define DRAM_ROW_BNDRY_0 0x55 +#define DRAM_ROW_BNDRY_1 0x56 + +#define DPMS_SYNC_SELECT 0x61 +#define VSYNC_CNTL 0x08 +#define VSYNC_ON 0x00 +#define VSYNC_OFF 0x08 +#define HSYNC_CNTL 0x02 +#define HSYNC_ON 0x00 +#define HSYNC_OFF 0x02 + +#define PIXPIPE_CONFIG_0 0x80 +#define DAC_8_BIT 0x80 +#define DAC_6_BIT 0x00 +#define HW_CURSOR_ENABLE 0x10 +#define EXTENDED_PALETTE 0x01 + +#define PIXPIPE_CONFIG_1 0x81 +#define DISPLAY_COLOR_MODE 0x0F +#define DISPLAY_VGA_MODE 0x00 +#define DISPLAY_8BPP_MODE 0x02 +#define DISPLAY_15BPP_MODE 0x04 +#define DISPLAY_16BPP_MODE 0x05 +#define DISPLAY_24BPP_MODE 0x06 +#define DISPLAY_32BPP_MODE 0x07 + +#define PIXPIPE_CONFIG_2 0x82 +#define DISPLAY_GAMMA_ENABLE 0x08 +#define DISPLAY_GAMMA_DISABLE 0x00 +#define OVERLAY_GAMMA_ENABLE 0x04 +#define OVERLAY_GAMMA_DISABLE 0x00 + +#define CURSOR_CONTROL 0xA0 +#define CURSOR_ORIGIN_SCREEN 0x00 +#define CURSOR_ORIGIN_DISPLAY 0x10 +#define CURSOR_MODE 0x07 +#define CURSOR_MODE_DISABLE 0x00 +#define CURSOR_MODE_32_4C_AX 0x01 +#define CURSOR_MODE_128_2C 0x02 +#define CURSOR_MODE_128_1C 0x03 +#define CURSOR_MODE_64_3C 0x04 +#define CURSOR_MODE_64_4C_AX 0x05 +#define CURSOR_MODE_64_4C 0x06 +#define CURSOR_MODE_RESERVED 0x07 +#define CURSOR_BASEADDR_LO 0xA2 +#define CURSOR_BASEADDR_HI 0xA3 +#define CURSOR_X_LO 0xA4 +#define CURSOR_X_HI 0xA5 +#define CURSOR_X_POS 0x00 +#define CURSOR_X_NEG 0x80 +#define CURSOR_Y_LO 0xA6 +#define CURSOR_Y_HI 0xA7 +#define CURSOR_Y_POS 0x00 +#define CURSOR_Y_NEG 0x80 + +#define VCLK2_VCO_M 0xC8 +#define VCLK2_VCO_N 0xC9 +#define VCLK2_VCO_MN_MSBS 0xCA +#define VCO_N_MSBS 0x30 +#define VCO_M_MSBS 0x03 +#define VCLK2_VCO_DIV_SEL 0xCB +#define POST_DIV_SELECT 0x70 +#define POST_DIV_1 0x00 +#define POST_DIV_2 0x10 +#define POST_DIV_4 0x20 +#define POST_DIV_8 0x30 +#define POST_DIV_16 0x40 +#define POST_DIV_32 0x50 +#define VCO_LOOP_DIV_BY_4M 0x00 +#define VCO_LOOP_DIV_BY_16M 0x04 +#define REF_CLK_DIV_BY_5 0x02 +#define REF_DIV_4 0x00 +#define REF_DIV_1 0x01 + +#define PLL_CNTL 0xCE +#define PLL_MEMCLK_SEL 0x03 +#define PLL_MEMCLK__66667KHZ 0x00 +#define PLL_MEMCLK__75000KHZ 0x01 +#define PLL_MEMCLK__88889KHZ 0x02 +#define PLL_MEMCLK_100000KHZ 0x03 + +/* Multimedia Extension Registers (MRX) */ +#define ACQ_CNTL_1 0x02 +#define ACQ_CNTL_2 0x03 +#define FRAME_CAP_MODE 0x01 +#define CONT_CAP_MODE 0x00 +#define SINGLE_CAP_MODE 0x01 +#define ACQ_CNTL_3 0x04 +#define COL_KEY_CNTL_1 0x3C +#define BLANK_DISP_OVERLAY 0x20 + +/* FIFOs */ +#define LP_FIFO 0x1000 +#define HP_FIFO 0x2000 +#define INSTPNT 0x3040 +#define LP_FIFO_COUNT 0x3040 +#define HP_FIFO_COUNT 0x3041 + +/* FIFO Commands */ +#define CLIENT 0xE0000000 +#define CLIENT_2D 0x60000000 + +/* Command Parser Mode Register */ +#define COMPARS 0x3038 +#define TWO_D_INST_DISABLE 0x08 +#define THREE_D_INST_DISABLE 0x04 +#define STATE_VAR_UPDATE_DISABLE 0x02 +#define PAL_STIP_DISABLE 0x01 + +/* Interrupt Control Registers */ +#define IER 0x3030 +#define IIR 0x3032 +#define IMR 0x3034 +#define ISR 0x3036 +#define VMIINTB_EVENT 0x2000 +#define GPIO4_INT 0x1000 +#define DISP_FLIP_EVENT 0x0800 +#define DVD_PORT_DMA 0x0400 +#define DISP_VBLANK 0x0200 +#define FIFO_EMPTY_DMA_DONE 0x0100 +#define INST_PARSER_ERROR 0x0080 +#define USER_DEFINED 0x0040 +#define BREAKPOINT 0x0020 +#define DISP_HORIZ_COUNT 0x0010 +#define DISP_VSYNC 0x0008 +#define CAPTURE_HORIZ_COUNT 0x0004 +#define CAPTURE_VSYNC 0x0002 +#define THREE_D_PIPE_FLUSHED 0x0001 + +/* FIFO Watermark and Burst Length Control Register */ +#define FWATER_BLC 0x00006000 +#define LMI_BURST_LENGTH 0x7F000000 +#define LMI_FIFO_WATERMARK 0x003F0000 +#define AGP_BURST_LENGTH 0x00007F00 +#define AGP_FIFO_WATERMARK 0x0000003F + +/* BitBLT Registers */ +#define SRC_DST_PITCH 0x00040000 +#define DST_PITCH 0x1FFF0000 +#define SRC_PITCH 0x00001FFF +#define COLEXP_BG_COLOR 0x00040004 +#define COLEXP_FG_COLOR 0x00040008 +#define MONO_SRC_CNTL 0x0004000C +#define MONO_USE_COLEXP 0x00000000 +#define MONO_USE_SRCEXP 0x08000000 +#define MONO_DATA_ALIGN 0x07000000 +#define MONO_BIT_ALIGN 0x01000000 +#define MONO_BYTE_ALIGN 0x02000000 +#define MONO_WORD_ALIGN 0x03000000 +#define MONO_DWORD_ALIGN 0x04000000 +#define MONO_QWORD_ALIGN 0x05000000 +#define MONO_SRC_INIT_DSCRD 0x003F0000 +#define MONO_SRC_RIGHT_CLIP 0x00003F00 +#define MONO_SRC_LEFT_CLIP 0x0000003F +#define BITBLT_CONTROL 0x00040010 +#define BLTR_STATUS 0x80000000 +#define DYN_DEPTH 0x03000000 +#define DYN_DEPTH_8BPP 0x00000000 +#define DYN_DEPTH_16BPP 0x01000000 +#define DYN_DEPTH_24BPP 0x02000000 +#define DYN_DEPTH_32BPP 0x03000000 /* Unimplemented on the i740 */ +#define DYN_DEPTH_ENABLE 0x00800000 +#define PAT_VERT_ALIGN 0x00700000 +#define SOLID_PAT_SELECT 0x00080000 +#define PAT_IS_IN_COLOR 0x00000000 +#define PAT_IS_MONO 0x00040000 +#define MONO_PAT_TRANSP 0x00020000 +#define COLOR_TRANSP_ROP 0x00000000 +#define COLOR_TRANSP_DST 0x00008000 +#define COLOR_TRANSP_EQ 0x00000000 +#define COLOR_TRANSP_NOT_EQ 0x00010000 +#define COLOR_TRANSP_ENABLE 0x00004000 +#define MONO_SRC_TRANSP 0x00002000 +#define SRC_IS_IN_COLOR 0x00000000 +#define SRC_IS_MONO 0x00001000 +#define SRC_USE_SRC_ADDR 0x00000000 +#define SRC_USE_BLTDATA 0x00000400 +#define BLT_TOP_TO_BOT 0x00000000 +#define BLT_BOT_TO_TOP 0x00000200 +#define BLT_LEFT_TO_RIGHT 0x00000000 +#define BLT_RIGHT_TO_LEFT 0x00000100 +#define BLT_ROP 0x000000FF +#define BLT_PAT_ADDR 0x00040014 +#define BLT_SRC_ADDR 0x00040018 +#define BLT_DST_ADDR 0x0004001C +#define BLT_DST_H_W 0x00040020 +#define BLT_DST_HEIGHT 0x1FFF0000 +#define BLT_DST_WIDTH 0x00001FFF +#define SRCEXP_BG_COLOR 0x00040024 +#define SRCEXP_FG_COLOR 0x00040028 +#define BLTDATA 0x00050000 diff --git a/drivers/video/i740fb.c b/drivers/video/i740fb.c new file mode 100644 index 000000000000..fe574d84ed99 --- /dev/null +++ b/drivers/video/i740fb.c @@ -0,0 +1,1337 @@ +/* + * i740fb - framebuffer driver for Intel740 + * Copyright (c) 2011 Ondrej Zary + * + * Based on old i740fb driver (c) 2001-2002 Andrey Ulanov <drey@rt.mipt.ru> + * which was partially based on: + * VGA 16-color framebuffer driver (c) 1999 Ben Pfaff <pfaffben@debian.org> + * and Petr Vandrovec <VANDROVE@vc.cvut.cz> + * i740 driver from XFree86 (c) 1998-1999 Precision Insight, Inc., Cedar Park, + * Texas. + * i740fb by Patrick LERDA, v0.9 + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/console.h> +#include <video/vga.h> + +#ifdef CONFIG_MTRR +#include <asm/mtrr.h> +#endif + +#include "i740_reg.h" + +static char *mode_option __devinitdata; + +#ifdef CONFIG_MTRR +static int mtrr __devinitdata = 1; +#endif + +struct i740fb_par { + unsigned char __iomem *regs; + bool has_sgram; +#ifdef CONFIG_MTRR + int mtrr_reg; +#endif + bool ddc_registered; + struct i2c_adapter ddc_adapter; + struct i2c_algo_bit_data ddc_algo; + u32 pseudo_palette[16]; + struct mutex open_lock; + unsigned int ref_count; + + u8 crtc[VGA_CRT_C]; + u8 atc[VGA_ATT_C]; + u8 gdc[VGA_GFX_C]; + u8 seq[VGA_SEQ_C]; + u8 misc; + u8 vss; + + /* i740 specific registers */ + u8 display_cntl; + u8 pixelpipe_cfg0; + u8 pixelpipe_cfg1; + u8 pixelpipe_cfg2; + u8 video_clk2_m; + u8 video_clk2_n; + u8 video_clk2_mn_msbs; + u8 video_clk2_div_sel; + u8 pll_cntl; + u8 address_mapping; + u8 io_cntl; + u8 bitblt_cntl; + u8 ext_vert_total; + u8 ext_vert_disp_end; + u8 ext_vert_sync_start; + u8 ext_vert_blank_start; + u8 ext_horiz_total; + u8 ext_horiz_blank; + u8 ext_offset; + u8 interlace_cntl; + u32 lmi_fifo_watermark; + u8 ext_start_addr; + u8 ext_start_addr_hi; +}; + +#define DACSPEED8 203 +#define DACSPEED16 163 +#define DACSPEED24_SG 136 +#define DACSPEED24_SD 128 +#define DACSPEED32 86 + +static struct fb_fix_screeninfo i740fb_fix __devinitdata = { + .id = "i740fb", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 8, + .ypanstep = 1, + .accel = FB_ACCEL_NONE, +}; + +static inline void i740outb(struct i740fb_par *par, u16 port, u8 val) +{ + vga_mm_w(par->regs, port, val); +} +static inline u8 i740inb(struct i740fb_par *par, u16 port) +{ + return vga_mm_r(par->regs, port); +} +static inline void i740outreg(struct i740fb_par *par, u16 port, u8 reg, u8 val) +{ + vga_mm_w_fast(par->regs, port, reg, val); +} +static inline u8 i740inreg(struct i740fb_par *par, u16 port, u8 reg) +{ + vga_mm_w(par->regs, port, reg); + return vga_mm_r(par->regs, port+1); +} +static inline void i740outreg_mask(struct i740fb_par *par, u16 port, u8 reg, + u8 val, u8 mask) +{ + vga_mm_w_fast(par->regs, port, reg, (val & mask) + | (i740inreg(par, port, reg) & ~mask)); +} + +#define REG_DDC_DRIVE 0x62 +#define REG_DDC_STATE 0x63 +#define DDC_SCL (1 << 3) +#define DDC_SDA (1 << 2) + +static void i740fb_ddc_setscl(void *data, int val) +{ + struct i740fb_par *par = data; + + i740outreg_mask(par, XRX, REG_DDC_DRIVE, DDC_SCL, DDC_SCL); + i740outreg_mask(par, XRX, REG_DDC_STATE, val ? DDC_SCL : 0, DDC_SCL); +} + +static void i740fb_ddc_setsda(void *data, int val) +{ + struct i740fb_par *par = data; + + i740outreg_mask(par, XRX, REG_DDC_DRIVE, DDC_SDA, DDC_SDA); + i740outreg_mask(par, XRX, REG_DDC_STATE, val ? DDC_SDA : 0, DDC_SDA); +} + +static int i740fb_ddc_getscl(void *data) +{ + struct i740fb_par *par = data; + + i740outreg_mask(par, XRX, REG_DDC_DRIVE, 0, DDC_SCL); + + return !!(i740inreg(par, XRX, REG_DDC_STATE) & DDC_SCL); +} + +static int i740fb_ddc_getsda(void *data) +{ + struct i740fb_par *par = data; + + i740outreg_mask(par, XRX, REG_DDC_DRIVE, 0, DDC_SDA); + + return !!(i740inreg(par, XRX, REG_DDC_STATE) & DDC_SDA); +} + +static int __devinit i740fb_setup_ddc_bus(struct fb_info *info) +{ + struct i740fb_par *par = info->par; + + strlcpy(par->ddc_adapter.name, info->fix.id, + sizeof(par->ddc_adapter.name)); + par->ddc_adapter.owner = THIS_MODULE; + par->ddc_adapter.class = I2C_CLASS_DDC; + par->ddc_adapter.algo_data = &par->ddc_algo; + par->ddc_adapter.dev.parent = info->device; + par->ddc_algo.setsda = i740fb_ddc_setsda; + par->ddc_algo.setscl = i740fb_ddc_setscl; + par->ddc_algo.getsda = i740fb_ddc_getsda; + par->ddc_algo.getscl = i740fb_ddc_getscl; + par->ddc_algo.udelay = 10; + par->ddc_algo.timeout = 20; + par->ddc_algo.data = par; + + i2c_set_adapdata(&par->ddc_adapter, par); + + return i2c_bit_add_bus(&par->ddc_adapter); +} + +static int i740fb_open(struct fb_info *info, int user) +{ + struct i740fb_par *par = info->par; + + mutex_lock(&(par->open_lock)); + par->ref_count++; + mutex_unlock(&(par->open_lock)); + + return 0; +} + +static int i740fb_release(struct fb_info *info, int user) +{ + struct i740fb_par *par = info->par; + + mutex_lock(&(par->open_lock)); + if (par->ref_count == 0) { + printk(KERN_ERR "fb%d: release called with zero refcount\n", + info->node); + mutex_unlock(&(par->open_lock)); + return -EINVAL; + } + + par->ref_count--; + mutex_unlock(&(par->open_lock)); + + return 0; +} + +static u32 i740_calc_fifo(struct i740fb_par *par, u32 freq, int bpp) +{ + /* + * Would like to calculate these values automatically, but a generic + * algorithm does not seem possible. Note: These FIFO water mark + * values were tested on several cards and seem to eliminate the + * all of the snow and vertical banding, but fine adjustments will + * probably be required for other cards. + */ + + u32 wm; + + switch (bpp) { + case 8: + if (freq > 200) + wm = 0x18120000; + else if (freq > 175) + wm = 0x16110000; + else if (freq > 135) + wm = 0x120E0000; + else + wm = 0x100D0000; + break; + case 15: + case 16: + if (par->has_sgram) { + if (freq > 140) + wm = 0x2C1D0000; + else if (freq > 120) + wm = 0x2C180000; + else if (freq > 100) + wm = 0x24160000; + else if (freq > 90) + wm = 0x18120000; + else if (freq > 50) + wm = 0x16110000; + else if (freq > 32) + wm = 0x13100000; + else + wm = 0x120E0000; + } else { + if (freq > 160) + wm = 0x28200000; + else if (freq > 140) + wm = 0x2A1E0000; + else if (freq > 130) + wm = 0x2B1A0000; + else if (freq > 120) + wm = 0x2C180000; + else if (freq > 100) + wm = 0x24180000; + else if (freq > 90) + wm = 0x18120000; + else if (freq > 50) + wm = 0x16110000; + else if (freq > 32) + wm = 0x13100000; + else + wm = 0x120E0000; + } + break; + case 24: + if (par->has_sgram) { + if (freq > 130) + wm = 0x31200000; + else if (freq > 120) + wm = 0x2E200000; + else if (freq > 100) + wm = 0x2C1D0000; + else if (freq > 80) + wm = 0x25180000; + else if (freq > 64) + wm = 0x24160000; + else if (freq > 49) + wm = 0x18120000; + else if (freq > 32) + wm = 0x16110000; + else + wm = 0x13100000; + } else { + if (freq > 120) + wm = 0x311F0000; + else if (freq > 100) + wm = 0x2C1D0000; + else if (freq > 80) + wm = 0x25180000; + else if (freq > 64) + wm = 0x24160000; + else if (freq > 49) + wm = 0x18120000; + else if (freq > 32) + wm = 0x16110000; + else + wm = 0x13100000; + } + break; + case 32: + if (par->has_sgram) { + if (freq > 80) + wm = 0x2A200000; + else if (freq > 60) + wm = 0x281A0000; + else if (freq > 49) + wm = 0x25180000; + else if (freq > 32) + wm = 0x18120000; + else + wm = 0x16110000; + } else { + if (freq > 80) + wm = 0x29200000; + else if (freq > 60) + wm = 0x281A0000; + else if (freq > 49) + wm = 0x25180000; + else if (freq > 32) + wm = 0x18120000; + else + wm = 0x16110000; + } + break; + } + + return wm; +} + +/* clock calculation from i740fb by Patrick LERDA */ + +#define I740_RFREQ 1000000 +#define TARGET_MAX_N 30 +#define I740_FFIX (1 << 8) +#define I740_RFREQ_FIX (I740_RFREQ / I740_FFIX) +#define I740_REF_FREQ (6667 * I740_FFIX / 100) /* 66.67 MHz */ +#define I740_MAX_VCO_FREQ (450 * I740_FFIX) /* 450 MHz */ + +static void i740_calc_vclk(u32 freq, struct i740fb_par *par) +{ + const u32 err_max = freq / (200 * I740_RFREQ / I740_FFIX); + const u32 err_target = freq / (1000 * I740_RFREQ / I740_FFIX); + u32 err_best = 512 * I740_FFIX; + u32 f_err, f_vco; + int m_best = 0, n_best = 0, p_best = 0, d_best = 0; + int m, n; + + p_best = min(15, ilog2(I740_MAX_VCO_FREQ / (freq / I740_RFREQ_FIX))); + d_best = 0; + f_vco = (freq * (1 << p_best)) / I740_RFREQ_FIX; + freq = freq / I740_RFREQ_FIX; + + n = 2; + do { + n++; + m = ((f_vco * n) / I740_REF_FREQ + 2) / 4; + + if (m < 3) + m = 3; + + { + u32 f_out = (((m * I740_REF_FREQ * (4 << 2 * d_best)) + / n) + ((1 << p_best) / 2)) / (1 << p_best); + + f_err = (freq - f_out); + + if (abs(f_err) < err_max) { + m_best = m; + n_best = n; + err_best = f_err; + } + } + } while ((abs(f_err) >= err_target) && + ((n <= TARGET_MAX_N) || (abs(err_best) > err_max))); + + if (abs(f_err) < err_target) { + m_best = m; + n_best = n; + } + + par->video_clk2_m = (m_best - 2) & 0xFF; + par->video_clk2_n = (n_best - 2) & 0xFF; + par->video_clk2_mn_msbs = ((((n_best - 2) >> 4) & VCO_N_MSBS) + | (((m_best - 2) >> 8) & VCO_M_MSBS)); + par->video_clk2_div_sel = + ((p_best << 4) | (d_best ? 4 : 0) | REF_DIV_1); +} + +static int i740fb_decode_var(const struct fb_var_screeninfo *var, + struct i740fb_par *par, struct fb_info *info) +{ + /* + * Get the video params out of 'var'. + * If a value doesn't fit, round it up, if it's too big, return -EINVAL. + */ + + u32 xres, right, hslen, left, xtotal; + u32 yres, lower, vslen, upper, ytotal; + u32 vxres, xoffset, vyres, yoffset; + u32 bpp, base, dacspeed24, mem; + u8 r7; + int i; + + dev_dbg(info->device, "decode_var: xres: %i, yres: %i, xres_v: %i, xres_v: %i\n", + var->xres, var->yres, var->xres_virtual, var->xres_virtual); + dev_dbg(info->device, " xoff: %i, yoff: %i, bpp: %i, graysc: %i\n", + var->xoffset, var->yoffset, var->bits_per_pixel, + var->grayscale); + dev_dbg(info->device, " activate: %i, nonstd: %i, vmode: %i\n", + var->activate, var->nonstd, var->vmode); + dev_dbg(info->device, " pixclock: %i, hsynclen:%i, vsynclen:%i\n", + var->pixclock, var->hsync_len, var->vsync_len); + dev_dbg(info->device, " left: %i, right: %i, up:%i, lower:%i\n", + var->left_margin, var->right_margin, var->upper_margin, + var->lower_margin); + + + bpp = var->bits_per_pixel; + switch (bpp) { + case 1 ... 8: + bpp = 8; + if ((1000000 / var->pixclock) > DACSPEED8) { + dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 8bpp)\n", + 1000000 / var->pixclock, DACSPEED8); + return -EINVAL; + } + break; + case 9 ... 15: + bpp = 15; + case 16: + if ((1000000 / var->pixclock) > DACSPEED16) { + dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 15/16bpp)\n", + 1000000 / var->pixclock, DACSPEED16); + return -EINVAL; + } + break; + case 17 ... 24: + bpp = 24; + dacspeed24 = par->has_sgram ? DACSPEED24_SG : DACSPEED24_SD; + if ((1000000 / var->pixclock) > dacspeed24) { + dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 24bpp)\n", + 1000000 / var->pixclock, dacspeed24); + return -EINVAL; + } + break; + case 25 ... 32: + bpp = 32; + if ((1000000 / var->pixclock) > DACSPEED32) { + dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 32bpp)\n", + 1000000 / var->pixclock, DACSPEED32); + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + xres = ALIGN(var->xres, 8); + vxres = ALIGN(var->xres_virtual, 16); + if (vxres < xres) + vxres = xres; + + xoffset = ALIGN(var->xoffset, 8); + if (xres + xoffset > vxres) + xoffset = vxres - xres; + + left = ALIGN(var->left_margin, 8); + right = ALIGN(var->right_margin, 8); + hslen = ALIGN(var->hsync_len, 8); + + yres = var->yres; + vyres = var->yres_virtual; + if (yres > vyres) + vyres = yres; + + yoffset = var->yoffset; + if (yres + yoffset > vyres) + yoffset = vyres - yres; + + lower = var->lower_margin; + vslen = var->vsync_len; + upper = var->upper_margin; + + mem = vxres * vyres * ((bpp + 1) / 8); + if (mem > info->screen_size) { + dev_err(info->device, "not enough video memory (%d KB requested, %ld KB avaliable)\n", + mem >> 10, info->screen_size >> 10); + return -ENOMEM; + } + + if (yoffset + yres > vyres) + yoffset = vyres - yres; + + xtotal = xres + right + hslen + left; + ytotal = yres + lower + vslen + upper; + + par->crtc[VGA_CRTC_H_TOTAL] = (xtotal >> 3) - 5; + par->crtc[VGA_CRTC_H_DISP] = (xres >> 3) - 1; + par->crtc[VGA_CRTC_H_BLANK_START] = ((xres + right) >> 3) - 1; + par->crtc[VGA_CRTC_H_SYNC_START] = (xres + right) >> 3; + par->crtc[VGA_CRTC_H_SYNC_END] = (((xres + right + hslen) >> 3) & 0x1F) + | ((((xres + right + hslen) >> 3) & 0x20) << 2); + par->crtc[VGA_CRTC_H_BLANK_END] = ((xres + right + hslen) >> 3 & 0x1F) + | 0x80; + + par->crtc[VGA_CRTC_V_TOTAL] = ytotal - 2; + + r7 = 0x10; /* disable linecompare */ + if (ytotal & 0x100) + r7 |= 0x01; + if (ytotal & 0x200) + r7 |= 0x20; + + par->crtc[VGA_CRTC_PRESET_ROW] = 0; + par->crtc[VGA_CRTC_MAX_SCAN] = 0x40; /* 1 scanline, no linecmp */ + if (var->vmode & FB_VMODE_DOUBLE) + par->crtc[VGA_CRTC_MAX_SCAN] |= 0x80; + par->crtc[VGA_CRTC_CURSOR_START] = 0x00; + par->crtc[VGA_CRTC_CURSOR_END] = 0x00; + par->crtc[VGA_CRTC_CURSOR_HI] = 0x00; + par->crtc[VGA_CRTC_CURSOR_LO] = 0x00; + par->crtc[VGA_CRTC_V_DISP_END] = yres-1; + if ((yres-1) & 0x100) + r7 |= 0x02; + if ((yres-1) & 0x200) + r7 |= 0x40; + + par->crtc[VGA_CRTC_V_BLANK_START] = yres + lower - 1; + par->crtc[VGA_CRTC_V_SYNC_START] = yres + lower - 1; + if ((yres + lower - 1) & 0x100) + r7 |= 0x0C; + if ((yres + lower - 1) & 0x200) { + par->crtc[VGA_CRTC_MAX_SCAN] |= 0x20; + r7 |= 0x80; + } + + /* disabled IRQ */ + par->crtc[VGA_CRTC_V_SYNC_END] = + ((yres + lower - 1 + vslen) & 0x0F) & ~0x10; + /* 0x7F for VGA, but some SVGA chips require all 8 bits to be set */ + par->crtc[VGA_CRTC_V_BLANK_END] = (yres + lower - 1 + vslen) & 0xFF; + + par->crtc[VGA_CRTC_UNDERLINE] = 0x00; + par->crtc[VGA_CRTC_MODE] = 0xC3 ; + par->crtc[VGA_CRTC_LINE_COMPARE] = 0xFF; + par->crtc[VGA_CRTC_OVERFLOW] = r7; + + par->vss = 0x00; /* 3DA */ + + for (i = 0x00; i < 0x10; i++) + par->atc[i] = i; + par->atc[VGA_ATC_MODE] = 0x81; + par->atc[VGA_ATC_OVERSCAN] = 0x00; /* 0 for EGA, 0xFF for VGA */ + par->atc[VGA_ATC_PLANE_ENABLE] = 0x0F; + par->atc[VGA_ATC_COLOR_PAGE] = 0x00; + + par->misc = 0xC3; + if (var->sync & FB_SYNC_HOR_HIGH_ACT) + par->misc &= ~0x40; + if (var->sync & FB_SYNC_VERT_HIGH_ACT) + par->misc &= ~0x80; + + par->seq[VGA_SEQ_CLOCK_MODE] = 0x01; + par->seq[VGA_SEQ_PLANE_WRITE] = 0x0F; + par->seq[VGA_SEQ_CHARACTER_MAP] = 0x00; + par->seq[VGA_SEQ_MEMORY_MODE] = 0x06; + + par->gdc[VGA_GFX_SR_VALUE] = 0x00; + par->gdc[VGA_GFX_SR_ENABLE] = 0x00; + par->gdc[VGA_GFX_COMPARE_VALUE] = 0x00; + par->gdc[VGA_GFX_DATA_ROTATE] = 0x00; + par->gdc[VGA_GFX_PLANE_READ] = 0; + par->gdc[VGA_GFX_MODE] = 0x02; + par->gdc[VGA_GFX_MISC] = 0x05; + par->gdc[VGA_GFX_COMPARE_MASK] = 0x0F; + par->gdc[VGA_GFX_BIT_MASK] = 0xFF; + + base = (yoffset * vxres + (xoffset & ~7)) >> 2; + switch (bpp) { + case 8: + par->crtc[VGA_CRTC_OFFSET] = vxres >> 3; + par->ext_offset = vxres >> 11; + par->pixelpipe_cfg1 = DISPLAY_8BPP_MODE; + par->bitblt_cntl = COLEXP_8BPP; + break; + case 15: /* 0rrrrrgg gggbbbbb */ + case 16: /* rrrrrggg gggbbbbb */ + par->pixelpipe_cfg1 = (var->green.length == 6) ? + DISPLAY_16BPP_MODE : DISPLAY_15BPP_MODE; + par->crtc[VGA_CRTC_OFFSET] = vxres >> 2; + par->ext_offset = vxres >> 10; + par->bitblt_cntl = COLEXP_16BPP; + base *= 2; + break; + case 24: + par->crtc[VGA_CRTC_OFFSET] = (vxres * 3) >> 3; + par->ext_offset = (vxres * 3) >> 11; + par->pixelpipe_cfg1 = DISPLAY_24BPP_MODE; + par->bitblt_cntl = COLEXP_24BPP; + base &= 0xFFFFFFFE; /* ...ignore the last bit. */ + base *= 3; + break; + case 32: + par->crtc[VGA_CRTC_OFFSET] = vxres >> 1; + par->ext_offset = vxres >> 9; + par->pixelpipe_cfg1 = DISPLAY_32BPP_MODE; + par->bitblt_cntl = COLEXP_RESERVED; /* Unimplemented on i740 */ + base *= 4; + break; + } + + par->crtc[VGA_CRTC_START_LO] = base & 0x000000FF; + par->crtc[VGA_CRTC_START_HI] = (base & 0x0000FF00) >> 8; + par->ext_start_addr = + ((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE; + par->ext_start_addr_hi = (base & 0x3FC00000) >> 22; + + par->pixelpipe_cfg0 = DAC_8_BIT; + + par->pixelpipe_cfg2 = DISPLAY_GAMMA_ENABLE | OVERLAY_GAMMA_ENABLE; + par->io_cntl = EXTENDED_CRTC_CNTL; + par->address_mapping = LINEAR_MODE_ENABLE | PAGE_MAPPING_ENABLE; + par->display_cntl = HIRES_MODE; + + /* Set the MCLK freq */ + par->pll_cntl = PLL_MEMCLK_100000KHZ; /* 100 MHz -- use as default */ + + /* Calculate the extended CRTC regs */ + par->ext_vert_total = (ytotal - 2) >> 8; + par->ext_vert_disp_end = (yres - 1) >> 8; + par->ext_vert_sync_start = (yres + lower) >> 8; + par->ext_vert_blank_start = (yres + lower) >> 8; + par->ext_horiz_total = ((xtotal >> 3) - 5) >> 8; + par->ext_horiz_blank = (((xres + right) >> 3) & 0x40) >> 6; + + par->interlace_cntl = INTERLACE_DISABLE; + + /* Set the overscan color to 0. (NOTE: This only affects >8bpp mode) */ + par->atc[VGA_ATC_OVERSCAN] = 0; + + /* Calculate VCLK that most closely matches the requested dot clock */ + i740_calc_vclk((((u32)1e9) / var->pixclock) * (u32)(1e3), par); + + /* Since we program the clocks ourselves, always use VCLK2. */ + par->misc |= 0x0C; + + /* Calculate the FIFO Watermark and Burst Length. */ + par->lmi_fifo_watermark = + i740_calc_fifo(par, 1000000 / var->pixclock, bpp); + + return 0; +} + +static int i740fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + switch (var->bits_per_pixel) { + case 8: + var->red.offset = var->green.offset = var->blue.offset = 0; + var->red.length = var->green.length = var->blue.length = 8; + break; + case 16: + switch (var->green.length) { + default: + case 5: + var->red.offset = 10; + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 5; + var->blue.length = 5; + break; + case 6: + var->red.offset = 11; + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = var->blue.length = 5; + break; + } + break; + case 24: + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = var->green.length = var->blue.length = 8; + break; + case 32: + var->transp.offset = 24; + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->transp.length = 8; + var->red.length = var->green.length = var->blue.length = 8; + break; + default: + return -EINVAL; + } + + if (var->xres > var->xres_virtual) + var->xres_virtual = var->xres; + + if (var->yres > var->yres_virtual) + var->yres_virtual = var->yres; + + if (info->monspecs.hfmax && info->monspecs.vfmax && + info->monspecs.dclkmax && fb_validate_mode(var, info) < 0) + return -EINVAL; + + return 0; +} + +static void vga_protect(struct i740fb_par *par) +{ + /* disable the display */ + i740outreg_mask(par, VGA_SEQ_I, VGA_SEQ_CLOCK_MODE, 0x20, 0x20); + + i740inb(par, 0x3DA); + i740outb(par, VGA_ATT_W, 0x00); /* enable pallete access */ +} + +static void vga_unprotect(struct i740fb_par *par) +{ + /* reenable display */ + i740outreg_mask(par, VGA_SEQ_I, VGA_SEQ_CLOCK_MODE, 0, 0x20); + + i740inb(par, 0x3DA); + i740outb(par, VGA_ATT_W, 0x20); /* disable pallete access */ +} + +static int i740fb_set_par(struct fb_info *info) +{ + struct i740fb_par *par = info->par; + u32 itemp; + int i; + + i = i740fb_decode_var(&info->var, par, info); + if (i) + return i; + + memset(info->screen_base, 0, info->screen_size); + + vga_protect(par); + + i740outreg(par, XRX, DRAM_EXT_CNTL, DRAM_REFRESH_DISABLE); + + mdelay(1); + + i740outreg(par, XRX, VCLK2_VCO_M, par->video_clk2_m); + i740outreg(par, XRX, VCLK2_VCO_N, par->video_clk2_n); + i740outreg(par, XRX, VCLK2_VCO_MN_MSBS, par->video_clk2_mn_msbs); + i740outreg(par, XRX, VCLK2_VCO_DIV_SEL, par->video_clk2_div_sel); + + i740outreg_mask(par, XRX, PIXPIPE_CONFIG_0, + par->pixelpipe_cfg0 & DAC_8_BIT, 0x80); + + i740inb(par, 0x3DA); + i740outb(par, 0x3C0, 0x00); + + /* update misc output register */ + i740outb(par, VGA_MIS_W, par->misc | 0x01); + + /* synchronous reset on */ + i740outreg(par, VGA_SEQ_I, VGA_SEQ_RESET, 0x01); + /* write sequencer registers */ + i740outreg(par, VGA_SEQ_I, VGA_SEQ_CLOCK_MODE, + par->seq[VGA_SEQ_CLOCK_MODE] | 0x20); + for (i = 2; i < VGA_SEQ_C; i++) + i740outreg(par, VGA_SEQ_I, i, par->seq[i]); + + /* synchronous reset off */ + i740outreg(par, VGA_SEQ_I, VGA_SEQ_RESET, 0x03); + + /* deprotect CRT registers 0-7 */ + i740outreg(par, VGA_CRT_IC, VGA_CRTC_V_SYNC_END, + par->crtc[VGA_CRTC_V_SYNC_END]); + + /* write CRT registers */ + for (i = 0; i < VGA_CRT_C; i++) + i740outreg(par, VGA_CRT_IC, i, par->crtc[i]); + + /* write graphics controller registers */ + for (i = 0; i < VGA_GFX_C; i++) + i740outreg(par, VGA_GFX_I, i, par->gdc[i]); + + /* write attribute controller registers */ + for (i = 0; i < VGA_ATT_C; i++) { + i740inb(par, VGA_IS1_RC); /* reset flip-flop */ + i740outb(par, VGA_ATT_IW, i); + i740outb(par, VGA_ATT_IW, par->atc[i]); + } + + i740inb(par, VGA_IS1_RC); + i740outb(par, VGA_ATT_IW, 0x20); + + i740outreg(par, VGA_CRT_IC, EXT_VERT_TOTAL, par->ext_vert_total); + i740outreg(par, VGA_CRT_IC, EXT_VERT_DISPLAY, par->ext_vert_disp_end); + i740outreg(par, VGA_CRT_IC, EXT_VERT_SYNC_START, + par->ext_vert_sync_start); + i740outreg(par, VGA_CRT_IC, EXT_VERT_BLANK_START, + par->ext_vert_blank_start); + i740outreg(par, VGA_CRT_IC, EXT_HORIZ_TOTAL, par->ext_horiz_total); + i740outreg(par, VGA_CRT_IC, EXT_HORIZ_BLANK, par->ext_horiz_blank); + i740outreg(par, VGA_CRT_IC, EXT_OFFSET, par->ext_offset); + i740outreg(par, VGA_CRT_IC, EXT_START_ADDR_HI, par->ext_start_addr_hi); + i740outreg(par, VGA_CRT_IC, EXT_START_ADDR, par->ext_start_addr); + + i740outreg_mask(par, VGA_CRT_IC, INTERLACE_CNTL, + par->interlace_cntl, INTERLACE_ENABLE); + i740outreg_mask(par, XRX, ADDRESS_MAPPING, par->address_mapping, 0x1F); + i740outreg_mask(par, XRX, BITBLT_CNTL, par->bitblt_cntl, COLEXP_MODE); + i740outreg_mask(par, XRX, DISPLAY_CNTL, + par->display_cntl, VGA_WRAP_MODE | GUI_MODE); + i740outreg_mask(par, XRX, PIXPIPE_CONFIG_0, par->pixelpipe_cfg0, 0x9B); + i740outreg_mask(par, XRX, PIXPIPE_CONFIG_2, par->pixelpipe_cfg2, 0x0C); + + i740outreg(par, XRX, PLL_CNTL, par->pll_cntl); + + i740outreg_mask(par, XRX, PIXPIPE_CONFIG_1, + par->pixelpipe_cfg1, DISPLAY_COLOR_MODE); + + itemp = readl(par->regs + FWATER_BLC); + itemp &= ~(LMI_BURST_LENGTH | LMI_FIFO_WATERMARK); + itemp |= par->lmi_fifo_watermark; + writel(itemp, par->regs + FWATER_BLC); + + i740outreg(par, XRX, DRAM_EXT_CNTL, DRAM_REFRESH_60HZ); + + i740outreg_mask(par, MRX, COL_KEY_CNTL_1, 0, BLANK_DISP_OVERLAY); + i740outreg_mask(par, XRX, IO_CTNL, + par->io_cntl, EXTENDED_ATTR_CNTL | EXTENDED_CRTC_CNTL); + + if (par->pixelpipe_cfg1 != DISPLAY_8BPP_MODE) { + i740outb(par, VGA_PEL_MSK, 0xFF); + i740outb(par, VGA_PEL_IW, 0x00); + for (i = 0; i < 256; i++) { + itemp = (par->pixelpipe_cfg0 & DAC_8_BIT) ? i : i >> 2; + i740outb(par, VGA_PEL_D, itemp); + i740outb(par, VGA_PEL_D, itemp); + i740outb(par, VGA_PEL_D, itemp); + } + } + + /* Wait for screen to stabilize. */ + mdelay(50); + vga_unprotect(par); + + info->fix.line_length = + info->var.xres_virtual * info->var.bits_per_pixel / 8; + if (info->var.bits_per_pixel == 8) + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + else + info->fix.visual = FB_VISUAL_TRUECOLOR; + + return 0; +} + +static int i740fb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + u32 r, g, b; + + dev_dbg(info->device, "setcolreg: regno: %i, red=%d, green=%d, blue=%d, transp=%d, bpp=%d\n", + regno, red, green, blue, transp, info->var.bits_per_pixel); + + switch (info->fix.visual) { + case FB_VISUAL_PSEUDOCOLOR: + if (regno >= 256) + return -EINVAL; + i740outb(info->par, VGA_PEL_IW, regno); + i740outb(info->par, VGA_PEL_D, red >> 8); + i740outb(info->par, VGA_PEL_D, green >> 8); + i740outb(info->par, VGA_PEL_D, blue >> 8); + break; + case FB_VISUAL_TRUECOLOR: + if (regno >= 16) + return -EINVAL; + r = (red >> (16 - info->var.red.length)) + << info->var.red.offset; + b = (blue >> (16 - info->var.blue.length)) + << info->var.blue.offset; + g = (green >> (16 - info->var.green.length)) + << info->var.green.offset; + ((u32 *) info->pseudo_palette)[regno] = r | g | b; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int i740fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct i740fb_par *par = info->par; + u32 base = (var->yoffset * info->var.xres_virtual + + (var->xoffset & ~7)) >> 2; + + dev_dbg(info->device, "pan_display: xoffset: %i yoffset: %i base: %i\n", + var->xoffset, var->yoffset, base); + + switch (info->var.bits_per_pixel) { + case 8: + break; + case 15: + case 16: + base *= 2; + break; + case 24: + /* + * The last bit does not seem to have any effect on the start + * address register in 24bpp mode, so... + */ + base &= 0xFFFFFFFE; /* ...ignore the last bit. */ + base *= 3; + break; + case 32: + base *= 4; + break; + } + + par->crtc[VGA_CRTC_START_LO] = base & 0x000000FF; + par->crtc[VGA_CRTC_START_HI] = (base & 0x0000FF00) >> 8; + par->ext_start_addr_hi = (base & 0x3FC00000) >> 22; + par->ext_start_addr = + ((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE; + + i740outreg(par, VGA_CRT_IC, VGA_CRTC_START_LO, base & 0x000000FF); + i740outreg(par, VGA_CRT_IC, VGA_CRTC_START_HI, + (base & 0x0000FF00) >> 8); + i740outreg(par, VGA_CRT_IC, EXT_START_ADDR_HI, + (base & 0x3FC00000) >> 22); + i740outreg(par, VGA_CRT_IC, EXT_START_ADDR, + ((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE); + + return 0; +} + +static int i740fb_blank(int blank_mode, struct fb_info *info) +{ + struct i740fb_par *par = info->par; + + unsigned char SEQ01; + int DPMSSyncSelect; + + switch (blank_mode) { + case FB_BLANK_UNBLANK: + case FB_BLANK_NORMAL: + SEQ01 = 0x00; + DPMSSyncSelect = HSYNC_ON | VSYNC_ON; + break; + case FB_BLANK_VSYNC_SUSPEND: + SEQ01 = 0x20; + DPMSSyncSelect = HSYNC_ON | VSYNC_OFF; + break; + case FB_BLANK_HSYNC_SUSPEND: + SEQ01 = 0x20; + DPMSSyncSelect = HSYNC_OFF | VSYNC_ON; + break; + case FB_BLANK_POWERDOWN: + SEQ01 = 0x20; + DPMSSyncSelect = HSYNC_OFF | VSYNC_OFF; + break; + default: + return -EINVAL; + } + /* Turn the screen on/off */ + i740outb(par, SRX, 0x01); + SEQ01 |= i740inb(par, SRX + 1) & ~0x20; + i740outb(par, SRX, 0x01); + i740outb(par, SRX + 1, SEQ01); + + /* Set the DPMS mode */ + i740outreg(par, XRX, DPMS_SYNC_SELECT, DPMSSyncSelect); + + /* Let fbcon do a soft blank for us */ + return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0; +} + +static struct fb_ops i740fb_ops = { + .owner = THIS_MODULE, + .fb_open = i740fb_open, + .fb_release = i740fb_release, + .fb_check_var = i740fb_check_var, + .fb_set_par = i740fb_set_par, + .fb_setcolreg = i740fb_setcolreg, + .fb_blank = i740fb_blank, + .fb_pan_display = i740fb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +/* ------------------------------------------------------------------------- */ + +static int __devinit i740fb_probe(struct pci_dev *dev, + const struct pci_device_id *ent) +{ + struct fb_info *info; + struct i740fb_par *par; + int ret, tmp; + bool found = false; + u8 *edid; + + info = framebuffer_alloc(sizeof(struct i740fb_par), &(dev->dev)); + if (!info) { + dev_err(&(dev->dev), "cannot allocate framebuffer\n"); + return -ENOMEM; + } + + par = info->par; + mutex_init(&par->open_lock); + + info->var.activate = FB_ACTIVATE_NOW; + info->var.bits_per_pixel = 8; + info->fbops = &i740fb_ops; + info->pseudo_palette = par->pseudo_palette; + + ret = pci_enable_device(dev); + if (ret) { + dev_err(info->device, "cannot enable PCI device\n"); + goto err_enable_device; + } + + ret = pci_request_regions(dev, info->fix.id); + if (ret) { + dev_err(info->device, "error requesting regions\n"); + goto err_request_regions; + } + + info->screen_base = pci_ioremap_bar(dev, 0); + if (!info->screen_base) { + dev_err(info->device, "error remapping base\n"); + ret = -ENOMEM; + goto err_ioremap_1; + } + + par->regs = pci_ioremap_bar(dev, 1); + if (!par->regs) { + dev_err(info->device, "error remapping MMIO\n"); + ret = -ENOMEM; + goto err_ioremap_2; + } + + /* detect memory size */ + if ((i740inreg(par, XRX, DRAM_ROW_TYPE) & DRAM_ROW_1) + == DRAM_ROW_1_SDRAM) + i740outb(par, XRX, DRAM_ROW_BNDRY_1); + else + i740outb(par, XRX, DRAM_ROW_BNDRY_0); + info->screen_size = i740inb(par, XRX + 1) * 1024 * 1024; + /* detect memory type */ + tmp = i740inreg(par, XRX, DRAM_ROW_CNTL_LO); + par->has_sgram = !((tmp & DRAM_RAS_TIMING) || + (tmp & DRAM_RAS_PRECHARGE)); + + printk(KERN_INFO "fb%d: Intel740 on %s, %ld KB %s\n", info->node, + pci_name(dev), info->screen_size >> 10, + par->has_sgram ? "SGRAM" : "SDRAM"); + + info->fix = i740fb_fix; + info->fix.mmio_start = pci_resource_start(dev, 1); + info->fix.mmio_len = pci_resource_len(dev, 1); + info->fix.smem_start = pci_resource_start(dev, 0); + info->fix.smem_len = info->screen_size; + info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; + + if (i740fb_setup_ddc_bus(info) == 0) { + par->ddc_registered = true; + edid = fb_ddc_read(&par->ddc_adapter); + if (edid) { + fb_edid_to_monspecs(edid, &info->monspecs); + kfree(edid); + if (!info->monspecs.modedb) + dev_err(info->device, + "error getting mode database\n"); + else { + const struct fb_videomode *m; + + fb_videomode_to_modelist( + info->monspecs.modedb, + info->monspecs.modedb_len, + &info->modelist); + m = fb_find_best_display(&info->monspecs, + &info->modelist); + if (m) { + fb_videomode_to_var(&info->var, m); + /* fill all other info->var's fields */ + if (!i740fb_check_var(&info->var, info)) + found = true; + } + } + } + } + + if (!mode_option && !found) + mode_option = "640x480-8@60"; + + if (mode_option) { + ret = fb_find_mode(&info->var, info, mode_option, + info->monspecs.modedb, + info->monspecs.modedb_len, + NULL, info->var.bits_per_pixel); + if (!ret || ret == 4) { + dev_err(info->device, "mode %s not found\n", + mode_option); + ret = -EINVAL; + } + } + + fb_destroy_modedb(info->monspecs.modedb); + info->monspecs.modedb = NULL; + + /* maximize virtual vertical size for fast scrolling */ + info->var.yres_virtual = info->fix.smem_len * 8 / + (info->var.bits_per_pixel * info->var.xres_virtual); + + if (ret == -EINVAL) + goto err_find_mode; + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret) { + dev_err(info->device, "cannot allocate colormap\n"); + goto err_alloc_cmap; + } + + ret = register_framebuffer(info); + if (ret) { + dev_err(info->device, "error registering framebuffer\n"); + goto err_reg_framebuffer; + } + + printk(KERN_INFO "fb%d: %s frame buffer device\n", + info->node, info->fix.id); + pci_set_drvdata(dev, info); +#ifdef CONFIG_MTRR + if (mtrr) { + par->mtrr_reg = -1; + par->mtrr_reg = mtrr_add(info->fix.smem_start, + info->fix.smem_len, MTRR_TYPE_WRCOMB, 1); + } +#endif + return 0; + +err_reg_framebuffer: + fb_dealloc_cmap(&info->cmap); +err_alloc_cmap: +err_find_mode: + if (par->ddc_registered) + i2c_del_adapter(&par->ddc_adapter); + pci_iounmap(dev, par->regs); +err_ioremap_2: + pci_iounmap(dev, info->screen_base); +err_ioremap_1: + pci_release_regions(dev); +err_request_regions: +/* pci_disable_device(dev); */ +err_enable_device: + framebuffer_release(info); + return ret; +} + +static void __devexit i740fb_remove(struct pci_dev *dev) +{ + struct fb_info *info = pci_get_drvdata(dev); + + if (info) { + struct i740fb_par *par = info->par; + +#ifdef CONFIG_MTRR + if (par->mtrr_reg >= 0) { + mtrr_del(par->mtrr_reg, 0, 0); + par->mtrr_reg = -1; + } +#endif + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); + if (par->ddc_registered) + i2c_del_adapter(&par->ddc_adapter); + pci_iounmap(dev, par->regs); + pci_iounmap(dev, info->screen_base); + pci_release_regions(dev); +/* pci_disable_device(dev); */ + pci_set_drvdata(dev, NULL); + framebuffer_release(info); + } +} + +#ifdef CONFIG_PM +static int i740fb_suspend(struct pci_dev *dev, pm_message_t state) +{ + struct fb_info *info = pci_get_drvdata(dev); + struct i740fb_par *par = info->par; + + /* don't disable console during hibernation and wakeup from it */ + if (state.event == PM_EVENT_FREEZE || state.event == PM_EVENT_PRETHAW) + return 0; + + console_lock(); + mutex_lock(&(par->open_lock)); + + /* do nothing if framebuffer is not active */ + if (par->ref_count == 0) { + mutex_unlock(&(par->open_lock)); + console_unlock(); + return 0; + } + + fb_set_suspend(info, 1); + + pci_save_state(dev); + pci_disable_device(dev); + pci_set_power_state(dev, pci_choose_state(dev, state)); + + mutex_unlock(&(par->open_lock)); + console_unlock(); + + return 0; +} + +static int i740fb_resume(struct pci_dev *dev) +{ + struct fb_info *info = pci_get_drvdata(dev); + struct i740fb_par *par = info->par; + + console_lock(); + mutex_lock(&(par->open_lock)); + + if (par->ref_count == 0) + goto fail; + + pci_set_power_state(dev, PCI_D0); + pci_restore_state(dev); + if (pci_enable_device(dev)) + goto fail; + + i740fb_set_par(info); + fb_set_suspend(info, 0); + +fail: + mutex_unlock(&(par->open_lock)); + console_unlock(); + return 0; +} +#else +#define i740fb_suspend NULL +#define i740fb_resume NULL +#endif /* CONFIG_PM */ + +#define I740_ID_PCI 0x00d1 +#define I740_ID_AGP 0x7800 + +static DEFINE_PCI_DEVICE_TABLE(i740fb_id_table) = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, I740_ID_PCI) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, I740_ID_AGP) }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, i740fb_id_table); + +static struct pci_driver i740fb_driver = { + .name = "i740fb", + .id_table = i740fb_id_table, + .probe = i740fb_probe, + .remove = __devexit_p(i740fb_remove), + .suspend = i740fb_suspend, + .resume = i740fb_resume, +}; + +#ifndef MODULE +static int __init i740fb_setup(char *options) +{ + char *opt; + + if (!options || !*options) + return 0; + + while ((opt = strsep(&options, ",")) != NULL) { + if (!*opt) + continue; +#ifdef CONFIG_MTRR + else if (!strncmp(opt, "mtrr:", 5)) + mtrr = simple_strtoul(opt + 5, NULL, 0); +#endif + else + mode_option = opt; + } + + return 0; +} +#endif + +int __init i740fb_init(void) +{ +#ifndef MODULE + char *option = NULL; + + if (fb_get_options("i740fb", &option)) + return -ENODEV; + i740fb_setup(option); +#endif + + return pci_register_driver(&i740fb_driver); +} + +static void __exit i740fb_exit(void) +{ + pci_unregister_driver(&i740fb_driver); +} + +module_init(i740fb_init); +module_exit(i740fb_exit); + +MODULE_AUTHOR("(c) 2011 Ondrej Zary <linux@rainbow-software.org>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("fbdev driver for Intel740"); + +module_param(mode_option, charp, 0444); +MODULE_PARM_DESC(mode_option, "Default video mode ('640x480-8@60', etc)"); + +#ifdef CONFIG_MTRR +module_param(mtrr, int, 0444); +MODULE_PARM_DESC(mtrr, "Enable write-combining with MTRR (1=enable, 0=disable, default=1)"); +#endif diff --git a/drivers/video/msm/mddi_client_nt35399.c b/drivers/video/msm/mddi_client_nt35399.c index f239f4a25e01..7fcd67e132bf 100644 --- a/drivers/video/msm/mddi_client_nt35399.c +++ b/drivers/video/msm/mddi_client_nt35399.c @@ -155,14 +155,10 @@ static int setup_vsync(struct panel_info *panel, int init) ret = 0; goto uninit; } - ret = gpio_request(gpio, "vsync"); + ret = gpio_request_one(gpio, GPIOF_IN, "vsync"); if (ret) goto err_request_gpio_failed; - ret = gpio_direction_input(gpio); - if (ret) - goto err_gpio_direction_input_failed; - ret = irq = gpio_to_irq(gpio); if (ret < 0) goto err_get_irq_num_failed; @@ -180,7 +176,6 @@ uninit: free_irq(gpio_to_irq(gpio), panel->client_data); err_request_irq_failed: err_get_irq_num_failed: -err_gpio_direction_input_failed: gpio_free(gpio); err_request_gpio_failed: return ret; diff --git a/drivers/video/msm/mddi_client_toshiba.c b/drivers/video/msm/mddi_client_toshiba.c index f9bc932ac46b..053eb6877330 100644 --- a/drivers/video/msm/mddi_client_toshiba.c +++ b/drivers/video/msm/mddi_client_toshiba.c @@ -186,14 +186,10 @@ static int setup_vsync(struct panel_info *panel, ret = 0; goto uninit; } - ret = gpio_request(gpio, "vsync"); + ret = gpio_request_one(gpio, GPIOF_IN, "vsync"); if (ret) goto err_request_gpio_failed; - ret = gpio_direction_input(gpio); - if (ret) - goto err_gpio_direction_input_failed; - ret = irq = gpio_to_irq(gpio); if (ret < 0) goto err_get_irq_num_failed; @@ -210,7 +206,6 @@ uninit: free_irq(gpio_to_irq(gpio), panel); err_request_irq_failed: err_get_irq_num_failed: -err_gpio_direction_input_failed: gpio_free(gpio); err_request_gpio_failed: return ret; diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index 84ff23208c25..1e7536d9a8fc 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -1,11 +1,10 @@ config FB_OMAP tristate "OMAP frame buffer support (EXPERIMENTAL)" - depends on FB && (OMAP2_DSS = "n") - depends on ARCH_OMAP1 || ARCH_OMAP2 || ARCH_OMAP3 + depends on FB + depends on ARCH_OMAP1 select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT - select TWL4030_CORE if MACH_OMAP_2430SDP help Frame buffer driver for OMAP based boards. @@ -23,13 +22,6 @@ config FB_OMAP_LCDC_HWA742 Say Y here if you want to have support for the external Epson HWA742 LCD controller. -config FB_OMAP_LCDC_BLIZZARD - bool "Epson Blizzard LCD controller support" - depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL - help - Say Y here if you want to have support for the external - Epson Blizzard LCD controller. - config FB_OMAP_MANUAL_UPDATE bool "Default to manual update mode" depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL @@ -49,7 +41,7 @@ config FB_OMAP_LCD_MIPID config FB_OMAP_BOOTLOADER_INIT bool "Check bootloader initialization" - depends on FB_OMAP || FB_OMAP2 + depends on FB_OMAP help Say Y here if you want to enable checking if the bootloader has already initialized the display controller. In this case the @@ -68,7 +60,7 @@ config FB_OMAP_CONSISTENT_DMA_SIZE config FB_OMAP_DMA_TUNE bool "Set DMA SDRAM access priority high" - depends on FB_OMAP && ARCH_OMAP1 + depends on FB_OMAP help On systems in which video memory is in system memory (SDRAM) this will speed up graphics DMA operations. diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index ef78550917ff..1927faffb5bc 100644 --- a/drivers/video/omap/Makefile +++ b/drivers/video/omap/Makefile @@ -1,20 +1,14 @@ # -# Makefile for the new OMAP framebuffer device driver +# Makefile for the OMAP1 framebuffer device driver # obj-$(CONFIG_FB_OMAP) += omapfb.o -objs-yy := omapfb_main.o +objs-yy := omapfb_main.o lcdc.o -objs-y$(CONFIG_ARCH_OMAP1) += lcdc.o -objs-y$(CONFIG_ARCH_OMAP2) += dispc.o -objs-y$(CONFIG_ARCH_OMAP3) += dispc.o - -objs-$(CONFIG_ARCH_OMAP1)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o -objs-$(CONFIG_ARCH_OMAP2)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += rfbi.o +objs-y$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o -objs-y$(CONFIG_FB_OMAP_LCDC_BLIZZARD) += blizzard.o objs-y$(CONFIG_MACH_AMS_DELTA) += lcd_ams_delta.o objs-y$(CONFIG_MACH_OMAP_H3) += lcd_h3.o diff --git a/drivers/video/omap/blizzard.c b/drivers/video/omap/blizzard.c deleted file mode 100644 index c0504a8a5079..000000000000 --- a/drivers/video/omap/blizzard.c +++ /dev/null @@ -1,1648 +0,0 @@ -/* - * Epson Blizzard LCD controller driver - * - * Copyright (C) 2004-2005 Nokia Corporation - * Authors: Juha Yrjola <juha.yrjola@nokia.com> - * Imre Deak <imre.deak@nokia.com> - * YUV support: Jussi Laako <jussi.laako@nokia.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#include <linux/module.h> -#include <linux/mm.h> -#include <linux/fb.h> -#include <linux/delay.h> -#include <linux/clk.h> - -#include <plat/dma.h> -#include <plat/blizzard.h> - -#include "omapfb.h" -#include "dispc.h" - -#define MODULE_NAME "blizzard" - -#define BLIZZARD_REV_CODE 0x00 -#define BLIZZARD_CONFIG 0x02 -#define BLIZZARD_PLL_DIV 0x04 -#define BLIZZARD_PLL_LOCK_RANGE 0x06 -#define BLIZZARD_PLL_CLOCK_SYNTH_0 0x08 -#define BLIZZARD_PLL_CLOCK_SYNTH_1 0x0a -#define BLIZZARD_PLL_MODE 0x0c -#define BLIZZARD_CLK_SRC 0x0e -#define BLIZZARD_MEM_BANK0_ACTIVATE 0x10 -#define BLIZZARD_MEM_BANK0_STATUS 0x14 -#define BLIZZARD_PANEL_CONFIGURATION 0x28 -#define BLIZZARD_HDISP 0x2a -#define BLIZZARD_HNDP 0x2c -#define BLIZZARD_VDISP0 0x2e -#define BLIZZARD_VDISP1 0x30 -#define BLIZZARD_VNDP 0x32 -#define BLIZZARD_HSW 0x34 -#define BLIZZARD_VSW 0x38 -#define BLIZZARD_DISPLAY_MODE 0x68 -#define BLIZZARD_INPUT_WIN_X_START_0 0x6c -#define BLIZZARD_DATA_SOURCE_SELECT 0x8e -#define BLIZZARD_DISP_MEM_DATA_PORT 0x90 -#define BLIZZARD_DISP_MEM_READ_ADDR0 0x92 -#define BLIZZARD_POWER_SAVE 0xE6 -#define BLIZZARD_NDISP_CTRL_STATUS 0xE8 - -/* Data source select */ -/* For S1D13745 */ -#define BLIZZARD_SRC_WRITE_LCD_BACKGROUND 0x00 -#define BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE 0x01 -#define BLIZZARD_SRC_WRITE_OVERLAY_ENABLE 0x04 -#define BLIZZARD_SRC_DISABLE_OVERLAY 0x05 -/* For S1D13744 */ -#define BLIZZARD_SRC_WRITE_LCD 0x00 -#define BLIZZARD_SRC_BLT_LCD 0x06 - -#define BLIZZARD_COLOR_RGB565 0x01 -#define BLIZZARD_COLOR_YUV420 0x09 - -#define BLIZZARD_VERSION_S1D13745 0x01 /* Hailstorm */ -#define BLIZZARD_VERSION_S1D13744 0x02 /* Blizzard */ - -#define BLIZZARD_AUTO_UPDATE_TIME (HZ / 20) - -/* Reserve 4 request slots for requests in irq context */ -#define REQ_POOL_SIZE 24 -#define IRQ_REQ_POOL_SIZE 4 - -#define REQ_FROM_IRQ_POOL 0x01 - -#define REQ_COMPLETE 0 -#define REQ_PENDING 1 - -struct blizzard_reg_list { - int start; - int end; -}; - -/* These need to be saved / restored separately from the rest. */ -static const struct blizzard_reg_list blizzard_pll_regs[] = { - { - .start = 0x04, /* Don't save PLL ctrl (0x0C) */ - .end = 0x0a, - }, - { - .start = 0x0e, /* Clock configuration */ - .end = 0x0e, - }, -}; - -static const struct blizzard_reg_list blizzard_gen_regs[] = { - { - .start = 0x18, /* SDRAM control */ - .end = 0x20, - }, - { - .start = 0x28, /* LCD Panel configuration */ - .end = 0x5a, /* HSSI interface, TV configuration */ - }, -}; - -static u8 blizzard_reg_cache[0x5a / 2]; - -struct update_param { - int plane; - int x, y, width, height; - int out_x, out_y; - int out_width, out_height; - int color_mode; - int bpp; - int flags; -}; - -struct blizzard_request { - struct list_head entry; - unsigned int flags; - - int (*handler)(struct blizzard_request *req); - void (*complete)(void *data); - void *complete_data; - - union { - struct update_param update; - struct completion *sync; - } par; -}; - -struct plane_info { - unsigned long offset; - int pos_x, pos_y; - int width, height; - int out_width, out_height; - int scr_width; - int color_mode; - int bpp; -}; - -struct blizzard_struct { - enum omapfb_update_mode update_mode; - enum omapfb_update_mode update_mode_before_suspend; - - struct timer_list auto_update_timer; - int stop_auto_update; - struct omapfb_update_window auto_update_window; - int enabled_planes; - int vid_nonstd_color; - int vid_scaled; - int last_color_mode; - int zoom_on; - int zoom_area_gx1; - int zoom_area_gx2; - int zoom_area_gy1; - int zoom_area_gy2; - int screen_width; - int screen_height; - unsigned te_connected:1; - unsigned vsync_only:1; - - struct plane_info plane[OMAPFB_PLANE_NUM]; - - struct blizzard_request req_pool[REQ_POOL_SIZE]; - struct list_head pending_req_list; - struct list_head free_req_list; - struct semaphore req_sema; - spinlock_t req_lock; - - unsigned long sys_ck_rate; - struct extif_timings reg_timings, lut_timings; - - u32 max_transmit_size; - u32 extif_clk_period; - int extif_clk_div; - unsigned long pix_tx_time; - unsigned long line_upd_time; - - struct omapfb_device *fbdev; - struct lcd_ctrl_extif *extif; - const struct lcd_ctrl *int_ctrl; - - void (*power_up)(struct device *dev); - void (*power_down)(struct device *dev); - - int version; -} blizzard; - -struct lcd_ctrl blizzard_ctrl; - -static u8 blizzard_read_reg(u8 reg) -{ - u8 data; - - blizzard.extif->set_bits_per_cycle(8); - blizzard.extif->write_command(®, 1); - blizzard.extif->read_data(&data, 1); - - return data; -} - -static void blizzard_write_reg(u8 reg, u8 val) -{ - blizzard.extif->set_bits_per_cycle(8); - blizzard.extif->write_command(®, 1); - blizzard.extif->write_data(&val, 1); -} - -static void blizzard_restart_sdram(void) -{ - unsigned long tmo; - - blizzard_write_reg(BLIZZARD_MEM_BANK0_ACTIVATE, 0); - udelay(50); - blizzard_write_reg(BLIZZARD_MEM_BANK0_ACTIVATE, 1); - tmo = jiffies + msecs_to_jiffies(200); - while (!(blizzard_read_reg(BLIZZARD_MEM_BANK0_STATUS) & 0x01)) { - if (time_after(jiffies, tmo)) { - dev_err(blizzard.fbdev->dev, - "s1d1374x: SDRAM not ready\n"); - break; - } - msleep(1); - } -} - -static void blizzard_stop_sdram(void) -{ - blizzard_write_reg(BLIZZARD_MEM_BANK0_ACTIVATE, 0); -} - -/* Wait until the last window was completely written into the controllers - * SDRAM and we can start transferring the next window. - */ -static void blizzard_wait_line_buffer(void) -{ - unsigned long tmo = jiffies + msecs_to_jiffies(30); - - while (blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS) & (1 << 7)) { - if (time_after(jiffies, tmo)) { - if (printk_ratelimit()) - dev_err(blizzard.fbdev->dev, - "s1d1374x: line buffer not ready\n"); - break; - } - } -} - -/* Wait until the YYC color space converter is idle. */ -static void blizzard_wait_yyc(void) -{ - unsigned long tmo = jiffies + msecs_to_jiffies(30); - - while (blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS) & (1 << 4)) { - if (time_after(jiffies, tmo)) { - if (printk_ratelimit()) - dev_err(blizzard.fbdev->dev, - "s1d1374x: YYC not ready\n"); - break; - } - } -} - -static void disable_overlay(void) -{ - blizzard_write_reg(BLIZZARD_DATA_SOURCE_SELECT, - BLIZZARD_SRC_DISABLE_OVERLAY); -} - -static void set_window_regs(int x_start, int y_start, int x_end, int y_end, - int x_out_start, int y_out_start, - int x_out_end, int y_out_end, int color_mode, - int zoom_off, int flags) -{ - u8 tmp[18]; - u8 cmd; - - x_end--; - y_end--; - tmp[0] = x_start; - tmp[1] = x_start >> 8; - tmp[2] = y_start; - tmp[3] = y_start >> 8; - tmp[4] = x_end; - tmp[5] = x_end >> 8; - tmp[6] = y_end; - tmp[7] = y_end >> 8; - - x_out_end--; - y_out_end--; - tmp[8] = x_out_start; - tmp[9] = x_out_start >> 8; - tmp[10] = y_out_start; - tmp[11] = y_out_start >> 8; - tmp[12] = x_out_end; - tmp[13] = x_out_end >> 8; - tmp[14] = y_out_end; - tmp[15] = y_out_end >> 8; - - tmp[16] = color_mode; - if (zoom_off && blizzard.version == BLIZZARD_VERSION_S1D13745) - tmp[17] = BLIZZARD_SRC_WRITE_LCD_BACKGROUND; - else if (flags & OMAPFB_FORMAT_FLAG_ENABLE_OVERLAY) - tmp[17] = BLIZZARD_SRC_WRITE_OVERLAY_ENABLE; - else - tmp[17] = blizzard.version == BLIZZARD_VERSION_S1D13744 ? - BLIZZARD_SRC_WRITE_LCD : - BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE; - - blizzard.extif->set_bits_per_cycle(8); - cmd = BLIZZARD_INPUT_WIN_X_START_0; - blizzard.extif->write_command(&cmd, 1); - blizzard.extif->write_data(tmp, 18); -} - -static void enable_tearsync(int y, int width, int height, int screen_height, - int out_height, int force_vsync) -{ - u8 b; - - b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS); - b |= 1 << 3; - blizzard_write_reg(BLIZZARD_NDISP_CTRL_STATUS, b); - - if (likely(blizzard.vsync_only || force_vsync)) { - blizzard.extif->enable_tearsync(1, 0); - return; - } - - if (width * blizzard.pix_tx_time < blizzard.line_upd_time) { - blizzard.extif->enable_tearsync(1, 0); - return; - } - - if ((width * blizzard.pix_tx_time / 1000) * height < - (y + out_height) * (blizzard.line_upd_time / 1000)) { - blizzard.extif->enable_tearsync(1, 0); - return; - } - - blizzard.extif->enable_tearsync(1, y + 1); -} - -static void disable_tearsync(void) -{ - u8 b; - - blizzard.extif->enable_tearsync(0, 0); - b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS); - b &= ~(1 << 3); - blizzard_write_reg(BLIZZARD_NDISP_CTRL_STATUS, b); - b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS); -} - -static inline void set_extif_timings(const struct extif_timings *t); - -static inline struct blizzard_request *alloc_req(void) -{ - unsigned long flags; - struct blizzard_request *req; - int req_flags = 0; - - if (!in_interrupt()) - down(&blizzard.req_sema); - else - req_flags = REQ_FROM_IRQ_POOL; - - spin_lock_irqsave(&blizzard.req_lock, flags); - BUG_ON(list_empty(&blizzard.free_req_list)); - req = list_entry(blizzard.free_req_list.next, - struct blizzard_request, entry); - list_del(&req->entry); - spin_unlock_irqrestore(&blizzard.req_lock, flags); - - INIT_LIST_HEAD(&req->entry); - req->flags = req_flags; - - return req; -} - -static inline void free_req(struct blizzard_request *req) -{ - unsigned long flags; - - spin_lock_irqsave(&blizzard.req_lock, flags); - - list_move(&req->entry, &blizzard.free_req_list); - if (!(req->flags & REQ_FROM_IRQ_POOL)) - up(&blizzard.req_sema); - - spin_unlock_irqrestore(&blizzard.req_lock, flags); -} - -static void process_pending_requests(void) -{ - unsigned long flags; - - spin_lock_irqsave(&blizzard.req_lock, flags); - - while (!list_empty(&blizzard.pending_req_list)) { - struct blizzard_request *req; - void (*complete)(void *); - void *complete_data; - - req = list_entry(blizzard.pending_req_list.next, - struct blizzard_request, entry); - spin_unlock_irqrestore(&blizzard.req_lock, flags); - - if (req->handler(req) == REQ_PENDING) - return; - - complete = req->complete; - complete_data = req->complete_data; - free_req(req); - - if (complete) - complete(complete_data); - - spin_lock_irqsave(&blizzard.req_lock, flags); - } - - spin_unlock_irqrestore(&blizzard.req_lock, flags); -} - -static void submit_req_list(struct list_head *head) -{ - unsigned long flags; - int process = 1; - - spin_lock_irqsave(&blizzard.req_lock, flags); - if (likely(!list_empty(&blizzard.pending_req_list))) - process = 0; - list_splice_init(head, blizzard.pending_req_list.prev); - spin_unlock_irqrestore(&blizzard.req_lock, flags); - - if (process) - process_pending_requests(); -} - -static void request_complete(void *data) -{ - struct blizzard_request *req = (struct blizzard_request *)data; - void (*complete)(void *); - void *complete_data; - - complete = req->complete; - complete_data = req->complete_data; - - free_req(req); - - if (complete) - complete(complete_data); - - process_pending_requests(); -} - - -static int do_full_screen_update(struct blizzard_request *req) -{ - int i; - int flags; - - for (i = 0; i < 3; i++) { - struct plane_info *p = &blizzard.plane[i]; - if (!(blizzard.enabled_planes & (1 << i))) { - blizzard.int_ctrl->enable_plane(i, 0); - continue; - } - dev_dbg(blizzard.fbdev->dev, "pw %d ph %d\n", - p->width, p->height); - blizzard.int_ctrl->setup_plane(i, - OMAPFB_CHANNEL_OUT_LCD, p->offset, - p->scr_width, p->pos_x, p->pos_y, - p->width, p->height, - p->color_mode); - blizzard.int_ctrl->enable_plane(i, 1); - } - - dev_dbg(blizzard.fbdev->dev, "sw %d sh %d\n", - blizzard.screen_width, blizzard.screen_height); - blizzard_wait_line_buffer(); - flags = req->par.update.flags; - if (flags & OMAPFB_FORMAT_FLAG_TEARSYNC) - enable_tearsync(0, blizzard.screen_width, - blizzard.screen_height, - blizzard.screen_height, - blizzard.screen_height, - flags & OMAPFB_FORMAT_FLAG_FORCE_VSYNC); - else - disable_tearsync(); - - set_window_regs(0, 0, blizzard.screen_width, blizzard.screen_height, - 0, 0, blizzard.screen_width, blizzard.screen_height, - BLIZZARD_COLOR_RGB565, blizzard.zoom_on, flags); - blizzard.zoom_on = 0; - - blizzard.extif->set_bits_per_cycle(16); - /* set_window_regs has left the register index at the right - * place, so no need to set it here. - */ - blizzard.extif->transfer_area(blizzard.screen_width, - blizzard.screen_height, - request_complete, req); - return REQ_PENDING; -} - -static int check_1d_intersect(int a1, int a2, int b1, int b2) -{ - if (a2 <= b1 || b2 <= a1) - return 0; - return 1; -} - -/* Setup all planes with an overlapping area with the update window. */ -static int do_partial_update(struct blizzard_request *req, int plane, - int x, int y, int w, int h, - int x_out, int y_out, int w_out, int h_out, - int wnd_color_mode, int bpp) -{ - int i; - int gx1, gy1, gx2, gy2; - int gx1_out, gy1_out, gx2_out, gy2_out; - int color_mode; - int flags; - int zoom_off; - int have_zoom_for_this_update = 0; - - /* Global coordinates, relative to pixel 0,0 of the LCD */ - gx1 = x + blizzard.plane[plane].pos_x; - gy1 = y + blizzard.plane[plane].pos_y; - gx2 = gx1 + w; - gy2 = gy1 + h; - - flags = req->par.update.flags; - if (flags & OMAPFB_FORMAT_FLAG_DOUBLE) { - gx1_out = gx1; - gy1_out = gy1; - gx2_out = gx1 + w * 2; - gy2_out = gy1 + h * 2; - } else { - gx1_out = x_out + blizzard.plane[plane].pos_x; - gy1_out = y_out + blizzard.plane[plane].pos_y; - gx2_out = gx1_out + w_out; - gy2_out = gy1_out + h_out; - } - - for (i = 0; i < OMAPFB_PLANE_NUM; i++) { - struct plane_info *p = &blizzard.plane[i]; - int px1, py1; - int px2, py2; - int pw, ph; - int pposx, pposy; - unsigned long offset; - - if (!(blizzard.enabled_planes & (1 << i)) || - (wnd_color_mode && i != plane)) { - blizzard.int_ctrl->enable_plane(i, 0); - continue; - } - /* Plane coordinates */ - if (i == plane) { - /* Plane in which we are doing the update. - * Local coordinates are the one in the update - * request. - */ - px1 = x; - py1 = y; - px2 = x + w; - py2 = y + h; - pposx = 0; - pposy = 0; - } else { - /* Check if this plane has an overlapping part */ - px1 = gx1 - p->pos_x; - py1 = gy1 - p->pos_y; - px2 = gx2 - p->pos_x; - py2 = gy2 - p->pos_y; - if (px1 >= p->width || py1 >= p->height || - px2 <= 0 || py2 <= 0) { - blizzard.int_ctrl->enable_plane(i, 0); - continue; - } - /* Calculate the coordinates for the overlapping - * part in the plane's local coordinates. - */ - pposx = -px1; - pposy = -py1; - if (px1 < 0) - px1 = 0; - if (py1 < 0) - py1 = 0; - if (px2 > p->width) - px2 = p->width; - if (py2 > p->height) - py2 = p->height; - if (pposx < 0) - pposx = 0; - if (pposy < 0) - pposy = 0; - } - pw = px2 - px1; - ph = py2 - py1; - offset = p->offset + (p->scr_width * py1 + px1) * p->bpp / 8; - if (wnd_color_mode) - /* Window embedded in the plane with a differing - * color mode / bpp. Calculate the number of DMA - * transfer elements in terms of the plane's bpp. - */ - pw = (pw + 1) * bpp / p->bpp; -#ifdef VERBOSE - dev_dbg(blizzard.fbdev->dev, - "plane %d offset %#08lx pposx %d pposy %d " - "px1 %d py1 %d pw %d ph %d\n", - i, offset, pposx, pposy, px1, py1, pw, ph); -#endif - blizzard.int_ctrl->setup_plane(i, - OMAPFB_CHANNEL_OUT_LCD, offset, - p->scr_width, - pposx, pposy, pw, ph, - p->color_mode); - - blizzard.int_ctrl->enable_plane(i, 1); - } - - switch (wnd_color_mode) { - case OMAPFB_COLOR_YUV420: - color_mode = BLIZZARD_COLOR_YUV420; - /* Currently only the 16 bits/pixel cycle format is - * supported on the external interface. Adjust the number - * of transfer elements per line for 12bpp format. - */ - w = (w + 1) * 3 / 4; - break; - default: - color_mode = BLIZZARD_COLOR_RGB565; - break; - } - - blizzard_wait_line_buffer(); - if (blizzard.last_color_mode == BLIZZARD_COLOR_YUV420) - blizzard_wait_yyc(); - blizzard.last_color_mode = color_mode; - if (flags & OMAPFB_FORMAT_FLAG_TEARSYNC) - enable_tearsync(gy1, w, h, - blizzard.screen_height, - h_out, - flags & OMAPFB_FORMAT_FLAG_FORCE_VSYNC); - else - disable_tearsync(); - - if ((gx2_out - gx1_out) != (gx2 - gx1) || - (gy2_out - gy1_out) != (gy2 - gy1)) - have_zoom_for_this_update = 1; - - /* 'background' type of screen update (as opposed to 'destructive') - can be used to disable scaling if scaling is active */ - zoom_off = blizzard.zoom_on && !have_zoom_for_this_update && - (gx1_out == 0) && (gx2_out == blizzard.screen_width) && - (gy1_out == 0) && (gy2_out == blizzard.screen_height) && - (gx1 == 0) && (gy1 == 0); - - if (blizzard.zoom_on && !have_zoom_for_this_update && !zoom_off && - check_1d_intersect(blizzard.zoom_area_gx1, blizzard.zoom_area_gx2, - gx1_out, gx2_out) && - check_1d_intersect(blizzard.zoom_area_gy1, blizzard.zoom_area_gy2, - gy1_out, gy2_out)) { - /* Previous screen update was using scaling, current update - * is not using it. Additionally, current screen update is - * going to overlap with the scaled area. Scaling needs to be - * disabled in order to avoid 'magnifying glass' effect. - * Dummy setup of background window can be used for this. - */ - set_window_regs(0, 0, blizzard.screen_width, - blizzard.screen_height, - 0, 0, blizzard.screen_width, - blizzard.screen_height, - BLIZZARD_COLOR_RGB565, 1, flags); - blizzard.zoom_on = 0; - } - - /* remember scaling settings if we have scaled update */ - if (have_zoom_for_this_update) { - blizzard.zoom_on = 1; - blizzard.zoom_area_gx1 = gx1_out; - blizzard.zoom_area_gx2 = gx2_out; - blizzard.zoom_area_gy1 = gy1_out; - blizzard.zoom_area_gy2 = gy2_out; - } - - set_window_regs(gx1, gy1, gx2, gy2, gx1_out, gy1_out, gx2_out, gy2_out, - color_mode, zoom_off, flags); - if (zoom_off) - blizzard.zoom_on = 0; - - blizzard.extif->set_bits_per_cycle(16); - /* set_window_regs has left the register index at the right - * place, so no need to set it here. - */ - blizzard.extif->transfer_area(w, h, request_complete, req); - - return REQ_PENDING; -} - -static int send_frame_handler(struct blizzard_request *req) -{ - struct update_param *par = &req->par.update; - int plane = par->plane; - -#ifdef VERBOSE - dev_dbg(blizzard.fbdev->dev, - "send_frame: x %d y %d w %d h %d " - "x_out %d y_out %d w_out %d h_out %d " - "color_mode %04x flags %04x planes %01x\n", - par->x, par->y, par->width, par->height, - par->out_x, par->out_y, par->out_width, par->out_height, - par->color_mode, par->flags, blizzard.enabled_planes); -#endif - if (par->flags & OMAPFB_FORMAT_FLAG_DISABLE_OVERLAY) - disable_overlay(); - - if ((blizzard.enabled_planes & blizzard.vid_nonstd_color) || - (blizzard.enabled_planes & blizzard.vid_scaled)) - return do_full_screen_update(req); - - return do_partial_update(req, plane, par->x, par->y, - par->width, par->height, - par->out_x, par->out_y, - par->out_width, par->out_height, - par->color_mode, par->bpp); -} - -static void send_frame_complete(void *data) -{ -} - -#define ADD_PREQ(_x, _y, _w, _h, _x_out, _y_out, _w_out, _h_out) do { \ - req = alloc_req(); \ - req->handler = send_frame_handler; \ - req->complete = send_frame_complete; \ - req->par.update.plane = plane_idx; \ - req->par.update.x = _x; \ - req->par.update.y = _y; \ - req->par.update.width = _w; \ - req->par.update.height = _h; \ - req->par.update.out_x = _x_out; \ - req->par.update.out_y = _y_out; \ - req->par.update.out_width = _w_out; \ - req->par.update.out_height = _h_out; \ - req->par.update.bpp = bpp; \ - req->par.update.color_mode = color_mode;\ - req->par.update.flags = flags; \ - list_add_tail(&req->entry, req_head); \ -} while(0) - -static void create_req_list(int plane_idx, - struct omapfb_update_window *win, - struct list_head *req_head) -{ - struct blizzard_request *req; - int x = win->x; - int y = win->y; - int width = win->width; - int height = win->height; - int x_out = win->out_x; - int y_out = win->out_y; - int width_out = win->out_width; - int height_out = win->out_height; - int color_mode; - int bpp; - int flags; - unsigned int ystart = y; - unsigned int yspan = height; - unsigned int ystart_out = y_out; - unsigned int yspan_out = height_out; - - flags = win->format & ~OMAPFB_FORMAT_MASK; - color_mode = win->format & OMAPFB_FORMAT_MASK; - switch (color_mode) { - case OMAPFB_COLOR_YUV420: - /* Embedded window with different color mode */ - bpp = 12; - /* X, Y, height must be aligned at 2, width at 4 pixels */ - x &= ~1; - y &= ~1; - height = yspan = height & ~1; - width = width & ~3; - break; - default: - /* Same as the plane color mode */ - bpp = blizzard.plane[plane_idx].bpp; - break; - } - if (width * height * bpp / 8 > blizzard.max_transmit_size) { - yspan = blizzard.max_transmit_size / (width * bpp / 8); - yspan_out = yspan * height_out / height; - ADD_PREQ(x, ystart, width, yspan, x_out, ystart_out, - width_out, yspan_out); - ystart += yspan; - ystart_out += yspan_out; - yspan = height - yspan; - yspan_out = height_out - yspan_out; - flags &= ~OMAPFB_FORMAT_FLAG_TEARSYNC; - } - - ADD_PREQ(x, ystart, width, yspan, x_out, ystart_out, - width_out, yspan_out); -} - -static void auto_update_complete(void *data) -{ - if (!blizzard.stop_auto_update) - mod_timer(&blizzard.auto_update_timer, - jiffies + BLIZZARD_AUTO_UPDATE_TIME); -} - -static void blizzard_update_window_auto(unsigned long arg) -{ - LIST_HEAD(req_list); - struct blizzard_request *last; - struct omapfb_plane_struct *plane; - - plane = blizzard.fbdev->fb_info[0]->par; - create_req_list(plane->idx, - &blizzard.auto_update_window, &req_list); - last = list_entry(req_list.prev, struct blizzard_request, entry); - - last->complete = auto_update_complete; - last->complete_data = NULL; - - submit_req_list(&req_list); -} - -int blizzard_update_window_async(struct fb_info *fbi, - struct omapfb_update_window *win, - void (*complete_callback)(void *arg), - void *complete_callback_data) -{ - LIST_HEAD(req_list); - struct blizzard_request *last; - struct omapfb_plane_struct *plane = fbi->par; - - if (unlikely(blizzard.update_mode != OMAPFB_MANUAL_UPDATE)) - return -EINVAL; - if (unlikely(!blizzard.te_connected && - (win->format & OMAPFB_FORMAT_FLAG_TEARSYNC))) - return -EINVAL; - - create_req_list(plane->idx, win, &req_list); - last = list_entry(req_list.prev, struct blizzard_request, entry); - - last->complete = complete_callback; - last->complete_data = (void *)complete_callback_data; - - submit_req_list(&req_list); - - return 0; -} -EXPORT_SYMBOL(blizzard_update_window_async); - -static int update_full_screen(void) -{ - return blizzard_update_window_async(blizzard.fbdev->fb_info[0], - &blizzard.auto_update_window, NULL, NULL); - -} - -static int blizzard_setup_plane(int plane, int channel_out, - unsigned long offset, int screen_width, - int pos_x, int pos_y, int width, int height, - int color_mode) -{ - struct plane_info *p; - -#ifdef VERBOSE - dev_dbg(blizzard.fbdev->dev, - "plane %d ch_out %d offset %#08lx scr_width %d " - "pos_x %d pos_y %d width %d height %d color_mode %d\n", - plane, channel_out, offset, screen_width, - pos_x, pos_y, width, height, color_mode); -#endif - if ((unsigned)plane > OMAPFB_PLANE_NUM) - return -EINVAL; - p = &blizzard.plane[plane]; - - switch (color_mode) { - case OMAPFB_COLOR_YUV422: - case OMAPFB_COLOR_YUY422: - p->bpp = 16; - blizzard.vid_nonstd_color &= ~(1 << plane); - break; - case OMAPFB_COLOR_YUV420: - p->bpp = 12; - blizzard.vid_nonstd_color |= 1 << plane; - break; - case OMAPFB_COLOR_RGB565: - p->bpp = 16; - blizzard.vid_nonstd_color &= ~(1 << plane); - break; - default: - return -EINVAL; - } - - p->offset = offset; - p->pos_x = pos_x; - p->pos_y = pos_y; - p->width = width; - p->height = height; - p->scr_width = screen_width; - if (!p->out_width) - p->out_width = width; - if (!p->out_height) - p->out_height = height; - - p->color_mode = color_mode; - - return 0; -} - -static int blizzard_set_scale(int plane, int orig_w, int orig_h, - int out_w, int out_h) -{ - struct plane_info *p = &blizzard.plane[plane]; - int r; - - dev_dbg(blizzard.fbdev->dev, - "plane %d orig_w %d orig_h %d out_w %d out_h %d\n", - plane, orig_w, orig_h, out_w, out_h); - if ((unsigned)plane > OMAPFB_PLANE_NUM) - return -ENODEV; - - r = blizzard.int_ctrl->set_scale(plane, orig_w, orig_h, out_w, out_h); - if (r < 0) - return r; - - p->width = orig_w; - p->height = orig_h; - p->out_width = out_w; - p->out_height = out_h; - if (orig_w == out_w && orig_h == out_h) - blizzard.vid_scaled &= ~(1 << plane); - else - blizzard.vid_scaled |= 1 << plane; - - return 0; -} - -static int blizzard_set_rotate(int angle) -{ - u32 l; - - l = blizzard_read_reg(BLIZZARD_PANEL_CONFIGURATION); - l &= ~0x03; - - switch (angle) { - case 0: - l = l | 0x00; - break; - case 90: - l = l | 0x03; - break; - case 180: - l = l | 0x02; - break; - case 270: - l = l | 0x01; - break; - default: - return -EINVAL; - } - - blizzard_write_reg(BLIZZARD_PANEL_CONFIGURATION, l); - - return 0; -} - -static int blizzard_enable_plane(int plane, int enable) -{ - if (enable) - blizzard.enabled_planes |= 1 << plane; - else - blizzard.enabled_planes &= ~(1 << plane); - - return 0; -} - -static int sync_handler(struct blizzard_request *req) -{ - complete(req->par.sync); - return REQ_COMPLETE; -} - -static void blizzard_sync(void) -{ - LIST_HEAD(req_list); - struct blizzard_request *req; - struct completion comp; - - req = alloc_req(); - - req->handler = sync_handler; - req->complete = NULL; - init_completion(&comp); - req->par.sync = ∁ - - list_add(&req->entry, &req_list); - submit_req_list(&req_list); - - wait_for_completion(&comp); -} - - -static void blizzard_bind_client(struct omapfb_notifier_block *nb) -{ - if (blizzard.update_mode == OMAPFB_MANUAL_UPDATE) { - omapfb_notify_clients(blizzard.fbdev, OMAPFB_EVENT_READY); - } -} - -static int blizzard_set_update_mode(enum omapfb_update_mode mode) -{ - if (unlikely(mode != OMAPFB_MANUAL_UPDATE && - mode != OMAPFB_AUTO_UPDATE && - mode != OMAPFB_UPDATE_DISABLED)) - return -EINVAL; - - if (mode == blizzard.update_mode) - return 0; - - dev_info(blizzard.fbdev->dev, "s1d1374x: setting update mode to %s\n", - mode == OMAPFB_UPDATE_DISABLED ? "disabled" : - (mode == OMAPFB_AUTO_UPDATE ? "auto" : "manual")); - - switch (blizzard.update_mode) { - case OMAPFB_MANUAL_UPDATE: - omapfb_notify_clients(blizzard.fbdev, OMAPFB_EVENT_DISABLED); - break; - case OMAPFB_AUTO_UPDATE: - blizzard.stop_auto_update = 1; - del_timer_sync(&blizzard.auto_update_timer); - break; - case OMAPFB_UPDATE_DISABLED: - break; - } - - blizzard.update_mode = mode; - blizzard_sync(); - blizzard.stop_auto_update = 0; - - switch (mode) { - case OMAPFB_MANUAL_UPDATE: - omapfb_notify_clients(blizzard.fbdev, OMAPFB_EVENT_READY); - break; - case OMAPFB_AUTO_UPDATE: - blizzard_update_window_auto(0); - break; - case OMAPFB_UPDATE_DISABLED: - break; - } - - return 0; -} - -static enum omapfb_update_mode blizzard_get_update_mode(void) -{ - return blizzard.update_mode; -} - -static inline void set_extif_timings(const struct extif_timings *t) -{ - blizzard.extif->set_timings(t); -} - -static inline unsigned long round_to_extif_ticks(unsigned long ps, int div) -{ - int bus_tick = blizzard.extif_clk_period * div; - return (ps + bus_tick - 1) / bus_tick * bus_tick; -} - -static int calc_reg_timing(unsigned long sysclk, int div) -{ - struct extif_timings *t; - unsigned long systim; - - /* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns, - * AccessTime 2 ns + 12.2 ns (regs), - * WEOffTime = WEOnTime + 1 ns, - * REOffTime = REOnTime + 12 ns (regs), - * CSOffTime = REOffTime + 1 ns - * ReadCycle = 2ns + 2*SYSCLK (regs), - * WriteCycle = 2*SYSCLK + 2 ns, - * CSPulseWidth = 10 ns */ - - systim = 1000000000 / (sysclk / 1000); - dev_dbg(blizzard.fbdev->dev, - "Blizzard systim %lu ps extif_clk_period %u div %d\n", - systim, blizzard.extif_clk_period, div); - - t = &blizzard.reg_timings; - memset(t, 0, sizeof(*t)); - - t->clk_div = div; - - t->cs_on_time = 0; - t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); - t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); - t->access_time = round_to_extif_ticks(t->re_on_time + 12200, div); - t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div); - t->re_off_time = round_to_extif_ticks(t->re_on_time + 13000, div); - t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div); - t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div); - if (t->we_cycle_time < t->we_off_time) - t->we_cycle_time = t->we_off_time; - t->re_cycle_time = round_to_extif_ticks(2 * systim + 2000, div); - if (t->re_cycle_time < t->re_off_time) - t->re_cycle_time = t->re_off_time; - t->cs_pulse_width = 0; - - dev_dbg(blizzard.fbdev->dev, "[reg]cson %d csoff %d reon %d reoff %d\n", - t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time); - dev_dbg(blizzard.fbdev->dev, "[reg]weon %d weoff %d recyc %d wecyc %d\n", - t->we_on_time, t->we_off_time, t->re_cycle_time, - t->we_cycle_time); - dev_dbg(blizzard.fbdev->dev, "[reg]rdaccess %d cspulse %d\n", - t->access_time, t->cs_pulse_width); - - return blizzard.extif->convert_timings(t); -} - -static int calc_lut_timing(unsigned long sysclk, int div) -{ - struct extif_timings *t; - unsigned long systim; - - /* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns, - * AccessTime 2 ns + 4 * SYSCLK + 26 (lut), - * WEOffTime = WEOnTime + 1 ns, - * REOffTime = REOnTime + 4*SYSCLK + 26 ns (lut), - * CSOffTime = REOffTime + 1 ns - * ReadCycle = 2ns + 4*SYSCLK + 26 ns (lut), - * WriteCycle = 2*SYSCLK + 2 ns, - * CSPulseWidth = 10 ns */ - - systim = 1000000000 / (sysclk / 1000); - dev_dbg(blizzard.fbdev->dev, - "Blizzard systim %lu ps extif_clk_period %u div %d\n", - systim, blizzard.extif_clk_period, div); - - t = &blizzard.lut_timings; - memset(t, 0, sizeof(*t)); - - t->clk_div = div; - - t->cs_on_time = 0; - t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); - t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); - t->access_time = round_to_extif_ticks(t->re_on_time + 4 * systim + - 26000, div); - t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div); - t->re_off_time = round_to_extif_ticks(t->re_on_time + 4 * systim + - 26000, div); - t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div); - t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div); - if (t->we_cycle_time < t->we_off_time) - t->we_cycle_time = t->we_off_time; - t->re_cycle_time = round_to_extif_ticks(2000 + 4 * systim + 26000, div); - if (t->re_cycle_time < t->re_off_time) - t->re_cycle_time = t->re_off_time; - t->cs_pulse_width = 0; - - dev_dbg(blizzard.fbdev->dev, - "[lut]cson %d csoff %d reon %d reoff %d\n", - t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time); - dev_dbg(blizzard.fbdev->dev, - "[lut]weon %d weoff %d recyc %d wecyc %d\n", - t->we_on_time, t->we_off_time, t->re_cycle_time, - t->we_cycle_time); - dev_dbg(blizzard.fbdev->dev, "[lut]rdaccess %d cspulse %d\n", - t->access_time, t->cs_pulse_width); - - return blizzard.extif->convert_timings(t); -} - -static int calc_extif_timings(unsigned long sysclk, int *extif_mem_div) -{ - int max_clk_div; - int div; - - blizzard.extif->get_clk_info(&blizzard.extif_clk_period, &max_clk_div); - for (div = 1; div <= max_clk_div; div++) { - if (calc_reg_timing(sysclk, div) == 0) - break; - } - if (div > max_clk_div) { - dev_dbg(blizzard.fbdev->dev, "reg timing failed\n"); - goto err; - } - *extif_mem_div = div; - - for (div = 1; div <= max_clk_div; div++) { - if (calc_lut_timing(sysclk, div) == 0) - break; - } - - if (div > max_clk_div) - goto err; - - blizzard.extif_clk_div = div; - - return 0; -err: - dev_err(blizzard.fbdev->dev, "can't setup timings\n"); - return -1; -} - -static void calc_blizzard_clk_rates(unsigned long ext_clk, - unsigned long *sys_clk, unsigned long *pix_clk) -{ - int pix_clk_src; - int sys_div = 0, sys_mul = 0; - int pix_div; - - pix_clk_src = blizzard_read_reg(BLIZZARD_CLK_SRC); - pix_div = ((pix_clk_src >> 3) & 0x1f) + 1; - if ((pix_clk_src & (0x3 << 1)) == 0) { - /* Source is the PLL */ - sys_div = (blizzard_read_reg(BLIZZARD_PLL_DIV) & 0x3f) + 1; - sys_mul = blizzard_read_reg(BLIZZARD_PLL_CLOCK_SYNTH_0); - sys_mul |= ((blizzard_read_reg(BLIZZARD_PLL_CLOCK_SYNTH_1) - & 0x0f) << 11); - *sys_clk = ext_clk * sys_mul / sys_div; - } else /* else source is ext clk, or oscillator */ - *sys_clk = ext_clk; - - *pix_clk = *sys_clk / pix_div; /* HZ */ - dev_dbg(blizzard.fbdev->dev, - "ext_clk %ld pix_src %d pix_div %d sys_div %d sys_mul %d\n", - ext_clk, pix_clk_src & (0x3 << 1), pix_div, sys_div, sys_mul); - dev_dbg(blizzard.fbdev->dev, "sys_clk %ld pix_clk %ld\n", - *sys_clk, *pix_clk); -} - -static int setup_tearsync(unsigned long pix_clk, int extif_div) -{ - int hdisp, vdisp; - int hndp, vndp; - int hsw, vsw; - int hs, vs; - int hs_pol_inv, vs_pol_inv; - int use_hsvs, use_ndp; - u8 b; - - hsw = blizzard_read_reg(BLIZZARD_HSW); - vsw = blizzard_read_reg(BLIZZARD_VSW); - hs_pol_inv = !(hsw & 0x80); - vs_pol_inv = !(vsw & 0x80); - hsw = hsw & 0x7f; - vsw = vsw & 0x3f; - - hdisp = blizzard_read_reg(BLIZZARD_HDISP) * 8; - vdisp = blizzard_read_reg(BLIZZARD_VDISP0) + - ((blizzard_read_reg(BLIZZARD_VDISP1) & 0x3) << 8); - - hndp = blizzard_read_reg(BLIZZARD_HNDP) & 0x3f; - vndp = blizzard_read_reg(BLIZZARD_VNDP); - - /* time to transfer one pixel (16bpp) in ps */ - blizzard.pix_tx_time = blizzard.reg_timings.we_cycle_time; - if (blizzard.extif->get_max_tx_rate != NULL) { - /* The external interface might have a rate limitation, - * if so, we have to maximize our transfer rate. - */ - unsigned long min_tx_time; - unsigned long max_tx_rate = blizzard.extif->get_max_tx_rate(); - - dev_dbg(blizzard.fbdev->dev, "max_tx_rate %ld HZ\n", - max_tx_rate); - min_tx_time = 1000000000 / (max_tx_rate / 1000); /* ps */ - if (blizzard.pix_tx_time < min_tx_time) - blizzard.pix_tx_time = min_tx_time; - } - - /* time to update one line in ps */ - blizzard.line_upd_time = (hdisp + hndp) * 1000000 / (pix_clk / 1000); - blizzard.line_upd_time *= 1000; - if (hdisp * blizzard.pix_tx_time > blizzard.line_upd_time) - /* transfer speed too low, we might have to use both - * HS and VS */ - use_hsvs = 1; - else - /* decent transfer speed, we'll always use only VS */ - use_hsvs = 0; - - if (use_hsvs && (hs_pol_inv || vs_pol_inv)) { - /* HS or'ed with VS doesn't work, use the active high - * TE signal based on HNDP / VNDP */ - use_ndp = 1; - hs_pol_inv = 0; - vs_pol_inv = 0; - hs = hndp; - vs = vndp; - } else { - /* Use HS or'ed with VS as a TE signal if both are needed - * or VNDP if only vsync is needed. */ - use_ndp = 0; - hs = hsw; - vs = vsw; - if (!use_hsvs) { - hs_pol_inv = 0; - vs_pol_inv = 0; - } - } - - hs = hs * 1000000 / (pix_clk / 1000); /* ps */ - hs *= 1000; - - vs = vs * (hdisp + hndp) * 1000000 / (pix_clk / 1000); /* ps */ - vs *= 1000; - - if (vs <= hs) - return -EDOM; - /* set VS to 120% of HS to minimize VS detection time */ - vs = hs * 12 / 10; - /* minimize HS too */ - if (hs > 10000) - hs = 10000; - - b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS); - b &= ~0x3; - b |= use_hsvs ? 1 : 0; - b |= (use_ndp && use_hsvs) ? 0 : 2; - blizzard_write_reg(BLIZZARD_NDISP_CTRL_STATUS, b); - - blizzard.vsync_only = !use_hsvs; - - dev_dbg(blizzard.fbdev->dev, - "pix_clk %ld HZ pix_tx_time %ld ps line_upd_time %ld ps\n", - pix_clk, blizzard.pix_tx_time, blizzard.line_upd_time); - dev_dbg(blizzard.fbdev->dev, - "hs %d ps vs %d ps mode %d vsync_only %d\n", - hs, vs, b & 0x3, !use_hsvs); - - return blizzard.extif->setup_tearsync(1, hs, vs, - hs_pol_inv, vs_pol_inv, - extif_div); -} - -static void blizzard_get_caps(int plane, struct omapfb_caps *caps) -{ - blizzard.int_ctrl->get_caps(plane, caps); - caps->ctrl |= OMAPFB_CAPS_MANUAL_UPDATE | - OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE | - OMAPFB_CAPS_WINDOW_SCALE | - OMAPFB_CAPS_WINDOW_OVERLAY | - OMAPFB_CAPS_WINDOW_ROTATE; - if (blizzard.te_connected) - caps->ctrl |= OMAPFB_CAPS_TEARSYNC; - caps->wnd_color |= (1 << OMAPFB_COLOR_RGB565) | - (1 << OMAPFB_COLOR_YUV420); -} - -static void _save_regs(const struct blizzard_reg_list *list, int cnt) -{ - int i; - - for (i = 0; i < cnt; i++, list++) { - int reg; - for (reg = list->start; reg <= list->end; reg += 2) - blizzard_reg_cache[reg / 2] = blizzard_read_reg(reg); - } -} - -static void _restore_regs(const struct blizzard_reg_list *list, int cnt) -{ - int i; - - for (i = 0; i < cnt; i++, list++) { - int reg; - for (reg = list->start; reg <= list->end; reg += 2) - blizzard_write_reg(reg, blizzard_reg_cache[reg / 2]); - } -} - -static void blizzard_save_all_regs(void) -{ - _save_regs(blizzard_pll_regs, ARRAY_SIZE(blizzard_pll_regs)); - _save_regs(blizzard_gen_regs, ARRAY_SIZE(blizzard_gen_regs)); -} - -static void blizzard_restore_pll_regs(void) -{ - _restore_regs(blizzard_pll_regs, ARRAY_SIZE(blizzard_pll_regs)); -} - -static void blizzard_restore_gen_regs(void) -{ - _restore_regs(blizzard_gen_regs, ARRAY_SIZE(blizzard_gen_regs)); -} - -static void blizzard_suspend(void) -{ - u32 l; - unsigned long tmo; - - if (blizzard.last_color_mode) { - update_full_screen(); - blizzard_sync(); - } - blizzard.update_mode_before_suspend = blizzard.update_mode; - /* the following will disable clocks as well */ - blizzard_set_update_mode(OMAPFB_UPDATE_DISABLED); - - blizzard_save_all_regs(); - - blizzard_stop_sdram(); - - l = blizzard_read_reg(BLIZZARD_POWER_SAVE); - /* Standby, Sleep. We assume we use an external clock. */ - l |= 0x03; - blizzard_write_reg(BLIZZARD_POWER_SAVE, l); - - tmo = jiffies + msecs_to_jiffies(100); - while (!(blizzard_read_reg(BLIZZARD_PLL_MODE) & (1 << 1))) { - if (time_after(jiffies, tmo)) { - dev_err(blizzard.fbdev->dev, - "s1d1374x: sleep timeout, stopping PLL manually\n"); - l = blizzard_read_reg(BLIZZARD_PLL_MODE); - l &= ~0x03; - /* Disable PLL, counter function */ - l |= 0x2; - blizzard_write_reg(BLIZZARD_PLL_MODE, l); - break; - } - msleep(1); - } - - if (blizzard.power_down != NULL) - blizzard.power_down(blizzard.fbdev->dev); -} - -static void blizzard_resume(void) -{ - u32 l; - - if (blizzard.power_up != NULL) - blizzard.power_up(blizzard.fbdev->dev); - - l = blizzard_read_reg(BLIZZARD_POWER_SAVE); - /* Standby, Sleep */ - l &= ~0x03; - blizzard_write_reg(BLIZZARD_POWER_SAVE, l); - - blizzard_restore_pll_regs(); - l = blizzard_read_reg(BLIZZARD_PLL_MODE); - l &= ~0x03; - /* Enable PLL, counter function */ - l |= 0x1; - blizzard_write_reg(BLIZZARD_PLL_MODE, l); - - while (!(blizzard_read_reg(BLIZZARD_PLL_DIV) & (1 << 7))) - msleep(1); - - blizzard_restart_sdram(); - - blizzard_restore_gen_regs(); - - /* Enable display */ - blizzard_write_reg(BLIZZARD_DISPLAY_MODE, 0x01); - - /* the following will enable clocks as necessary */ - blizzard_set_update_mode(blizzard.update_mode_before_suspend); - - /* Force a background update */ - blizzard.zoom_on = 1; - update_full_screen(); - blizzard_sync(); -} - -static int blizzard_init(struct omapfb_device *fbdev, int ext_mode, - struct omapfb_mem_desc *req_vram) -{ - int r = 0, i; - u8 rev, conf; - unsigned long ext_clk; - int extif_div; - unsigned long sys_clk, pix_clk; - struct omapfb_platform_data *omapfb_conf; - struct blizzard_platform_data *ctrl_conf; - - blizzard.fbdev = fbdev; - - BUG_ON(!fbdev->ext_if || !fbdev->int_ctrl); - - blizzard.fbdev = fbdev; - blizzard.extif = fbdev->ext_if; - blizzard.int_ctrl = fbdev->int_ctrl; - - omapfb_conf = fbdev->dev->platform_data; - ctrl_conf = omapfb_conf->ctrl_platform_data; - if (ctrl_conf == NULL || ctrl_conf->get_clock_rate == NULL) { - dev_err(fbdev->dev, "s1d1374x: missing platform data\n"); - r = -ENOENT; - goto err1; - } - - blizzard.power_down = ctrl_conf->power_down; - blizzard.power_up = ctrl_conf->power_up; - - spin_lock_init(&blizzard.req_lock); - - if ((r = blizzard.int_ctrl->init(fbdev, 1, req_vram)) < 0) - goto err1; - - if ((r = blizzard.extif->init(fbdev)) < 0) - goto err2; - - blizzard_ctrl.set_color_key = blizzard.int_ctrl->set_color_key; - blizzard_ctrl.get_color_key = blizzard.int_ctrl->get_color_key; - blizzard_ctrl.setup_mem = blizzard.int_ctrl->setup_mem; - blizzard_ctrl.mmap = blizzard.int_ctrl->mmap; - - ext_clk = ctrl_conf->get_clock_rate(fbdev->dev); - if ((r = calc_extif_timings(ext_clk, &extif_div)) < 0) - goto err3; - - set_extif_timings(&blizzard.reg_timings); - - if (blizzard.power_up != NULL) - blizzard.power_up(fbdev->dev); - - calc_blizzard_clk_rates(ext_clk, &sys_clk, &pix_clk); - - if ((r = calc_extif_timings(sys_clk, &extif_div)) < 0) - goto err3; - set_extif_timings(&blizzard.reg_timings); - - if (!(blizzard_read_reg(BLIZZARD_PLL_DIV) & 0x80)) { - dev_err(fbdev->dev, - "controller not initialized by the bootloader\n"); - r = -ENODEV; - goto err3; - } - - if (ctrl_conf->te_connected) { - if ((r = setup_tearsync(pix_clk, extif_div)) < 0) - goto err3; - blizzard.te_connected = 1; - } - - rev = blizzard_read_reg(BLIZZARD_REV_CODE); - conf = blizzard_read_reg(BLIZZARD_CONFIG); - - switch (rev & 0xfc) { - case 0x9c: - blizzard.version = BLIZZARD_VERSION_S1D13744; - pr_info("omapfb: s1d13744 LCD controller rev %d " - "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07); - break; - case 0xa4: - blizzard.version = BLIZZARD_VERSION_S1D13745; - pr_info("omapfb: s1d13745 LCD controller rev %d " - "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07); - break; - default: - dev_err(fbdev->dev, "invalid s1d1374x revision %02x\n", - rev); - r = -ENODEV; - goto err3; - } - - blizzard.max_transmit_size = blizzard.extif->max_transmit_size; - - blizzard.update_mode = OMAPFB_UPDATE_DISABLED; - - blizzard.auto_update_window.x = 0; - blizzard.auto_update_window.y = 0; - blizzard.auto_update_window.width = fbdev->panel->x_res; - blizzard.auto_update_window.height = fbdev->panel->y_res; - blizzard.auto_update_window.out_x = 0; - blizzard.auto_update_window.out_y = 0; - blizzard.auto_update_window.out_width = fbdev->panel->x_res; - blizzard.auto_update_window.out_height = fbdev->panel->y_res; - blizzard.auto_update_window.format = 0; - - blizzard.screen_width = fbdev->panel->x_res; - blizzard.screen_height = fbdev->panel->y_res; - - init_timer(&blizzard.auto_update_timer); - blizzard.auto_update_timer.function = blizzard_update_window_auto; - blizzard.auto_update_timer.data = 0; - - INIT_LIST_HEAD(&blizzard.free_req_list); - INIT_LIST_HEAD(&blizzard.pending_req_list); - for (i = 0; i < ARRAY_SIZE(blizzard.req_pool); i++) - list_add(&blizzard.req_pool[i].entry, &blizzard.free_req_list); - BUG_ON(i <= IRQ_REQ_POOL_SIZE); - sema_init(&blizzard.req_sema, i - IRQ_REQ_POOL_SIZE); - - return 0; -err3: - if (blizzard.power_down != NULL) - blizzard.power_down(fbdev->dev); - blizzard.extif->cleanup(); -err2: - blizzard.int_ctrl->cleanup(); -err1: - return r; -} - -static void blizzard_cleanup(void) -{ - blizzard_set_update_mode(OMAPFB_UPDATE_DISABLED); - blizzard.extif->cleanup(); - blizzard.int_ctrl->cleanup(); - if (blizzard.power_down != NULL) - blizzard.power_down(blizzard.fbdev->dev); -} - -struct lcd_ctrl blizzard_ctrl = { - .name = "blizzard", - .init = blizzard_init, - .cleanup = blizzard_cleanup, - .bind_client = blizzard_bind_client, - .get_caps = blizzard_get_caps, - .set_update_mode = blizzard_set_update_mode, - .get_update_mode = blizzard_get_update_mode, - .setup_plane = blizzard_setup_plane, - .set_scale = blizzard_set_scale, - .enable_plane = blizzard_enable_plane, - .set_rotate = blizzard_set_rotate, - .update_window = blizzard_update_window_async, - .sync = blizzard_sync, - .suspend = blizzard_suspend, - .resume = blizzard_resume, -}; - diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c deleted file mode 100644 index 6f61e781f15a..000000000000 --- a/drivers/video/omap/dispc.c +++ /dev/null @@ -1,1547 +0,0 @@ -/* - * OMAP2 display controller support - * - * Copyright (C) 2005 Nokia Corporation - * Author: Imre Deak <imre.deak@nokia.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/dma-mapping.h> -#include <linux/mm.h> -#include <linux/vmalloc.h> -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/platform_device.h> -#include <linux/slab.h> - -#include <plat/sram.h> -#include <plat/board.h> - -#include "omapfb.h" -#include "dispc.h" - -#define MODULE_NAME "dispc" - -#define DSS_BASE 0x48050000 -#define DSS_SYSCONFIG 0x0010 - -#define DISPC_BASE 0x48050400 - -/* DISPC common */ -#define DISPC_REVISION 0x0000 -#define DISPC_SYSCONFIG 0x0010 -#define DISPC_SYSSTATUS 0x0014 -#define DISPC_IRQSTATUS 0x0018 -#define DISPC_IRQENABLE 0x001C -#define DISPC_CONTROL 0x0040 -#define DISPC_CONFIG 0x0044 -#define DISPC_CAPABLE 0x0048 -#define DISPC_DEFAULT_COLOR0 0x004C -#define DISPC_DEFAULT_COLOR1 0x0050 -#define DISPC_TRANS_COLOR0 0x0054 -#define DISPC_TRANS_COLOR1 0x0058 -#define DISPC_LINE_STATUS 0x005C -#define DISPC_LINE_NUMBER 0x0060 -#define DISPC_TIMING_H 0x0064 -#define DISPC_TIMING_V 0x0068 -#define DISPC_POL_FREQ 0x006C -#define DISPC_DIVISOR 0x0070 -#define DISPC_SIZE_DIG 0x0078 -#define DISPC_SIZE_LCD 0x007C - -#define DISPC_DATA_CYCLE1 0x01D4 -#define DISPC_DATA_CYCLE2 0x01D8 -#define DISPC_DATA_CYCLE3 0x01DC - -/* DISPC GFX plane */ -#define DISPC_GFX_BA0 0x0080 -#define DISPC_GFX_BA1 0x0084 -#define DISPC_GFX_POSITION 0x0088 -#define DISPC_GFX_SIZE 0x008C -#define DISPC_GFX_ATTRIBUTES 0x00A0 -#define DISPC_GFX_FIFO_THRESHOLD 0x00A4 -#define DISPC_GFX_FIFO_SIZE_STATUS 0x00A8 -#define DISPC_GFX_ROW_INC 0x00AC -#define DISPC_GFX_PIXEL_INC 0x00B0 -#define DISPC_GFX_WINDOW_SKIP 0x00B4 -#define DISPC_GFX_TABLE_BA 0x00B8 - -/* DISPC Video plane 1/2 */ -#define DISPC_VID1_BASE 0x00BC -#define DISPC_VID2_BASE 0x014C - -/* Offsets into DISPC_VID1/2_BASE */ -#define DISPC_VID_BA0 0x0000 -#define DISPC_VID_BA1 0x0004 -#define DISPC_VID_POSITION 0x0008 -#define DISPC_VID_SIZE 0x000C -#define DISPC_VID_ATTRIBUTES 0x0010 -#define DISPC_VID_FIFO_THRESHOLD 0x0014 -#define DISPC_VID_FIFO_SIZE_STATUS 0x0018 -#define DISPC_VID_ROW_INC 0x001C -#define DISPC_VID_PIXEL_INC 0x0020 -#define DISPC_VID_FIR 0x0024 -#define DISPC_VID_PICTURE_SIZE 0x0028 -#define DISPC_VID_ACCU0 0x002C -#define DISPC_VID_ACCU1 0x0030 - -/* 8 elements in 8 byte increments */ -#define DISPC_VID_FIR_COEF_H0 0x0034 -/* 8 elements in 8 byte increments */ -#define DISPC_VID_FIR_COEF_HV0 0x0038 -/* 5 elements in 4 byte increments */ -#define DISPC_VID_CONV_COEF0 0x0074 - -#define DISPC_IRQ_FRAMEMASK 0x0001 -#define DISPC_IRQ_VSYNC 0x0002 -#define DISPC_IRQ_EVSYNC_EVEN 0x0004 -#define DISPC_IRQ_EVSYNC_ODD 0x0008 -#define DISPC_IRQ_ACBIAS_COUNT_STAT 0x0010 -#define DISPC_IRQ_PROG_LINE_NUM 0x0020 -#define DISPC_IRQ_GFX_FIFO_UNDERFLOW 0x0040 -#define DISPC_IRQ_GFX_END_WIN 0x0080 -#define DISPC_IRQ_PAL_GAMMA_MASK 0x0100 -#define DISPC_IRQ_OCP_ERR 0x0200 -#define DISPC_IRQ_VID1_FIFO_UNDERFLOW 0x0400 -#define DISPC_IRQ_VID1_END_WIN 0x0800 -#define DISPC_IRQ_VID2_FIFO_UNDERFLOW 0x1000 -#define DISPC_IRQ_VID2_END_WIN 0x2000 -#define DISPC_IRQ_SYNC_LOST 0x4000 - -#define DISPC_IRQ_MASK_ALL 0x7fff - -#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \ - DISPC_IRQ_VID1_FIFO_UNDERFLOW | \ - DISPC_IRQ_VID2_FIFO_UNDERFLOW | \ - DISPC_IRQ_SYNC_LOST) - -#define RFBI_CONTROL 0x48050040 - -#define MAX_PALETTE_SIZE (256 * 16) - -#define FLD_MASK(pos, len) (((1 << len) - 1) << pos) - -#define MOD_REG_FLD(reg, mask, val) \ - dispc_write_reg((reg), (dispc_read_reg(reg) & ~(mask)) | (val)); - -#define OMAP2_SRAM_START 0x40200000 -/* Maximum size, in reality this is smaller if SRAM is partially locked. */ -#define OMAP2_SRAM_SIZE 0xa0000 /* 640k */ - -/* We support the SDRAM / SRAM types. See OMAPFB_PLANE_MEMTYPE_* in omapfb.h */ -#define DISPC_MEMTYPE_NUM 2 - -#define RESMAP_SIZE(_page_cnt) \ - ((_page_cnt + (sizeof(unsigned long) * 8) - 1) / 8) -#define RESMAP_PTR(_res_map, _page_nr) \ - (((_res_map)->map) + (_page_nr) / (sizeof(unsigned long) * 8)) -#define RESMAP_MASK(_page_nr) \ - (1 << ((_page_nr) & (sizeof(unsigned long) * 8 - 1))) - -struct resmap { - unsigned long start; - unsigned page_cnt; - unsigned long *map; -}; - -#define MAX_IRQ_HANDLERS 4 - -static struct { - void __iomem *base; - - struct omapfb_mem_desc mem_desc; - struct resmap *res_map[DISPC_MEMTYPE_NUM]; - atomic_t map_count[OMAPFB_PLANE_NUM]; - - dma_addr_t palette_paddr; - void *palette_vaddr; - - int ext_mode; - - struct { - u32 irq_mask; - void (*callback)(void *); - void *data; - } irq_handlers[MAX_IRQ_HANDLERS]; - struct completion frame_done; - - int fir_hinc[OMAPFB_PLANE_NUM]; - int fir_vinc[OMAPFB_PLANE_NUM]; - - struct clk *dss_ick, *dss1_fck; - struct clk *dss_54m_fck; - - enum omapfb_update_mode update_mode; - struct omapfb_device *fbdev; - - struct omapfb_color_key color_key; -} dispc; - -static void enable_lcd_clocks(int enable); - -static void inline dispc_write_reg(int idx, u32 val) -{ - __raw_writel(val, dispc.base + idx); -} - -static u32 inline dispc_read_reg(int idx) -{ - u32 l = __raw_readl(dispc.base + idx); - return l; -} - -/* Select RFBI or bypass mode */ -static void enable_rfbi_mode(int enable) -{ - void __iomem *rfbi_control; - u32 l; - - l = dispc_read_reg(DISPC_CONTROL); - /* Enable RFBI, GPIO0/1 */ - l &= ~((1 << 11) | (1 << 15) | (1 << 16)); - l |= enable ? (1 << 11) : 0; - /* RFBI En: GPIO0/1=10 RFBI Dis: GPIO0/1=11 */ - l |= 1 << 15; - l |= enable ? 0 : (1 << 16); - dispc_write_reg(DISPC_CONTROL, l); - - /* Set bypass mode in RFBI module */ - rfbi_control = ioremap(RFBI_CONTROL, SZ_1K); - if (!rfbi_control) { - pr_err("Unable to ioremap rfbi_control\n"); - return; - } - l = __raw_readl(rfbi_control); - l |= enable ? 0 : (1 << 1); - __raw_writel(l, rfbi_control); - iounmap(rfbi_control); -} - -static void set_lcd_data_lines(int data_lines) -{ - u32 l; - int code = 0; - - switch (data_lines) { - case 12: - code = 0; - break; - case 16: - code = 1; - break; - case 18: - code = 2; - break; - case 24: - code = 3; - break; - default: - BUG(); - } - - l = dispc_read_reg(DISPC_CONTROL); - l &= ~(0x03 << 8); - l |= code << 8; - dispc_write_reg(DISPC_CONTROL, l); -} - -static void set_load_mode(int mode) -{ - BUG_ON(mode & ~(DISPC_LOAD_CLUT_ONLY | DISPC_LOAD_FRAME_ONLY | - DISPC_LOAD_CLUT_ONCE_FRAME)); - MOD_REG_FLD(DISPC_CONFIG, 0x03 << 1, mode << 1); -} - -void omap_dispc_set_lcd_size(int x, int y) -{ - BUG_ON((x > (1 << 11)) || (y > (1 << 11))); - enable_lcd_clocks(1); - MOD_REG_FLD(DISPC_SIZE_LCD, FLD_MASK(16, 11) | FLD_MASK(0, 11), - ((y - 1) << 16) | (x - 1)); - enable_lcd_clocks(0); -} -EXPORT_SYMBOL(omap_dispc_set_lcd_size); - -void omap_dispc_set_digit_size(int x, int y) -{ - BUG_ON((x > (1 << 11)) || (y > (1 << 11))); - enable_lcd_clocks(1); - MOD_REG_FLD(DISPC_SIZE_DIG, FLD_MASK(16, 11) | FLD_MASK(0, 11), - ((y - 1) << 16) | (x - 1)); - enable_lcd_clocks(0); -} -EXPORT_SYMBOL(omap_dispc_set_digit_size); - -static void setup_plane_fifo(int plane, int ext_mode) -{ - const u32 ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD, - DISPC_VID1_BASE + DISPC_VID_FIFO_THRESHOLD, - DISPC_VID2_BASE + DISPC_VID_FIFO_THRESHOLD }; - const u32 fsz_reg[] = { DISPC_GFX_FIFO_SIZE_STATUS, - DISPC_VID1_BASE + DISPC_VID_FIFO_SIZE_STATUS, - DISPC_VID2_BASE + DISPC_VID_FIFO_SIZE_STATUS }; - int low, high; - u32 l; - - BUG_ON(plane > 2); - - l = dispc_read_reg(fsz_reg[plane]); - l &= FLD_MASK(0, 11); - if (ext_mode) { - low = l * 3 / 4; - high = l; - } else { - low = l / 4; - high = l * 3 / 4; - } - MOD_REG_FLD(ftrs_reg[plane], FLD_MASK(16, 12) | FLD_MASK(0, 12), - (high << 16) | low); -} - -void omap_dispc_enable_lcd_out(int enable) -{ - enable_lcd_clocks(1); - MOD_REG_FLD(DISPC_CONTROL, 1, enable ? 1 : 0); - enable_lcd_clocks(0); -} -EXPORT_SYMBOL(omap_dispc_enable_lcd_out); - -void omap_dispc_enable_digit_out(int enable) -{ - enable_lcd_clocks(1); - MOD_REG_FLD(DISPC_CONTROL, 1 << 1, enable ? 1 << 1 : 0); - enable_lcd_clocks(0); -} -EXPORT_SYMBOL(omap_dispc_enable_digit_out); - -static inline int _setup_plane(int plane, int channel_out, - u32 paddr, int screen_width, - int pos_x, int pos_y, int width, int height, - int color_mode) -{ - const u32 at_reg[] = { DISPC_GFX_ATTRIBUTES, - DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES, - DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES }; - const u32 ba_reg[] = { DISPC_GFX_BA0, DISPC_VID1_BASE + DISPC_VID_BA0, - DISPC_VID2_BASE + DISPC_VID_BA0 }; - const u32 ps_reg[] = { DISPC_GFX_POSITION, - DISPC_VID1_BASE + DISPC_VID_POSITION, - DISPC_VID2_BASE + DISPC_VID_POSITION }; - const u32 sz_reg[] = { DISPC_GFX_SIZE, - DISPC_VID1_BASE + DISPC_VID_PICTURE_SIZE, - DISPC_VID2_BASE + DISPC_VID_PICTURE_SIZE }; - const u32 ri_reg[] = { DISPC_GFX_ROW_INC, - DISPC_VID1_BASE + DISPC_VID_ROW_INC, - DISPC_VID2_BASE + DISPC_VID_ROW_INC }; - const u32 vs_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_SIZE, - DISPC_VID2_BASE + DISPC_VID_SIZE }; - - int chout_shift, burst_shift; - int chout_val; - int color_code; - int bpp; - int cconv_en; - int set_vsize; - u32 l; - -#ifdef VERBOSE - dev_dbg(dispc.fbdev->dev, "plane %d channel %d paddr %#08x scr_width %d" - " pos_x %d pos_y %d width %d height %d color_mode %d\n", - plane, channel_out, paddr, screen_width, pos_x, pos_y, - width, height, color_mode); -#endif - - set_vsize = 0; - switch (plane) { - case OMAPFB_PLANE_GFX: - burst_shift = 6; - chout_shift = 8; - break; - case OMAPFB_PLANE_VID1: - case OMAPFB_PLANE_VID2: - burst_shift = 14; - chout_shift = 16; - set_vsize = 1; - break; - default: - return -EINVAL; - } - - switch (channel_out) { - case OMAPFB_CHANNEL_OUT_LCD: - chout_val = 0; - break; - case OMAPFB_CHANNEL_OUT_DIGIT: - chout_val = 1; - break; - default: - return -EINVAL; - } - - cconv_en = 0; - switch (color_mode) { - case OMAPFB_COLOR_RGB565: - color_code = DISPC_RGB_16_BPP; - bpp = 16; - break; - case OMAPFB_COLOR_YUV422: - if (plane == 0) - return -EINVAL; - color_code = DISPC_UYVY_422; - cconv_en = 1; - bpp = 16; - break; - case OMAPFB_COLOR_YUY422: - if (plane == 0) - return -EINVAL; - color_code = DISPC_YUV2_422; - cconv_en = 1; - bpp = 16; - break; - default: - return -EINVAL; - } - - l = dispc_read_reg(at_reg[plane]); - - l &= ~(0x0f << 1); - l |= color_code << 1; - l &= ~(1 << 9); - l |= cconv_en << 9; - - l &= ~(0x03 << burst_shift); - l |= DISPC_BURST_8x32 << burst_shift; - - l &= ~(1 << chout_shift); - l |= chout_val << chout_shift; - - dispc_write_reg(at_reg[plane], l); - - dispc_write_reg(ba_reg[plane], paddr); - MOD_REG_FLD(ps_reg[plane], - FLD_MASK(16, 11) | FLD_MASK(0, 11), (pos_y << 16) | pos_x); - - MOD_REG_FLD(sz_reg[plane], FLD_MASK(16, 11) | FLD_MASK(0, 11), - ((height - 1) << 16) | (width - 1)); - - if (set_vsize) { - /* Set video size if set_scale hasn't set it */ - if (!dispc.fir_vinc[plane]) - MOD_REG_FLD(vs_reg[plane], - FLD_MASK(16, 11), (height - 1) << 16); - if (!dispc.fir_hinc[plane]) - MOD_REG_FLD(vs_reg[plane], - FLD_MASK(0, 11), width - 1); - } - - dispc_write_reg(ri_reg[plane], (screen_width - width) * bpp / 8 + 1); - - return height * screen_width * bpp / 8; -} - -static int omap_dispc_setup_plane(int plane, int channel_out, - unsigned long offset, - int screen_width, - int pos_x, int pos_y, int width, int height, - int color_mode) -{ - u32 paddr; - int r; - - if ((unsigned)plane > dispc.mem_desc.region_cnt) - return -EINVAL; - paddr = dispc.mem_desc.region[plane].paddr + offset; - enable_lcd_clocks(1); - r = _setup_plane(plane, channel_out, paddr, - screen_width, - pos_x, pos_y, width, height, color_mode); - enable_lcd_clocks(0); - return r; -} - -static void write_firh_reg(int plane, int reg, u32 value) -{ - u32 base; - - if (plane == 1) - base = DISPC_VID1_BASE + DISPC_VID_FIR_COEF_H0; - else - base = DISPC_VID2_BASE + DISPC_VID_FIR_COEF_H0; - dispc_write_reg(base + reg * 8, value); -} - -static void write_firhv_reg(int plane, int reg, u32 value) -{ - u32 base; - - if (plane == 1) - base = DISPC_VID1_BASE + DISPC_VID_FIR_COEF_HV0; - else - base = DISPC_VID2_BASE + DISPC_VID_FIR_COEF_HV0; - dispc_write_reg(base + reg * 8, value); -} - -static void set_upsampling_coef_table(int plane) -{ - const u32 coef[][2] = { - { 0x00800000, 0x00800000 }, - { 0x0D7CF800, 0x037B02FF }, - { 0x1E70F5FF, 0x0C6F05FE }, - { 0x335FF5FE, 0x205907FB }, - { 0xF74949F7, 0x00404000 }, - { 0xF55F33FB, 0x075920FE }, - { 0xF5701EFE, 0x056F0CFF }, - { 0xF87C0DFF, 0x027B0300 }, - }; - int i; - - for (i = 0; i < 8; i++) { - write_firh_reg(plane, i, coef[i][0]); - write_firhv_reg(plane, i, coef[i][1]); - } -} - -static int omap_dispc_set_scale(int plane, - int orig_width, int orig_height, - int out_width, int out_height) -{ - const u32 at_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES, - DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES }; - const u32 vs_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_SIZE, - DISPC_VID2_BASE + DISPC_VID_SIZE }; - const u32 fir_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_FIR, - DISPC_VID2_BASE + DISPC_VID_FIR }; - - u32 l; - int fir_hinc; - int fir_vinc; - - if ((unsigned)plane > OMAPFB_PLANE_NUM) - return -ENODEV; - - if (plane == OMAPFB_PLANE_GFX && - (out_width != orig_width || out_height != orig_height)) - return -EINVAL; - - enable_lcd_clocks(1); - if (orig_width < out_width) { - /* - * Upsampling. - * Currently you can only scale both dimensions in one way. - */ - if (orig_height > out_height || - orig_width * 8 < out_width || - orig_height * 8 < out_height) { - enable_lcd_clocks(0); - return -EINVAL; - } - set_upsampling_coef_table(plane); - } else if (orig_width > out_width) { - /* Downsampling not yet supported - */ - - enable_lcd_clocks(0); - return -EINVAL; - } - if (!orig_width || orig_width == out_width) - fir_hinc = 0; - else - fir_hinc = 1024 * orig_width / out_width; - if (!orig_height || orig_height == out_height) - fir_vinc = 0; - else - fir_vinc = 1024 * orig_height / out_height; - dispc.fir_hinc[plane] = fir_hinc; - dispc.fir_vinc[plane] = fir_vinc; - - MOD_REG_FLD(fir_reg[plane], - FLD_MASK(16, 12) | FLD_MASK(0, 12), - ((fir_vinc & 4095) << 16) | - (fir_hinc & 4095)); - - dev_dbg(dispc.fbdev->dev, "out_width %d out_height %d orig_width %d " - "orig_height %d fir_hinc %d fir_vinc %d\n", - out_width, out_height, orig_width, orig_height, - fir_hinc, fir_vinc); - - MOD_REG_FLD(vs_reg[plane], - FLD_MASK(16, 11) | FLD_MASK(0, 11), - ((out_height - 1) << 16) | (out_width - 1)); - - l = dispc_read_reg(at_reg[plane]); - l &= ~(0x03 << 5); - l |= fir_hinc ? (1 << 5) : 0; - l |= fir_vinc ? (1 << 6) : 0; - dispc_write_reg(at_reg[plane], l); - - enable_lcd_clocks(0); - return 0; -} - -static int omap_dispc_enable_plane(int plane, int enable) -{ - const u32 at_reg[] = { DISPC_GFX_ATTRIBUTES, - DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES, - DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES }; - if ((unsigned int)plane > dispc.mem_desc.region_cnt) - return -EINVAL; - - enable_lcd_clocks(1); - MOD_REG_FLD(at_reg[plane], 1, enable ? 1 : 0); - enable_lcd_clocks(0); - - return 0; -} - -static int omap_dispc_set_color_key(struct omapfb_color_key *ck) -{ - u32 df_reg, tr_reg; - int shift, val; - - switch (ck->channel_out) { - case OMAPFB_CHANNEL_OUT_LCD: - df_reg = DISPC_DEFAULT_COLOR0; - tr_reg = DISPC_TRANS_COLOR0; - shift = 10; - break; - case OMAPFB_CHANNEL_OUT_DIGIT: - df_reg = DISPC_DEFAULT_COLOR1; - tr_reg = DISPC_TRANS_COLOR1; - shift = 12; - break; - default: - return -EINVAL; - } - switch (ck->key_type) { - case OMAPFB_COLOR_KEY_DISABLED: - val = 0; - break; - case OMAPFB_COLOR_KEY_GFX_DST: - val = 1; - break; - case OMAPFB_COLOR_KEY_VID_SRC: - val = 3; - break; - default: - return -EINVAL; - } - enable_lcd_clocks(1); - MOD_REG_FLD(DISPC_CONFIG, FLD_MASK(shift, 2), val << shift); - - if (val != 0) - dispc_write_reg(tr_reg, ck->trans_key); - dispc_write_reg(df_reg, ck->background); - enable_lcd_clocks(0); - - dispc.color_key = *ck; - - return 0; -} - -static int omap_dispc_get_color_key(struct omapfb_color_key *ck) -{ - *ck = dispc.color_key; - return 0; -} - -static void load_palette(void) -{ -} - -static int omap_dispc_set_update_mode(enum omapfb_update_mode mode) -{ - int r = 0; - - if (mode != dispc.update_mode) { - switch (mode) { - case OMAPFB_AUTO_UPDATE: - case OMAPFB_MANUAL_UPDATE: - enable_lcd_clocks(1); - omap_dispc_enable_lcd_out(1); - dispc.update_mode = mode; - break; - case OMAPFB_UPDATE_DISABLED: - init_completion(&dispc.frame_done); - omap_dispc_enable_lcd_out(0); - if (!wait_for_completion_timeout(&dispc.frame_done, - msecs_to_jiffies(500))) { - dev_err(dispc.fbdev->dev, - "timeout waiting for FRAME DONE\n"); - } - dispc.update_mode = mode; - enable_lcd_clocks(0); - break; - default: - r = -EINVAL; - } - } - - return r; -} - -static void omap_dispc_get_caps(int plane, struct omapfb_caps *caps) -{ - caps->ctrl |= OMAPFB_CAPS_PLANE_RELOCATE_MEM; - if (plane > 0) - caps->ctrl |= OMAPFB_CAPS_PLANE_SCALE; - caps->plane_color |= (1 << OMAPFB_COLOR_RGB565) | - (1 << OMAPFB_COLOR_YUV422) | - (1 << OMAPFB_COLOR_YUY422); - if (plane == 0) - caps->plane_color |= (1 << OMAPFB_COLOR_CLUT_8BPP) | - (1 << OMAPFB_COLOR_CLUT_4BPP) | - (1 << OMAPFB_COLOR_CLUT_2BPP) | - (1 << OMAPFB_COLOR_CLUT_1BPP) | - (1 << OMAPFB_COLOR_RGB444); -} - -static enum omapfb_update_mode omap_dispc_get_update_mode(void) -{ - return dispc.update_mode; -} - -static void setup_color_conv_coef(void) -{ - u32 mask = FLD_MASK(16, 11) | FLD_MASK(0, 11); - int cf1_reg = DISPC_VID1_BASE + DISPC_VID_CONV_COEF0; - int cf2_reg = DISPC_VID2_BASE + DISPC_VID_CONV_COEF0; - int at1_reg = DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES; - int at2_reg = DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES; - const struct color_conv_coef { - int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb; - int full_range; - } ctbl_bt601_5 = { - 298, 409, 0, 298, -208, -100, 298, 0, 517, 0, - }; - const struct color_conv_coef *ct; -#define CVAL(x, y) (((x & 2047) << 16) | (y & 2047)) - - ct = &ctbl_bt601_5; - - MOD_REG_FLD(cf1_reg, mask, CVAL(ct->rcr, ct->ry)); - MOD_REG_FLD(cf1_reg + 4, mask, CVAL(ct->gy, ct->rcb)); - MOD_REG_FLD(cf1_reg + 8, mask, CVAL(ct->gcb, ct->gcr)); - MOD_REG_FLD(cf1_reg + 12, mask, CVAL(ct->bcr, ct->by)); - MOD_REG_FLD(cf1_reg + 16, mask, CVAL(0, ct->bcb)); - - MOD_REG_FLD(cf2_reg, mask, CVAL(ct->rcr, ct->ry)); - MOD_REG_FLD(cf2_reg + 4, mask, CVAL(ct->gy, ct->rcb)); - MOD_REG_FLD(cf2_reg + 8, mask, CVAL(ct->gcb, ct->gcr)); - MOD_REG_FLD(cf2_reg + 12, mask, CVAL(ct->bcr, ct->by)); - MOD_REG_FLD(cf2_reg + 16, mask, CVAL(0, ct->bcb)); -#undef CVAL - - MOD_REG_FLD(at1_reg, (1 << 11), ct->full_range); - MOD_REG_FLD(at2_reg, (1 << 11), ct->full_range); -} - -static void calc_ck_div(int is_tft, int pck, int *lck_div, int *pck_div) -{ - unsigned long fck, lck; - - *lck_div = 1; - pck = max(1, pck); - fck = clk_get_rate(dispc.dss1_fck); - lck = fck; - *pck_div = (lck + pck - 1) / pck; - if (is_tft) - *pck_div = max(2, *pck_div); - else - *pck_div = max(3, *pck_div); - if (*pck_div > 255) { - *pck_div = 255; - lck = pck * *pck_div; - *lck_div = fck / lck; - BUG_ON(*lck_div < 1); - if (*lck_div > 255) { - *lck_div = 255; - dev_warn(dispc.fbdev->dev, "pixclock %d kHz too low.\n", - pck / 1000); - } - } -} - -static void set_lcd_tft_mode(int enable) -{ - u32 mask; - - mask = 1 << 3; - MOD_REG_FLD(DISPC_CONTROL, mask, enable ? mask : 0); -} - -static void set_lcd_timings(void) -{ - u32 l; - int lck_div, pck_div; - struct lcd_panel *panel = dispc.fbdev->panel; - int is_tft = panel->config & OMAP_LCDC_PANEL_TFT; - unsigned long fck; - - l = dispc_read_reg(DISPC_TIMING_H); - l &= ~(FLD_MASK(0, 6) | FLD_MASK(8, 8) | FLD_MASK(20, 8)); - l |= ( max(1, (min(64, panel->hsw))) - 1 ) << 0; - l |= ( max(1, (min(256, panel->hfp))) - 1 ) << 8; - l |= ( max(1, (min(256, panel->hbp))) - 1 ) << 20; - dispc_write_reg(DISPC_TIMING_H, l); - - l = dispc_read_reg(DISPC_TIMING_V); - l &= ~(FLD_MASK(0, 6) | FLD_MASK(8, 8) | FLD_MASK(20, 8)); - l |= ( max(1, (min(64, panel->vsw))) - 1 ) << 0; - l |= ( max(0, (min(255, panel->vfp))) - 0 ) << 8; - l |= ( max(0, (min(255, panel->vbp))) - 0 ) << 20; - dispc_write_reg(DISPC_TIMING_V, l); - - l = dispc_read_reg(DISPC_POL_FREQ); - l &= ~FLD_MASK(12, 6); - l |= (panel->config & OMAP_LCDC_SIGNAL_MASK) << 12; - l |= panel->acb & 0xff; - dispc_write_reg(DISPC_POL_FREQ, l); - - calc_ck_div(is_tft, panel->pixel_clock * 1000, &lck_div, &pck_div); - - l = dispc_read_reg(DISPC_DIVISOR); - l &= ~(FLD_MASK(16, 8) | FLD_MASK(0, 8)); - l |= (lck_div << 16) | (pck_div << 0); - dispc_write_reg(DISPC_DIVISOR, l); - - /* update panel info with the exact clock */ - fck = clk_get_rate(dispc.dss1_fck); - panel->pixel_clock = fck / lck_div / pck_div / 1000; -} - -static void recalc_irq_mask(void) -{ - int i; - unsigned long irq_mask = DISPC_IRQ_MASK_ERROR; - - for (i = 0; i < MAX_IRQ_HANDLERS; i++) { - if (!dispc.irq_handlers[i].callback) - continue; - - irq_mask |= dispc.irq_handlers[i].irq_mask; - } - - enable_lcd_clocks(1); - MOD_REG_FLD(DISPC_IRQENABLE, 0x7fff, irq_mask); - enable_lcd_clocks(0); -} - -int omap_dispc_request_irq(unsigned long irq_mask, void (*callback)(void *data), - void *data) -{ - int i; - - BUG_ON(callback == NULL); - - for (i = 0; i < MAX_IRQ_HANDLERS; i++) { - if (dispc.irq_handlers[i].callback) - continue; - - dispc.irq_handlers[i].irq_mask = irq_mask; - dispc.irq_handlers[i].callback = callback; - dispc.irq_handlers[i].data = data; - recalc_irq_mask(); - - return 0; - } - - return -EBUSY; -} -EXPORT_SYMBOL(omap_dispc_request_irq); - -void omap_dispc_free_irq(unsigned long irq_mask, void (*callback)(void *data), - void *data) -{ - int i; - - for (i = 0; i < MAX_IRQ_HANDLERS; i++) { - if (dispc.irq_handlers[i].callback == callback && - dispc.irq_handlers[i].data == data) { - dispc.irq_handlers[i].irq_mask = 0; - dispc.irq_handlers[i].callback = NULL; - dispc.irq_handlers[i].data = NULL; - recalc_irq_mask(); - return; - } - } - - BUG(); -} -EXPORT_SYMBOL(omap_dispc_free_irq); - -static irqreturn_t omap_dispc_irq_handler(int irq, void *dev) -{ - u32 stat; - int i = 0; - - enable_lcd_clocks(1); - - stat = dispc_read_reg(DISPC_IRQSTATUS); - if (stat & DISPC_IRQ_FRAMEMASK) - complete(&dispc.frame_done); - - if (stat & DISPC_IRQ_MASK_ERROR) { - if (printk_ratelimit()) { - dev_err(dispc.fbdev->dev, "irq error status %04x\n", - stat & 0x7fff); - } - } - - for (i = 0; i < MAX_IRQ_HANDLERS; i++) { - if (unlikely(dispc.irq_handlers[i].callback && - (stat & dispc.irq_handlers[i].irq_mask))) - dispc.irq_handlers[i].callback( - dispc.irq_handlers[i].data); - } - - dispc_write_reg(DISPC_IRQSTATUS, stat); - - enable_lcd_clocks(0); - - return IRQ_HANDLED; -} - -static int get_dss_clocks(void) -{ - dispc.dss_ick = clk_get(&dispc.fbdev->dssdev->dev, "ick"); - if (IS_ERR(dispc.dss_ick)) { - dev_err(dispc.fbdev->dev, "can't get ick\n"); - return PTR_ERR(dispc.dss_ick); - } - - dispc.dss1_fck = clk_get(&dispc.fbdev->dssdev->dev, "fck"); - if (IS_ERR(dispc.dss1_fck)) { - dev_err(dispc.fbdev->dev, "can't get dss1_fck\n"); - clk_put(dispc.dss_ick); - return PTR_ERR(dispc.dss1_fck); - } - - dispc.dss_54m_fck = clk_get(&dispc.fbdev->dssdev->dev, "tv_clk"); - if (IS_ERR(dispc.dss_54m_fck)) { - dev_err(dispc.fbdev->dev, "can't get tv_fck\n"); - clk_put(dispc.dss_ick); - clk_put(dispc.dss1_fck); - return PTR_ERR(dispc.dss_54m_fck); - } - - return 0; -} - -static void put_dss_clocks(void) -{ - clk_put(dispc.dss_54m_fck); - clk_put(dispc.dss1_fck); - clk_put(dispc.dss_ick); -} - -static void enable_lcd_clocks(int enable) -{ - if (enable) { - clk_enable(dispc.dss_ick); - clk_enable(dispc.dss1_fck); - } else { - clk_disable(dispc.dss1_fck); - clk_disable(dispc.dss_ick); - } -} - -static void enable_digit_clocks(int enable) -{ - if (enable) - clk_enable(dispc.dss_54m_fck); - else - clk_disable(dispc.dss_54m_fck); -} - -static void omap_dispc_suspend(void) -{ - if (dispc.update_mode == OMAPFB_AUTO_UPDATE) { - init_completion(&dispc.frame_done); - omap_dispc_enable_lcd_out(0); - if (!wait_for_completion_timeout(&dispc.frame_done, - msecs_to_jiffies(500))) { - dev_err(dispc.fbdev->dev, - "timeout waiting for FRAME DONE\n"); - } - enable_lcd_clocks(0); - } -} - -static void omap_dispc_resume(void) -{ - if (dispc.update_mode == OMAPFB_AUTO_UPDATE) { - enable_lcd_clocks(1); - if (!dispc.ext_mode) { - set_lcd_timings(); - load_palette(); - } - omap_dispc_enable_lcd_out(1); - } -} - - -static int omap_dispc_update_window(struct fb_info *fbi, - struct omapfb_update_window *win, - void (*complete_callback)(void *arg), - void *complete_callback_data) -{ - return dispc.update_mode == OMAPFB_UPDATE_DISABLED ? -ENODEV : 0; -} - -static int mmap_kern(struct omapfb_mem_region *region) -{ - struct vm_struct *kvma; - struct vm_area_struct vma; - pgprot_t pgprot; - unsigned long vaddr; - - kvma = get_vm_area(region->size, VM_IOREMAP); - if (kvma == NULL) { - dev_err(dispc.fbdev->dev, "can't get kernel vm area\n"); - return -ENOMEM; - } - vma.vm_mm = &init_mm; - - vaddr = (unsigned long)kvma->addr; - - pgprot = pgprot_writecombine(pgprot_kernel); - vma.vm_start = vaddr; - vma.vm_end = vaddr + region->size; - if (io_remap_pfn_range(&vma, vaddr, region->paddr >> PAGE_SHIFT, - region->size, pgprot) < 0) { - dev_err(dispc.fbdev->dev, "kernel mmap for FBMEM failed\n"); - return -EAGAIN; - } - region->vaddr = (void *)vaddr; - - return 0; -} - -static void mmap_user_open(struct vm_area_struct *vma) -{ - int plane = (int)vma->vm_private_data; - - atomic_inc(&dispc.map_count[plane]); -} - -static void mmap_user_close(struct vm_area_struct *vma) -{ - int plane = (int)vma->vm_private_data; - - atomic_dec(&dispc.map_count[plane]); -} - -static const struct vm_operations_struct mmap_user_ops = { - .open = mmap_user_open, - .close = mmap_user_close, -}; - -static int omap_dispc_mmap_user(struct fb_info *info, - struct vm_area_struct *vma) -{ - struct omapfb_plane_struct *plane = info->par; - unsigned long off; - unsigned long start; - u32 len; - - if (vma->vm_end - vma->vm_start == 0) - return 0; - if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) - return -EINVAL; - off = vma->vm_pgoff << PAGE_SHIFT; - - start = info->fix.smem_start; - len = info->fix.smem_len; - if (off >= len) - return -EINVAL; - if ((vma->vm_end - vma->vm_start + off) > len) - return -EINVAL; - off += start; - vma->vm_pgoff = off >> PAGE_SHIFT; - vma->vm_flags |= VM_IO | VM_RESERVED; - vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); - vma->vm_ops = &mmap_user_ops; - vma->vm_private_data = (void *)plane->idx; - if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, vma->vm_page_prot)) - return -EAGAIN; - /* vm_ops.open won't be called for mmap itself. */ - atomic_inc(&dispc.map_count[plane->idx]); - return 0; -} - -static void unmap_kern(struct omapfb_mem_region *region) -{ - vunmap(region->vaddr); -} - -static int alloc_palette_ram(void) -{ - dispc.palette_vaddr = dma_alloc_writecombine(dispc.fbdev->dev, - MAX_PALETTE_SIZE, &dispc.palette_paddr, GFP_KERNEL); - if (dispc.palette_vaddr == NULL) { - dev_err(dispc.fbdev->dev, "failed to alloc palette memory\n"); - return -ENOMEM; - } - - return 0; -} - -static void free_palette_ram(void) -{ - dma_free_writecombine(dispc.fbdev->dev, MAX_PALETTE_SIZE, - dispc.palette_vaddr, dispc.palette_paddr); -} - -static int alloc_fbmem(struct omapfb_mem_region *region) -{ - region->vaddr = dma_alloc_writecombine(dispc.fbdev->dev, - region->size, ®ion->paddr, GFP_KERNEL); - - if (region->vaddr == NULL) { - dev_err(dispc.fbdev->dev, "unable to allocate FB DMA memory\n"); - return -ENOMEM; - } - - return 0; -} - -static void free_fbmem(struct omapfb_mem_region *region) -{ - dma_free_writecombine(dispc.fbdev->dev, region->size, - region->vaddr, region->paddr); -} - -static struct resmap *init_resmap(unsigned long start, size_t size) -{ - unsigned page_cnt; - struct resmap *res_map; - - page_cnt = PAGE_ALIGN(size) / PAGE_SIZE; - res_map = - kzalloc(sizeof(struct resmap) + RESMAP_SIZE(page_cnt), GFP_KERNEL); - if (res_map == NULL) - return NULL; - res_map->start = start; - res_map->page_cnt = page_cnt; - res_map->map = (unsigned long *)(res_map + 1); - return res_map; -} - -static void cleanup_resmap(struct resmap *res_map) -{ - kfree(res_map); -} - -static inline int resmap_mem_type(unsigned long start) -{ - if (start >= OMAP2_SRAM_START && - start < OMAP2_SRAM_START + OMAP2_SRAM_SIZE) - return OMAPFB_MEMTYPE_SRAM; - else - return OMAPFB_MEMTYPE_SDRAM; -} - -static inline int resmap_page_reserved(struct resmap *res_map, unsigned page_nr) -{ - return *RESMAP_PTR(res_map, page_nr) & RESMAP_MASK(page_nr) ? 1 : 0; -} - -static inline void resmap_reserve_page(struct resmap *res_map, unsigned page_nr) -{ - BUG_ON(resmap_page_reserved(res_map, page_nr)); - *RESMAP_PTR(res_map, page_nr) |= RESMAP_MASK(page_nr); -} - -static inline void resmap_free_page(struct resmap *res_map, unsigned page_nr) -{ - BUG_ON(!resmap_page_reserved(res_map, page_nr)); - *RESMAP_PTR(res_map, page_nr) &= ~RESMAP_MASK(page_nr); -} - -static void resmap_reserve_region(unsigned long start, size_t size) -{ - - struct resmap *res_map; - unsigned start_page; - unsigned end_page; - int mtype; - unsigned i; - - mtype = resmap_mem_type(start); - res_map = dispc.res_map[mtype]; - dev_dbg(dispc.fbdev->dev, "reserve mem type %d start %08lx size %d\n", - mtype, start, size); - start_page = (start - res_map->start) / PAGE_SIZE; - end_page = start_page + PAGE_ALIGN(size) / PAGE_SIZE; - for (i = start_page; i < end_page; i++) - resmap_reserve_page(res_map, i); -} - -static void resmap_free_region(unsigned long start, size_t size) -{ - struct resmap *res_map; - unsigned start_page; - unsigned end_page; - unsigned i; - int mtype; - - mtype = resmap_mem_type(start); - res_map = dispc.res_map[mtype]; - dev_dbg(dispc.fbdev->dev, "free mem type %d start %08lx size %d\n", - mtype, start, size); - start_page = (start - res_map->start) / PAGE_SIZE; - end_page = start_page + PAGE_ALIGN(size) / PAGE_SIZE; - for (i = start_page; i < end_page; i++) - resmap_free_page(res_map, i); -} - -static unsigned long resmap_alloc_region(int mtype, size_t size) -{ - unsigned i; - unsigned total; - unsigned start_page; - unsigned long start; - struct resmap *res_map = dispc.res_map[mtype]; - - BUG_ON(mtype >= DISPC_MEMTYPE_NUM || res_map == NULL || !size); - - size = PAGE_ALIGN(size) / PAGE_SIZE; - start_page = 0; - total = 0; - for (i = 0; i < res_map->page_cnt; i++) { - if (resmap_page_reserved(res_map, i)) { - start_page = i + 1; - total = 0; - } else if (++total == size) - break; - } - if (total < size) - return 0; - - start = res_map->start + start_page * PAGE_SIZE; - resmap_reserve_region(start, size * PAGE_SIZE); - - return start; -} - -/* Note that this will only work for user mappings, we don't deal with - * kernel mappings here, so fbcon will keep using the old region. - */ -static int omap_dispc_setup_mem(int plane, size_t size, int mem_type, - unsigned long *paddr) -{ - struct omapfb_mem_region *rg; - unsigned long new_addr = 0; - - if ((unsigned)plane > dispc.mem_desc.region_cnt) - return -EINVAL; - if (mem_type >= DISPC_MEMTYPE_NUM) - return -EINVAL; - if (dispc.res_map[mem_type] == NULL) - return -ENOMEM; - rg = &dispc.mem_desc.region[plane]; - if (size == rg->size && mem_type == rg->type) - return 0; - if (atomic_read(&dispc.map_count[plane])) - return -EBUSY; - if (rg->size != 0) - resmap_free_region(rg->paddr, rg->size); - if (size != 0) { - new_addr = resmap_alloc_region(mem_type, size); - if (!new_addr) { - /* Reallocate old region. */ - resmap_reserve_region(rg->paddr, rg->size); - return -ENOMEM; - } - } - rg->paddr = new_addr; - rg->size = size; - rg->type = mem_type; - - *paddr = new_addr; - - return 0; -} - -static int setup_fbmem(struct omapfb_mem_desc *req_md) -{ - struct omapfb_mem_region *rg; - int i; - int r; - unsigned long mem_start[DISPC_MEMTYPE_NUM]; - unsigned long mem_end[DISPC_MEMTYPE_NUM]; - - if (!req_md->region_cnt) { - dev_err(dispc.fbdev->dev, "no memory regions defined\n"); - return -ENOENT; - } - - rg = &req_md->region[0]; - memset(mem_start, 0xff, sizeof(mem_start)); - memset(mem_end, 0, sizeof(mem_end)); - - for (i = 0; i < req_md->region_cnt; i++, rg++) { - int mtype; - if (rg->paddr) { - rg->alloc = 0; - if (rg->vaddr == NULL) { - rg->map = 1; - if ((r = mmap_kern(rg)) < 0) - return r; - } - } else { - if (rg->type != OMAPFB_MEMTYPE_SDRAM) { - dev_err(dispc.fbdev->dev, - "unsupported memory type\n"); - return -EINVAL; - } - rg->alloc = rg->map = 1; - if ((r = alloc_fbmem(rg)) < 0) - return r; - } - mtype = rg->type; - - if (rg->paddr < mem_start[mtype]) - mem_start[mtype] = rg->paddr; - if (rg->paddr + rg->size > mem_end[mtype]) - mem_end[mtype] = rg->paddr + rg->size; - } - - for (i = 0; i < DISPC_MEMTYPE_NUM; i++) { - unsigned long start; - size_t size; - if (mem_end[i] == 0) - continue; - start = mem_start[i]; - size = mem_end[i] - start; - dispc.res_map[i] = init_resmap(start, size); - r = -ENOMEM; - if (dispc.res_map[i] == NULL) - goto fail; - /* Initial state is that everything is reserved. This - * includes possible holes as well, which will never be - * freed. - */ - resmap_reserve_region(start, size); - } - - dispc.mem_desc = *req_md; - - return 0; -fail: - for (i = 0; i < DISPC_MEMTYPE_NUM; i++) { - if (dispc.res_map[i] != NULL) - cleanup_resmap(dispc.res_map[i]); - } - return r; -} - -static void cleanup_fbmem(void) -{ - struct omapfb_mem_region *rg; - int i; - - for (i = 0; i < DISPC_MEMTYPE_NUM; i++) { - if (dispc.res_map[i] != NULL) - cleanup_resmap(dispc.res_map[i]); - } - rg = &dispc.mem_desc.region[0]; - for (i = 0; i < dispc.mem_desc.region_cnt; i++, rg++) { - if (rg->alloc) - free_fbmem(rg); - else { - if (rg->map) - unmap_kern(rg); - } - } -} - -static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, - struct omapfb_mem_desc *req_vram) -{ - int r; - u32 l; - struct lcd_panel *panel = fbdev->panel; - void __iomem *ram_fw_base; - int tmo = 10000; - int skip_init = 0; - int i; - - memset(&dispc, 0, sizeof(dispc)); - - dispc.base = ioremap(DISPC_BASE, SZ_1K); - if (!dispc.base) { - dev_err(fbdev->dev, "can't ioremap DISPC\n"); - return -ENOMEM; - } - - dispc.fbdev = fbdev; - dispc.ext_mode = ext_mode; - - init_completion(&dispc.frame_done); - - if ((r = get_dss_clocks()) < 0) - goto fail0; - - enable_lcd_clocks(1); - -#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT - l = dispc_read_reg(DISPC_CONTROL); - /* LCD enabled ? */ - if (l & 1) { - pr_info("omapfb: skipping hardware initialization\n"); - skip_init = 1; - } -#endif - - if (!skip_init) { - /* Reset monitoring works only w/ the 54M clk */ - enable_digit_clocks(1); - - /* Soft reset */ - MOD_REG_FLD(DISPC_SYSCONFIG, 1 << 1, 1 << 1); - - while (!(dispc_read_reg(DISPC_SYSSTATUS) & 1)) { - if (!--tmo) { - dev_err(dispc.fbdev->dev, "soft reset failed\n"); - r = -ENODEV; - enable_digit_clocks(0); - goto fail1; - } - } - - enable_digit_clocks(0); - } - - /* Enable smart standby/idle, autoidle and wakeup */ - l = dispc_read_reg(DISPC_SYSCONFIG); - l &= ~((3 << 12) | (3 << 3)); - l |= (2 << 12) | (2 << 3) | (1 << 2) | (1 << 0); - dispc_write_reg(DISPC_SYSCONFIG, l); - omap_writel(1 << 0, DSS_BASE + DSS_SYSCONFIG); - - /* Set functional clock autogating */ - l = dispc_read_reg(DISPC_CONFIG); - l |= 1 << 9; - dispc_write_reg(DISPC_CONFIG, l); - - l = dispc_read_reg(DISPC_IRQSTATUS); - dispc_write_reg(DISPC_IRQSTATUS, l); - - recalc_irq_mask(); - - if ((r = request_irq(INT_24XX_DSS_IRQ, omap_dispc_irq_handler, - 0, MODULE_NAME, fbdev)) < 0) { - dev_err(dispc.fbdev->dev, "can't get DSS IRQ\n"); - goto fail1; - } - - /* L3 firewall setting: enable access to OCM RAM */ - ram_fw_base = ioremap(0x68005000, SZ_1K); - if (!ram_fw_base) { - dev_err(dispc.fbdev->dev, "Cannot ioremap to enable OCM RAM\n"); - goto fail1; - } - __raw_writel(0x402000b0, ram_fw_base + 0xa0); - iounmap(ram_fw_base); - - if ((r = alloc_palette_ram()) < 0) - goto fail2; - - if ((r = setup_fbmem(req_vram)) < 0) - goto fail3; - - if (!skip_init) { - for (i = 0; i < dispc.mem_desc.region_cnt; i++) { - memset(dispc.mem_desc.region[i].vaddr, 0, - dispc.mem_desc.region[i].size); - } - - /* Set logic clock to fck, pixel clock to fck/2 for now */ - MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(16, 8), 1 << 16); - MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(0, 8), 2 << 0); - - setup_plane_fifo(0, ext_mode); - setup_plane_fifo(1, ext_mode); - setup_plane_fifo(2, ext_mode); - - setup_color_conv_coef(); - - set_lcd_tft_mode(panel->config & OMAP_LCDC_PANEL_TFT); - set_load_mode(DISPC_LOAD_FRAME_ONLY); - - if (!ext_mode) { - set_lcd_data_lines(panel->data_lines); - omap_dispc_set_lcd_size(panel->x_res, panel->y_res); - set_lcd_timings(); - } else - set_lcd_data_lines(panel->bpp); - enable_rfbi_mode(ext_mode); - } - - l = dispc_read_reg(DISPC_REVISION); - pr_info("omapfb: DISPC version %d.%d initialized\n", - l >> 4 & 0x0f, l & 0x0f); - enable_lcd_clocks(0); - - return 0; -fail3: - free_palette_ram(); -fail2: - free_irq(INT_24XX_DSS_IRQ, fbdev); -fail1: - enable_lcd_clocks(0); - put_dss_clocks(); -fail0: - iounmap(dispc.base); - return r; -} - -static void omap_dispc_cleanup(void) -{ - int i; - - omap_dispc_set_update_mode(OMAPFB_UPDATE_DISABLED); - /* This will also disable clocks that are on */ - for (i = 0; i < dispc.mem_desc.region_cnt; i++) - omap_dispc_enable_plane(i, 0); - cleanup_fbmem(); - free_palette_ram(); - free_irq(INT_24XX_DSS_IRQ, dispc.fbdev); - put_dss_clocks(); - iounmap(dispc.base); -} - -const struct lcd_ctrl omap2_int_ctrl = { - .name = "internal", - .init = omap_dispc_init, - .cleanup = omap_dispc_cleanup, - .get_caps = omap_dispc_get_caps, - .set_update_mode = omap_dispc_set_update_mode, - .get_update_mode = omap_dispc_get_update_mode, - .update_window = omap_dispc_update_window, - .suspend = omap_dispc_suspend, - .resume = omap_dispc_resume, - .setup_plane = omap_dispc_setup_plane, - .setup_mem = omap_dispc_setup_mem, - .set_scale = omap_dispc_set_scale, - .enable_plane = omap_dispc_enable_plane, - .set_color_key = omap_dispc_set_color_key, - .get_color_key = omap_dispc_get_color_key, - .mmap = omap_dispc_mmap_user, -}; diff --git a/drivers/video/omap/dispc.h b/drivers/video/omap/dispc.h deleted file mode 100644 index c15ea77f0604..000000000000 --- a/drivers/video/omap/dispc.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef _DISPC_H -#define _DISPC_H - -#include <linux/interrupt.h> - -#define DISPC_PLANE_GFX 0 -#define DISPC_PLANE_VID1 1 -#define DISPC_PLANE_VID2 2 - -#define DISPC_RGB_1_BPP 0x00 -#define DISPC_RGB_2_BPP 0x01 -#define DISPC_RGB_4_BPP 0x02 -#define DISPC_RGB_8_BPP 0x03 -#define DISPC_RGB_12_BPP 0x04 -#define DISPC_RGB_16_BPP 0x06 -#define DISPC_RGB_24_BPP 0x08 -#define DISPC_RGB_24_BPP_UNPACK_32 0x09 -#define DISPC_YUV2_422 0x0a -#define DISPC_UYVY_422 0x0b - -#define DISPC_BURST_4x32 0 -#define DISPC_BURST_8x32 1 -#define DISPC_BURST_16x32 2 - -#define DISPC_LOAD_CLUT_AND_FRAME 0x00 -#define DISPC_LOAD_CLUT_ONLY 0x01 -#define DISPC_LOAD_FRAME_ONLY 0x02 -#define DISPC_LOAD_CLUT_ONCE_FRAME 0x03 - -#define DISPC_TFT_DATA_LINES_12 0 -#define DISPC_TFT_DATA_LINES_16 1 -#define DISPC_TFT_DATA_LINES_18 2 -#define DISPC_TFT_DATA_LINES_24 3 - -extern void omap_dispc_set_lcd_size(int width, int height); - -extern void omap_dispc_enable_lcd_out(int enable); -extern void omap_dispc_enable_digit_out(int enable); - -extern int omap_dispc_request_irq(unsigned long irq_mask, - void (*callback)(void *data), void *data); -extern void omap_dispc_free_irq(unsigned long irq_mask, - void (*callback)(void *data), void *data); - -extern const struct lcd_ctrl omap2_int_ctrl; -#endif diff --git a/drivers/video/omap/hwa742.c b/drivers/video/omap/hwa742.c index 084aa0ac562b..9f1d23c319cb 100644 --- a/drivers/video/omap/hwa742.c +++ b/drivers/video/omap/hwa742.c @@ -28,7 +28,6 @@ #include <linux/interrupt.h> #include <plat/dma.h> -#include <plat/hwa742.h> #include "omapfb.h" #define HWA742_REV_CODE_REG 0x0 @@ -942,7 +941,6 @@ static int hwa742_init(struct omapfb_device *fbdev, int ext_mode, unsigned long sys_clk, pix_clk; int extif_mem_div; struct omapfb_platform_data *omapfb_conf; - struct hwa742_platform_data *ctrl_conf; BUG_ON(!fbdev->ext_if || !fbdev->int_ctrl); @@ -951,13 +949,6 @@ static int hwa742_init(struct omapfb_device *fbdev, int ext_mode, hwa742.int_ctrl = fbdev->int_ctrl; omapfb_conf = fbdev->dev->platform_data; - ctrl_conf = omapfb_conf->ctrl_platform_data; - - if (ctrl_conf == NULL) { - dev_err(fbdev->dev, "HWA742: missing platform data\n"); - r = -ENOENT; - goto err1; - } hwa742.sys_ck = clk_get(NULL, "hwa_sys_ck"); @@ -995,14 +986,12 @@ static int hwa742_init(struct omapfb_device *fbdev, int ext_mode, goto err4; } - if (ctrl_conf->te_connected) { - if ((r = setup_tearsync(pix_clk, extif_mem_div)) < 0) { - dev_err(hwa742.fbdev->dev, - "HWA742: can't setup tearing synchronization\n"); - goto err4; - } - hwa742.te_connected = 1; + if ((r = setup_tearsync(pix_clk, extif_mem_div)) < 0) { + dev_err(hwa742.fbdev->dev, + "HWA742: can't setup tearing synchronization\n"); + goto err4; } + hwa742.te_connected = 1; hwa742.max_transmit_size = hwa742.extif->max_transmit_size; diff --git a/drivers/video/omap/lcd_ams_delta.c b/drivers/video/omap/lcd_ams_delta.c index 0fdd6f6873bf..d3a311327227 100644 --- a/drivers/video/omap/lcd_ams_delta.c +++ b/drivers/video/omap/lcd_ams_delta.c @@ -25,6 +25,7 @@ #include <linux/io.h> #include <linux/delay.h> #include <linux/lcd.h> +#include <linux/gpio.h> #include <plat/board-ams-delta.h> #include <mach/hardware.h> @@ -98,29 +99,41 @@ static struct lcd_ops ams_delta_lcd_ops = { /* omapfb panel section */ +static const struct gpio _gpios[] = { + { + .gpio = AMS_DELTA_GPIO_PIN_LCD_VBLEN, + .flags = GPIOF_OUT_INIT_LOW, + .label = "lcd_vblen", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_LCD_NDISP, + .flags = GPIOF_OUT_INIT_LOW, + .label = "lcd_ndisp", + }, +}; + static int ams_delta_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) { - return 0; + return gpio_request_array(_gpios, ARRAY_SIZE(_gpios)); } static void ams_delta_panel_cleanup(struct lcd_panel *panel) { + gpio_free_array(_gpios, ARRAY_SIZE(_gpios)); } static int ams_delta_panel_enable(struct lcd_panel *panel) { - ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_NDISP, - AMS_DELTA_LATCH2_LCD_NDISP); - ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_VBLEN, - AMS_DELTA_LATCH2_LCD_VBLEN); + gpio_set_value(AMS_DELTA_GPIO_PIN_LCD_NDISP, 1); + gpio_set_value(AMS_DELTA_GPIO_PIN_LCD_VBLEN, 1); return 0; } static void ams_delta_panel_disable(struct lcd_panel *panel) { - ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_VBLEN, 0); - ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_NDISP, 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_LCD_VBLEN, 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_LCD_NDISP, 0); } static unsigned long ams_delta_panel_get_caps(struct lcd_panel *panel) diff --git a/drivers/video/omap/lcd_inn1610.c b/drivers/video/omap/lcd_inn1610.c index 7e8bd8e08a98..e3d3d135aa48 100644 --- a/drivers/video/omap/lcd_inn1610.c +++ b/drivers/video/omap/lcd_inn1610.c @@ -22,7 +22,7 @@ #include <linux/module.h> #include <linux/platform_device.h> -#include <asm/gpio.h> +#include <linux/gpio.h> #include "omapfb.h" #define MODULE_NAME "omapfb-lcd_h3" @@ -32,20 +32,18 @@ static int innovator1610_panel_init(struct lcd_panel *panel, { int r = 0; - if (gpio_request(14, "lcd_en0")) { + /* configure GPIO(14, 15) as outputs */ + if (gpio_request_one(14, GPIOF_OUT_INIT_LOW, "lcd_en0")) { pr_err(MODULE_NAME ": can't request GPIO 14\n"); r = -1; goto exit; } - if (gpio_request(15, "lcd_en1")) { + if (gpio_request_one(15, GPIOF_OUT_INIT_LOW, "lcd_en1")) { pr_err(MODULE_NAME ": can't request GPIO 15\n"); gpio_free(14); r = -1; goto exit; } - /* configure GPIO(14, 15) as outputs */ - gpio_direction_output(14, 0); - gpio_direction_output(15, 0); exit: return r; } diff --git a/drivers/video/omap/lcd_mipid.c b/drivers/video/omap/lcd_mipid.c index 8d546dd55e81..e3880c4a0bb1 100644 --- a/drivers/video/omap/lcd_mipid.c +++ b/drivers/video/omap/lcd_mipid.c @@ -609,19 +609,7 @@ static struct spi_driver mipid_spi_driver = { .remove = __devexit_p(mipid_spi_remove), }; -static int __init mipid_drv_init(void) -{ - spi_register_driver(&mipid_spi_driver); - - return 0; -} -module_init(mipid_drv_init); - -static void __exit mipid_drv_cleanup(void) -{ - spi_unregister_driver(&mipid_spi_driver); -} -module_exit(mipid_drv_cleanup); +module_spi_driver(mipid_spi_driver); MODULE_DESCRIPTION("MIPI display driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap/omapfb.h b/drivers/video/omap/omapfb.h index af3c9e571ec3..2921d20e4fba 100644 --- a/drivers/video/omap/omapfb.h +++ b/drivers/video/omap/omapfb.h @@ -47,6 +47,27 @@ struct omapfb_device; +#define OMAPFB_PLANE_NUM 1 + +struct omapfb_mem_region { + u32 paddr; + void __iomem *vaddr; + unsigned long size; + u8 type; /* OMAPFB_PLANE_MEM_* */ + enum omapfb_color_format format;/* OMAPFB_COLOR_* */ + unsigned format_used:1; /* Must be set when format is set. + * Needed b/c of the badly chosen 0 + * base for OMAPFB_COLOR_* values + */ + unsigned alloc:1; /* allocated by the driver */ + unsigned map:1; /* kernel mapped by the driver */ +}; + +struct omapfb_mem_desc { + int region_cnt; + struct omapfb_mem_region region[OMAPFB_PLANE_NUM]; +}; + struct lcd_panel { const char *name; int config; /* TFT/STN, signal inversion */ @@ -207,11 +228,7 @@ struct omapfb_device { struct platform_device *dssdev; /* dummy dev for clocks */ }; -#ifdef CONFIG_ARCH_OMAP1 extern struct lcd_ctrl omap1_lcd_ctrl; -#else -extern struct lcd_ctrl omap2_disp_ctrl; -#endif extern void omapfb_register_panel(struct lcd_panel *panel); extern void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval); diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c index b291bfaac80e..f54b463709e9 100644 --- a/drivers/video/omap/omapfb_main.c +++ b/drivers/video/omap/omapfb_main.c @@ -34,7 +34,6 @@ #include "omapfb.h" #include "lcdc.h" -#include "dispc.h" #define MODULE_NAME "omapfb" @@ -104,29 +103,17 @@ static struct platform_device omapdss_device = { * --------------------------------------------------------------------------- */ extern struct lcd_ctrl hwa742_ctrl; -extern struct lcd_ctrl blizzard_ctrl; static const struct lcd_ctrl *ctrls[] = { -#ifdef CONFIG_ARCH_OMAP1 &omap1_int_ctrl, -#else - &omap2_int_ctrl, -#endif #ifdef CONFIG_FB_OMAP_LCDC_HWA742 &hwa742_ctrl, #endif -#ifdef CONFIG_FB_OMAP_LCDC_BLIZZARD - &blizzard_ctrl, -#endif }; #ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL -#ifdef CONFIG_ARCH_OMAP1 extern struct lcd_ctrl_extif omap1_ext_if; -#else -extern struct lcd_ctrl_extif omap2_ext_if; -#endif #endif static void omapfb_rqueue_lock(struct omapfb_device *fbdev) @@ -170,11 +157,6 @@ static int ctrl_init(struct omapfb_device *fbdev) fbdev->mem_desc.region[i].size = PAGE_ALIGN(def_vram[i]); fbdev->mem_desc.region_cnt = i; - } else { - struct omapfb_platform_data *conf; - - conf = fbdev->dev->platform_data; - fbdev->mem_desc = conf->mem_desc; } if (!fbdev->mem_desc.region_cnt) { @@ -880,7 +862,7 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) if (fbdev->ctrl->setup_mem == NULL) return -ENODEV; - if (mi->type > OMAPFB_MEMTYPE_MAX) + if (mi->type != OMAPFB_MEMTYPE_SDRAM) return -EINVAL; size = PAGE_ALIGN(mi->size); @@ -1721,17 +1703,10 @@ static int omapfb_do_probe(struct platform_device *pdev, mutex_init(&fbdev->rqueue_mutex); -#ifdef CONFIG_ARCH_OMAP1 fbdev->int_ctrl = &omap1_int_ctrl; #ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL fbdev->ext_if = &omap1_ext_if; #endif -#else /* OMAP2 */ - fbdev->int_ctrl = &omap2_int_ctrl; -#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL - fbdev->ext_if = &omap2_ext_if; -#endif -#endif if (omapfb_find_ctrl(fbdev) < 0) { dev_err(fbdev->dev, "LCD controller not found, board not supported\n"); @@ -1766,8 +1741,7 @@ static int omapfb_do_probe(struct platform_device *pdev, #ifdef CONFIG_FB_OMAP_DMA_TUNE /* Set DMA priority for EMIFF access to highest */ - if (cpu_class_is_omap1()) - omap_set_dma_priority(0, OMAP_DMA_PORT_EMIFF, 15); + omap_set_dma_priority(0, OMAP_DMA_PORT_EMIFF, 15); #endif r = ctrl_change_mode(fbdev->fb_info[0]); diff --git a/drivers/video/omap/rfbi.c b/drivers/video/omap/rfbi.c deleted file mode 100644 index 2c1a3402bef0..000000000000 --- a/drivers/video/omap/rfbi.c +++ /dev/null @@ -1,598 +0,0 @@ -/* - * OMAP2 Remote Frame Buffer Interface support - * - * Copyright (C) 2005 Nokia Corporation - * Author: Juha Yrjölä <juha.yrjola@nokia.com> - * Imre Deak <imre.deak@nokia.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/i2c.h> -#include <linux/err.h> -#include <linux/interrupt.h> -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/platform_device.h> - -#include "omapfb.h" -#include "dispc.h" - -/* To work around an RFBI transfer rate limitation */ -#define OMAP_RFBI_RATE_LIMIT 1 - -#define RFBI_BASE 0x48050800 -#define RFBI_REVISION 0x0000 -#define RFBI_SYSCONFIG 0x0010 -#define RFBI_SYSSTATUS 0x0014 -#define RFBI_CONTROL 0x0040 -#define RFBI_PIXEL_CNT 0x0044 -#define RFBI_LINE_NUMBER 0x0048 -#define RFBI_CMD 0x004c -#define RFBI_PARAM 0x0050 -#define RFBI_DATA 0x0054 -#define RFBI_READ 0x0058 -#define RFBI_STATUS 0x005c -#define RFBI_CONFIG0 0x0060 -#define RFBI_ONOFF_TIME0 0x0064 -#define RFBI_CYCLE_TIME0 0x0068 -#define RFBI_DATA_CYCLE1_0 0x006c -#define RFBI_DATA_CYCLE2_0 0x0070 -#define RFBI_DATA_CYCLE3_0 0x0074 -#define RFBI_VSYNC_WIDTH 0x0090 -#define RFBI_HSYNC_WIDTH 0x0094 - -#define DISPC_BASE 0x48050400 -#define DISPC_CONTROL 0x0040 -#define DISPC_IRQ_FRAMEMASK 0x0001 - -static struct { - void __iomem *base; - void (*lcdc_callback)(void *data); - void *lcdc_callback_data; - unsigned long l4_khz; - int bits_per_cycle; - struct omapfb_device *fbdev; - struct clk *dss_ick; - struct clk *dss1_fck; - unsigned tearsync_pin_cnt; - unsigned tearsync_mode; -} rfbi; - -static inline void rfbi_write_reg(int idx, u32 val) -{ - __raw_writel(val, rfbi.base + idx); -} - -static inline u32 rfbi_read_reg(int idx) -{ - return __raw_readl(rfbi.base + idx); -} - -static int rfbi_get_clocks(void) -{ - rfbi.dss_ick = clk_get(&rfbi.fbdev->dssdev->dev, "ick"); - if (IS_ERR(rfbi.dss_ick)) { - dev_err(rfbi.fbdev->dev, "can't get ick\n"); - return PTR_ERR(rfbi.dss_ick); - } - - rfbi.dss1_fck = clk_get(&rfbi.fbdev->dssdev->dev, "fck"); - if (IS_ERR(rfbi.dss1_fck)) { - dev_err(rfbi.fbdev->dev, "can't get dss1_fck\n"); - clk_put(rfbi.dss_ick); - return PTR_ERR(rfbi.dss1_fck); - } - - return 0; -} - -static void rfbi_put_clocks(void) -{ - clk_put(rfbi.dss1_fck); - clk_put(rfbi.dss_ick); -} - -static void rfbi_enable_clocks(int enable) -{ - if (enable) { - clk_enable(rfbi.dss_ick); - clk_enable(rfbi.dss1_fck); - } else { - clk_disable(rfbi.dss1_fck); - clk_disable(rfbi.dss_ick); - } -} - - -#ifdef VERBOSE -static void rfbi_print_timings(void) -{ - u32 l; - u32 time; - - l = rfbi_read_reg(RFBI_CONFIG0); - time = 1000000000 / rfbi.l4_khz; - if (l & (1 << 4)) - time *= 2; - - dev_dbg(rfbi.fbdev->dev, "Tick time %u ps\n", time); - l = rfbi_read_reg(RFBI_ONOFF_TIME0); - dev_dbg(rfbi.fbdev->dev, - "CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, " - "REONTIME %d, REOFFTIME %d\n", - l & 0x0f, (l >> 4) & 0x3f, (l >> 10) & 0x0f, (l >> 14) & 0x3f, - (l >> 20) & 0x0f, (l >> 24) & 0x3f); - - l = rfbi_read_reg(RFBI_CYCLE_TIME0); - dev_dbg(rfbi.fbdev->dev, - "WECYCLETIME %d, RECYCLETIME %d, CSPULSEWIDTH %d, " - "ACCESSTIME %d\n", - (l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f, - (l >> 22) & 0x3f); -} -#else -static void rfbi_print_timings(void) {} -#endif - -static void rfbi_set_timings(const struct extif_timings *t) -{ - u32 l; - - BUG_ON(!t->converted); - - rfbi_enable_clocks(1); - rfbi_write_reg(RFBI_ONOFF_TIME0, t->tim[0]); - rfbi_write_reg(RFBI_CYCLE_TIME0, t->tim[1]); - - l = rfbi_read_reg(RFBI_CONFIG0); - l &= ~(1 << 4); - l |= (t->tim[2] ? 1 : 0) << 4; - rfbi_write_reg(RFBI_CONFIG0, l); - - rfbi_print_timings(); - rfbi_enable_clocks(0); -} - -static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div) -{ - *clk_period = 1000000000 / rfbi.l4_khz; - *max_clk_div = 2; -} - -static int ps_to_rfbi_ticks(int time, int div) -{ - unsigned long tick_ps; - int ret; - - /* Calculate in picosecs to yield more exact results */ - tick_ps = 1000000000 / (rfbi.l4_khz) * div; - - ret = (time + tick_ps - 1) / tick_ps; - - return ret; -} - -#ifdef OMAP_RFBI_RATE_LIMIT -static unsigned long rfbi_get_max_tx_rate(void) -{ - unsigned long l4_rate, dss1_rate; - int min_l4_ticks = 0; - int i; - - /* According to TI this can't be calculated so make the - * adjustments for a couple of known frequencies and warn for - * others. - */ - static const struct { - unsigned long l4_clk; /* HZ */ - unsigned long dss1_clk; /* HZ */ - unsigned long min_l4_ticks; - } ftab[] = { - { 55, 132, 7, }, /* 7.86 MPix/s */ - { 110, 110, 12, }, /* 9.16 MPix/s */ - { 110, 132, 10, }, /* 11 Mpix/s */ - { 120, 120, 10, }, /* 12 Mpix/s */ - { 133, 133, 10, }, /* 13.3 Mpix/s */ - }; - - l4_rate = rfbi.l4_khz / 1000; - dss1_rate = clk_get_rate(rfbi.dss1_fck) / 1000000; - - for (i = 0; i < ARRAY_SIZE(ftab); i++) { - /* Use a window instead of an exact match, to account - * for different DPLL multiplier / divider pairs. - */ - if (abs(ftab[i].l4_clk - l4_rate) < 3 && - abs(ftab[i].dss1_clk - dss1_rate) < 3) { - min_l4_ticks = ftab[i].min_l4_ticks; - break; - } - } - if (i == ARRAY_SIZE(ftab)) { - /* Can't be sure, return anyway the maximum not - * rate-limited. This might cause a problem only for the - * tearing synchronisation. - */ - dev_err(rfbi.fbdev->dev, - "can't determine maximum RFBI transfer rate\n"); - return rfbi.l4_khz * 1000; - } - return rfbi.l4_khz * 1000 / min_l4_ticks; -} -#else -static int rfbi_get_max_tx_rate(void) -{ - return rfbi.l4_khz * 1000; -} -#endif - - -static int rfbi_convert_timings(struct extif_timings *t) -{ - u32 l; - int reon, reoff, weon, weoff, cson, csoff, cs_pulse; - int actim, recyc, wecyc; - int div = t->clk_div; - - if (div <= 0 || div > 2) - return -1; - - /* Make sure that after conversion it still holds that: - * weoff > weon, reoff > reon, recyc >= reoff, wecyc >= weoff, - * csoff > cson, csoff >= max(weoff, reoff), actim > reon - */ - weon = ps_to_rfbi_ticks(t->we_on_time, div); - weoff = ps_to_rfbi_ticks(t->we_off_time, div); - if (weoff <= weon) - weoff = weon + 1; - if (weon > 0x0f) - return -1; - if (weoff > 0x3f) - return -1; - - reon = ps_to_rfbi_ticks(t->re_on_time, div); - reoff = ps_to_rfbi_ticks(t->re_off_time, div); - if (reoff <= reon) - reoff = reon + 1; - if (reon > 0x0f) - return -1; - if (reoff > 0x3f) - return -1; - - cson = ps_to_rfbi_ticks(t->cs_on_time, div); - csoff = ps_to_rfbi_ticks(t->cs_off_time, div); - if (csoff <= cson) - csoff = cson + 1; - if (csoff < max(weoff, reoff)) - csoff = max(weoff, reoff); - if (cson > 0x0f) - return -1; - if (csoff > 0x3f) - return -1; - - l = cson; - l |= csoff << 4; - l |= weon << 10; - l |= weoff << 14; - l |= reon << 20; - l |= reoff << 24; - - t->tim[0] = l; - - actim = ps_to_rfbi_ticks(t->access_time, div); - if (actim <= reon) - actim = reon + 1; - if (actim > 0x3f) - return -1; - - wecyc = ps_to_rfbi_ticks(t->we_cycle_time, div); - if (wecyc < weoff) - wecyc = weoff; - if (wecyc > 0x3f) - return -1; - - recyc = ps_to_rfbi_ticks(t->re_cycle_time, div); - if (recyc < reoff) - recyc = reoff; - if (recyc > 0x3f) - return -1; - - cs_pulse = ps_to_rfbi_ticks(t->cs_pulse_width, div); - if (cs_pulse > 0x3f) - return -1; - - l = wecyc; - l |= recyc << 6; - l |= cs_pulse << 12; - l |= actim << 22; - - t->tim[1] = l; - - t->tim[2] = div - 1; - - t->converted = 1; - - return 0; -} - -static int rfbi_setup_tearsync(unsigned pin_cnt, - unsigned hs_pulse_time, unsigned vs_pulse_time, - int hs_pol_inv, int vs_pol_inv, int extif_div) -{ - int hs, vs; - int min; - u32 l; - - if (pin_cnt != 1 && pin_cnt != 2) - return -EINVAL; - - hs = ps_to_rfbi_ticks(hs_pulse_time, 1); - vs = ps_to_rfbi_ticks(vs_pulse_time, 1); - if (hs < 2) - return -EDOM; - if (pin_cnt == 2) - min = 2; - else - min = 4; - if (vs < min) - return -EDOM; - if (vs == hs) - return -EINVAL; - rfbi.tearsync_pin_cnt = pin_cnt; - dev_dbg(rfbi.fbdev->dev, - "setup_tearsync: pins %d hs %d vs %d hs_inv %d vs_inv %d\n", - pin_cnt, hs, vs, hs_pol_inv, vs_pol_inv); - - rfbi_enable_clocks(1); - rfbi_write_reg(RFBI_HSYNC_WIDTH, hs); - rfbi_write_reg(RFBI_VSYNC_WIDTH, vs); - - l = rfbi_read_reg(RFBI_CONFIG0); - if (hs_pol_inv) - l &= ~(1 << 21); - else - l |= 1 << 21; - if (vs_pol_inv) - l &= ~(1 << 20); - else - l |= 1 << 20; - rfbi_enable_clocks(0); - - return 0; -} - -static int rfbi_enable_tearsync(int enable, unsigned line) -{ - u32 l; - - dev_dbg(rfbi.fbdev->dev, "tearsync %d line %d mode %d\n", - enable, line, rfbi.tearsync_mode); - if (line > (1 << 11) - 1) - return -EINVAL; - - rfbi_enable_clocks(1); - l = rfbi_read_reg(RFBI_CONFIG0); - l &= ~(0x3 << 2); - if (enable) { - rfbi.tearsync_mode = rfbi.tearsync_pin_cnt; - l |= rfbi.tearsync_mode << 2; - } else - rfbi.tearsync_mode = 0; - rfbi_write_reg(RFBI_CONFIG0, l); - rfbi_write_reg(RFBI_LINE_NUMBER, line); - rfbi_enable_clocks(0); - - return 0; -} - -static void rfbi_write_command(const void *buf, unsigned int len) -{ - rfbi_enable_clocks(1); - if (rfbi.bits_per_cycle == 16) { - const u16 *w = buf; - BUG_ON(len & 1); - for (; len; len -= 2) - rfbi_write_reg(RFBI_CMD, *w++); - } else { - const u8 *b = buf; - BUG_ON(rfbi.bits_per_cycle != 8); - for (; len; len--) - rfbi_write_reg(RFBI_CMD, *b++); - } - rfbi_enable_clocks(0); -} - -static void rfbi_read_data(void *buf, unsigned int len) -{ - rfbi_enable_clocks(1); - if (rfbi.bits_per_cycle == 16) { - u16 *w = buf; - BUG_ON(len & ~1); - for (; len; len -= 2) { - rfbi_write_reg(RFBI_READ, 0); - *w++ = rfbi_read_reg(RFBI_READ); - } - } else { - u8 *b = buf; - BUG_ON(rfbi.bits_per_cycle != 8); - for (; len; len--) { - rfbi_write_reg(RFBI_READ, 0); - *b++ = rfbi_read_reg(RFBI_READ); - } - } - rfbi_enable_clocks(0); -} - -static void rfbi_write_data(const void *buf, unsigned int len) -{ - rfbi_enable_clocks(1); - if (rfbi.bits_per_cycle == 16) { - const u16 *w = buf; - BUG_ON(len & 1); - for (; len; len -= 2) - rfbi_write_reg(RFBI_PARAM, *w++); - } else { - const u8 *b = buf; - BUG_ON(rfbi.bits_per_cycle != 8); - for (; len; len--) - rfbi_write_reg(RFBI_PARAM, *b++); - } - rfbi_enable_clocks(0); -} - -static void rfbi_transfer_area(int width, int height, - void (callback)(void * data), void *data) -{ - u32 w; - - BUG_ON(callback == NULL); - - rfbi_enable_clocks(1); - omap_dispc_set_lcd_size(width, height); - - rfbi.lcdc_callback = callback; - rfbi.lcdc_callback_data = data; - - rfbi_write_reg(RFBI_PIXEL_CNT, width * height); - - w = rfbi_read_reg(RFBI_CONTROL); - w |= 1; /* enable */ - if (!rfbi.tearsync_mode) - w |= 1 << 4; /* internal trigger, reset by HW */ - rfbi_write_reg(RFBI_CONTROL, w); - - omap_dispc_enable_lcd_out(1); -} - -static inline void _stop_transfer(void) -{ - u32 w; - - w = rfbi_read_reg(RFBI_CONTROL); - rfbi_write_reg(RFBI_CONTROL, w & ~(1 << 0)); - rfbi_enable_clocks(0); -} - -static void rfbi_dma_callback(void *data) -{ - _stop_transfer(); - rfbi.lcdc_callback(rfbi.lcdc_callback_data); -} - -static void rfbi_set_bits_per_cycle(int bpc) -{ - u32 l; - - rfbi_enable_clocks(1); - l = rfbi_read_reg(RFBI_CONFIG0); - l &= ~(0x03 << 0); - - switch (bpc) { - case 8: - break; - case 16: - l |= 3; - break; - default: - BUG(); - } - rfbi_write_reg(RFBI_CONFIG0, l); - rfbi.bits_per_cycle = bpc; - rfbi_enable_clocks(0); -} - -static int rfbi_init(struct omapfb_device *fbdev) -{ - u32 l; - int r; - - rfbi.fbdev = fbdev; - rfbi.base = ioremap(RFBI_BASE, SZ_1K); - if (!rfbi.base) { - dev_err(fbdev->dev, "can't ioremap RFBI\n"); - return -ENOMEM; - } - - if ((r = rfbi_get_clocks()) < 0) - return r; - rfbi_enable_clocks(1); - - rfbi.l4_khz = clk_get_rate(rfbi.dss_ick) / 1000; - - /* Reset */ - rfbi_write_reg(RFBI_SYSCONFIG, 1 << 1); - while (!(rfbi_read_reg(RFBI_SYSSTATUS) & (1 << 0))); - - l = rfbi_read_reg(RFBI_SYSCONFIG); - /* Enable autoidle and smart-idle */ - l |= (1 << 0) | (2 << 3); - rfbi_write_reg(RFBI_SYSCONFIG, l); - - /* 16-bit interface, ITE trigger mode, 16-bit data */ - l = (0x03 << 0) | (0x00 << 2) | (0x01 << 5) | (0x02 << 7); - l |= (0 << 9) | (1 << 20) | (1 << 21); - rfbi_write_reg(RFBI_CONFIG0, l); - - rfbi_write_reg(RFBI_DATA_CYCLE1_0, 0x00000010); - - l = rfbi_read_reg(RFBI_CONTROL); - /* Select CS0, clear bypass mode */ - l = (0x01 << 2); - rfbi_write_reg(RFBI_CONTROL, l); - - r = omap_dispc_request_irq(DISPC_IRQ_FRAMEMASK, rfbi_dma_callback, - NULL); - if (r < 0) { - dev_err(fbdev->dev, "can't get DISPC irq\n"); - rfbi_enable_clocks(0); - return r; - } - - l = rfbi_read_reg(RFBI_REVISION); - pr_info("omapfb: RFBI version %d.%d initialized\n", - (l >> 4) & 0x0f, l & 0x0f); - - rfbi_enable_clocks(0); - - return 0; -} - -static void rfbi_cleanup(void) -{ - omap_dispc_free_irq(DISPC_IRQ_FRAMEMASK, rfbi_dma_callback, NULL); - rfbi_put_clocks(); - iounmap(rfbi.base); -} - -const struct lcd_ctrl_extif omap2_ext_if = { - .init = rfbi_init, - .cleanup = rfbi_cleanup, - .get_clk_info = rfbi_get_clk_info, - .get_max_tx_rate = rfbi_get_max_tx_rate, - .set_bits_per_cycle = rfbi_set_bits_per_cycle, - .convert_timings = rfbi_convert_timings, - .set_timings = rfbi_set_timings, - .write_command = rfbi_write_command, - .read_data = rfbi_read_data, - .write_data = rfbi_write_data, - .transfer_area = rfbi_transfer_area, - .setup_tearsync = rfbi_setup_tearsync, - .enable_tearsync = rfbi_enable_tearsync, - - .max_transmit_size = (u32) ~0, -}; - diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c index 51a87e149e24..d26f37ac69d8 100644 --- a/drivers/video/omap2/displays/panel-acx565akm.c +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -809,18 +809,7 @@ static struct spi_driver acx565akm_spi_driver = { .remove = __devexit_p(acx565akm_spi_remove), }; -static int __init acx565akm_init(void) -{ - return spi_register_driver(&acx565akm_spi_driver); -} - -static void __exit acx565akm_exit(void) -{ - spi_unregister_driver(&acx565akm_spi_driver); -} - -module_init(acx565akm_init); -module_exit(acx565akm_exit); +module_spi_driver(acx565akm_spi_driver); MODULE_AUTHOR("Nokia Corporation"); MODULE_DESCRIPTION("acx565akm LCD Driver"); diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c index 28b9a6d61b0f..30fe4dfeb227 100644 --- a/drivers/video/omap2/displays/panel-generic-dpi.c +++ b/drivers/video/omap2/displays/panel-generic-dpi.c @@ -363,6 +363,29 @@ static struct panel_config generic_dpi_panels[] = { .name = "ortustech_com43h4m10xtc", }, + + /* Innolux AT080TN52 */ + { + { + .x_res = 800, + .y_res = 600, + + .pixel_clock = 41142, + + .hsw = 20, + .hfp = 210, + .hbp = 46, + + .vsw = 10, + .vfp = 12, + .vbp = 23, + }, + .acb = 0x0, + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO, + + .name = "innolux_at080tn52", + }, }; struct panel_drv_data { diff --git a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c index e0eb35be303e..0841cc2b3f77 100644 --- a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c +++ b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c @@ -264,16 +264,6 @@ static struct spi_driver lb035q02_spi_driver = { .remove = __devexit_p(lb035q02_panel_spi_remove), }; -static int __init lb035q02_panel_drv_init(void) -{ - return spi_register_driver(&lb035q02_spi_driver); -} - -static void __exit lb035q02_panel_drv_exit(void) -{ - spi_unregister_driver(&lb035q02_spi_driver); -} +module_spi_driver(lb035q02_spi_driver); -module_init(lb035q02_panel_drv_init); -module_exit(lb035q02_panel_drv_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c index 0eb31caddca8..8b38b39213f4 100644 --- a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c +++ b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c @@ -350,18 +350,8 @@ static struct spi_driver nec_8048_spi_driver = { }, }; -static int __init nec_8048_lcd_init(void) -{ - return spi_register_driver(&nec_8048_spi_driver); -} - -static void __exit nec_8048_lcd_exit(void) -{ - return spi_unregister_driver(&nec_8048_spi_driver); -} +module_spi_driver(nec_8048_spi_driver); -module_init(nec_8048_lcd_init); -module_exit(nec_8048_lcd_exit); MODULE_AUTHOR("Erik Gilling <konkers@android.com>"); MODULE_DESCRIPTION("NEC-nl8048hl11-01b Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c index 00c5c615585f..0f21fa5a16ae 100644 --- a/drivers/video/omap2/displays/panel-taal.c +++ b/drivers/video/omap2/displays/panel-taal.c @@ -1019,14 +1019,12 @@ static int taal_probe(struct omap_dss_device *dssdev) if (panel_data->use_ext_te) { int gpio = panel_data->ext_te_gpio; - r = gpio_request(gpio, "taal irq"); + r = gpio_request_one(gpio, GPIOF_IN, "taal irq"); if (r) { dev_err(&dssdev->dev, "GPIO request failed\n"); goto err_gpio; } - gpio_direction_input(gpio); - r = request_irq(gpio_to_irq(gpio), taal_te_isr, IRQF_TRIGGER_RISING, "taal vsync", dssdev); diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c index e6649aa89591..32f3fcd7f0f0 100644 --- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c +++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c @@ -47,16 +47,20 @@ TPO_R03_EN_PRE_CHARGE | TPO_R03_SOFTWARE_CTL) static const u16 tpo_td043_def_gamma[12] = { - 106, 200, 289, 375, 460, 543, 625, 705, 785, 864, 942, 1020 + 105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023 }; struct tpo_td043_device { struct spi_device *spi; struct regulator *vcc_reg; + int nreset_gpio; u16 gamma[12]; u32 mode; u32 hmirror:1; u32 vmirror:1; + u32 powered_on:1; + u32 spi_suspended:1; + u32 power_on_resume:1; }; static int tpo_td043_write(struct spi_device *spi, u8 addr, u8 data) @@ -265,28 +269,16 @@ static const struct omap_video_timings tpo_td043_timings = { .vbp = 34, }; -static int tpo_td043_power_on(struct omap_dss_device *dssdev) +static int tpo_td043_power_on(struct tpo_td043_device *tpo_td043) { - struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev); - int nreset_gpio = dssdev->reset_gpio; - int r; + int nreset_gpio = tpo_td043->nreset_gpio; - if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + if (tpo_td043->powered_on) return 0; - r = omapdss_dpi_display_enable(dssdev); - if (r) - goto err0; - - if (dssdev->platform_enable) { - r = dssdev->platform_enable(dssdev); - if (r) - goto err1; - } - regulator_enable(tpo_td043->vcc_reg); - /* wait for power up */ + /* wait for regulator to stabilize */ msleep(160); if (gpio_is_valid(nreset_gpio)) @@ -301,19 +293,15 @@ static int tpo_td043_power_on(struct omap_dss_device *dssdev) tpo_td043->vmirror); tpo_td043_write_gamma(tpo_td043->spi, tpo_td043->gamma); + tpo_td043->powered_on = 1; return 0; -err1: - omapdss_dpi_display_disable(dssdev); -err0: - return r; } -static void tpo_td043_power_off(struct omap_dss_device *dssdev) +static void tpo_td043_power_off(struct tpo_td043_device *tpo_td043) { - struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev); - int nreset_gpio = dssdev->reset_gpio; + int nreset_gpio = tpo_td043->nreset_gpio; - if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + if (!tpo_td043->powered_on) return; tpo_td043_write(tpo_td043->spi, 3, @@ -329,54 +317,94 @@ static void tpo_td043_power_off(struct omap_dss_device *dssdev) regulator_disable(tpo_td043->vcc_reg); + tpo_td043->powered_on = 0; +} + +static int tpo_td043_enable_dss(struct omap_dss_device *dssdev) +{ + struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev); + int r; + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + return 0; + + r = omapdss_dpi_display_enable(dssdev); + if (r) + goto err0; + + if (dssdev->platform_enable) { + r = dssdev->platform_enable(dssdev); + if (r) + goto err1; + } + + /* + * If we are resuming from system suspend, SPI clocks might not be + * enabled yet, so we'll program the LCD from SPI PM resume callback. + */ + if (!tpo_td043->spi_suspended) { + r = tpo_td043_power_on(tpo_td043); + if (r) + goto err1; + } + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +err1: + omapdss_dpi_display_disable(dssdev); +err0: + return r; +} + +static void tpo_td043_disable_dss(struct omap_dss_device *dssdev) +{ + struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return; + if (dssdev->platform_disable) dssdev->platform_disable(dssdev); omapdss_dpi_display_disable(dssdev); + + if (!tpo_td043->spi_suspended) + tpo_td043_power_off(tpo_td043); } static int tpo_td043_enable(struct omap_dss_device *dssdev) { - int ret; - dev_dbg(&dssdev->dev, "enable\n"); - ret = tpo_td043_power_on(dssdev); - if (ret) - return ret; - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; + return tpo_td043_enable_dss(dssdev); } static void tpo_td043_disable(struct omap_dss_device *dssdev) { dev_dbg(&dssdev->dev, "disable\n"); - tpo_td043_power_off(dssdev); + tpo_td043_disable_dss(dssdev); dssdev->state = OMAP_DSS_DISPLAY_DISABLED; } static int tpo_td043_suspend(struct omap_dss_device *dssdev) { - tpo_td043_power_off(dssdev); + dev_dbg(&dssdev->dev, "suspend\n"); + + tpo_td043_disable_dss(dssdev); + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + return 0; } static int tpo_td043_resume(struct omap_dss_device *dssdev) { - int r = 0; - - r = tpo_td043_power_on(dssdev); - if (r) - return r; + dev_dbg(&dssdev->dev, "resume\n"); - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; + return tpo_td043_enable_dss(dssdev); } static int tpo_td043_probe(struct omap_dss_device *dssdev) @@ -408,17 +436,12 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev) } if (gpio_is_valid(nreset_gpio)) { - ret = gpio_request(nreset_gpio, "lcd reset"); + ret = gpio_request_one(nreset_gpio, GPIOF_OUT_INIT_LOW, + "lcd reset"); if (ret < 0) { dev_err(&dssdev->dev, "couldn't request reset GPIO\n"); goto fail_gpio_req; } - - ret = gpio_direction_output(nreset_gpio, 0); - if (ret < 0) { - dev_err(&dssdev->dev, "couldn't set GPIO direction\n"); - goto fail_gpio_direction; - } } ret = sysfs_create_group(&dssdev->dev.kobj, &tpo_td043_attr_group); @@ -427,8 +450,6 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev) return 0; -fail_gpio_direction: - gpio_free(nreset_gpio); fail_gpio_req: regulator_put(tpo_td043->vcc_reg); fail_regulator: @@ -491,6 +512,7 @@ static int tpo_td043_spi_probe(struct spi_device *spi) return -ENOMEM; tpo_td043->spi = spi; + tpo_td043->nreset_gpio = dssdev->reset_gpio; dev_set_drvdata(&spi->dev, tpo_td043); dev_set_drvdata(&dssdev->dev, tpo_td043); @@ -509,27 +531,52 @@ static int __devexit tpo_td043_spi_remove(struct spi_device *spi) return 0; } +#ifdef CONFIG_PM_SLEEP +static int tpo_td043_spi_suspend(struct device *dev) +{ + struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev); + + dev_dbg(dev, "tpo_td043_spi_suspend, tpo %p\n", tpo_td043); + + tpo_td043->power_on_resume = tpo_td043->powered_on; + tpo_td043_power_off(tpo_td043); + tpo_td043->spi_suspended = 1; + + return 0; +} + +static int tpo_td043_spi_resume(struct device *dev) +{ + struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev); + int ret; + + dev_dbg(dev, "tpo_td043_spi_resume\n"); + + if (tpo_td043->power_on_resume) { + ret = tpo_td043_power_on(tpo_td043); + if (ret) + return ret; + } + tpo_td043->spi_suspended = 0; + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(tpo_td043_spi_pm, + tpo_td043_spi_suspend, tpo_td043_spi_resume); + static struct spi_driver tpo_td043_spi_driver = { .driver = { .name = "tpo_td043mtea1_panel_spi", .owner = THIS_MODULE, + .pm = &tpo_td043_spi_pm, }, .probe = tpo_td043_spi_probe, .remove = __devexit_p(tpo_td043_spi_remove), }; -static int __init tpo_td043_init(void) -{ - return spi_register_driver(&tpo_td043_spi_driver); -} - -static void __exit tpo_td043_exit(void) -{ - spi_unregister_driver(&tpo_td043_spi_driver); -} - -module_init(tpo_td043_init); -module_exit(tpo_td043_exit); +module_spi_driver(tpo_td043_spi_driver); MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>"); MODULE_DESCRIPTION("TPO TD043MTEA1 LCD Driver"); diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c index 87b3e25294cf..b10b3bc1931e 100644 --- a/drivers/video/omap2/dss/apply.c +++ b/drivers/video/omap2/dss/apply.c @@ -105,6 +105,9 @@ static struct { struct ovl_priv_data ovl_priv_data_array[MAX_DSS_OVERLAYS]; struct mgr_priv_data mgr_priv_data_array[MAX_DSS_MANAGERS]; + bool fifo_merge_dirty; + bool fifo_merge; + bool irq_enabled; } dss_data; @@ -351,6 +354,7 @@ static void wait_pending_extra_info_updates(void) bool updating; unsigned long flags; unsigned long t; + int r; spin_lock_irqsave(&data_lock, flags); @@ -366,11 +370,11 @@ static void wait_pending_extra_info_updates(void) spin_unlock_irqrestore(&data_lock, flags); t = msecs_to_jiffies(500); - wait_for_completion_timeout(&extra_updated_completion, t); - - updating = extra_info_update_ongoing(); - - WARN_ON(updating); + r = wait_for_completion_timeout(&extra_updated_completion, t); + if (r == 0) + DSSWARN("timeout in wait_pending_extra_info_updates\n"); + else if (r < 0) + DSSERR("wait_pending_extra_info_updates failed: %d\n", r); } int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) @@ -388,6 +392,10 @@ int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) if (mgr_manual_update(mgr)) return 0; + r = dispc_runtime_get(); + if (r) + return r; + irq = dispc_mgr_get_vsync_irq(mgr->id); mp = get_mgr_priv(mgr); @@ -428,6 +436,8 @@ int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) } } + dispc_runtime_put(); + return r; } @@ -451,6 +461,10 @@ int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) if (ovl_manual_update(ovl)) return 0; + r = dispc_runtime_get(); + if (r) + return r; + irq = dispc_mgr_get_vsync_irq(ovl->manager->id); op = get_ovl_priv(ovl); @@ -491,6 +505,8 @@ int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) } } + dispc_runtime_put(); + return r; } @@ -585,11 +601,40 @@ static void dss_mgr_write_regs(struct omap_overlay_manager *mgr) } } +static void dss_write_regs_common(void) +{ + const int num_mgrs = omap_dss_get_num_overlay_managers(); + int i; + + if (!dss_data.fifo_merge_dirty) + return; + + for (i = 0; i < num_mgrs; ++i) { + struct omap_overlay_manager *mgr; + struct mgr_priv_data *mp; + + mgr = omap_dss_get_overlay_manager(i); + mp = get_mgr_priv(mgr); + + if (mp->enabled) { + if (dss_data.fifo_merge_dirty) { + dispc_enable_fifomerge(dss_data.fifo_merge); + dss_data.fifo_merge_dirty = false; + } + + if (mp->updating) + mp->shadow_info_dirty = true; + } + } +} + static void dss_write_regs(void) { const int num_mgrs = omap_dss_get_num_overlay_managers(); int i; + dss_write_regs_common(); + for (i = 0; i < num_mgrs; ++i) { struct omap_overlay_manager *mgr; struct mgr_priv_data *mp; @@ -640,6 +685,22 @@ static void dss_set_go_bits(void) } +static void mgr_clear_shadow_dirty(struct omap_overlay_manager *mgr) +{ + struct omap_overlay *ovl; + struct mgr_priv_data *mp; + struct ovl_priv_data *op; + + mp = get_mgr_priv(mgr); + mp->shadow_info_dirty = false; + + list_for_each_entry(ovl, &mgr->overlays, list) { + op = get_ovl_priv(ovl); + op->shadow_info_dirty = false; + op->shadow_extra_info_dirty = false; + } +} + void dss_mgr_start_update(struct omap_overlay_manager *mgr) { struct mgr_priv_data *mp = get_mgr_priv(mgr); @@ -659,6 +720,8 @@ void dss_mgr_start_update(struct omap_overlay_manager *mgr) dss_mgr_write_regs(mgr); + dss_write_regs_common(); + mp->updating = true; if (!dss_data.irq_enabled && need_isr()) @@ -666,6 +729,8 @@ void dss_mgr_start_update(struct omap_overlay_manager *mgr) dispc_mgr_enable(mgr->id, true); + mgr_clear_shadow_dirty(mgr); + spin_unlock_irqrestore(&data_lock, flags); } @@ -709,22 +774,6 @@ static void dss_unregister_vsync_isr(void) dss_data.irq_enabled = false; } -static void mgr_clear_shadow_dirty(struct omap_overlay_manager *mgr) -{ - struct omap_overlay *ovl; - struct mgr_priv_data *mp; - struct ovl_priv_data *op; - - mp = get_mgr_priv(mgr); - mp->shadow_info_dirty = false; - - list_for_each_entry(ovl, &mgr->overlays, list) { - op = get_ovl_priv(ovl); - op->shadow_info_dirty = false; - op->shadow_extra_info_dirty = false; - } -} - static void dss_apply_irq_handler(void *data, u32 mask) { const int num_mgrs = dss_feat_get_num_mgrs(); @@ -754,9 +803,6 @@ static void dss_apply_irq_handler(void *data, u32 mask) if (was_busy && !mp->busy) mgr_clear_shadow_dirty(mgr); - } else { - if (was_updating && !mp->updating) - mgr_clear_shadow_dirty(mgr); } } @@ -859,11 +905,20 @@ static void dss_apply_ovl_fifo_thresholds(struct omap_overlay *ovl, op->extra_info_dirty = true; } -static void dss_ovl_setup_fifo(struct omap_overlay *ovl) +static void dss_apply_fifo_merge(bool use_fifo_merge) +{ + if (dss_data.fifo_merge == use_fifo_merge) + return; + + dss_data.fifo_merge = use_fifo_merge; + dss_data.fifo_merge_dirty = true; +} + +static void dss_ovl_setup_fifo(struct omap_overlay *ovl, + bool use_fifo_merge) { struct ovl_priv_data *op = get_ovl_priv(ovl); struct omap_dss_device *dssdev; - u32 size, burst_size; u32 fifo_low, fifo_high; if (!op->enabled && !op->enabling) @@ -871,33 +926,14 @@ static void dss_ovl_setup_fifo(struct omap_overlay *ovl) dssdev = ovl->manager->device; - size = dispc_ovl_get_fifo_size(ovl->id); - - burst_size = dispc_ovl_get_burst_size(ovl->id); - - switch (dssdev->type) { - case OMAP_DISPLAY_TYPE_DPI: - case OMAP_DISPLAY_TYPE_DBI: - case OMAP_DISPLAY_TYPE_SDI: - case OMAP_DISPLAY_TYPE_VENC: - case OMAP_DISPLAY_TYPE_HDMI: - default_get_overlay_fifo_thresholds(ovl->id, size, - burst_size, &fifo_low, &fifo_high); - break; -#ifdef CONFIG_OMAP2_DSS_DSI - case OMAP_DISPLAY_TYPE_DSI: - dsi_get_overlay_fifo_thresholds(ovl->id, size, - burst_size, &fifo_low, &fifo_high); - break; -#endif - default: - BUG(); - } + dispc_ovl_compute_fifo_thresholds(ovl->id, &fifo_low, &fifo_high, + use_fifo_merge); dss_apply_ovl_fifo_thresholds(ovl, fifo_low, fifo_high); } -static void dss_mgr_setup_fifos(struct omap_overlay_manager *mgr) +static void dss_mgr_setup_fifos(struct omap_overlay_manager *mgr, + bool use_fifo_merge) { struct omap_overlay *ovl; struct mgr_priv_data *mp; @@ -908,19 +944,94 @@ static void dss_mgr_setup_fifos(struct omap_overlay_manager *mgr) return; list_for_each_entry(ovl, &mgr->overlays, list) - dss_ovl_setup_fifo(ovl); + dss_ovl_setup_fifo(ovl, use_fifo_merge); +} + +static void dss_setup_fifos(bool use_fifo_merge) +{ + const int num_mgrs = omap_dss_get_num_overlay_managers(); + struct omap_overlay_manager *mgr; + int i; + + for (i = 0; i < num_mgrs; ++i) { + mgr = omap_dss_get_overlay_manager(i); + dss_mgr_setup_fifos(mgr, use_fifo_merge); + } } -static void dss_setup_fifos(void) +static int get_num_used_managers(void) { const int num_mgrs = omap_dss_get_num_overlay_managers(); struct omap_overlay_manager *mgr; + struct mgr_priv_data *mp; int i; + int enabled_mgrs; + + enabled_mgrs = 0; for (i = 0; i < num_mgrs; ++i) { mgr = omap_dss_get_overlay_manager(i); - dss_mgr_setup_fifos(mgr); + mp = get_mgr_priv(mgr); + + if (!mp->enabled) + continue; + + enabled_mgrs++; + } + + return enabled_mgrs; +} + +static int get_num_used_overlays(void) +{ + const int num_ovls = omap_dss_get_num_overlays(); + struct omap_overlay *ovl; + struct ovl_priv_data *op; + struct mgr_priv_data *mp; + int i; + int enabled_ovls; + + enabled_ovls = 0; + + for (i = 0; i < num_ovls; ++i) { + ovl = omap_dss_get_overlay(i); + op = get_ovl_priv(ovl); + + if (!op->enabled && !op->enabling) + continue; + + mp = get_mgr_priv(ovl->manager); + + if (!mp->enabled) + continue; + + enabled_ovls++; } + + return enabled_ovls; +} + +static bool get_use_fifo_merge(void) +{ + int enabled_mgrs = get_num_used_managers(); + int enabled_ovls = get_num_used_overlays(); + + if (!dss_has_feature(FEAT_FIFO_MERGE)) + return false; + + /* + * In theory the only requirement for fifomerge is enabled_ovls <= 1. + * However, if we have two managers enabled and set/unset the fifomerge, + * we need to set the GO bits in particular sequence for the managers, + * and wait in between. + * + * This is rather difficult as new apply calls can happen at any time, + * so we simplify the problem by requiring also that enabled_mgrs <= 1. + * In practice this shouldn't matter, because when only one overlay is + * enabled, most likely only one output is enabled. + */ + + return enabled_mgrs <= 1 && enabled_ovls <= 1; } int dss_mgr_enable(struct omap_overlay_manager *mgr) @@ -928,6 +1039,7 @@ int dss_mgr_enable(struct omap_overlay_manager *mgr) struct mgr_priv_data *mp = get_mgr_priv(mgr); unsigned long flags; int r; + bool fifo_merge; mutex_lock(&apply_lock); @@ -945,11 +1057,23 @@ int dss_mgr_enable(struct omap_overlay_manager *mgr) goto err; } - dss_setup_fifos(); + /* step 1: setup fifos/fifomerge before enabling the manager */ + + fifo_merge = get_use_fifo_merge(); + dss_setup_fifos(fifo_merge); + dss_apply_fifo_merge(fifo_merge); dss_write_regs(); dss_set_go_bits(); + spin_unlock_irqrestore(&data_lock, flags); + + /* wait until fifo config is in */ + wait_pending_extra_info_updates(); + + /* step 2: enable the manager */ + spin_lock_irqsave(&data_lock, flags); + if (!mgr_manual_update(mgr)) mp->updating = true; @@ -974,6 +1098,7 @@ void dss_mgr_disable(struct omap_overlay_manager *mgr) { struct mgr_priv_data *mp = get_mgr_priv(mgr); unsigned long flags; + bool fifo_merge; mutex_lock(&apply_lock); @@ -988,8 +1113,16 @@ void dss_mgr_disable(struct omap_overlay_manager *mgr) mp->updating = false; mp->enabled = false; + fifo_merge = get_use_fifo_merge(); + dss_setup_fifos(fifo_merge); + dss_apply_fifo_merge(fifo_merge); + + dss_write_regs(); + dss_set_go_bits(); + spin_unlock_irqrestore(&data_lock, flags); + wait_pending_extra_info_updates(); out: mutex_unlock(&apply_lock); } @@ -1241,6 +1374,7 @@ int dss_ovl_enable(struct omap_overlay *ovl) { struct ovl_priv_data *op = get_ovl_priv(ovl); unsigned long flags; + bool fifo_merge; int r; mutex_lock(&apply_lock); @@ -1266,7 +1400,22 @@ int dss_ovl_enable(struct omap_overlay *ovl) goto err2; } - dss_setup_fifos(); + /* step 1: configure fifos/fifomerge for currently enabled ovls */ + + fifo_merge = get_use_fifo_merge(); + dss_setup_fifos(fifo_merge); + dss_apply_fifo_merge(fifo_merge); + + dss_write_regs(); + dss_set_go_bits(); + + spin_unlock_irqrestore(&data_lock, flags); + + /* wait for fifo configs to go in */ + wait_pending_extra_info_updates(); + + /* step 2: enable the overlay */ + spin_lock_irqsave(&data_lock, flags); op->enabling = false; dss_apply_ovl_enable(ovl, true); @@ -1294,6 +1443,7 @@ int dss_ovl_disable(struct omap_overlay *ovl) { struct ovl_priv_data *op = get_ovl_priv(ovl); unsigned long flags; + bool fifo_merge; int r; mutex_lock(&apply_lock); @@ -1308,9 +1458,11 @@ int dss_ovl_disable(struct omap_overlay *ovl) goto err; } + /* step 1: disable the overlay */ spin_lock_irqsave(&data_lock, flags); dss_apply_ovl_enable(ovl, false); + dss_write_regs(); dss_set_go_bits(); @@ -1319,6 +1471,21 @@ int dss_ovl_disable(struct omap_overlay *ovl) /* wait for the overlay to be disabled */ wait_pending_extra_info_updates(); + /* step 2: configure fifos/fifomerge */ + spin_lock_irqsave(&data_lock, flags); + + fifo_merge = get_use_fifo_merge(); + dss_setup_fifos(fifo_merge); + dss_apply_fifo_merge(fifo_merge); + + dss_write_regs(); + dss_set_go_bits(); + + spin_unlock_irqrestore(&data_lock, flags); + + /* wait for fifo config to go in */ + wait_pending_extra_info_updates(); + mutex_unlock(&apply_lock); return 0; diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c index 8613f86fb56d..e8a120771ac6 100644 --- a/drivers/video/omap2/dss/core.c +++ b/drivers/video/omap2/dss/core.c @@ -183,42 +183,6 @@ static int omap_dss_probe(struct platform_device *pdev) dss_init_overlay_managers(pdev); dss_init_overlays(pdev); - r = dss_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize DSS platform driver\n"); - goto err_dss; - } - - r = dispc_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize dispc platform driver\n"); - goto err_dispc; - } - - r = rfbi_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize rfbi platform driver\n"); - goto err_rfbi; - } - - r = venc_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize venc platform driver\n"); - goto err_venc; - } - - r = dsi_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize DSI platform driver\n"); - goto err_dsi; - } - - r = hdmi_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize hdmi\n"); - goto err_hdmi; - } - r = dss_initialize_debugfs(); if (r) goto err_debugfs; @@ -246,18 +210,6 @@ static int omap_dss_probe(struct platform_device *pdev) err_register: dss_uninitialize_debugfs(); err_debugfs: - hdmi_uninit_platform_driver(); -err_hdmi: - dsi_uninit_platform_driver(); -err_dsi: - venc_uninit_platform_driver(); -err_venc: - dispc_uninit_platform_driver(); -err_dispc: - rfbi_uninit_platform_driver(); -err_rfbi: - dss_uninit_platform_driver(); -err_dss: return r; } @@ -269,13 +221,6 @@ static int omap_dss_remove(struct platform_device *pdev) dss_uninitialize_debugfs(); - hdmi_uninit_platform_driver(); - dsi_uninit_platform_driver(); - venc_uninit_platform_driver(); - rfbi_uninit_platform_driver(); - dispc_uninit_platform_driver(); - dss_uninit_platform_driver(); - dss_uninit_overlays(pdev); dss_uninit_overlay_managers(pdev); @@ -525,6 +470,80 @@ static int omap_dss_bus_register(void) /* INIT */ +static int __init omap_dss_register_drivers(void) +{ + int r; + + r = platform_driver_register(&omap_dss_driver); + if (r) + return r; + + r = dss_init_platform_driver(); + if (r) { + DSSERR("Failed to initialize DSS platform driver\n"); + goto err_dss; + } + + r = dispc_init_platform_driver(); + if (r) { + DSSERR("Failed to initialize dispc platform driver\n"); + goto err_dispc; + } + + r = rfbi_init_platform_driver(); + if (r) { + DSSERR("Failed to initialize rfbi platform driver\n"); + goto err_rfbi; + } + + r = venc_init_platform_driver(); + if (r) { + DSSERR("Failed to initialize venc platform driver\n"); + goto err_venc; + } + + r = dsi_init_platform_driver(); + if (r) { + DSSERR("Failed to initialize DSI platform driver\n"); + goto err_dsi; + } + + r = hdmi_init_platform_driver(); + if (r) { + DSSERR("Failed to initialize hdmi\n"); + goto err_hdmi; + } + + return 0; + +err_hdmi: + dsi_uninit_platform_driver(); +err_dsi: + venc_uninit_platform_driver(); +err_venc: + rfbi_uninit_platform_driver(); +err_rfbi: + dispc_uninit_platform_driver(); +err_dispc: + dss_uninit_platform_driver(); +err_dss: + platform_driver_unregister(&omap_dss_driver); + + return r; +} + +static void __exit omap_dss_unregister_drivers(void) +{ + hdmi_uninit_platform_driver(); + dsi_uninit_platform_driver(); + venc_uninit_platform_driver(); + rfbi_uninit_platform_driver(); + dispc_uninit_platform_driver(); + dss_uninit_platform_driver(); + + platform_driver_unregister(&omap_dss_driver); +} + #ifdef CONFIG_OMAP2_DSS_MODULE static void omap_dss_bus_unregister(void) { @@ -541,7 +560,7 @@ static int __init omap_dss_init(void) if (r) return r; - r = platform_driver_register(&omap_dss_driver); + r = omap_dss_register_drivers(); if (r) { omap_dss_bus_unregister(); return r; @@ -562,7 +581,7 @@ static void __exit omap_dss_exit(void) core.vdds_sdi_reg = NULL; } - platform_driver_unregister(&omap_dss_driver); + omap_dss_unregister_drivers(); omap_dss_bus_unregister(); } @@ -577,7 +596,7 @@ static int __init omap_dss_init(void) static int __init omap_dss_init2(void) { - return platform_driver_register(&omap_dss_driver); + return omap_dss_register_drivers(); } core_initcall(omap_dss_init); diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index e1626a1d5c45..ee30937482e1 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -37,7 +37,6 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> -#include <plat/sram.h> #include <plat/clock.h> #include <video/omapdss.h> @@ -736,11 +735,11 @@ static void dispc_ovl_set_color_mode(enum omap_plane plane, switch (color_mode) { case OMAP_DSS_COLOR_NV12: m = 0x0; break; - case OMAP_DSS_COLOR_RGB12U: + case OMAP_DSS_COLOR_RGBX16: m = 0x1; break; case OMAP_DSS_COLOR_RGBA16: m = 0x2; break; - case OMAP_DSS_COLOR_RGBX16: + case OMAP_DSS_COLOR_RGB12U: m = 0x4; break; case OMAP_DSS_COLOR_ARGB16: m = 0x5; break; @@ -789,9 +788,9 @@ static void dispc_ovl_set_color_mode(enum omap_plane plane, m = 0x8; break; case OMAP_DSS_COLOR_RGB24P: m = 0x9; break; - case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_RGBX16: m = 0xa; break; - case OMAP_DSS_COLOR_UYVY: + case OMAP_DSS_COLOR_RGBA16: m = 0xb; break; case OMAP_DSS_COLOR_ARGB32: m = 0xc; break; @@ -909,7 +908,7 @@ static void dispc_configure_burst_sizes(void) dispc_ovl_set_burst_size(i, burst_size); } -u32 dispc_ovl_get_burst_size(enum omap_plane plane) +static u32 dispc_ovl_get_burst_size(enum omap_plane plane) { unsigned unit = dss_feat_get_burst_size_unit(); /* burst multiplier is always x8 (see dispc_configure_burst_sizes()) */ @@ -1018,7 +1017,7 @@ static void dispc_read_plane_fifo_sizes(void) } } -u32 dispc_ovl_get_fifo_size(enum omap_plane plane) +static u32 dispc_ovl_get_fifo_size(enum omap_plane plane) { return dispc.fifo_size[plane]; } @@ -1039,13 +1038,13 @@ void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high) dss_feat_get_reg_field(FEAT_REG_FIFOHIGHTHRESHOLD, &hi_start, &hi_end); dss_feat_get_reg_field(FEAT_REG_FIFOLOWTHRESHOLD, &lo_start, &lo_end); - DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n", + DSSDBG("fifo(%d) threshold (bytes), old %u/%u, new %u/%u\n", plane, REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane), - lo_start, lo_end), + lo_start, lo_end) * unit, REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane), - hi_start, hi_end), - low, high); + hi_start, hi_end) * unit, + low * unit, high * unit); dispc_write_reg(DISPC_OVL_FIFO_THRESHOLD(plane), FLD_VAL(high, hi_start, hi_end) | @@ -1054,10 +1053,53 @@ void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high) void dispc_enable_fifomerge(bool enable) { + if (!dss_has_feature(FEAT_FIFO_MERGE)) { + WARN_ON(enable); + return; + } + DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled"); REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14); } +void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, + u32 *fifo_low, u32 *fifo_high, bool use_fifomerge) +{ + /* + * All sizes are in bytes. Both the buffer and burst are made of + * buffer_units, and the fifo thresholds must be buffer_unit aligned. + */ + + unsigned buf_unit = dss_feat_get_buffer_size_unit(); + unsigned ovl_fifo_size, total_fifo_size, burst_size; + int i; + + burst_size = dispc_ovl_get_burst_size(plane); + ovl_fifo_size = dispc_ovl_get_fifo_size(plane); + + if (use_fifomerge) { + total_fifo_size = 0; + for (i = 0; i < omap_dss_get_num_overlays(); ++i) + total_fifo_size += dispc_ovl_get_fifo_size(i); + } else { + total_fifo_size = ovl_fifo_size; + } + + /* + * We use the same low threshold for both fifomerge and non-fifomerge + * cases, but for fifomerge we calculate the high threshold using the + * combined fifo size + */ + + if (dss_has_feature(FEAT_OMAP3_DSI_FIFO_BUG)) { + *fifo_low = ovl_fifo_size - burst_size * 2; + *fifo_high = total_fifo_size - burst_size; + } else { + *fifo_low = ovl_fifo_size - burst_size; + *fifo_high = total_fifo_size - buf_unit; + } +} + static void dispc_ovl_set_fir(enum omap_plane plane, int hinc, int vinc, enum omap_color_component color_comp) @@ -1651,6 +1693,7 @@ static unsigned long calc_fclk(enum omap_channel channel, u16 width, u16 height, u16 out_width, u16 out_height) { unsigned int hf, vf; + unsigned long pclk = dispc_mgr_pclk_rate(channel); /* * FIXME how to determine the 'A' factor @@ -1673,13 +1716,16 @@ static unsigned long calc_fclk(enum omap_channel channel, u16 width, if (cpu_is_omap24xx()) { if (vf > 1 && hf > 1) - return dispc_mgr_pclk_rate(channel) * 4; + return pclk * 4; else - return dispc_mgr_pclk_rate(channel) * 2; + return pclk * 2; } else if (cpu_is_omap34xx()) { - return dispc_mgr_pclk_rate(channel) * vf * hf; + return pclk * vf * hf; } else { - return dispc_mgr_pclk_rate(channel) * hf; + if (hf > 1) + return DIV_ROUND_UP(pclk, out_width) * width; + else + return pclk; } } @@ -3272,11 +3318,6 @@ static void _omap_dispc_initial_config(void) if (dss_has_feature(FEAT_FUNCGATED)) REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9); - /* L3 firewall setting: enable access to OCM RAM */ - /* XXX this should be somewhere in plat-omap */ - if (cpu_is_omap24xx()) - __raw_writel(0x402000b0, OMAP2_L3_IO_ADDRESS(0x680050a0)); - _dispc_setup_color_conv_coef(); dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY); @@ -3298,15 +3339,6 @@ static int omap_dispchw_probe(struct platform_device *pdev) dispc.pdev = pdev; - clk = clk_get(&pdev->dev, "fck"); - if (IS_ERR(clk)) { - DSSERR("can't get fck\n"); - r = PTR_ERR(clk); - goto err_get_clk; - } - - dispc.dss_clk = clk; - spin_lock_init(&dispc.irq_lock); #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS @@ -3319,29 +3351,38 @@ static int omap_dispchw_probe(struct platform_device *pdev) dispc_mem = platform_get_resource(dispc.pdev, IORESOURCE_MEM, 0); if (!dispc_mem) { DSSERR("can't get IORESOURCE_MEM DISPC\n"); - r = -EINVAL; - goto err_ioremap; + return -EINVAL; } - dispc.base = ioremap(dispc_mem->start, resource_size(dispc_mem)); + + dispc.base = devm_ioremap(&pdev->dev, dispc_mem->start, + resource_size(dispc_mem)); if (!dispc.base) { DSSERR("can't ioremap DISPC\n"); - r = -ENOMEM; - goto err_ioremap; + return -ENOMEM; } + dispc.irq = platform_get_irq(dispc.pdev, 0); if (dispc.irq < 0) { DSSERR("platform_get_irq failed\n"); - r = -ENODEV; - goto err_irq; + return -ENODEV; } - r = request_irq(dispc.irq, omap_dispc_irq_handler, IRQF_SHARED, - "OMAP DISPC", dispc.pdev); + r = devm_request_irq(&pdev->dev, dispc.irq, omap_dispc_irq_handler, + IRQF_SHARED, "OMAP DISPC", dispc.pdev); if (r < 0) { DSSERR("request_irq failed\n"); - goto err_irq; + return r; + } + + clk = clk_get(&pdev->dev, "fck"); + if (IS_ERR(clk)) { + DSSERR("can't get fck\n"); + r = PTR_ERR(clk); + return r; } + dispc.dss_clk = clk; + pm_runtime_enable(&pdev->dev); r = dispc_runtime_get(); @@ -3362,12 +3403,7 @@ static int omap_dispchw_probe(struct platform_device *pdev) err_runtime_get: pm_runtime_disable(&pdev->dev); - free_irq(dispc.irq, dispc.pdev); -err_irq: - iounmap(dispc.base); -err_ioremap: clk_put(dispc.dss_clk); -err_get_clk: return r; } @@ -3377,8 +3413,6 @@ static int omap_dispchw_remove(struct platform_device *pdev) clk_put(dispc.dss_clk); - free_irq(dispc.irq, dispc.pdev); - iounmap(dispc.base); return 0; } diff --git a/drivers/video/omap2/dss/dispc_coefs.c b/drivers/video/omap2/dss/dispc_coefs.c index 069bccbb3f12..038c15b04215 100644 --- a/drivers/video/omap2/dss/dispc_coefs.c +++ b/drivers/video/omap2/dss/dispc_coefs.c @@ -19,14 +19,13 @@ #include <linux/kernel.h> #include <video/omapdss.h> -#include "dispc.h" -#define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0])) +#include "dispc.h" static const struct dispc_coef coef3_M8[8] = { { 0, 0, 128, 0, 0 }, { 0, -4, 123, 9, 0 }, - { 0, -4, 108, 87, 0 }, + { 0, -4, 108, 24, 0 }, { 0, -2, 87, 43, 0 }, { 0, 64, 64, 0, 0 }, { 0, 43, 87, -2, 0 }, @@ -168,7 +167,7 @@ static const struct dispc_coef coef5_M8[8] = { static const struct dispc_coef coef5_M9[8] = { { -3, 10, 114, 10, -3 }, - { -6, 24, 110, 0, -1 }, + { -6, 24, 111, 0, -1 }, { -8, 40, 103, -7, 0 }, { -11, 58, 91, -11, 1 }, { 0, -12, 76, 76, -12 }, @@ -319,7 +318,7 @@ const struct dispc_coef *dispc_ovl_get_scale_coef(int inc, int five_taps) }; inc /= 128; - for (i = 0; i < ARRAY_LEN(coefs); ++i) + for (i = 0; i < ARRAY_SIZE(coefs); ++i) if (inc >= coefs[i].Mmin && inc <= coefs[i].Mmax) return five_taps ? coefs[i].coef_5 : coefs[i].coef_3; return NULL; diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c index be331dc5a61b..4424c198dbcd 100644 --- a/drivers/video/omap2/dss/display.c +++ b/drivers/video/omap2/dss/display.c @@ -279,16 +279,6 @@ void omapdss_default_get_resolution(struct omap_dss_device *dssdev, } EXPORT_SYMBOL(omapdss_default_get_resolution); -void default_get_overlay_fifo_thresholds(enum omap_plane plane, - u32 fifo_size, u32 burst_size, - u32 *fifo_low, u32 *fifo_high) -{ - unsigned buf_unit = dss_feat_get_buffer_size_unit(); - - *fifo_high = fifo_size - buf_unit; - *fifo_low = fifo_size - burst_size; -} - int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) { switch (dssdev->type) { diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index 52f36ec1c8bb..662d14f8c2c3 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -4524,14 +4524,6 @@ int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable) } EXPORT_SYMBOL(omapdss_dsi_enable_te); -void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, - u32 fifo_size, u32 burst_size, - u32 *fifo_low, u32 *fifo_high) -{ - *fifo_high = fifo_size - burst_size; - *fifo_low = fifo_size - burst_size * 2; -} - int dsi_init_display(struct omap_dss_device *dssdev) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); @@ -4695,11 +4687,9 @@ static int omap_dsihw_probe(struct platform_device *dsidev) struct resource *dsi_mem; struct dsi_data *dsi; - dsi = kzalloc(sizeof(*dsi), GFP_KERNEL); - if (!dsi) { - r = -ENOMEM; - goto err_alloc; - } + dsi = devm_kzalloc(&dsidev->dev, sizeof(*dsi), GFP_KERNEL); + if (!dsi) + return -ENOMEM; dsi->pdev = dsidev; dsi_pdev_map[dsi_module] = dsidev; @@ -4722,12 +4712,6 @@ static int omap_dsihw_probe(struct platform_device *dsidev) mutex_init(&dsi->lock); sema_init(&dsi->bus_lock, 1); - r = dsi_get_clocks(dsidev); - if (r) - goto err_get_clk; - - pm_runtime_enable(&dsidev->dev); - INIT_DELAYED_WORK_DEFERRABLE(&dsi->framedone_timeout_work, dsi_framedone_timeout_work_callback); @@ -4739,27 +4723,27 @@ static int omap_dsihw_probe(struct platform_device *dsidev) dsi_mem = platform_get_resource(dsi->pdev, IORESOURCE_MEM, 0); if (!dsi_mem) { DSSERR("can't get IORESOURCE_MEM DSI\n"); - r = -EINVAL; - goto err_ioremap; + return -EINVAL; } - dsi->base = ioremap(dsi_mem->start, resource_size(dsi_mem)); + + dsi->base = devm_ioremap(&dsidev->dev, dsi_mem->start, + resource_size(dsi_mem)); if (!dsi->base) { DSSERR("can't ioremap DSI\n"); - r = -ENOMEM; - goto err_ioremap; + return -ENOMEM; } + dsi->irq = platform_get_irq(dsi->pdev, 0); if (dsi->irq < 0) { DSSERR("platform_get_irq failed\n"); - r = -ENODEV; - goto err_get_irq; + return -ENODEV; } - r = request_irq(dsi->irq, omap_dsi_irq_handler, IRQF_SHARED, - dev_name(&dsidev->dev), dsi->pdev); + r = devm_request_irq(&dsidev->dev, dsi->irq, omap_dsi_irq_handler, + IRQF_SHARED, dev_name(&dsidev->dev), dsi->pdev); if (r < 0) { DSSERR("request_irq failed\n"); - goto err_get_irq; + return r; } /* DSI VCs initialization */ @@ -4771,9 +4755,15 @@ static int omap_dsihw_probe(struct platform_device *dsidev) dsi_calc_clock_param_ranges(dsidev); + r = dsi_get_clocks(dsidev); + if (r) + return r; + + pm_runtime_enable(&dsidev->dev); + r = dsi_runtime_get(dsidev); if (r) - goto err_get_dsi; + goto err_runtime_get; rev = dsi_read_reg(dsidev, DSI_REVISION); dev_dbg(&dsidev->dev, "OMAP DSI rev %d.%d\n", @@ -4791,15 +4781,9 @@ static int omap_dsihw_probe(struct platform_device *dsidev) return 0; -err_get_dsi: - free_irq(dsi->irq, dsi->pdev); -err_get_irq: - iounmap(dsi->base); -err_ioremap: +err_runtime_get: pm_runtime_disable(&dsidev->dev); -err_get_clk: - kfree(dsi); -err_alloc: + dsi_put_clocks(dsidev); return r; } @@ -4823,11 +4807,6 @@ static int omap_dsihw_remove(struct platform_device *dsidev) dsi->vdds_dsi_reg = NULL; } - free_irq(dsi->irq, dsi->pdev); - iounmap(dsi->base); - - kfree(dsi); - return 0; } diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index 77c2b5a32b5d..bd2d5e159463 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c @@ -33,7 +33,10 @@ #include <linux/pm_runtime.h> #include <video/omapdss.h> + +#include <plat/cpu.h> #include <plat/clock.h> + #include "dss.h" #include "dss_features.h" @@ -748,19 +751,19 @@ static int omap_dsshw_probe(struct platform_device *pdev) dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0); if (!dss_mem) { DSSERR("can't get IORESOURCE_MEM DSS\n"); - r = -EINVAL; - goto err_ioremap; + return -EINVAL; } - dss.base = ioremap(dss_mem->start, resource_size(dss_mem)); + + dss.base = devm_ioremap(&pdev->dev, dss_mem->start, + resource_size(dss_mem)); if (!dss.base) { DSSERR("can't ioremap DSS\n"); - r = -ENOMEM; - goto err_ioremap; + return -ENOMEM; } r = dss_get_clocks(); if (r) - goto err_clocks; + return r; pm_runtime_enable(&pdev->dev); @@ -808,9 +811,6 @@ err_dpi: err_runtime_get: pm_runtime_disable(&pdev->dev); dss_put_clocks(); -err_clocks: - iounmap(dss.base); -err_ioremap: return r; } @@ -819,8 +819,6 @@ static int omap_dsshw_remove(struct platform_device *pdev) dpi_exit(); sdi_exit(); - iounmap(dss.base); - pm_runtime_disable(&pdev->dev); dss_put_clocks(); diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index 32ff69fb3333..d4b3dff2ead3 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h @@ -202,9 +202,6 @@ void dss_uninit_device(struct platform_device *pdev, struct omap_dss_device *dssdev); bool dss_use_replication(struct omap_dss_device *dssdev, enum omap_color_mode mode); -void default_get_overlay_fifo_thresholds(enum omap_plane plane, - u32 fifo_size, u32 burst_size, - u32 *fifo_low, u32 *fifo_high); /* manager */ int dss_init_overlay_managers(struct platform_device *pdev); @@ -313,9 +310,6 @@ int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft, int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk, bool enable_hsdiv); void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes); -void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, - u32 fifo_size, u32 burst_size, - u32 *fifo_low, u32 *fifo_high); void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev); void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev); struct platform_device *dsi_get_dsidev_from_id(int module); @@ -429,8 +423,8 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high); -u32 dispc_ovl_get_fifo_size(enum omap_plane plane); -u32 dispc_ovl_get_burst_size(enum omap_plane plane); +void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, + u32 *fifo_low, u32 *fifo_high, bool use_fifomerge); int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, bool ilace, bool replication); int dispc_ovl_enable(enum omap_plane plane, bool enable); diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index afcb59301c37..ce14aa6dd672 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -41,7 +41,8 @@ struct omap_dss_features { const struct dss_reg_field *reg_fields; const int num_reg_fields; - const u32 has_feature; + const enum dss_feat_id *features; + const int num_features; const int num_mgrs; const int num_ovls; @@ -189,7 +190,8 @@ static const enum omap_color_mode omap4_dss_supported_color_modes[] = { OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32 | - OMAP_DSS_COLOR_ARGB16_1555, + OMAP_DSS_COLOR_ARGB16_1555 | OMAP_DSS_COLOR_RGBX16 | + OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_XRGB16_1555, /* OMAP_DSS_VIDEO1 */ OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U | @@ -337,15 +339,110 @@ static const struct dss_param_range omap4_dss_param_range[] = { [FEAT_PARAM_LINEWIDTH] = { 1, 2048 }, }; +static const enum dss_feat_id omap2_dss_feat_list[] = { + FEAT_LCDENABLEPOL, + FEAT_LCDENABLESIGNAL, + FEAT_PCKFREEENABLE, + FEAT_FUNCGATED, + FEAT_ROWREPEATENABLE, + FEAT_RESIZECONF, +}; + +static const enum dss_feat_id omap3430_dss_feat_list[] = { + FEAT_LCDENABLEPOL, + FEAT_LCDENABLESIGNAL, + FEAT_PCKFREEENABLE, + FEAT_FUNCGATED, + FEAT_LINEBUFFERSPLIT, + FEAT_ROWREPEATENABLE, + FEAT_RESIZECONF, + FEAT_DSI_PLL_FREQSEL, + FEAT_DSI_REVERSE_TXCLKESC, + FEAT_VENC_REQUIRES_TV_DAC_CLK, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FIXED_ZORDER, + FEAT_FIFO_MERGE, + FEAT_OMAP3_DSI_FIFO_BUG, +}; + +static const enum dss_feat_id omap3630_dss_feat_list[] = { + FEAT_LCDENABLEPOL, + FEAT_LCDENABLESIGNAL, + FEAT_PCKFREEENABLE, + FEAT_FUNCGATED, + FEAT_LINEBUFFERSPLIT, + FEAT_ROWREPEATENABLE, + FEAT_RESIZECONF, + FEAT_DSI_PLL_PWR_BUG, + FEAT_DSI_PLL_FREQSEL, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FIXED_ZORDER, + FEAT_FIFO_MERGE, + FEAT_OMAP3_DSI_FIFO_BUG, +}; + +static const enum dss_feat_id omap4430_es1_0_dss_feat_list[] = { + FEAT_MGR_LCD2, + FEAT_CORE_CLK_DIV, + FEAT_LCD_CLK_SRC, + FEAT_DSI_DCS_CMD_CONFIG_VC, + FEAT_DSI_VC_OCP_WIDTH, + FEAT_DSI_GNQ, + FEAT_HANDLE_UV_SEPARATE, + FEAT_ATTR2, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FREE_ZORDER, + FEAT_FIFO_MERGE, +}; + +static const enum dss_feat_id omap4430_es2_0_1_2_dss_feat_list[] = { + FEAT_MGR_LCD2, + FEAT_CORE_CLK_DIV, + FEAT_LCD_CLK_SRC, + FEAT_DSI_DCS_CMD_CONFIG_VC, + FEAT_DSI_VC_OCP_WIDTH, + FEAT_DSI_GNQ, + FEAT_HDMI_CTS_SWMODE, + FEAT_HANDLE_UV_SEPARATE, + FEAT_ATTR2, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FREE_ZORDER, + FEAT_FIFO_MERGE, +}; + +static const enum dss_feat_id omap4_dss_feat_list[] = { + FEAT_MGR_LCD2, + FEAT_CORE_CLK_DIV, + FEAT_LCD_CLK_SRC, + FEAT_DSI_DCS_CMD_CONFIG_VC, + FEAT_DSI_VC_OCP_WIDTH, + FEAT_DSI_GNQ, + FEAT_HDMI_CTS_SWMODE, + FEAT_HDMI_AUDIO_USE_MCLK, + FEAT_HANDLE_UV_SEPARATE, + FEAT_ATTR2, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FREE_ZORDER, + FEAT_FIFO_MERGE, +}; + /* OMAP2 DSS Features */ static const struct omap_dss_features omap2_dss_features = { .reg_fields = omap2_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap2_dss_reg_fields), - .has_feature = - FEAT_LCDENABLEPOL | FEAT_LCDENABLESIGNAL | - FEAT_PCKFREEENABLE | FEAT_FUNCGATED | - FEAT_ROWREPEATENABLE | FEAT_RESIZECONF, + .features = omap2_dss_feat_list, + .num_features = ARRAY_SIZE(omap2_dss_feat_list), .num_mgrs = 2, .num_ovls = 3, @@ -363,14 +460,8 @@ static const struct omap_dss_features omap3430_dss_features = { .reg_fields = omap3_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), - .has_feature = - FEAT_LCDENABLEPOL | - FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE | - FEAT_FUNCGATED | FEAT_ROWREPEATENABLE | - FEAT_LINEBUFFERSPLIT | FEAT_RESIZECONF | - FEAT_DSI_PLL_FREQSEL | FEAT_DSI_REVERSE_TXCLKESC | - FEAT_VENC_REQUIRES_TV_DAC_CLK | FEAT_CPR | FEAT_PRELOAD | - FEAT_FIR_COEF_V | FEAT_ALPHA_FIXED_ZORDER, + .features = omap3430_dss_feat_list, + .num_features = ARRAY_SIZE(omap3430_dss_feat_list), .num_mgrs = 2, .num_ovls = 3, @@ -387,14 +478,8 @@ static const struct omap_dss_features omap3630_dss_features = { .reg_fields = omap3_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), - .has_feature = - FEAT_LCDENABLEPOL | - FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE | - FEAT_FUNCGATED | - FEAT_ROWREPEATENABLE | FEAT_LINEBUFFERSPLIT | - FEAT_RESIZECONF | FEAT_DSI_PLL_PWR_BUG | - FEAT_DSI_PLL_FREQSEL | FEAT_CPR | FEAT_PRELOAD | - FEAT_FIR_COEF_V | FEAT_ALPHA_FIXED_ZORDER, + .features = omap3630_dss_feat_list, + .num_features = ARRAY_SIZE(omap3630_dss_feat_list), .num_mgrs = 2, .num_ovls = 3, @@ -413,13 +498,27 @@ static const struct omap_dss_features omap4430_es1_0_dss_features = { .reg_fields = omap4_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields), - .has_feature = - FEAT_MGR_LCD2 | - FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC | - FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH | - FEAT_DSI_GNQ | FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 | - FEAT_CPR | FEAT_PRELOAD | FEAT_FIR_COEF_V | - FEAT_ALPHA_FREE_ZORDER, + .features = omap4430_es1_0_dss_feat_list, + .num_features = ARRAY_SIZE(omap4430_es1_0_dss_feat_list), + + .num_mgrs = 3, + .num_ovls = 4, + .supported_displays = omap4_dss_supported_displays, + .supported_color_modes = omap4_dss_supported_color_modes, + .overlay_caps = omap4_dss_overlay_caps, + .clksrc_names = omap4_dss_clk_source_names, + .dss_params = omap4_dss_param_range, + .buffer_size_unit = 16, + .burst_size_unit = 16, +}; + +/* For OMAP4430 ES 2.0, 2.1 and 2.2 revisions */ +static const struct omap_dss_features omap4430_es2_0_1_2_dss_features = { + .reg_fields = omap4_dss_reg_fields, + .num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields), + + .features = omap4430_es2_0_1_2_dss_feat_list, + .num_features = ARRAY_SIZE(omap4430_es2_0_1_2_dss_feat_list), .num_mgrs = 3, .num_ovls = 4, @@ -437,13 +536,8 @@ static const struct omap_dss_features omap4_dss_features = { .reg_fields = omap4_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields), - .has_feature = - FEAT_MGR_LCD2 | - FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC | - FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH | - FEAT_DSI_GNQ | FEAT_HDMI_CTS_SWMODE | - FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 | FEAT_CPR | - FEAT_PRELOAD | FEAT_FIR_COEF_V | FEAT_ALPHA_FREE_ZORDER, + .features = omap4_dss_feat_list, + .num_features = ARRAY_SIZE(omap4_dss_feat_list), .num_mgrs = 3, .num_ovls = 4, @@ -547,7 +641,16 @@ u32 dss_feat_get_burst_size_unit(void) /* DSS has_feature check */ bool dss_has_feature(enum dss_feat_id id) { - return omap_current_dss_features->has_feature & id; + int i; + const enum dss_feat_id *features = omap_current_dss_features->features; + const int num_features = omap_current_dss_features->num_features; + + for (i = 0; i < num_features; i++) { + if (features[i] == id) + return true; + } + + return false; } void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end) @@ -569,6 +672,10 @@ void dss_features_init(void) omap_current_dss_features = &omap3430_dss_features; else if (omap_rev() == OMAP4430_REV_ES1_0) omap_current_dss_features = &omap4430_es1_0_dss_features; + else if (omap_rev() == OMAP4430_REV_ES2_0 || + omap_rev() == OMAP4430_REV_ES2_1 || + omap_rev() == OMAP4430_REV_ES2_2) + omap_current_dss_features = &omap4430_es2_0_1_2_dss_features; else if (cpu_is_omap44xx()) omap_current_dss_features = &omap4_dss_features; else diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h index cd833bbaac3d..c332e7ddfce1 100644 --- a/drivers/video/omap2/dss/dss_features.h +++ b/drivers/video/omap2/dss/dss_features.h @@ -31,33 +31,37 @@ /* DSS has feature id */ enum dss_feat_id { - FEAT_LCDENABLEPOL = 1 << 3, - FEAT_LCDENABLESIGNAL = 1 << 4, - FEAT_PCKFREEENABLE = 1 << 5, - FEAT_FUNCGATED = 1 << 6, - FEAT_MGR_LCD2 = 1 << 7, - FEAT_LINEBUFFERSPLIT = 1 << 8, - FEAT_ROWREPEATENABLE = 1 << 9, - FEAT_RESIZECONF = 1 << 10, + FEAT_LCDENABLEPOL, + FEAT_LCDENABLESIGNAL, + FEAT_PCKFREEENABLE, + FEAT_FUNCGATED, + FEAT_MGR_LCD2, + FEAT_LINEBUFFERSPLIT, + FEAT_ROWREPEATENABLE, + FEAT_RESIZECONF, /* Independent core clk divider */ - FEAT_CORE_CLK_DIV = 1 << 11, - FEAT_LCD_CLK_SRC = 1 << 12, + FEAT_CORE_CLK_DIV, + FEAT_LCD_CLK_SRC, /* DSI-PLL power command 0x3 is not working */ - FEAT_DSI_PLL_PWR_BUG = 1 << 13, - FEAT_DSI_PLL_FREQSEL = 1 << 14, - FEAT_DSI_DCS_CMD_CONFIG_VC = 1 << 15, - FEAT_DSI_VC_OCP_WIDTH = 1 << 16, - FEAT_DSI_REVERSE_TXCLKESC = 1 << 17, - FEAT_DSI_GNQ = 1 << 18, - FEAT_HDMI_CTS_SWMODE = 1 << 19, - FEAT_HANDLE_UV_SEPARATE = 1 << 20, - FEAT_ATTR2 = 1 << 21, - FEAT_VENC_REQUIRES_TV_DAC_CLK = 1 << 22, - FEAT_CPR = 1 << 23, - FEAT_PRELOAD = 1 << 24, - FEAT_FIR_COEF_V = 1 << 25, - FEAT_ALPHA_FIXED_ZORDER = 1 << 26, - FEAT_ALPHA_FREE_ZORDER = 1 << 27, + FEAT_DSI_PLL_PWR_BUG, + FEAT_DSI_PLL_FREQSEL, + FEAT_DSI_DCS_CMD_CONFIG_VC, + FEAT_DSI_VC_OCP_WIDTH, + FEAT_DSI_REVERSE_TXCLKESC, + FEAT_DSI_GNQ, + FEAT_HDMI_CTS_SWMODE, + FEAT_HDMI_AUDIO_USE_MCLK, + FEAT_HANDLE_UV_SEPARATE, + FEAT_ATTR2, + FEAT_VENC_REQUIRES_TV_DAC_CLK, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FIXED_ZORDER, + FEAT_ALPHA_FREE_ZORDER, + FEAT_FIFO_MERGE, + /* An unknown HW bug causing the normal FIFO thresholds not to work */ + FEAT_OMAP3_DSI_FIFO_BUG, }; /* DSS register field id */ diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index a36b934b2db4..c4b4f6950a92 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -58,8 +58,6 @@ #define EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR 4 #define EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR 4 -#define OMAP_HDMI_TIMINGS_NB 34 - #define HDMI_DEFAULT_REGN 16 #define HDMI_DEFAULT_REGM2 1 @@ -68,8 +66,6 @@ static struct { struct omap_display_platform_data *pdata; struct platform_device *pdev; struct hdmi_ip_data ip_data; - int code; - int mode; struct clk *sys_clk; } hdmi; @@ -88,77 +84,46 @@ static struct { * map it to corresponding CEA or VESA index. */ -static const struct hdmi_timings cea_vesa_timings[OMAP_HDMI_TIMINGS_NB] = { - { {640, 480, 25200, 96, 16, 48, 2, 10, 33} , 0 , 0}, - { {1280, 720, 74250, 40, 440, 220, 5, 5, 20}, 1, 1}, - { {1280, 720, 74250, 40, 110, 220, 5, 5, 20}, 1, 1}, - { {720, 480, 27027, 62, 16, 60, 6, 9, 30}, 0, 0}, - { {2880, 576, 108000, 256, 48, 272, 5, 5, 39}, 0, 0}, - { {1440, 240, 27027, 124, 38, 114, 3, 4, 15}, 0, 0}, - { {1440, 288, 27000, 126, 24, 138, 3, 2, 19}, 0, 0}, - { {1920, 540, 74250, 44, 528, 148, 5, 2, 15}, 1, 1}, - { {1920, 540, 74250, 44, 88, 148, 5, 2, 15}, 1, 1}, - { {1920, 1080, 148500, 44, 88, 148, 5, 4, 36}, 1, 1}, - { {720, 576, 27000, 64, 12, 68, 5, 5, 39}, 0, 0}, - { {1440, 576, 54000, 128, 24, 136, 5, 5, 39}, 0, 0}, - { {1920, 1080, 148500, 44, 528, 148, 5, 4, 36}, 1, 1}, - { {2880, 480, 108108, 248, 64, 240, 6, 9, 30}, 0, 0}, - { {1920, 1080, 74250, 44, 638, 148, 5, 4, 36}, 1, 1}, - /* VESA From Here */ - { {640, 480, 25175, 96, 16, 48, 2 , 11, 31}, 0, 0}, - { {800, 600, 40000, 128, 40, 88, 4 , 1, 23}, 1, 1}, - { {848, 480, 33750, 112, 16, 112, 8 , 6, 23}, 1, 1}, - { {1280, 768, 79500, 128, 64, 192, 7 , 3, 20}, 1, 0}, - { {1280, 800, 83500, 128, 72, 200, 6 , 3, 22}, 1, 0}, - { {1360, 768, 85500, 112, 64, 256, 6 , 3, 18}, 1, 1}, - { {1280, 960, 108000, 112, 96, 312, 3 , 1, 36}, 1, 1}, - { {1280, 1024, 108000, 112, 48, 248, 3 , 1, 38}, 1, 1}, - { {1024, 768, 65000, 136, 24, 160, 6, 3, 29}, 0, 0}, - { {1400, 1050, 121750, 144, 88, 232, 4, 3, 32}, 1, 0}, - { {1440, 900, 106500, 152, 80, 232, 6, 3, 25}, 1, 0}, - { {1680, 1050, 146250, 176 , 104, 280, 6, 3, 30}, 1, 0}, - { {1366, 768, 85500, 143, 70, 213, 3, 3, 24}, 1, 1}, - { {1920, 1080, 148500, 44, 148, 80, 5, 4, 36}, 1, 1}, - { {1280, 768, 68250, 32, 48, 80, 7, 3, 12}, 0, 1}, - { {1400, 1050, 101000, 32, 48, 80, 4, 3, 23}, 0, 1}, - { {1680, 1050, 119000, 32, 48, 80, 6, 3, 21}, 0, 1}, - { {1280, 800, 79500, 32, 48, 80, 6, 3, 14}, 0, 1}, - { {1280, 720, 74250, 40, 110, 220, 5, 5, 20}, 1, 1} -}; - -/* - * This is a static mapping array which maps the timing values - * with corresponding CEA / VESA code - */ -static const int code_index[OMAP_HDMI_TIMINGS_NB] = { - 1, 19, 4, 2, 37, 6, 21, 20, 5, 16, 17, 29, 31, 35, 32, - /* <--15 CEA 17--> vesa*/ - 4, 9, 0xE, 0x17, 0x1C, 0x27, 0x20, 0x23, 0x10, 0x2A, - 0X2F, 0x3A, 0X51, 0X52, 0x16, 0x29, 0x39, 0x1B +static const struct hdmi_config cea_timings[] = { +{ {640, 480, 25200, 96, 16, 48, 2, 10, 33, 0, 0, 0}, {1, HDMI_HDMI} }, +{ {720, 480, 27027, 62, 16, 60, 6, 9, 30, 0, 0, 0}, {2, HDMI_HDMI} }, +{ {1280, 720, 74250, 40, 110, 220, 5, 5, 20, 1, 1, 0}, {4, HDMI_HDMI} }, +{ {1920, 540, 74250, 44, 88, 148, 5, 2, 15, 1, 1, 1}, {5, HDMI_HDMI} }, +{ {1440, 240, 27027, 124, 38, 114, 3, 4, 15, 0, 0, 1}, {6, HDMI_HDMI} }, +{ {1920, 1080, 148500, 44, 88, 148, 5, 4, 36, 1, 1, 0}, {16, HDMI_HDMI} }, +{ {720, 576, 27000, 64, 12, 68, 5, 5, 39, 0, 0, 0}, {17, HDMI_HDMI} }, +{ {1280, 720, 74250, 40, 440, 220, 5, 5, 20, 1, 1, 0}, {19, HDMI_HDMI} }, +{ {1920, 540, 74250, 44, 528, 148, 5, 2, 15, 1, 1, 1}, {20, HDMI_HDMI} }, +{ {1440, 288, 27000, 126, 24, 138, 3, 2, 19, 0, 0, 1}, {21, HDMI_HDMI} }, +{ {1440, 576, 54000, 128, 24, 136, 5, 5, 39, 0, 0, 0}, {29, HDMI_HDMI} }, +{ {1920, 1080, 148500, 44, 528, 148, 5, 4, 36, 1, 1, 0}, {31, HDMI_HDMI} }, +{ {1920, 1080, 74250, 44, 638, 148, 5, 4, 36, 1, 1, 0}, {32, HDMI_HDMI} }, +{ {2880, 480, 108108, 248, 64, 240, 6, 9, 30, 0, 0, 0}, {35, HDMI_HDMI} }, +{ {2880, 576, 108000, 256, 48, 272, 5, 5, 39, 0, 0, 0}, {37, HDMI_HDMI} }, }; - -/* - * This is reverse static mapping which maps the CEA / VESA code - * to the corresponding timing values - */ -static const int code_cea[39] = { - -1, 0, 3, 3, 2, 8, 5, 5, -1, -1, - -1, -1, -1, -1, -1, -1, 9, 10, 10, 1, - 7, 6, 6, -1, -1, -1, -1, -1, -1, 11, - 11, 12, 14, -1, -1, 13, 13, 4, 4 +static const struct hdmi_config vesa_timings[] = { +/* VESA From Here */ +{ {640, 480, 25175, 96, 16, 48, 2 , 11, 31, 0, 0, 0}, {4, HDMI_DVI} }, +{ {800, 600, 40000, 128, 40, 88, 4 , 1, 23, 1, 1, 0}, {9, HDMI_DVI} }, +{ {848, 480, 33750, 112, 16, 112, 8 , 6, 23, 1, 1, 0}, {0xE, HDMI_DVI} }, +{ {1280, 768, 79500, 128, 64, 192, 7 , 3, 20, 1, 0, 0}, {0x17, HDMI_DVI} }, +{ {1280, 800, 83500, 128, 72, 200, 6 , 3, 22, 1, 0, 0}, {0x1C, HDMI_DVI} }, +{ {1360, 768, 85500, 112, 64, 256, 6 , 3, 18, 1, 1, 0}, {0x27, HDMI_DVI} }, +{ {1280, 960, 108000, 112, 96, 312, 3 , 1, 36, 1, 1, 0}, {0x20, HDMI_DVI} }, +{ {1280, 1024, 108000, 112, 48, 248, 3 , 1, 38, 1, 1, 0}, {0x23, HDMI_DVI} }, +{ {1024, 768, 65000, 136, 24, 160, 6, 3, 29, 0, 0, 0}, {0x10, HDMI_DVI} }, +{ {1400, 1050, 121750, 144, 88, 232, 4, 3, 32, 1, 0, 0}, {0x2A, HDMI_DVI} }, +{ {1440, 900, 106500, 152, 80, 232, 6, 3, 25, 1, 0, 0}, {0x2F, HDMI_DVI} }, +{ {1680, 1050, 146250, 176 , 104, 280, 6, 3, 30, 1, 0, 0}, {0x3A, HDMI_DVI} }, +{ {1366, 768, 85500, 143, 70, 213, 3, 3, 24, 1, 1, 0}, {0x51, HDMI_DVI} }, +{ {1920, 1080, 148500, 44, 148, 80, 5, 4, 36, 1, 1, 0}, {0x52, HDMI_DVI} }, +{ {1280, 768, 68250, 32, 48, 80, 7, 3, 12, 0, 1, 0}, {0x16, HDMI_DVI} }, +{ {1400, 1050, 101000, 32, 48, 80, 4, 3, 23, 0, 1, 0}, {0x29, HDMI_DVI} }, +{ {1680, 1050, 119000, 32, 48, 80, 6, 3, 21, 0, 1, 0}, {0x39, HDMI_DVI} }, +{ {1280, 800, 79500, 32, 48, 80, 6, 3, 14, 0, 1, 0}, {0x1B, HDMI_DVI} }, +{ {1280, 720, 74250, 40, 110, 220, 5, 5, 20, 1, 1, 0}, {0x55, HDMI_DVI} } }; -static const int code_vesa[85] = { - -1, -1, -1, -1, 15, -1, -1, -1, -1, 16, - -1, -1, -1, -1, 17, -1, 23, -1, -1, -1, - -1, -1, 29, 18, -1, -1, -1, 32, 19, -1, - -1, -1, 21, -1, -1, 22, -1, -1, -1, 20, - -1, 30, 24, -1, -1, -1, -1, 25, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 31, 26, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 27, 28, -1, 33}; - static int hdmi_runtime_get(void) { int r; @@ -210,88 +175,89 @@ int hdmi_init_display(struct omap_dss_device *dssdev) return 0; } -static int get_timings_index(void) +static const struct hdmi_config *hdmi_find_timing( + const struct hdmi_config *timings_arr, + int len) { - int code; + int i; - if (hdmi.mode == 0) - code = code_vesa[hdmi.code]; - else - code = code_cea[hdmi.code]; + for (i = 0; i < len; i++) { + if (timings_arr[i].cm.code == hdmi.ip_data.cfg.cm.code) + return &timings_arr[i]; + } + return NULL; +} - if (code == -1) { - /* HDMI code 4 corresponds to 640 * 480 VGA */ - hdmi.code = 4; - /* DVI mode 1 corresponds to HDMI 0 to DVI */ - hdmi.mode = HDMI_DVI; +static const struct hdmi_config *hdmi_get_timings(void) +{ + const struct hdmi_config *arr; + int len; + + if (hdmi.ip_data.cfg.cm.mode == HDMI_DVI) { + arr = vesa_timings; + len = ARRAY_SIZE(vesa_timings); + } else { + arr = cea_timings; + len = ARRAY_SIZE(cea_timings); + } + + return hdmi_find_timing(arr, len); +} + +static bool hdmi_timings_compare(struct omap_video_timings *timing1, + const struct hdmi_video_timings *timing2) +{ + int timing1_vsync, timing1_hsync, timing2_vsync, timing2_hsync; - code = code_vesa[hdmi.code]; + if ((timing2->pixel_clock == timing1->pixel_clock) && + (timing2->x_res == timing1->x_res) && + (timing2->y_res == timing1->y_res)) { + + timing2_hsync = timing2->hfp + timing2->hsw + timing2->hbp; + timing1_hsync = timing1->hfp + timing1->hsw + timing1->hbp; + timing2_vsync = timing2->vfp + timing2->vsw + timing2->vbp; + timing1_vsync = timing2->vfp + timing2->vsw + timing2->vbp; + + DSSDBG("timing1_hsync = %d timing1_vsync = %d"\ + "timing2_hsync = %d timing2_vsync = %d\n", + timing1_hsync, timing1_vsync, + timing2_hsync, timing2_vsync); + + if ((timing1_hsync == timing2_hsync) && + (timing1_vsync == timing2_vsync)) { + return true; + } } - return code; + return false; } static struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing) { - int i = 0, code = -1, temp_vsync = 0, temp_hsync = 0; - int timing_vsync = 0, timing_hsync = 0; - struct hdmi_video_timings temp; + int i; struct hdmi_cm cm = {-1}; DSSDBG("hdmi_get_code\n"); - for (i = 0; i < OMAP_HDMI_TIMINGS_NB; i++) { - temp = cea_vesa_timings[i].timings; - if ((temp.pixel_clock == timing->pixel_clock) && - (temp.x_res == timing->x_res) && - (temp.y_res == timing->y_res)) { - - temp_hsync = temp.hfp + temp.hsw + temp.hbp; - timing_hsync = timing->hfp + timing->hsw + timing->hbp; - temp_vsync = temp.vfp + temp.vsw + temp.vbp; - timing_vsync = timing->vfp + timing->vsw + timing->vbp; - - DSSDBG("temp_hsync = %d , temp_vsync = %d" - "timing_hsync = %d, timing_vsync = %d\n", - temp_hsync, temp_hsync, - timing_hsync, timing_vsync); - - if ((temp_hsync == timing_hsync) && - (temp_vsync == timing_vsync)) { - code = i; - cm.code = code_index[i]; - if (code < 14) - cm.mode = HDMI_HDMI; - else - cm.mode = HDMI_DVI; - DSSDBG("Hdmi_code = %d mode = %d\n", - cm.code, cm.mode); - break; - } + for (i = 0; i < ARRAY_SIZE(cea_timings); i++) { + if (hdmi_timings_compare(timing, &cea_timings[i].timings)) { + cm = cea_timings[i].cm; + goto end; + } + } + for (i = 0; i < ARRAY_SIZE(vesa_timings); i++) { + if (hdmi_timings_compare(timing, &vesa_timings[i].timings)) { + cm = vesa_timings[i].cm; + goto end; } } - return cm; -} +end: return cm; -static void update_hdmi_timings(struct hdmi_config *cfg, - struct omap_video_timings *timings, int code) -{ - cfg->timings.timings.x_res = timings->x_res; - cfg->timings.timings.y_res = timings->y_res; - cfg->timings.timings.hbp = timings->hbp; - cfg->timings.timings.hfp = timings->hfp; - cfg->timings.timings.hsw = timings->hsw; - cfg->timings.timings.vbp = timings->vbp; - cfg->timings.timings.vfp = timings->vfp; - cfg->timings.timings.vsw = timings->vsw; - cfg->timings.timings.pixel_clock = timings->pixel_clock; - cfg->timings.vsync_pol = cea_vesa_timings[code].vsync_pol; - cfg->timings.hsync_pol = cea_vesa_timings[code].hsync_pol; } unsigned long hdmi_get_pixel_clock(void) { /* HDMI Pixel Clock in Mhz */ - return hdmi.ip_data.cfg.timings.timings.pixel_clock * 1000; + return hdmi.ip_data.cfg.timings.pixel_clock * 1000; } static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, @@ -312,24 +278,24 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, refclk = clkin / pi->regn; - /* - * multiplier is pixel_clk/ref_clk - * Multiplying by 100 to avoid fractional part removal - */ - pi->regm = (phy * 100 / (refclk)) / 100; - if (dssdev->clocks.hdmi.regm2 == 0) pi->regm2 = HDMI_DEFAULT_REGM2; else pi->regm2 = dssdev->clocks.hdmi.regm2; /* + * multiplier is pixel_clk/ref_clk + * Multiplying by 100 to avoid fractional part removal + */ + pi->regm = phy * pi->regm2 / refclk; + + /* * fractional multiplier is remainder of the difference between * multiplier and actual phy(required pixel clock thus should be * multiplied by 2^18(262144) divided by the reference clock */ - mf = (phy - pi->regm * refclk) * 262144; - pi->regmf = mf / (refclk); + mf = (phy - pi->regm / pi->regm2 * refclk) * 262144; + pi->regmf = pi->regm2 * mf / refclk; /* * Dcofreq should be set to 1 if required pixel clock @@ -347,7 +313,8 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, static int hdmi_power_on(struct omap_dss_device *dssdev) { - int r, code = 0; + int r; + const struct hdmi_config *timing; struct omap_video_timings *p; unsigned long phy; @@ -363,9 +330,16 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) dssdev->panel.timings.x_res, dssdev->panel.timings.y_res); - code = get_timings_index(); - update_hdmi_timings(&hdmi.ip_data.cfg, p, code); - + timing = hdmi_get_timings(); + if (timing == NULL) { + /* HDMI code 4 corresponds to 640 * 480 VGA */ + hdmi.ip_data.cfg.cm.code = 4; + /* DVI mode 1 corresponds to HDMI 0 to DVI */ + hdmi.ip_data.cfg.cm.mode = HDMI_DVI; + hdmi.ip_data.cfg = vesa_timings[0]; + } else { + hdmi.ip_data.cfg = *timing; + } phy = p->pixel_clock; hdmi_compute_pll(dssdev, phy, &hdmi.ip_data.pll_data); @@ -385,8 +359,6 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) goto err; } - hdmi.ip_data.cfg.cm.mode = hdmi.mode; - hdmi.ip_data.cfg.cm.code = hdmi.code; hdmi.ip_data.ops->video_configure(&hdmi.ip_data); /* Make selection of HDMI in DSS */ @@ -453,8 +425,8 @@ void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev) struct hdmi_cm cm; cm = hdmi_get_code(&dssdev->panel.timings); - hdmi.code = cm.code; - hdmi.mode = cm.mode; + hdmi.ip_data.cfg.cm.code = cm.code; + hdmi.ip_data.cfg.cm.mode = cm.mode; if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { int r; @@ -717,13 +689,15 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream, if (dss_has_feature(FEAT_HDMI_CTS_SWMODE)) { core_cfg.aud_par_busclk = 0; core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_SW; - core_cfg.use_mclk = false; + core_cfg.use_mclk = dss_has_feature(FEAT_HDMI_AUDIO_USE_MCLK); } else { core_cfg.aud_par_busclk = (((128 * 31) - 1) << 8); core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_HW; core_cfg.use_mclk = true; - core_cfg.mclk_mode = HDMI_AUDIO_MCLK_128FS; } + + if (core_cfg.use_mclk) + core_cfg.mclk_mode = HDMI_AUDIO_MCLK_128FS; core_cfg.layout = HDMI_AUDIO_LAYOUT_2CH; core_cfg.en_spdif = false; /* Use sample frequency from channel status word */ @@ -756,7 +730,7 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream, static int hdmi_audio_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - if (!hdmi.mode) { + if (!hdmi.ip_data.cfg.cm.mode) { pr_err("Current video settings do not support audio.\n"); return -EIO; } diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c index d1858e71c64e..e7364603f6a1 100644 --- a/drivers/video/omap2/dss/manager.c +++ b/drivers/video/omap2/dss/manager.c @@ -494,6 +494,11 @@ static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr) { unsigned long timeout = msecs_to_jiffies(500); u32 irq; + int r; + + r = dispc_runtime_get(); + if (r) + return r; if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) { irq = DISPC_IRQ_EVSYNC_ODD; @@ -505,7 +510,12 @@ static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr) else irq = DISPC_IRQ_VSYNC2; } - return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); + + r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); + + dispc_runtime_put(); + + return r; } int dss_init_overlay_managers(struct platform_device *pdev) diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c index 55f398014f33..788a0ef6323a 100644 --- a/drivers/video/omap2/dss/rfbi.c +++ b/drivers/video/omap2/dss/rfbi.c @@ -922,35 +922,34 @@ static int omap_rfbihw_probe(struct platform_device *pdev) rfbi_mem = platform_get_resource(rfbi.pdev, IORESOURCE_MEM, 0); if (!rfbi_mem) { DSSERR("can't get IORESOURCE_MEM RFBI\n"); - r = -EINVAL; - goto err_ioremap; + return -EINVAL; } - rfbi.base = ioremap(rfbi_mem->start, resource_size(rfbi_mem)); + + rfbi.base = devm_ioremap(&pdev->dev, rfbi_mem->start, + resource_size(rfbi_mem)); if (!rfbi.base) { DSSERR("can't ioremap RFBI\n"); - r = -ENOMEM; - goto err_ioremap; + return -ENOMEM; } - pm_runtime_enable(&pdev->dev); - - r = rfbi_runtime_get(); - if (r) - goto err_get_rfbi; - - msleep(10); - clk = clk_get(&pdev->dev, "ick"); if (IS_ERR(clk)) { DSSERR("can't get ick\n"); - r = PTR_ERR(clk); - goto err_get_ick; + return PTR_ERR(clk); } rfbi.l4_khz = clk_get_rate(clk) / 1000; clk_put(clk); + pm_runtime_enable(&pdev->dev); + + r = rfbi_runtime_get(); + if (r) + goto err_runtime_get; + + msleep(10); + rev = rfbi_read_reg(RFBI_REVISION); dev_dbg(&pdev->dev, "OMAP RFBI rev %d.%d\n", FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); @@ -959,19 +958,14 @@ static int omap_rfbihw_probe(struct platform_device *pdev) return 0; -err_get_ick: - rfbi_runtime_put(); -err_get_rfbi: +err_runtime_get: pm_runtime_disable(&pdev->dev); - iounmap(rfbi.base); -err_ioremap: return r; } static int omap_rfbihw_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); - iounmap(rfbi.base); return 0; } diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h index 50dadba5070a..1f58b84d6901 100644 --- a/drivers/video/omap2/dss/ti_hdmi.h +++ b/drivers/video/omap2/dss/ti_hdmi.h @@ -42,6 +42,7 @@ enum hdmi_clk_refsel { HDMI_REFSEL_SYSCLK = 3 }; +/* HDMI timing structure */ struct hdmi_video_timings { u16 x_res; u16 y_res; @@ -53,13 +54,9 @@ struct hdmi_video_timings { u16 vsw; u16 vfp; u16 vbp; -}; - -/* HDMI timing structure */ -struct hdmi_timings { - struct hdmi_video_timings timings; - int vsync_pol; - int hsync_pol; + bool vsync_pol; + bool hsync_pol; + bool interlace; }; struct hdmi_cm { @@ -68,8 +65,7 @@ struct hdmi_cm { }; struct hdmi_config { - struct hdmi_timings timings; - u16 interlace; + struct hdmi_video_timings timings; struct hdmi_cm cm; }; @@ -117,6 +113,47 @@ struct ti_hdmi_ip_ops { }; +/* + * Refer to section 8.2 in HDMI 1.3 specification for + * details about infoframe databytes + */ +struct hdmi_core_infoframe_avi { + /* Y0, Y1 rgb,yCbCr */ + u8 db1_format; + /* A0 Active information Present */ + u8 db1_active_info; + /* B0, B1 Bar info data valid */ + u8 db1_bar_info_dv; + /* S0, S1 scan information */ + u8 db1_scan_info; + /* C0, C1 colorimetry */ + u8 db2_colorimetry; + /* M0, M1 Aspect ratio (4:3, 16:9) */ + u8 db2_aspect_ratio; + /* R0...R3 Active format aspect ratio */ + u8 db2_active_fmt_ar; + /* ITC IT content. */ + u8 db3_itc; + /* EC0, EC1, EC2 Extended colorimetry */ + u8 db3_ec; + /* Q1, Q0 Quantization range */ + u8 db3_q_range; + /* SC1, SC0 Non-uniform picture scaling */ + u8 db3_nup_scaling; + /* VIC0..6 Video format identification */ + u8 db4_videocode; + /* PR0..PR3 Pixel repetition factor */ + u8 db5_pixel_repeat; + /* Line number end of top bar */ + u16 db6_7_line_eoftop; + /* Line number start of bottom bar */ + u16 db8_9_line_sofbottom; + /* Pixel number end of left bar */ + u16 db10_11_pixel_eofleft; + /* Pixel number start of right bar */ + u16 db12_13_pixel_sofright; +}; + struct hdmi_ip_data { void __iomem *base_wp; /* HDMI wrapper */ unsigned long core_sys_offset; @@ -126,6 +163,7 @@ struct hdmi_ip_data { const struct ti_hdmi_ip_ops *ops; struct hdmi_config cfg; struct hdmi_pll_info pll_data; + struct hdmi_core_infoframe_avi avi_cfg; /* ti_hdmi_4xxx_ip private data. These should be in a separate struct */ int hpd_gpio; diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index 6847a478b459..bfe6fe65c8be 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -587,12 +587,12 @@ static void hdmi_core_video_config(struct hdmi_ip_data *ip_data, HDMI_CORE_SYS_TMDS_CTRL, cfg->tclk_sel_clkmult, 6, 5); } -static void hdmi_core_aux_infoframe_avi_config(struct hdmi_ip_data *ip_data, - struct hdmi_core_infoframe_avi info_avi) +static void hdmi_core_aux_infoframe_avi_config(struct hdmi_ip_data *ip_data) { u32 val; char sum = 0, checksum = 0; void __iomem *av_base = hdmi_av_base(ip_data); + struct hdmi_core_infoframe_avi info_avi = ip_data->avi_cfg; sum += 0x82 + 0x002 + 0x00D; hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_TYPE, 0x082); @@ -682,8 +682,7 @@ static void hdmi_core_av_packet_config(struct hdmi_ip_data *ip_data, } static void hdmi_wp_init(struct omap_video_timings *timings, - struct hdmi_video_format *video_fmt, - struct hdmi_video_interface *video_int) + struct hdmi_video_format *video_fmt) { pr_debug("Enter hdmi_wp_init\n"); @@ -698,12 +697,6 @@ static void hdmi_wp_init(struct omap_video_timings *timings, video_fmt->y_res = 0; video_fmt->x_res = 0; - video_int->vsp = 0; - video_int->hsp = 0; - - video_int->interlacing = 0; - video_int->tm = 0; /* HDMI_TIMING_SLAVE */ - } void ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data, bool start) @@ -716,15 +709,15 @@ static void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt, { pr_debug("Enter hdmi_wp_video_init_format\n"); - video_fmt->y_res = param->timings.timings.y_res; - video_fmt->x_res = param->timings.timings.x_res; + video_fmt->y_res = param->timings.y_res; + video_fmt->x_res = param->timings.x_res; - timings->hbp = param->timings.timings.hbp; - timings->hfp = param->timings.timings.hfp; - timings->hsw = param->timings.timings.hsw; - timings->vbp = param->timings.timings.vbp; - timings->vfp = param->timings.timings.vfp; - timings->vsw = param->timings.timings.vsw; + timings->hbp = param->timings.hbp; + timings->hfp = param->timings.hfp; + timings->hsw = param->timings.hsw; + timings->vbp = param->timings.vbp; + timings->vfp = param->timings.vfp; + timings->vsw = param->timings.vsw; } static void hdmi_wp_video_config_format(struct hdmi_ip_data *ip_data, @@ -740,17 +733,16 @@ static void hdmi_wp_video_config_format(struct hdmi_ip_data *ip_data, hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_SIZE, l); } -static void hdmi_wp_video_config_interface(struct hdmi_ip_data *ip_data, - struct hdmi_video_interface *video_int) +static void hdmi_wp_video_config_interface(struct hdmi_ip_data *ip_data) { u32 r; pr_debug("Enter hdmi_wp_video_config_interface\n"); r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG); - r = FLD_MOD(r, video_int->vsp, 7, 7); - r = FLD_MOD(r, video_int->hsp, 6, 6); - r = FLD_MOD(r, video_int->interlacing, 3, 3); - r = FLD_MOD(r, video_int->tm, 1, 0); + r = FLD_MOD(r, ip_data->cfg.timings.vsync_pol, 7, 7); + r = FLD_MOD(r, ip_data->cfg.timings.hsync_pol, 6, 6); + r = FLD_MOD(r, ip_data->cfg.timings.interlace, 3, 3); + r = FLD_MOD(r, 1, 1, 0); /* HDMI_TIMING_MASTER_24BIT */ hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, r); } @@ -778,15 +770,13 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data) /* HDMI */ struct omap_video_timings video_timing; struct hdmi_video_format video_format; - struct hdmi_video_interface video_interface; /* HDMI core */ - struct hdmi_core_infoframe_avi avi_cfg; + struct hdmi_core_infoframe_avi avi_cfg = ip_data->avi_cfg; struct hdmi_core_video_config v_core_cfg; struct hdmi_core_packet_enable_repeat repeat_cfg; struct hdmi_config *cfg = &ip_data->cfg; - hdmi_wp_init(&video_timing, &video_format, - &video_interface); + hdmi_wp_init(&video_timing, &video_format); hdmi_core_init(&v_core_cfg, &avi_cfg, @@ -801,12 +791,7 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data) hdmi_wp_video_config_format(ip_data, &video_format); - video_interface.vsp = cfg->timings.vsync_pol; - video_interface.hsp = cfg->timings.hsync_pol; - video_interface.interlacing = cfg->interlace; - video_interface.tm = 1 ; /* HDMI_TIMING_MASTER_24BIT */ - - hdmi_wp_video_config_interface(ip_data, &video_interface); + hdmi_wp_video_config_interface(ip_data); /* * configure core video part @@ -848,7 +833,7 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data) avi_cfg.db10_11_pixel_eofleft = 0; avi_cfg.db12_13_pixel_sofright = 0; - hdmi_core_aux_infoframe_avi_config(ip_data, avi_cfg); + hdmi_core_aux_infoframe_avi_config(ip_data); /* enable/repeat the infoframe */ repeat_cfg.avi_infoframe = HDMI_PACKETENABLE; @@ -1076,13 +1061,9 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, u32 r; void __iomem *av_base = hdmi_av_base(ip_data); - /* audio clock recovery parameters */ - r = hdmi_read_reg(av_base, HDMI_CORE_AV_ACR_CTRL); - r = FLD_MOD(r, cfg->use_mclk, 2, 2); - r = FLD_MOD(r, cfg->en_acr_pkt, 1, 1); - r = FLD_MOD(r, cfg->cts_mode, 0, 0); - hdmi_write_reg(av_base, HDMI_CORE_AV_ACR_CTRL, r); - + /* + * Parameters for generation of Audio Clock Recovery packets + */ REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL1, cfg->n, 7, 0); REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL2, cfg->n >> 8, 7, 0); REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL3, cfg->n >> 16, 7, 0); @@ -1094,14 +1075,6 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, REG_FLD_MOD(av_base, HDMI_CORE_AV_CTS_SVAL3, cfg->cts >> 16, 7, 0); } else { - /* - * HDMI IP uses this configuration to divide the MCLK to - * update CTS value. - */ - REG_FLD_MOD(av_base, - HDMI_CORE_AV_FREQ_SVAL, cfg->mclk_mode, 2, 0); - - /* Configure clock for audio packets */ REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_1, cfg->aud_par_busclk, 7, 0); REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_2, @@ -1110,6 +1083,25 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, (cfg->aud_par_busclk >> 16), 7, 0); } + /* Set ACR clock divisor */ + REG_FLD_MOD(av_base, + HDMI_CORE_AV_FREQ_SVAL, cfg->mclk_mode, 2, 0); + + r = hdmi_read_reg(av_base, HDMI_CORE_AV_ACR_CTRL); + /* + * Use TMDS clock for ACR packets. For devices that use + * the MCLK, this is the first part of the MCLK initialization. + */ + r = FLD_MOD(r, 0, 2, 2); + + r = FLD_MOD(r, cfg->en_acr_pkt, 1, 1); + r = FLD_MOD(r, cfg->cts_mode, 0, 0); + hdmi_write_reg(av_base, HDMI_CORE_AV_ACR_CTRL, r); + + /* For devices using MCLK, this completes its initialization. */ + if (cfg->use_mclk) + REG_FLD_MOD(av_base, HDMI_CORE_AV_ACR_CTRL, 1, 2, 2); + /* Override of SPDIF sample frequency with value in I2S_CHST4 */ REG_FLD_MOD(av_base, HDMI_CORE_AV_SPDIF_CTRL, cfg->fs_override, 1, 1); @@ -1205,7 +1197,7 @@ int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data, { u32 r; u32 deep_color = 0; - u32 pclk = ip_data->cfg.timings.timings.pixel_clock; + u32 pclk = ip_data->cfg.timings.pixel_clock; if (n == NULL || cts == NULL) return -EINVAL; diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h index a442998980f1..a14d1a0e6e41 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h @@ -450,46 +450,6 @@ struct hdmi_core_video_config { * Refer to section 8.2 in HDMI 1.3 specification for * details about infoframe databytes */ -struct hdmi_core_infoframe_avi { - /* Y0, Y1 rgb,yCbCr */ - u8 db1_format; - /* A0 Active information Present */ - u8 db1_active_info; - /* B0, B1 Bar info data valid */ - u8 db1_bar_info_dv; - /* S0, S1 scan information */ - u8 db1_scan_info; - /* C0, C1 colorimetry */ - u8 db2_colorimetry; - /* M0, M1 Aspect ratio (4:3, 16:9) */ - u8 db2_aspect_ratio; - /* R0...R3 Active format aspect ratio */ - u8 db2_active_fmt_ar; - /* ITC IT content. */ - u8 db3_itc; - /* EC0, EC1, EC2 Extended colorimetry */ - u8 db3_ec; - /* Q1, Q0 Quantization range */ - u8 db3_q_range; - /* SC1, SC0 Non-uniform picture scaling */ - u8 db3_nup_scaling; - /* VIC0..6 Video format identification */ - u8 db4_videocode; - /* PR0..PR3 Pixel repetition factor */ - u8 db5_pixel_repeat; - /* Line number end of top bar */ - u16 db6_7_line_eoftop; - /* Line number start of bottom bar */ - u16 db8_9_line_sofbottom; - /* Pixel number end of left bar */ - u16 db10_11_pixel_eofleft; - /* Pixel number start of right bar */ - u16 db12_13_pixel_sofright; -}; -/* - * Refer to section 8.2 in HDMI 1.3 specification for - * details about infoframe databytes - */ struct hdmi_core_infoframe_audio { u8 db1_coding_type; u8 db1_channel_count; @@ -517,13 +477,6 @@ struct hdmi_video_format { u32 x_res; /* pixel per line */ }; -struct hdmi_video_interface { - int vsp; /* Vsync polarity */ - int hsp; /* Hsync polarity */ - int interlacing; - int tm; /* Timing mode */ -}; - struct hdmi_audio_format { enum hdmi_stereo_channels stereo_channels; u8 active_chnnls_msk; diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c index 5c3d0f901510..9c3daf71750c 100644 --- a/drivers/video/omap2/dss/venc.c +++ b/drivers/video/omap2/dss/venc.c @@ -699,6 +699,11 @@ void venc_dump_regs(struct seq_file *s) { #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r)) + if (cpu_is_omap44xx()) { + seq_printf(s, "VENC currently disabled on OMAP44xx\n"); + return; + } + if (venc_runtime_get()) return; @@ -790,39 +795,41 @@ static int omap_venchw_probe(struct platform_device *pdev) venc_mem = platform_get_resource(venc.pdev, IORESOURCE_MEM, 0); if (!venc_mem) { DSSERR("can't get IORESOURCE_MEM VENC\n"); - r = -EINVAL; - goto err_ioremap; + return -EINVAL; } - venc.base = ioremap(venc_mem->start, resource_size(venc_mem)); + + venc.base = devm_ioremap(&pdev->dev, venc_mem->start, + resource_size(venc_mem)); if (!venc.base) { DSSERR("can't ioremap VENC\n"); - r = -ENOMEM; - goto err_ioremap; + return -ENOMEM; } r = venc_get_clocks(pdev); if (r) - goto err_get_clk; + return r; pm_runtime_enable(&pdev->dev); r = venc_runtime_get(); if (r) - goto err_get_venc; + goto err_runtime_get; rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff); dev_dbg(&pdev->dev, "OMAP VENC rev %d\n", rev_id); venc_runtime_put(); - return omap_dss_register_driver(&venc_driver); + r = omap_dss_register_driver(&venc_driver); + if (r) + goto err_reg_panel_driver; + + return 0; -err_get_venc: +err_reg_panel_driver: +err_runtime_get: pm_runtime_disable(&pdev->dev); venc_put_clocks(); -err_get_clk: - iounmap(venc.base); -err_ioremap: return r; } @@ -837,7 +844,6 @@ static int omap_venchw_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); venc_put_clocks(); - iounmap(venc.base); return 0; } diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c index 16ba6196f330..6a09ef87e14f 100644 --- a/drivers/video/omap2/omapfb/omapfb-ioctl.c +++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c @@ -215,7 +215,7 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) int r = 0, i; size_t size; - if (mi->type > OMAPFB_MEMTYPE_MAX) + if (mi->type != OMAPFB_MEMTYPE_SDRAM) return -EINVAL; size = PAGE_ALIGN(mi->size); diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c index ce158311ff59..b00db4068d21 100644 --- a/drivers/video/omap2/omapfb/omapfb-main.c +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -1399,7 +1399,7 @@ static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size, if (!paddr) { DBG("allocating %lu bytes for fb %d\n", size, ofbi->id); - r = omap_vram_alloc(OMAP_VRAM_MEMTYPE_SDRAM, size, &paddr); + r = omap_vram_alloc(size, &paddr); } else { DBG("reserving %lu bytes at %lx for fb %d\n", size, paddr, ofbi->id); @@ -1487,60 +1487,6 @@ static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size, return omapfb_alloc_fbmem(fbi, size, paddr); } -static enum omap_color_mode fb_format_to_dss_mode(enum omapfb_color_format fmt) -{ - enum omap_color_mode mode; - - switch (fmt) { - case OMAPFB_COLOR_RGB565: - mode = OMAP_DSS_COLOR_RGB16; - break; - case OMAPFB_COLOR_YUV422: - mode = OMAP_DSS_COLOR_YUV2; - break; - case OMAPFB_COLOR_CLUT_8BPP: - mode = OMAP_DSS_COLOR_CLUT8; - break; - case OMAPFB_COLOR_CLUT_4BPP: - mode = OMAP_DSS_COLOR_CLUT4; - break; - case OMAPFB_COLOR_CLUT_2BPP: - mode = OMAP_DSS_COLOR_CLUT2; - break; - case OMAPFB_COLOR_CLUT_1BPP: - mode = OMAP_DSS_COLOR_CLUT1; - break; - case OMAPFB_COLOR_RGB444: - mode = OMAP_DSS_COLOR_RGB12U; - break; - case OMAPFB_COLOR_YUY422: - mode = OMAP_DSS_COLOR_UYVY; - break; - case OMAPFB_COLOR_ARGB16: - mode = OMAP_DSS_COLOR_ARGB16; - break; - case OMAPFB_COLOR_RGB24U: - mode = OMAP_DSS_COLOR_RGB24U; - break; - case OMAPFB_COLOR_RGB24P: - mode = OMAP_DSS_COLOR_RGB24P; - break; - case OMAPFB_COLOR_ARGB32: - mode = OMAP_DSS_COLOR_ARGB32; - break; - case OMAPFB_COLOR_RGBA32: - mode = OMAP_DSS_COLOR_RGBA32; - break; - case OMAPFB_COLOR_RGBX32: - mode = OMAP_DSS_COLOR_RGBX32; - break; - default: - mode = -EINVAL; - } - - return mode; -} - static int omapfb_parse_vram_param(const char *param, int max_entries, unsigned long *sizes, unsigned long *paddrs) { @@ -1614,23 +1560,6 @@ static int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev) memset(&vram_paddrs, 0, sizeof(vram_paddrs)); } - if (fbdev->dev->platform_data) { - struct omapfb_platform_data *opd; - opd = fbdev->dev->platform_data; - for (i = 0; i < opd->mem_desc.region_cnt; ++i) { - if (!vram_sizes[i]) { - unsigned long size; - unsigned long paddr; - - size = opd->mem_desc.region[i].size; - paddr = opd->mem_desc.region[i].paddr; - - vram_sizes[i] = size; - vram_paddrs[i] = paddr; - } - } - } - for (i = 0; i < fbdev->num_fbs; i++) { /* allocate memory automatically only for fb0, or if * excplicitly defined with vram or plat data option */ @@ -1669,7 +1598,7 @@ int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type) int old_type = rg->type; int r; - if (type > OMAPFB_MEMTYPE_MAX) + if (type != OMAPFB_MEMTYPE_SDRAM) return -EINVAL; size = PAGE_ALIGN(size); @@ -1828,32 +1757,6 @@ static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi) var->rotate = def_rotate; - /* - * Check if there is a default color format set in the board file, - * and use this format instead the default deducted from the - * display bpp. - */ - if (fbdev->dev->platform_data) { - struct omapfb_platform_data *opd; - int id = ofbi->id; - - opd = fbdev->dev->platform_data; - if (opd->mem_desc.region[id].format_used) { - enum omap_color_mode mode; - enum omapfb_color_format format; - - format = opd->mem_desc.region[id].format; - mode = fb_format_to_dss_mode(format); - if (mode < 0) { - r = mode; - goto err; - } - r = dss_mode_to_fb_mode(mode, var); - if (r < 0) - goto err; - } - } - if (display) { u16 w, h; int rotation = (var->rotate + ofbi->rotation[0]) % 4; diff --git a/drivers/video/omap2/vram.c b/drivers/video/omap2/vram.c index 9441e2eb3dee..87e421e25afe 100644 --- a/drivers/video/omap2/vram.c +++ b/drivers/video/omap2/vram.c @@ -33,7 +33,6 @@ #include <asm/setup.h> -#include <plat/sram.h> #include <plat/vram.h> #include <plat/dma.h> @@ -43,10 +42,6 @@ #define DBG(format, ...) #endif -#define OMAP2_SRAM_START 0x40200000 -/* Maximum size, in reality this is smaller if SRAM is partially locked. */ -#define OMAP2_SRAM_SIZE 0xa0000 /* 640k */ - /* postponed regions are used to temporarily store region information at boot * time when we cannot yet allocate the region list */ #define MAX_POSTPONED_REGIONS 10 @@ -74,15 +69,6 @@ struct vram_region { static DEFINE_MUTEX(region_mutex); static LIST_HEAD(region_list); -static inline int region_mem_type(unsigned long paddr) -{ - if (paddr >= OMAP2_SRAM_START && - paddr < OMAP2_SRAM_START + OMAP2_SRAM_SIZE) - return OMAP_VRAM_MEMTYPE_SRAM; - else - return OMAP_VRAM_MEMTYPE_SDRAM; -} - static struct vram_region *omap_vram_create_region(unsigned long paddr, unsigned pages) { @@ -212,9 +198,6 @@ static int _omap_vram_reserve(unsigned long paddr, unsigned pages) DBG("checking region %lx %d\n", rm->paddr, rm->pages); - if (region_mem_type(rm->paddr) != region_mem_type(paddr)) - continue; - start = rm->paddr; end = start + (rm->pages << PAGE_SHIFT) - 1; if (start > paddr || end < paddr + size - 1) @@ -320,7 +303,7 @@ err: return r; } -static int _omap_vram_alloc(int mtype, unsigned pages, unsigned long *paddr) +static int _omap_vram_alloc(unsigned pages, unsigned long *paddr) { struct vram_region *rm; struct vram_alloc *alloc; @@ -330,9 +313,6 @@ static int _omap_vram_alloc(int mtype, unsigned pages, unsigned long *paddr) DBG("checking region %lx %d\n", rm->paddr, rm->pages); - if (region_mem_type(rm->paddr) != mtype) - continue; - start = rm->paddr; list_for_each_entry(alloc, &rm->alloc_list, list) { @@ -365,21 +345,21 @@ found: return -ENOMEM; } -int omap_vram_alloc(int mtype, size_t size, unsigned long *paddr) +int omap_vram_alloc(size_t size, unsigned long *paddr) { unsigned pages; int r; - BUG_ON(mtype > OMAP_VRAM_MEMTYPE_MAX || !size); + BUG_ON(!size); - DBG("alloc mem type %d size %d\n", mtype, size); + DBG("alloc mem size %d\n", size); size = PAGE_ALIGN(size); pages = size >> PAGE_SHIFT; mutex_lock(®ion_mutex); - r = _omap_vram_alloc(mtype, pages, paddr); + r = _omap_vram_alloc(pages, paddr); mutex_unlock(®ion_mutex); @@ -501,10 +481,6 @@ arch_initcall(omap_vram_init); /* boottime vram alloc stuff */ /* set from board file */ -static u32 omap_vram_sram_start __initdata; -static u32 omap_vram_sram_size __initdata; - -/* set from board file */ static u32 omap_vram_sdram_start __initdata; static u32 omap_vram_sdram_size __initdata; @@ -587,73 +563,8 @@ void __init omap_vram_reserve_sdram_memblock(void) pr_info("Reserving %u bytes SDRAM for VRAM\n", size); } -/* - * Called at sram init time, before anything is pushed to the SRAM stack. - * Because of the stack scheme, we will allocate everything from the - * start of the lowest address region to the end of SRAM. This will also - * include padding for page alignment and possible holes between regions. - * - * As opposed to the SDRAM case, we'll also do any dynamic allocations at - * this point, since the driver built as a module would have problem with - * freeing / reallocating the regions. - */ -unsigned long __init omap_vram_reserve_sram(unsigned long sram_pstart, - unsigned long sram_vstart, - unsigned long sram_size, - unsigned long pstart_avail, - unsigned long size_avail) -{ - unsigned long pend_avail; - unsigned long reserved; - u32 paddr; - u32 size; - - paddr = omap_vram_sram_start; - size = omap_vram_sram_size; - - if (!size) - return 0; - - reserved = 0; - pend_avail = pstart_avail + size_avail; - - if (!paddr) { - /* Dynamic allocation */ - if ((size_avail & PAGE_MASK) < size) { - pr_err("Not enough SRAM for VRAM\n"); - return 0; - } - size_avail = (size_avail - size) & PAGE_MASK; - paddr = pstart_avail + size_avail; - } - - if (paddr < sram_pstart || - paddr + size > sram_pstart + sram_size) { - pr_err("Illegal SRAM region for VRAM\n"); - return 0; - } - - /* Reserve everything above the start of the region. */ - if (pend_avail - paddr > reserved) - reserved = pend_avail - paddr; - size_avail = pend_avail - reserved - pstart_avail; - - omap_vram_add_region(paddr, size); - - if (reserved) - pr_info("Reserving %lu bytes SRAM for VRAM\n", reserved); - - return reserved; -} - void __init omap_vram_set_sdram_vram(u32 size, u32 start) { omap_vram_sdram_start = start; omap_vram_sdram_size = size; } - -void __init omap_vram_set_sram_vram(u32 size, u32 start) -{ - omap_vram_sram_start = start; - omap_vram_sram_size = size; -} diff --git a/drivers/video/pvr2fb.c b/drivers/video/pvr2fb.c index 3a3fdc62c75b..bcd44c32a2ed 100644 --- a/drivers/video/pvr2fb.c +++ b/drivers/video/pvr2fb.c @@ -895,7 +895,7 @@ static int __init pvr2fb_dc_init(void) #ifdef CONFIG_PVR2_DMA if (request_dma(pvr2dma, "pvr2") != 0) { - free_irq(HW_EVENT_VSYNC, 0); + free_irq(HW_EVENT_VSYNC, fb_info); return -EBUSY; } #endif @@ -914,7 +914,7 @@ static void __exit pvr2fb_dc_exit(void) currentpar->mmio_base = 0; } - free_irq(HW_EVENT_VSYNC, 0); + free_irq(HW_EVENT_VSYNC, fb_info); #ifdef CONFIG_PVR2_DMA free_dma(pvr2dma); #endif diff --git a/drivers/video/pxa168fb.c b/drivers/video/pxa168fb.c index 8384b941f6ba..f146089261f4 100644 --- a/drivers/video/pxa168fb.c +++ b/drivers/video/pxa168fb.c @@ -21,6 +21,7 @@ #include <linux/fb.h> #include <linux/delay.h> #include <linux/init.h> +#include <linux/io.h> #include <linux/ioport.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> @@ -670,7 +671,8 @@ static int __devinit pxa168fb_probe(struct platform_device *pdev) /* * Map LCD controller registers. */ - fbi->reg_base = ioremap_nocache(res->start, resource_size(res)); + fbi->reg_base = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); if (fbi->reg_base == NULL) { ret = -ENOMEM; goto failed_free_info; @@ -739,8 +741,8 @@ static int __devinit pxa168fb_probe(struct platform_device *pdev) /* * Register irq handler. */ - ret = request_irq(irq, pxa168fb_handle_irq, IRQF_SHARED, - info->fix.id, fbi); + ret = devm_request_irq(&pdev->dev, irq, pxa168fb_handle_irq, + IRQF_SHARED, info->fix.id, fbi); if (ret < 0) { dev_err(&pdev->dev, "unable to request IRQ\n"); ret = -ENXIO; @@ -759,14 +761,12 @@ static int __devinit pxa168fb_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "Failed to register pxa168-fb: %d\n", ret); ret = -ENXIO; - goto failed_free_irq; + goto failed_free_cmap; } platform_set_drvdata(pdev, fbi); return 0; -failed_free_irq: - free_irq(irq, fbi); failed_free_cmap: fb_dealloc_cmap(&info->cmap); failed_free_clk: @@ -808,13 +808,10 @@ static int __devexit pxa168fb_remove(struct platform_device *pdev) fb_dealloc_cmap(&info->cmap); irq = platform_get_irq(pdev, 0); - free_irq(irq, fbi); dma_free_writecombine(fbi->dev, PAGE_ALIGN(info->fix.smem_len), info->screen_base, info->fix.smem_start); - iounmap(fbi->reg_base); - clk_disable(fbi->clk); clk_put(fbi->clk); diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index 1d1e4f175e78..3f902557690e 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -54,6 +54,7 @@ #include <linux/mutex.h> #include <linux/kthread.h> #include <linux/freezer.h> +#include <linux/console.h> #include <mach/hardware.h> #include <asm/io.h> @@ -730,9 +731,12 @@ static int overlayfb_open(struct fb_info *info, int user) if (user == 0) return -ENODEV; - if (ofb->usage++ == 0) + if (ofb->usage++ == 0) { /* unblank the base framebuffer */ + console_lock(); fb_blank(&ofb->fbi->fb, FB_BLANK_UNBLANK); + console_unlock(); + } return 0; } @@ -1431,7 +1435,7 @@ static void pxafb_enable_controller(struct pxafb_info *fbi) pr_debug("reg_lccr3 0x%08x\n", (unsigned int) fbi->reg_lccr3); /* enable LCD controller clock */ - clk_enable(fbi->clk); + clk_prepare_enable(fbi->clk); if (fbi->lccr0 & LCCR0_LCDT) return; @@ -1471,7 +1475,7 @@ static void pxafb_disable_controller(struct pxafb_info *fbi) wait_for_completion_timeout(&fbi->disable_done, 200 * HZ / 1000); /* disable LCD controller clock */ - clk_disable(fbi->clk); + clk_disable_unprepare(fbi->clk); } /* diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c index 2f58cf9c813b..90df1a60bd16 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -1816,6 +1816,8 @@ static void __devinit riva_update_default_var(struct fb_var_screeninfo *var, specs->modedb, specs->modedb_len, NULL, 8); } else if (specs->modedb != NULL) { + /* get first mode in database as fallback */ + modedb = specs->modedb[0]; /* get preferred timing */ if (info->monspecs.misc & FB_MISC_1ST_DETAIL) { int i; @@ -1826,9 +1828,6 @@ static void __devinit riva_update_default_var(struct fb_var_screeninfo *var, break; } } - } else { - /* otherwise, get first mode in database */ - modedb = specs->modedb[0]; } var->bits_per_pixel = 8; riva_update_var(var, &modedb); diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index 0c63b69b6340..f3105160bf98 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -48,7 +48,8 @@ #undef writel #define writel(v, r) do { \ printk(KERN_DEBUG "%s: %08x => %p\n", __func__, (unsigned int)v, r); \ - __raw_writel(v, r); } while (0) + __raw_writel(v, r); \ +} while (0) #endif /* FB_S3C_DEBUG_REGWRITE */ /* irq_flags bits */ @@ -81,12 +82,14 @@ struct s3c_fb; * @palette: Address of palette memory, or 0 if none. * @has_prtcon: Set if has PRTCON register. * @has_shadowcon: Set if has SHADOWCON register. + * @has_blendcon: Set if has BLENDCON register. * @has_clksel: Set if VIDCON0 register has CLKSEL bit. + * @has_fixvclk: Set if VIDCON1 register has FIXVCLK bits. */ struct s3c_fb_variant { unsigned int is_2443:1; unsigned short nr_windows; - unsigned short vidtcon; + unsigned int vidtcon; unsigned short wincon; unsigned short winmap; unsigned short keycon; @@ -99,7 +102,9 @@ struct s3c_fb_variant { unsigned int has_prtcon:1; unsigned int has_shadowcon:1; + unsigned int has_blendcon:1; unsigned int has_clksel:1; + unsigned int has_fixvclk:1; }; /** @@ -186,7 +191,6 @@ struct s3c_fb_vsync { * struct s3c_fb - overall hardware state of the hardware * @slock: The spinlock protection for this data sturcture. * @dev: The device that we bound to, for printing, etc. - * @regs_res: The resource we claimed for the IO registers. * @bus_clk: The clk (hclk) feeding our interface and possibly pixclk. * @lcd_clk: The clk (sclk) feeding pixclk. * @regs: The mapped hardware registers. @@ -202,7 +206,6 @@ struct s3c_fb_vsync { struct s3c_fb { spinlock_t slock; struct device *dev; - struct resource *regs_res; struct clk *bus_clk; struct clk *lcd_clk; void __iomem *regs; @@ -565,7 +568,9 @@ static int s3c_fb_set_par(struct fb_info *info) writel(data, regs + sfb->variant.vidtcon + 4); data = VIDTCON2_LINEVAL(var->yres - 1) | - VIDTCON2_HOZVAL(var->xres - 1); + VIDTCON2_HOZVAL(var->xres - 1) | + VIDTCON2_LINEVAL_E(var->yres - 1) | + VIDTCON2_HOZVAL_E(var->xres - 1); writel(data, regs + sfb->variant.vidtcon + 8); } @@ -581,17 +586,23 @@ static int s3c_fb_set_par(struct fb_info *info) pagewidth = (var->xres * var->bits_per_pixel) >> 3; data = VIDW_BUF_SIZE_OFFSET(info->fix.line_length - pagewidth) | - VIDW_BUF_SIZE_PAGEWIDTH(pagewidth); + VIDW_BUF_SIZE_PAGEWIDTH(pagewidth) | + VIDW_BUF_SIZE_OFFSET_E(info->fix.line_length - pagewidth) | + VIDW_BUF_SIZE_PAGEWIDTH_E(pagewidth); writel(data, regs + sfb->variant.buf_size + (win_no * 4)); /* write 'OSD' registers to control position of framebuffer */ - data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0); + data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0) | + VIDOSDxA_TOPLEFT_X_E(0) | VIDOSDxA_TOPLEFT_Y_E(0); writel(data, regs + VIDOSD_A(win_no, sfb->variant)); data = VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(var->bits_per_pixel, var->xres - 1)) | - VIDOSDxB_BOTRIGHT_Y(var->yres - 1); + VIDOSDxB_BOTRIGHT_Y(var->yres - 1) | + VIDOSDxB_BOTRIGHT_X_E(s3c_fb_align_word(var->bits_per_pixel, + var->xres - 1)) | + VIDOSDxB_BOTRIGHT_Y_E(var->yres - 1); writel(data, regs + VIDOSD_B(win_no, sfb->variant)); @@ -692,6 +703,17 @@ static int s3c_fb_set_par(struct fb_info *info) writel(data, regs + sfb->variant.wincon + (win_no * 4)); writel(0x0, regs + sfb->variant.winmap + (win_no * 4)); + /* Set alpha value width */ + if (sfb->variant.has_blendcon) { + data = readl(sfb->regs + BLENDCON); + data &= ~BLENDCON_NEW_MASK; + if (var->transp.length > 4) + data |= BLENDCON_NEW_8BIT_ALPHA_VALUE; + else + data |= BLENDCON_NEW_4BIT_ALPHA_VALUE; + writel(data, sfb->regs + BLENDCON); + } + shadow_protect_win(win, 0); pm_runtime_put_sync(sfb->dev); @@ -1346,6 +1368,7 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) struct resource *res; int win; int ret = 0; + u32 reg; platid = platform_get_device_id(pdev); fbdrv = (struct s3c_fb_driverdata *)platid->driver_data; @@ -1361,7 +1384,7 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) return -EINVAL; } - sfb = kzalloc(sizeof(struct s3c_fb), GFP_KERNEL); + sfb = devm_kzalloc(dev, sizeof(struct s3c_fb), GFP_KERNEL); if (!sfb) { dev_err(dev, "no memory for framebuffers\n"); return -ENOMEM; @@ -1404,33 +1427,25 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) goto err_lcd_clk; } - sfb->regs_res = request_mem_region(res->start, resource_size(res), - dev_name(dev)); - if (!sfb->regs_res) { - dev_err(dev, "failed to claim register region\n"); - ret = -ENOENT; - goto err_lcd_clk; - } - - sfb->regs = ioremap(res->start, resource_size(res)); + sfb->regs = devm_request_and_ioremap(dev, res); if (!sfb->regs) { dev_err(dev, "failed to map registers\n"); ret = -ENXIO; - goto err_req_region; + goto err_lcd_clk; } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { dev_err(dev, "failed to acquire irq resource\n"); ret = -ENOENT; - goto err_ioremap; + goto err_lcd_clk; } sfb->irq_no = res->start; - ret = request_irq(sfb->irq_no, s3c_fb_irq, + ret = devm_request_irq(dev, sfb->irq_no, s3c_fb_irq, 0, "s3c_fb", sfb); if (ret) { dev_err(dev, "irq request failed\n"); - goto err_ioremap; + goto err_lcd_clk; } dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs); @@ -1444,6 +1459,14 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) writel(pd->vidcon1, sfb->regs + VIDCON1); + /* set video clock running at under-run */ + if (sfb->variant.has_fixvclk) { + reg = readl(sfb->regs + VIDCON1); + reg &= ~VIDCON1_VCLK_MASK; + reg |= VIDCON1_VCLK_RUN; + writel(reg, sfb->regs + VIDCON1); + } + /* zero all windows before we do anything */ for (win = 0; win < fbdrv->variant.nr_windows; win++) @@ -1484,13 +1507,6 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) err_pm_runtime: pm_runtime_put_sync(sfb->dev); - free_irq(sfb->irq_no, sfb); - -err_ioremap: - iounmap(sfb->regs); - -err_req_region: - release_mem_region(sfb->regs_res->start, resource_size(sfb->regs_res)); err_lcd_clk: pm_runtime_disable(sfb->dev); @@ -1505,7 +1521,6 @@ err_bus_clk: clk_put(sfb->bus_clk); err_sfb: - kfree(sfb); return ret; } @@ -1527,10 +1542,6 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) if (sfb->windows[win]) s3c_fb_release_win(sfb, sfb->windows[win]); - free_irq(sfb->irq_no, sfb); - - iounmap(sfb->regs); - if (!sfb->variant.has_clksel) { clk_disable(sfb->lcd_clk); clk_put(sfb->lcd_clk); @@ -1539,12 +1550,9 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) clk_disable(sfb->bus_clk); clk_put(sfb->bus_clk); - release_mem_region(sfb->regs_res->start, resource_size(sfb->regs_res)); - pm_runtime_put_sync(sfb->dev); pm_runtime_disable(sfb->dev); - kfree(sfb); return 0; } @@ -1579,6 +1587,7 @@ static int s3c_fb_resume(struct device *dev) struct s3c_fb_platdata *pd = sfb->pdata; struct s3c_fb_win *win; int win_no; + u32 reg; clk_enable(sfb->bus_clk); @@ -1589,6 +1598,14 @@ static int s3c_fb_resume(struct device *dev) pd->setup_gpio(); writel(pd->vidcon1, sfb->regs + VIDCON1); + /* set video clock running at under-run */ + if (sfb->variant.has_fixvclk) { + reg = readl(sfb->regs + VIDCON1); + reg &= ~VIDCON1_VCLK_MASK; + reg |= VIDCON1_VCLK_RUN; + writel(reg, sfb->regs + VIDCON1); + } + /* zero all windows before we do anything */ for (win_no = 0; win_no < sfb->variant.nr_windows; win_no++) s3c_fb_clear_win(sfb, win_no); @@ -1819,6 +1836,7 @@ static struct s3c_fb_driverdata s3c_fb_data_s5pc100 = { }, .has_prtcon = 1, + .has_blendcon = 1, .has_clksel = 1, }, .win[0] = &s3c_fb_data_s5p_wins[0], @@ -1850,7 +1868,9 @@ static struct s3c_fb_driverdata s3c_fb_data_s5pv210 = { }, .has_shadowcon = 1, + .has_blendcon = 1, .has_clksel = 1, + .has_fixvclk = 1, }, .win[0] = &s3c_fb_data_s5p_wins[0], .win[1] = &s3c_fb_data_s5p_wins[1], @@ -1881,6 +1901,39 @@ static struct s3c_fb_driverdata s3c_fb_data_exynos4 = { }, .has_shadowcon = 1, + .has_blendcon = 1, + .has_fixvclk = 1, + }, + .win[0] = &s3c_fb_data_s5p_wins[0], + .win[1] = &s3c_fb_data_s5p_wins[1], + .win[2] = &s3c_fb_data_s5p_wins[2], + .win[3] = &s3c_fb_data_s5p_wins[3], + .win[4] = &s3c_fb_data_s5p_wins[4], +}; + +static struct s3c_fb_driverdata s3c_fb_data_exynos5 = { + .variant = { + .nr_windows = 5, + .vidtcon = VIDTCON0, + .wincon = WINCON(0), + .winmap = WINxMAP(0), + .keycon = WKEYCON, + .osd = VIDOSD_BASE, + .osd_stride = 16, + .buf_start = VIDW_BUF_START(0), + .buf_size = VIDW_BUF_SIZE(0), + .buf_end = VIDW_BUF_END(0), + + .palette = { + [0] = 0x2400, + [1] = 0x2800, + [2] = 0x2c00, + [3] = 0x3000, + [4] = 0x3400, + }, + .has_shadowcon = 1, + .has_blendcon = 1, + .has_fixvclk = 1, }, .win[0] = &s3c_fb_data_s5p_wins[0], .win[1] = &s3c_fb_data_s5p_wins[1], @@ -1944,6 +1997,9 @@ static struct s3c_fb_driverdata s3c_fb_data_s5p64x0 = { [1] = 0x2800, [2] = 0x2c00, }, + + .has_blendcon = 1, + .has_fixvclk = 1, }, .win[0] = &s3c_fb_data_s5p_wins[0], .win[1] = &s3c_fb_data_s5p_wins[1], @@ -1964,6 +2020,9 @@ static struct platform_device_id s3c_fb_driver_ids[] = { .name = "exynos4-fb", .driver_data = (unsigned long)&s3c_fb_data_exynos4, }, { + .name = "exynos5-fb", + .driver_data = (unsigned long)&s3c_fb_data_exynos5, + }, { .name = "s3c2443-fb", .driver_data = (unsigned long)&s3c_fb_data_s3c2443, }, { diff --git a/drivers/video/sa1100fb.c b/drivers/video/sa1100fb.c index 98d55d0e2da5..b6325848ad61 100644 --- a/drivers/video/sa1100fb.c +++ b/drivers/video/sa1100fb.c @@ -173,282 +173,48 @@ #include <linux/init.h> #include <linux/ioport.h> #include <linux/cpufreq.h> +#include <linux/gpio.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/mutex.h> #include <linux/io.h> +#include <video/sa1100fb.h> + #include <mach/hardware.h> #include <asm/mach-types.h> -#include <mach/assabet.h> #include <mach/shannon.h> /* - * debugging? - */ -#define DEBUG 0 -/* * Complain if VAR is out of range. */ #define DEBUG_VAR 1 -#undef ASSABET_PAL_VIDEO - #include "sa1100fb.h" -extern void (*sa1100fb_backlight_power)(int on); -extern void (*sa1100fb_lcd_power)(int on); - -static struct sa1100fb_rgb rgb_4 = { +static const struct sa1100fb_rgb rgb_4 = { .red = { .offset = 0, .length = 4, }, .green = { .offset = 0, .length = 4, }, .blue = { .offset = 0, .length = 4, }, .transp = { .offset = 0, .length = 0, }, }; -static struct sa1100fb_rgb rgb_8 = { +static const struct sa1100fb_rgb rgb_8 = { .red = { .offset = 0, .length = 8, }, .green = { .offset = 0, .length = 8, }, .blue = { .offset = 0, .length = 8, }, .transp = { .offset = 0, .length = 0, }, }; -static struct sa1100fb_rgb def_rgb_16 = { +static const struct sa1100fb_rgb def_rgb_16 = { .red = { .offset = 11, .length = 5, }, .green = { .offset = 5, .length = 6, }, .blue = { .offset = 0, .length = 5, }, .transp = { .offset = 0, .length = 0, }, }; -#ifdef CONFIG_SA1100_ASSABET -#ifndef ASSABET_PAL_VIDEO -/* - * The assabet uses a sharp LQ039Q2DS54 LCD module. It is actually - * takes an RGB666 signal, but we provide it with an RGB565 signal - * instead (def_rgb_16). - */ -static struct sa1100fb_mach_info lq039q2ds54_info __initdata = { - .pixclock = 171521, .bpp = 16, - .xres = 320, .yres = 240, - - .hsync_len = 5, .vsync_len = 1, - .left_margin = 61, .upper_margin = 3, - .right_margin = 9, .lower_margin = 0, - - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2), -}; -#else -static struct sa1100fb_mach_info pal_info __initdata = { - .pixclock = 67797, .bpp = 16, - .xres = 640, .yres = 512, - - .hsync_len = 64, .vsync_len = 6, - .left_margin = 125, .upper_margin = 70, - .right_margin = 115, .lower_margin = 36, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(512), -}; -#endif -#endif - -#ifdef CONFIG_SA1100_H3600 -static struct sa1100fb_mach_info h3600_info __initdata = { - .pixclock = 174757, .bpp = 16, - .xres = 320, .yres = 240, - - .hsync_len = 3, .vsync_len = 3, - .left_margin = 12, .upper_margin = 10, - .right_margin = 17, .lower_margin = 1, - - .cmap_static = 1, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2), -}; - -static struct sa1100fb_rgb h3600_rgb_16 = { - .red = { .offset = 12, .length = 4, }, - .green = { .offset = 7, .length = 4, }, - .blue = { .offset = 1, .length = 4, }, - .transp = { .offset = 0, .length = 0, }, -}; -#endif - -#ifdef CONFIG_SA1100_H3100 -static struct sa1100fb_mach_info h3100_info __initdata = { - .pixclock = 406977, .bpp = 4, - .xres = 320, .yres = 240, - - .hsync_len = 26, .vsync_len = 41, - .left_margin = 4, .upper_margin = 0, - .right_margin = 4, .lower_margin = 0, - - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - .cmap_greyscale = 1, - .cmap_inverse = 1, - - .lccr0 = LCCR0_Mono | LCCR0_4PixMono | LCCR0_Sngl | LCCR0_Pas, - .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2), -}; -#endif - -#ifdef CONFIG_SA1100_COLLIE -static struct sa1100fb_mach_info collie_info __initdata = { - .pixclock = 171521, .bpp = 16, - .xres = 320, .yres = 240, - - .hsync_len = 5, .vsync_len = 1, - .left_margin = 11, .upper_margin = 2, - .right_margin = 30, .lower_margin = 0, - - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2), -}; -#endif - -#ifdef LART_GREY_LCD -static struct sa1100fb_mach_info lart_grey_info __initdata = { - .pixclock = 150000, .bpp = 4, - .xres = 320, .yres = 240, - - .hsync_len = 1, .vsync_len = 1, - .left_margin = 4, .upper_margin = 0, - .right_margin = 2, .lower_margin = 0, - - .cmap_greyscale = 1, - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - - .lccr0 = LCCR0_Mono | LCCR0_Sngl | LCCR0_Pas | LCCR0_4PixMono, - .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(512), -}; -#endif -#ifdef LART_COLOR_LCD -static struct sa1100fb_mach_info lart_color_info __initdata = { - .pixclock = 150000, .bpp = 16, - .xres = 320, .yres = 240, - - .hsync_len = 2, .vsync_len = 3, - .left_margin = 69, .upper_margin = 14, - .right_margin = 8, .lower_margin = 4, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnH | LCCR3_PixFlEdg | LCCR3_ACBsDiv(512), -}; -#endif -#ifdef LART_VIDEO_OUT -static struct sa1100fb_mach_info lart_video_info __initdata = { - .pixclock = 39721, .bpp = 16, - .xres = 640, .yres = 480, - - .hsync_len = 95, .vsync_len = 2, - .left_margin = 40, .upper_margin = 32, - .right_margin = 24, .lower_margin = 11, - - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnL | LCCR3_PixFlEdg | LCCR3_ACBsDiv(512), -}; -#endif - -#ifdef LART_KIT01_LCD -static struct sa1100fb_mach_info lart_kit01_info __initdata = { - .pixclock = 63291, .bpp = 16, - .xres = 640, .yres = 480, - - .hsync_len = 64, .vsync_len = 3, - .left_margin = 122, .upper_margin = 45, - .right_margin = 10, .lower_margin = 10, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnH | LCCR3_PixFlEdg -}; -#endif - -#ifdef CONFIG_SA1100_SHANNON -static struct sa1100fb_mach_info shannon_info __initdata = { - .pixclock = 152500, .bpp = 8, - .xres = 640, .yres = 480, - - .hsync_len = 4, .vsync_len = 3, - .left_margin = 2, .upper_margin = 0, - .right_margin = 1, .lower_margin = 0, - - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - - .lccr0 = LCCR0_Color | LCCR0_Dual | LCCR0_Pas, - .lccr3 = LCCR3_ACBsDiv(512), -}; -#endif - -static struct sa1100fb_mach_info * __init -sa1100fb_get_machine_info(struct sa1100fb_info *fbi) -{ - struct sa1100fb_mach_info *inf = NULL; - - /* - * R G B T - * default {11,5}, { 5,6}, { 0,5}, { 0,0} - * h3600 {12,4}, { 7,4}, { 1,4}, { 0,0} - * freebird { 8,4}, { 4,4}, { 0,4}, {12,4} - */ -#ifdef CONFIG_SA1100_ASSABET - if (machine_is_assabet()) { -#ifndef ASSABET_PAL_VIDEO - inf = &lq039q2ds54_info; -#else - inf = &pal_info; -#endif - } -#endif -#ifdef CONFIG_SA1100_H3100 - if (machine_is_h3100()) { - inf = &h3100_info; - } -#endif -#ifdef CONFIG_SA1100_H3600 - if (machine_is_h3600()) { - inf = &h3600_info; - fbi->rgb[RGB_16] = &h3600_rgb_16; - } -#endif -#ifdef CONFIG_SA1100_COLLIE - if (machine_is_collie()) { - inf = &collie_info; - } -#endif -#ifdef CONFIG_SA1100_LART - if (machine_is_lart()) { -#ifdef LART_GREY_LCD - inf = &lart_grey_info; -#endif -#ifdef LART_COLOR_LCD - inf = &lart_color_info; -#endif -#ifdef LART_VIDEO_OUT - inf = &lart_video_info; -#endif -#ifdef LART_KIT01_LCD - inf = &lart_kit01_info; -#endif - } -#endif -#ifdef CONFIG_SA1100_SHANNON - if (machine_is_shannon()) { - inf = &shannon_info; - } -#endif - return inf; -} - static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_info *); static void set_ctrlr_state(struct sa1100fb_info *fbi, u_int state); @@ -533,7 +299,7 @@ sa1100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, * is what you poke into the framebuffer to produce the * colour you requested. */ - if (fbi->cmap_inverse) { + if (fbi->inf->cmap_inverse) { red = 0xffff - red; green = 0xffff - green; blue = 0xffff - blue; @@ -607,14 +373,14 @@ sa1100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) var->xres = MIN_XRES; if (var->yres < MIN_YRES) var->yres = MIN_YRES; - if (var->xres > fbi->max_xres) - var->xres = fbi->max_xres; - if (var->yres > fbi->max_yres) - var->yres = fbi->max_yres; + if (var->xres > fbi->inf->xres) + var->xres = fbi->inf->xres; + if (var->yres > fbi->inf->yres) + var->yres = fbi->inf->yres; var->xres_virtual = max(var->xres_virtual, var->xres); var->yres_virtual = max(var->yres_virtual, var->yres); - DPRINTK("var->bits_per_pixel=%d\n", var->bits_per_pixel); + dev_dbg(fbi->dev, "var->bits_per_pixel=%d\n", var->bits_per_pixel); switch (var->bits_per_pixel) { case 4: rgbidx = RGB_4; @@ -638,16 +404,16 @@ sa1100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) var->blue = fbi->rgb[rgbidx]->blue; var->transp = fbi->rgb[rgbidx]->transp; - DPRINTK("RGBT length = %d:%d:%d:%d\n", + dev_dbg(fbi->dev, "RGBT length = %d:%d:%d:%d\n", var->red.length, var->green.length, var->blue.length, var->transp.length); - DPRINTK("RGBT offset = %d:%d:%d:%d\n", + dev_dbg(fbi->dev, "RGBT offset = %d:%d:%d:%d\n", var->red.offset, var->green.offset, var->blue.offset, var->transp.offset); #ifdef CONFIG_CPU_FREQ - printk(KERN_DEBUG "dma period = %d ps, clock = %d kHz\n", + dev_dbg(fbi->dev, "dma period = %d ps, clock = %d kHz\n", sa1100fb_display_dma_period(var), cpufreq_get(smp_processor_id())); #endif @@ -655,22 +421,10 @@ sa1100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) return 0; } -static inline void sa1100fb_set_truecolor(u_int is_true_color) +static void sa1100fb_set_visual(struct sa1100fb_info *fbi, u32 visual) { - if (machine_is_assabet()) { -#if 1 // phase 4 or newer Assabet's - if (is_true_color) - ASSABET_BCR_set(ASSABET_BCR_LCD_12RGB); - else - ASSABET_BCR_clear(ASSABET_BCR_LCD_12RGB); -#else - // older Assabet's - if (is_true_color) - ASSABET_BCR_clear(ASSABET_BCR_LCD_12RGB); - else - ASSABET_BCR_set(ASSABET_BCR_LCD_12RGB); -#endif - } + if (fbi->inf->set_visual) + fbi->inf->set_visual(visual); } /* @@ -683,11 +437,11 @@ static int sa1100fb_set_par(struct fb_info *info) struct fb_var_screeninfo *var = &info->var; unsigned long palette_mem_size; - DPRINTK("set_par\n"); + dev_dbg(fbi->dev, "set_par\n"); if (var->bits_per_pixel == 16) fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR; - else if (!fbi->cmap_static) + else if (!fbi->inf->cmap_static) fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; else { /* @@ -704,7 +458,7 @@ static int sa1100fb_set_par(struct fb_info *info) palette_mem_size = fbi->palette_size * sizeof(u16); - DPRINTK("palette_mem_size = 0x%08lx\n", (u_long) palette_mem_size); + dev_dbg(fbi->dev, "palette_mem_size = 0x%08lx\n", palette_mem_size); fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size); fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size; @@ -712,7 +466,7 @@ static int sa1100fb_set_par(struct fb_info *info) /* * Set (any) board control register to handle new color depth */ - sa1100fb_set_truecolor(fbi->fb.fix.visual == FB_VISUAL_TRUECOLOR); + sa1100fb_set_visual(fbi, fbi->fb.fix.visual); sa1100fb_activate_var(var, fbi); return 0; @@ -728,7 +482,7 @@ sa1100fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, /* * Make sure the user isn't doing something stupid. */ - if (!kspc && (fbi->fb.var.bits_per_pixel == 16 || fbi->cmap_static)) + if (!kspc && (fbi->fb.var.bits_per_pixel == 16 || fbi->inf->cmap_static)) return -EINVAL; return gen_set_cmap(cmap, kspc, con, info); @@ -775,7 +529,7 @@ static int sa1100fb_blank(int blank, struct fb_info *info) struct sa1100fb_info *fbi = (struct sa1100fb_info *)info; int i; - DPRINTK("sa1100fb_blank: blank=%d\n", blank); + dev_dbg(fbi->dev, "sa1100fb_blank: blank=%d\n", blank); switch (blank) { case FB_BLANK_POWERDOWN: @@ -863,43 +617,43 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_ u_int half_screen_size, yres, pcd; u_long flags; - DPRINTK("Configuring SA1100 LCD\n"); + dev_dbg(fbi->dev, "Configuring SA1100 LCD\n"); - DPRINTK("var: xres=%d hslen=%d lm=%d rm=%d\n", + dev_dbg(fbi->dev, "var: xres=%d hslen=%d lm=%d rm=%d\n", var->xres, var->hsync_len, var->left_margin, var->right_margin); - DPRINTK("var: yres=%d vslen=%d um=%d bm=%d\n", + dev_dbg(fbi->dev, "var: yres=%d vslen=%d um=%d bm=%d\n", var->yres, var->vsync_len, var->upper_margin, var->lower_margin); #if DEBUG_VAR if (var->xres < 16 || var->xres > 1024) - printk(KERN_ERR "%s: invalid xres %d\n", + dev_err(fbi->dev, "%s: invalid xres %d\n", fbi->fb.fix.id, var->xres); if (var->hsync_len < 1 || var->hsync_len > 64) - printk(KERN_ERR "%s: invalid hsync_len %d\n", + dev_err(fbi->dev, "%s: invalid hsync_len %d\n", fbi->fb.fix.id, var->hsync_len); if (var->left_margin < 1 || var->left_margin > 255) - printk(KERN_ERR "%s: invalid left_margin %d\n", + dev_err(fbi->dev, "%s: invalid left_margin %d\n", fbi->fb.fix.id, var->left_margin); if (var->right_margin < 1 || var->right_margin > 255) - printk(KERN_ERR "%s: invalid right_margin %d\n", + dev_err(fbi->dev, "%s: invalid right_margin %d\n", fbi->fb.fix.id, var->right_margin); if (var->yres < 1 || var->yres > 1024) - printk(KERN_ERR "%s: invalid yres %d\n", + dev_err(fbi->dev, "%s: invalid yres %d\n", fbi->fb.fix.id, var->yres); if (var->vsync_len < 1 || var->vsync_len > 64) - printk(KERN_ERR "%s: invalid vsync_len %d\n", + dev_err(fbi->dev, "%s: invalid vsync_len %d\n", fbi->fb.fix.id, var->vsync_len); if (var->upper_margin < 0 || var->upper_margin > 255) - printk(KERN_ERR "%s: invalid upper_margin %d\n", + dev_err(fbi->dev, "%s: invalid upper_margin %d\n", fbi->fb.fix.id, var->upper_margin); if (var->lower_margin < 0 || var->lower_margin > 255) - printk(KERN_ERR "%s: invalid lower_margin %d\n", + dev_err(fbi->dev, "%s: invalid lower_margin %d\n", fbi->fb.fix.id, var->lower_margin); #endif - new_regs.lccr0 = fbi->lccr0 | + new_regs.lccr0 = fbi->inf->lccr0 | LCCR0_LEN | LCCR0_LDM | LCCR0_BAM | LCCR0_ERM | LCCR0_LtlEnd | LCCR0_DMADel(0); @@ -914,7 +668,7 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_ * the YRES parameter. */ yres = var->yres; - if (fbi->lccr0 & LCCR0_Dual) + if (fbi->inf->lccr0 & LCCR0_Dual) yres /= 2; new_regs.lccr2 = @@ -924,14 +678,14 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_ LCCR2_EndFrmDel(var->lower_margin); pcd = get_pcd(var->pixclock, cpufreq_get(0)); - new_regs.lccr3 = LCCR3_PixClkDiv(pcd) | fbi->lccr3 | + new_regs.lccr3 = LCCR3_PixClkDiv(pcd) | fbi->inf->lccr3 | (var->sync & FB_SYNC_HOR_HIGH_ACT ? LCCR3_HorSnchH : LCCR3_HorSnchL) | (var->sync & FB_SYNC_VERT_HIGH_ACT ? LCCR3_VrtSnchH : LCCR3_VrtSnchL); - DPRINTK("nlccr0 = 0x%08lx\n", new_regs.lccr0); - DPRINTK("nlccr1 = 0x%08lx\n", new_regs.lccr1); - DPRINTK("nlccr2 = 0x%08lx\n", new_regs.lccr2); - DPRINTK("nlccr3 = 0x%08lx\n", new_regs.lccr3); + dev_dbg(fbi->dev, "nlccr0 = 0x%08lx\n", new_regs.lccr0); + dev_dbg(fbi->dev, "nlccr1 = 0x%08lx\n", new_regs.lccr1); + dev_dbg(fbi->dev, "nlccr2 = 0x%08lx\n", new_regs.lccr2); + dev_dbg(fbi->dev, "nlccr3 = 0x%08lx\n", new_regs.lccr3); half_screen_size = var->bits_per_pixel; half_screen_size = half_screen_size * var->xres * var->yres / 16; @@ -951,9 +705,12 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_ * Only update the registers if the controller is enabled * and something has changed. */ - if ((LCCR0 != fbi->reg_lccr0) || (LCCR1 != fbi->reg_lccr1) || - (LCCR2 != fbi->reg_lccr2) || (LCCR3 != fbi->reg_lccr3) || - (DBAR1 != fbi->dbar1) || (DBAR2 != fbi->dbar2)) + if (readl_relaxed(fbi->base + LCCR0) != fbi->reg_lccr0 || + readl_relaxed(fbi->base + LCCR1) != fbi->reg_lccr1 || + readl_relaxed(fbi->base + LCCR2) != fbi->reg_lccr2 || + readl_relaxed(fbi->base + LCCR3) != fbi->reg_lccr3 || + readl_relaxed(fbi->base + DBAR1) != fbi->dbar1 || + readl_relaxed(fbi->base + DBAR2) != fbi->dbar2) sa1100fb_schedule_work(fbi, C_REENABLE); return 0; @@ -967,18 +724,18 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_ */ static inline void __sa1100fb_backlight_power(struct sa1100fb_info *fbi, int on) { - DPRINTK("backlight o%s\n", on ? "n" : "ff"); + dev_dbg(fbi->dev, "backlight o%s\n", on ? "n" : "ff"); - if (sa1100fb_backlight_power) - sa1100fb_backlight_power(on); + if (fbi->inf->backlight_power) + fbi->inf->backlight_power(on); } static inline void __sa1100fb_lcd_power(struct sa1100fb_info *fbi, int on) { - DPRINTK("LCD power o%s\n", on ? "n" : "ff"); + dev_dbg(fbi->dev, "LCD power o%s\n", on ? "n" : "ff"); - if (sa1100fb_lcd_power) - sa1100fb_lcd_power(on); + if (fbi->inf->lcd_power) + fbi->inf->lcd_power(on); } static void sa1100fb_setup_gpio(struct sa1100fb_info *fbi) @@ -1008,14 +765,25 @@ static void sa1100fb_setup_gpio(struct sa1100fb_info *fbi) } if (mask) { + unsigned long flags; + + /* + * SA-1100 requires the GPIO direction register set + * appropriately for the alternate function. Hence + * we set it here via bitmask rather than excessive + * fiddling via the GPIO subsystem - and even then + * we'll still have to deal with GAFR. + */ + local_irq_save(flags); GPDR |= mask; GAFR |= mask; + local_irq_restore(flags); } } static void sa1100fb_enable_controller(struct sa1100fb_info *fbi) { - DPRINTK("Enabling LCD controller\n"); + dev_dbg(fbi->dev, "Enabling LCD controller\n"); /* * Make sure the mode bits are present in the first palette entry @@ -1024,43 +792,46 @@ static void sa1100fb_enable_controller(struct sa1100fb_info *fbi) fbi->palette_cpu[0] |= palette_pbs(&fbi->fb.var); /* Sequence from 11.7.10 */ - LCCR3 = fbi->reg_lccr3; - LCCR2 = fbi->reg_lccr2; - LCCR1 = fbi->reg_lccr1; - LCCR0 = fbi->reg_lccr0 & ~LCCR0_LEN; - DBAR1 = fbi->dbar1; - DBAR2 = fbi->dbar2; - LCCR0 |= LCCR0_LEN; - - if (machine_is_shannon()) { - GPDR |= SHANNON_GPIO_DISP_EN; - GPSR |= SHANNON_GPIO_DISP_EN; - } - - DPRINTK("DBAR1 = 0x%08x\n", DBAR1); - DPRINTK("DBAR2 = 0x%08x\n", DBAR2); - DPRINTK("LCCR0 = 0x%08x\n", LCCR0); - DPRINTK("LCCR1 = 0x%08x\n", LCCR1); - DPRINTK("LCCR2 = 0x%08x\n", LCCR2); - DPRINTK("LCCR3 = 0x%08x\n", LCCR3); + writel_relaxed(fbi->reg_lccr3, fbi->base + LCCR3); + writel_relaxed(fbi->reg_lccr2, fbi->base + LCCR2); + writel_relaxed(fbi->reg_lccr1, fbi->base + LCCR1); + writel_relaxed(fbi->reg_lccr0 & ~LCCR0_LEN, fbi->base + LCCR0); + writel_relaxed(fbi->dbar1, fbi->base + DBAR1); + writel_relaxed(fbi->dbar2, fbi->base + DBAR2); + writel_relaxed(fbi->reg_lccr0 | LCCR0_LEN, fbi->base + LCCR0); + + if (machine_is_shannon()) + gpio_set_value(SHANNON_GPIO_DISP_EN, 1); + + dev_dbg(fbi->dev, "DBAR1: 0x%08x\n", readl_relaxed(fbi->base + DBAR1)); + dev_dbg(fbi->dev, "DBAR2: 0x%08x\n", readl_relaxed(fbi->base + DBAR2)); + dev_dbg(fbi->dev, "LCCR0: 0x%08x\n", readl_relaxed(fbi->base + LCCR0)); + dev_dbg(fbi->dev, "LCCR1: 0x%08x\n", readl_relaxed(fbi->base + LCCR1)); + dev_dbg(fbi->dev, "LCCR2: 0x%08x\n", readl_relaxed(fbi->base + LCCR2)); + dev_dbg(fbi->dev, "LCCR3: 0x%08x\n", readl_relaxed(fbi->base + LCCR3)); } static void sa1100fb_disable_controller(struct sa1100fb_info *fbi) { DECLARE_WAITQUEUE(wait, current); + u32 lccr0; - DPRINTK("Disabling LCD controller\n"); + dev_dbg(fbi->dev, "Disabling LCD controller\n"); - if (machine_is_shannon()) { - GPCR |= SHANNON_GPIO_DISP_EN; - } + if (machine_is_shannon()) + gpio_set_value(SHANNON_GPIO_DISP_EN, 0); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&fbi->ctrlr_wait, &wait); - LCSR = 0xffffffff; /* Clear LCD Status Register */ - LCCR0 &= ~LCCR0_LDM; /* Enable LCD Disable Done Interrupt */ - LCCR0 &= ~LCCR0_LEN; /* Disable LCD Controller */ + /* Clear LCD Status Register */ + writel_relaxed(~0, fbi->base + LCSR); + + lccr0 = readl_relaxed(fbi->base + LCCR0); + lccr0 &= ~LCCR0_LDM; /* Enable LCD Disable Done Interrupt */ + writel_relaxed(lccr0, fbi->base + LCCR0); + lccr0 &= ~LCCR0_LEN; /* Disable LCD Controller */ + writel_relaxed(lccr0, fbi->base + LCCR0); schedule_timeout(20 * HZ / 1000); remove_wait_queue(&fbi->ctrlr_wait, &wait); @@ -1072,14 +843,15 @@ static void sa1100fb_disable_controller(struct sa1100fb_info *fbi) static irqreturn_t sa1100fb_handle_irq(int irq, void *dev_id) { struct sa1100fb_info *fbi = dev_id; - unsigned int lcsr = LCSR; + unsigned int lcsr = readl_relaxed(fbi->base + LCSR); if (lcsr & LCSR_LDD) { - LCCR0 |= LCCR0_LDM; + u32 lccr0 = readl_relaxed(fbi->base + LCCR0) | LCCR0_LDM; + writel_relaxed(lccr0, fbi->base + LCCR0); wake_up(&fbi->ctrlr_wait); } - LCSR = lcsr; + writel_relaxed(lcsr, fbi->base + LCSR); return IRQ_HANDLED; } @@ -1268,7 +1040,7 @@ sa1100fb_freq_policy(struct notifier_block *nb, unsigned long val, switch (val) { case CPUFREQ_ADJUST: case CPUFREQ_INCOMPATIBLE: - printk(KERN_DEBUG "min dma period: %d ps, " + dev_dbg(fbi->dev, "min dma period: %d ps, " "new clock %d kHz\n", sa1100fb_min_dma_period(fbi), policy->max); /* todo: fill in min/max values */ @@ -1318,7 +1090,7 @@ static int sa1100fb_resume(struct platform_device *dev) * cache. Once this area is remapped, all virtual memory * access to the video memory should occur at the new region. */ -static int __init sa1100fb_map_video_memory(struct sa1100fb_info *fbi) +static int __devinit sa1100fb_map_video_memory(struct sa1100fb_info *fbi) { /* * We reserve one page for the palette, plus the size @@ -1344,7 +1116,7 @@ static int __init sa1100fb_map_video_memory(struct sa1100fb_info *fbi) } /* Fake monspecs to fill in fbinfo structure */ -static struct fb_monspecs monspecs __initdata = { +static struct fb_monspecs monspecs __devinitdata = { .hfmin = 30000, .hfmax = 70000, .vfmin = 50, @@ -1352,10 +1124,11 @@ static struct fb_monspecs monspecs __initdata = { }; -static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev) +static struct sa1100fb_info * __devinit sa1100fb_init_fbinfo(struct device *dev) { - struct sa1100fb_mach_info *inf; + struct sa1100fb_mach_info *inf = dev->platform_data; struct sa1100fb_info *fbi; + unsigned i; fbi = kmalloc(sizeof(struct sa1100fb_info) + sizeof(u32) * 16, GFP_KERNEL); @@ -1390,8 +1163,6 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev) fbi->rgb[RGB_8] = &rgb_8; fbi->rgb[RGB_16] = &def_rgb_16; - inf = sa1100fb_get_machine_info(fbi); - /* * People just don't seem to get this. We don't support * anything but correct entries now, so panic if someone @@ -1402,13 +1173,10 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev) panic("sa1100fb error: invalid LCCR3 fields set or zero " "pixclock."); - fbi->max_xres = inf->xres; fbi->fb.var.xres = inf->xres; fbi->fb.var.xres_virtual = inf->xres; - fbi->max_yres = inf->yres; fbi->fb.var.yres = inf->yres; fbi->fb.var.yres_virtual = inf->yres; - fbi->max_bpp = inf->bpp; fbi->fb.var.bits_per_pixel = inf->bpp; fbi->fb.var.pixclock = inf->pixclock; fbi->fb.var.hsync_len = inf->hsync_len; @@ -1419,14 +1187,16 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev) fbi->fb.var.lower_margin = inf->lower_margin; fbi->fb.var.sync = inf->sync; fbi->fb.var.grayscale = inf->cmap_greyscale; - fbi->cmap_inverse = inf->cmap_inverse; - fbi->cmap_static = inf->cmap_static; - fbi->lccr0 = inf->lccr0; - fbi->lccr3 = inf->lccr3; fbi->state = C_STARTUP; fbi->task_state = (u_char)-1; - fbi->fb.fix.smem_len = fbi->max_xres * fbi->max_yres * - fbi->max_bpp / 8; + fbi->fb.fix.smem_len = inf->xres * inf->yres * + inf->bpp / 8; + fbi->inf = inf; + + /* Copy the RGB bitfield overrides */ + for (i = 0; i < NR_RGB; i++) + if (inf->rgb[i]) + fbi->rgb[i] = inf->rgb[i]; init_waitqueue_head(&fbi->ctrlr_wait); INIT_WORK(&fbi->task, sa1100fb_task); @@ -1438,13 +1208,20 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev) static int __devinit sa1100fb_probe(struct platform_device *pdev) { struct sa1100fb_info *fbi; + struct resource *res; int ret, irq; + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "no platform LCD data\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); - if (irq < 0) + if (irq < 0 || !res) return -EINVAL; - if (!request_mem_region(0xb0100000, 0x10000, "LCD")) + if (!request_mem_region(res->start, resource_size(res), "LCD")) return -EBUSY; fbi = sa1100fb_init_fbinfo(&pdev->dev); @@ -1452,6 +1229,10 @@ static int __devinit sa1100fb_probe(struct platform_device *pdev) if (!fbi) goto failed; + fbi->base = ioremap(res->start, resource_size(res)); + if (!fbi->base) + goto failed; + /* Initialize video memory */ ret = sa1100fb_map_video_memory(fbi); if (ret) @@ -1459,14 +1240,16 @@ static int __devinit sa1100fb_probe(struct platform_device *pdev) ret = request_irq(irq, sa1100fb_handle_irq, 0, "LCD", fbi); if (ret) { - printk(KERN_ERR "sa1100fb: request_irq failed: %d\n", ret); + dev_err(&pdev->dev, "request_irq failed: %d\n", ret); goto failed; } -#ifdef ASSABET_PAL_VIDEO - if (machine_is_assabet()) - ASSABET_BCR_clear(ASSABET_BCR_LCD_ON); -#endif + if (machine_is_shannon()) { + ret = gpio_request_one(SHANNON_GPIO_DISP_EN, + GPIOF_OUT_INIT_LOW, "display enable"); + if (ret) + goto err_free_irq; + } /* * This makes sure that our colour bitfield @@ -1478,7 +1261,7 @@ static int __devinit sa1100fb_probe(struct platform_device *pdev) ret = register_framebuffer(&fbi->fb); if (ret < 0) - goto err_free_irq; + goto err_reg_fb; #ifdef CONFIG_CPU_FREQ fbi->freq_transition.notifier_call = sa1100fb_freq_transition; @@ -1490,12 +1273,17 @@ static int __devinit sa1100fb_probe(struct platform_device *pdev) /* This driver cannot be unloaded at the moment */ return 0; + err_reg_fb: + if (machine_is_shannon()) + gpio_free(SHANNON_GPIO_DISP_EN); err_free_irq: free_irq(irq, fbi); failed: + if (fbi) + iounmap(fbi->base); platform_set_drvdata(pdev, NULL); kfree(fbi); - release_mem_region(0xb0100000, 0x10000); + release_mem_region(res->start, resource_size(res)); return ret; } @@ -1505,6 +1293,7 @@ static struct platform_driver sa1100fb_driver = { .resume = sa1100fb_resume, .driver = { .name = "sa11x0-fb", + .owner = THIS_MODULE, }, }; diff --git a/drivers/video/sa1100fb.h b/drivers/video/sa1100fb.h index 1c3b459865d8..fc5d4292fad6 100644 --- a/drivers/video/sa1100fb.h +++ b/drivers/video/sa1100fb.h @@ -10,44 +10,15 @@ * for more details. */ -/* - * These are the bitfields for each - * display depth that we support. - */ -struct sa1100fb_rgb { - struct fb_bitfield red; - struct fb_bitfield green; - struct fb_bitfield blue; - struct fb_bitfield transp; -}; - -/* - * This structure describes the machine which we are running on. - */ -struct sa1100fb_mach_info { - u_long pixclock; - - u_short xres; - u_short yres; - - u_char bpp; - u_char hsync_len; - u_char left_margin; - u_char right_margin; - - u_char vsync_len; - u_char upper_margin; - u_char lower_margin; - u_char sync; - - u_int cmap_greyscale:1, - cmap_inverse:1, - cmap_static:1, - unused:29; - - u_int lccr0; - u_int lccr3; -}; +#define LCCR0 0x0000 /* LCD Control Reg. 0 */ +#define LCSR 0x0004 /* LCD Status Reg. */ +#define DBAR1 0x0010 /* LCD DMA Base Address Reg. channel 1 */ +#define DCAR1 0x0014 /* LCD DMA Current Address Reg. channel 1 */ +#define DBAR2 0x0018 /* LCD DMA Base Address Reg. channel 2 */ +#define DCAR2 0x001C /* LCD DMA Current Address Reg. channel 2 */ +#define LCCR1 0x0020 /* LCD Control Reg. 1 */ +#define LCCR2 0x0024 /* LCD Control Reg. 2 */ +#define LCCR3 0x0028 /* LCD Control Reg. 3 */ /* Shadows for LCD controller registers */ struct sa1100fb_lcd_reg { @@ -57,19 +28,11 @@ struct sa1100fb_lcd_reg { unsigned long lccr3; }; -#define RGB_4 (0) -#define RGB_8 (1) -#define RGB_16 (2) -#define NR_RGB 3 - struct sa1100fb_info { struct fb_info fb; struct device *dev; - struct sa1100fb_rgb *rgb[NR_RGB]; - - u_int max_bpp; - u_int max_xres; - u_int max_yres; + const struct sa1100fb_rgb *rgb[NR_RGB]; + void __iomem *base; /* * These are the addresses we mapped @@ -88,12 +51,6 @@ struct sa1100fb_info { dma_addr_t dbar1; dma_addr_t dbar2; - u_int lccr0; - u_int lccr3; - u_int cmap_inverse:1, - cmap_static:1, - unused:30; - u_int reg_lccr0; u_int reg_lccr1; u_int reg_lccr2; @@ -109,6 +66,8 @@ struct sa1100fb_info { struct notifier_block freq_transition; struct notifier_block freq_policy; #endif + + const struct sa1100fb_mach_info *inf; }; #define TO_INF(ptr,member) container_of(ptr,struct sa1100fb_info,member) @@ -130,15 +89,6 @@ struct sa1100fb_info { #define SA1100_NAME "SA1100" /* - * Debug macros - */ -#if DEBUG -# define DPRINTK(fmt, args...) printk("%s: " fmt, __func__ , ## args) -#else -# define DPRINTK(fmt, args...) -#endif - -/* * Minimum X and Y resolutions */ #define MIN_XRES 64 diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c index 05151b82f40f..4c6b84488561 100644 --- a/drivers/video/sh_mipi_dsi.c +++ b/drivers/video/sh_mipi_dsi.c @@ -24,6 +24,8 @@ #include <video/sh_mipi_dsi.h> #include <video/sh_mobile_lcdc.h> +#include "sh_mobile_lcdcfb.h" + #define SYSCTRL 0x0000 #define SYSCONF 0x0004 #define TIMSET 0x0008 @@ -50,16 +52,16 @@ #define MAX_SH_MIPI_DSI 2 struct sh_mipi { + struct sh_mobile_lcdc_entity entity; + void __iomem *base; void __iomem *linkbase; struct clk *dsit_clk; struct platform_device *pdev; - - void *next_board_data; - void (*next_display_on)(void *board_data, struct fb_info *info); - void (*next_display_off)(void *board_data); }; +#define to_sh_mipi(e) container_of(e, struct sh_mipi, entity) + static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI]; /* Protect the above array */ @@ -120,7 +122,7 @@ static void sh_mipi_dsi_enable(struct sh_mipi *mipi, bool enable) static void sh_mipi_shutdown(struct platform_device *pdev) { - struct sh_mipi *mipi = platform_get_drvdata(pdev); + struct sh_mipi *mipi = to_sh_mipi(platform_get_drvdata(pdev)); sh_mipi_dsi_enable(mipi, false); } @@ -145,77 +147,77 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, pctype = 0; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; - linelength = ch->lcd_cfg[0].xres * 3; + linelength = ch->lcd_modes[0].xres * 3; yuv = false; break; case MIPI_RGB565: pctype = 1; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; - linelength = ch->lcd_cfg[0].xres * 2; + linelength = ch->lcd_modes[0].xres * 2; yuv = false; break; case MIPI_RGB666_LP: pctype = 2; datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; - linelength = ch->lcd_cfg[0].xres * 3; + linelength = ch->lcd_modes[0].xres * 3; yuv = false; break; case MIPI_RGB666: pctype = 3; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; - linelength = (ch->lcd_cfg[0].xres * 18 + 7) / 8; + linelength = (ch->lcd_modes[0].xres * 18 + 7) / 8; yuv = false; break; case MIPI_BGR888: pctype = 8; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; - linelength = ch->lcd_cfg[0].xres * 3; + linelength = ch->lcd_modes[0].xres * 3; yuv = false; break; case MIPI_BGR565: pctype = 9; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; - linelength = ch->lcd_cfg[0].xres * 2; + linelength = ch->lcd_modes[0].xres * 2; yuv = false; break; case MIPI_BGR666_LP: pctype = 0xa; datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; - linelength = ch->lcd_cfg[0].xres * 3; + linelength = ch->lcd_modes[0].xres * 3; yuv = false; break; case MIPI_BGR666: pctype = 0xb; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; - linelength = (ch->lcd_cfg[0].xres * 18 + 7) / 8; + linelength = (ch->lcd_modes[0].xres * 18 + 7) / 8; yuv = false; break; case MIPI_YUYV: pctype = 4; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; - linelength = ch->lcd_cfg[0].xres * 2; + linelength = ch->lcd_modes[0].xres * 2; yuv = true; break; case MIPI_UYVY: pctype = 5; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; - linelength = ch->lcd_cfg[0].xres * 2; + linelength = ch->lcd_modes[0].xres * 2; yuv = true; break; case MIPI_YUV420_L: pctype = 6; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; - linelength = (ch->lcd_cfg[0].xres * 12 + 7) / 8; + linelength = (ch->lcd_modes[0].xres * 12 + 7) / 8; yuv = true; break; case MIPI_YUV420: @@ -223,7 +225,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; /* Length of U/V line */ - linelength = (ch->lcd_cfg[0].xres + 1) / 2; + linelength = (ch->lcd_modes[0].xres + 1) / 2; yuv = true; break; default: @@ -271,7 +273,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, iowrite32(0x00000001, base + PHYCTRL); udelay(200); /* Deassert resets, power on */ - iowrite32(0x03070001, base + PHYCTRL); + iowrite32(0x03070001 | pdata->phyctrl, base + PHYCTRL); /* * Default = ULPS enable | @@ -292,7 +294,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, */ iowrite32(0x00000006, mipi->linkbase + DTCTR); /* VSYNC width = 2 (<< 17) */ - iowrite32((ch->lcd_cfg[0].vsync_len << pdata->vsynw_offset) | + iowrite32((ch->lcd_modes[0].vsync_len << pdata->vsynw_offset) | (pdata->clksrc << 16) | (pctype << 12) | datatype, mipi->linkbase + VMCTR1); @@ -326,7 +328,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, top = linelength << 16; /* RGBLEN */ bottom = 0x00000001; if (pdata->flags & SH_MIPI_DSI_HSABM) /* HSALEN */ - bottom = (pdata->lane * ch->lcd_cfg[0].hsync_len) - 10; + bottom = (pdata->lane * ch->lcd_modes[0].hsync_len) - 10; iowrite32(top | bottom , mipi->linkbase + VMLEN1); /* @@ -346,18 +348,18 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, div = 2; if (pdata->flags & SH_MIPI_DSI_HFPBM) { /* HBPLEN */ - top = ch->lcd_cfg[0].hsync_len + ch->lcd_cfg[0].left_margin; + top = ch->lcd_modes[0].hsync_len + ch->lcd_modes[0].left_margin; top = ((pdata->lane * top / div) - 10) << 16; } if (pdata->flags & SH_MIPI_DSI_HBPBM) { /* HFPLEN */ - bottom = ch->lcd_cfg[0].right_margin; + bottom = ch->lcd_modes[0].right_margin; bottom = (pdata->lane * bottom / div) - 12; } - bpp = linelength / ch->lcd_cfg[0].xres; /* byte / pixel */ + bpp = linelength / ch->lcd_modes[0].xres; /* byte / pixel */ if ((pdata->lane / div) > bpp) { - tmp = ch->lcd_cfg[0].xres / bpp; /* output cycle */ - tmp = ch->lcd_cfg[0].xres - tmp; /* (input - output) cycle */ + tmp = ch->lcd_modes[0].xres / bpp; /* output cycle */ + tmp = ch->lcd_modes[0].xres - tmp; /* (input - output) cycle */ delay = (pdata->lane * tmp); } @@ -392,9 +394,9 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, return 0; } -static void mipi_display_on(void *arg, struct fb_info *info) +static int mipi_display_on(struct sh_mobile_lcdc_entity *entity) { - struct sh_mipi *mipi = arg; + struct sh_mipi *mipi = to_sh_mipi(entity); struct sh_mipi_dsi_info *pdata = mipi->pdev->dev.platform_data; int ret; @@ -410,25 +412,21 @@ static void mipi_display_on(void *arg, struct fb_info *info) sh_mipi_dsi_enable(mipi, true); - if (mipi->next_display_on) - mipi->next_display_on(mipi->next_board_data, info); - - return; + return SH_MOBILE_LCDC_DISPLAY_CONNECTED; mipi_display_on_fail1: pm_runtime_put_sync(&mipi->pdev->dev); mipi_display_on_fail2: pdata->set_dot_clock(mipi->pdev, mipi->base, 0); + + return ret; } -static void mipi_display_off(void *arg) +static void mipi_display_off(struct sh_mobile_lcdc_entity *entity) { - struct sh_mipi *mipi = arg; + struct sh_mipi *mipi = to_sh_mipi(entity); struct sh_mipi_dsi_info *pdata = mipi->pdev->dev.platform_data; - if (mipi->next_display_off) - mipi->next_display_off(mipi->next_board_data); - sh_mipi_dsi_enable(mipi, false); pdata->set_dot_clock(mipi->pdev, mipi->base, 0); @@ -436,6 +434,11 @@ static void mipi_display_off(void *arg) pm_runtime_put_sync(&mipi->pdev->dev); } +static const struct sh_mobile_lcdc_entity_ops mipi_ops = { + .display_on = mipi_display_on, + .display_off = mipi_display_off, +}; + static int __init sh_mipi_probe(struct platform_device *pdev) { struct sh_mipi *mipi; @@ -467,6 +470,9 @@ static int __init sh_mipi_probe(struct platform_device *pdev) goto ealloc; } + mipi->entity.owner = THIS_MODULE; + mipi->entity.ops = &mipi_ops; + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { dev_err(&pdev->dev, "MIPI register region already claimed\n"); ret = -EBUSY; @@ -521,18 +527,7 @@ static int __init sh_mipi_probe(struct platform_device *pdev) pm_runtime_resume(&pdev->dev); mutex_unlock(&array_lock); - platform_set_drvdata(pdev, mipi); - - /* Save original LCDC callbacks */ - mipi->next_board_data = pdata->lcd_chan->board_cfg.board_data; - mipi->next_display_on = pdata->lcd_chan->board_cfg.display_on; - mipi->next_display_off = pdata->lcd_chan->board_cfg.display_off; - - /* Set up LCDC callbacks */ - pdata->lcd_chan->board_cfg.board_data = mipi; - pdata->lcd_chan->board_cfg.display_on = mipi_display_on; - pdata->lcd_chan->board_cfg.display_off = mipi_display_off; - pdata->lcd_chan->board_cfg.owner = THIS_MODULE; + platform_set_drvdata(pdev, &mipi->entity); return 0; @@ -558,10 +553,9 @@ efindslot: static int __exit sh_mipi_remove(struct platform_device *pdev) { - struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data; struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); - struct sh_mipi *mipi = platform_get_drvdata(pdev); + struct sh_mipi *mipi = to_sh_mipi(platform_get_drvdata(pdev)); int i, ret; mutex_lock(&array_lock); @@ -581,11 +575,6 @@ static int __exit sh_mipi_remove(struct platform_device *pdev) if (ret < 0) return ret; - pdata->lcd_chan->board_cfg.owner = NULL; - pdata->lcd_chan->board_cfg.display_on = NULL; - pdata->lcd_chan->board_cfg.display_off = NULL; - pdata->lcd_chan->board_cfg.board_data = NULL; - pm_runtime_disable(&pdev->dev); clk_disable(mipi->dsit_clk); clk_put(mipi->dsit_clk); diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index 647ba984f00f..eafb19da2c07 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c @@ -208,6 +208,8 @@ enum hotplug_state { }; struct sh_hdmi { + struct sh_mobile_lcdc_entity entity; + void __iomem *base; enum hotplug_state hp_state; /* hot-plug status */ u8 preprogrammed_vic; /* use a pre-programmed VIC or @@ -217,14 +219,13 @@ struct sh_hdmi { u8 edid_blocks; struct clk *hdmi_clk; struct device *dev; - struct fb_info *info; - struct mutex mutex; /* Protect the info pointer */ struct delayed_work edid_work; - struct fb_var_screeninfo var; + struct fb_videomode mode; struct fb_monspecs monspec; - struct notifier_block notifier; }; +#define entity_to_sh_hdmi(e) container_of(e, struct sh_hdmi, entity) + static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) { iowrite8(data, hdmi->base + reg); @@ -290,24 +291,24 @@ static struct snd_soc_codec_driver soc_codec_dev_sh_hdmi = { /* External video parameter settings */ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) { - struct fb_var_screeninfo *var = &hdmi->var; + struct fb_videomode *mode = &hdmi->mode; u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset; u8 sync = 0; - htotal = var->xres + var->right_margin + var->left_margin + var->hsync_len; - - hdelay = var->hsync_len + var->left_margin; - hblank = var->right_margin + hdelay; + htotal = mode->xres + mode->right_margin + mode->left_margin + + mode->hsync_len; + hdelay = mode->hsync_len + mode->left_margin; + hblank = mode->right_margin + hdelay; /* * Vertical timing looks a bit different in Figure 18, * but let's try the same first by setting offset = 0 */ - vtotal = var->yres + var->upper_margin + var->lower_margin + var->vsync_len; - - vdelay = var->vsync_len + var->upper_margin; - vblank = var->lower_margin + vdelay; - voffset = min(var->upper_margin / 2, 6U); + vtotal = mode->yres + mode->upper_margin + mode->lower_margin + + mode->vsync_len; + vdelay = mode->vsync_len + mode->upper_margin; + vblank = mode->lower_margin + vdelay; + voffset = min(mode->upper_margin / 2, 6U); /* * [3]: VSYNC polarity: Positive @@ -315,14 +316,14 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) * [1]: Interlace/Progressive: Progressive * [0]: External video settings enable: used. */ - if (var->sync & FB_SYNC_HOR_HIGH_ACT) + if (mode->sync & FB_SYNC_HOR_HIGH_ACT) sync |= 4; - if (var->sync & FB_SYNC_VERT_HIGH_ACT) + if (mode->sync & FB_SYNC_VERT_HIGH_ACT) sync |= 8; dev_dbg(hdmi->dev, "H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n", - htotal, hblank, hdelay, var->hsync_len, - vtotal, vblank, vdelay, var->vsync_len, sync); + htotal, hblank, hdelay, mode->hsync_len, + vtotal, vblank, vdelay, mode->vsync_len, sync); hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); @@ -335,8 +336,8 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) hdmi_write(hdmi, hdelay, HDMI_EXTERNAL_H_DELAY_7_0); hdmi_write(hdmi, hdelay >> 8, HDMI_EXTERNAL_H_DELAY_9_8); - hdmi_write(hdmi, var->hsync_len, HDMI_EXTERNAL_H_DURATION_7_0); - hdmi_write(hdmi, var->hsync_len >> 8, HDMI_EXTERNAL_H_DURATION_9_8); + hdmi_write(hdmi, mode->hsync_len, HDMI_EXTERNAL_H_DURATION_7_0); + hdmi_write(hdmi, mode->hsync_len >> 8, HDMI_EXTERNAL_H_DURATION_9_8); hdmi_write(hdmi, vtotal, HDMI_EXTERNAL_V_TOTAL_7_0); hdmi_write(hdmi, vtotal >> 8, HDMI_EXTERNAL_V_TOTAL_9_8); @@ -345,7 +346,7 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) hdmi_write(hdmi, vdelay, HDMI_EXTERNAL_V_DELAY); - hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION); + hdmi_write(hdmi, mode->vsync_len, HDMI_EXTERNAL_V_DURATION); /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for external mode */ if (!hdmi->preprogrammed_vic) @@ -472,7 +473,7 @@ static void sh_hdmi_audio_config(struct sh_hdmi *hdmi) */ static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) { - if (hdmi->var.pixclock < 10000) { + if (hdmi->mode.pixclock < 10000) { /* for 1080p8bit 148MHz */ hdmi_write(hdmi, 0x1d, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); @@ -483,7 +484,7 @@ static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) hdmi_write(hdmi, 0x0e, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); - } else if (hdmi->var.pixclock < 30000) { + } else if (hdmi->mode.pixclock < 30000) { /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */ /* * [1:0] Speed_A @@ -732,14 +733,12 @@ static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, unsigned long *parent_rate) { - struct fb_var_screeninfo tmpvar; - struct fb_var_screeninfo *var = &tmpvar; + struct sh_mobile_lcdc_chan *ch = hdmi->entity.lcdc; const struct fb_videomode *mode, *found = NULL; - struct fb_info *info = hdmi->info; - struct fb_modelist *modelist = NULL; unsigned int f_width = 0, f_height = 0, f_refresh = 0; unsigned long found_rate_error = ULONG_MAX; /* silly compiler... */ bool scanning = false, preferred_bad = false; + bool use_edid_mode = false; u8 edid[128]; char *forced; int i; @@ -854,12 +853,9 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, } /* Check if supported: sufficient fb memory, supported clock-rate */ - fb_videomode_to_var(var, mode); - - var->bits_per_pixel = info->var.bits_per_pixel; - - if (info && info->fbops->fb_check_var && - info->fbops->fb_check_var(var, info)) { + if (ch && ch->notify && + ch->notify(ch, SH_MOBILE_LCDC_EVENT_DISPLAY_MODE, mode, + NULL)) { scanning = true; preferred_bad = true; continue; @@ -867,28 +863,19 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, found = mode; found_rate_error = rate_error; + use_edid_mode = true; } - hdmi->var.width = hdmi->monspec.max_x * 10; - hdmi->var.height = hdmi->monspec.max_y * 10; - /* - * TODO 1: if no ->info is present, postpone running the config until - * after ->info first gets registered. + * TODO 1: if no default mode is present, postpone running the config + * until after the LCDC channel is initialized. * TODO 2: consider registering the HDMI platform device from the LCDC - * driver, and passing ->info with HDMI platform data. + * driver. */ - if (info && !found) { - modelist = info->modelist.next && - !list_empty(&info->modelist) ? - list_entry(info->modelist.next, - struct fb_modelist, list) : - NULL; - - if (modelist) { - found = &modelist->mode; - found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate, parent_rate); - } + if (!found && hdmi->entity.def_mode.xres != 0) { + found = &hdmi->entity.def_mode; + found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate, + parent_rate); } /* No cookie today */ @@ -912,12 +899,13 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, else hdmi->preprogrammed_vic = 0; - dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", - modelist ? "default" : "EDID", hdmi->preprogrammed_vic ? "VIC" : "external", - found->xres, found->yres, found->refresh, - PICOS2KHZ(found->pixclock) * 1000, found_rate_error); + dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), " + "clock error %luHz\n", use_edid_mode ? "EDID" : "default", + hdmi->preprogrammed_vic ? "VIC" : "external", found->xres, + found->yres, found->refresh, PICOS2KHZ(found->pixclock) * 1000, + found_rate_error); - fb_videomode_to_var(&hdmi->var, found); + hdmi->mode = *found; sh_hdmi_external_video_param(hdmi); return 0; @@ -998,22 +986,12 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) return IRQ_HANDLED; } -/* locking: called with info->lock held, or before register_framebuffer() */ -static void sh_hdmi_display_on(void *arg, struct fb_info *info) +static int sh_hdmi_display_on(struct sh_mobile_lcdc_entity *entity) { - /* - * info is guaranteed to be valid, when we are called, because our - * FB_EVENT_FB_UNBIND notify is also called with info->lock held - */ - struct sh_hdmi *hdmi = arg; - struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; - struct sh_mobile_lcdc_chan *ch = info->par; + struct sh_hdmi *hdmi = entity_to_sh_hdmi(entity); - dev_dbg(hdmi->dev, "%s(%p): state %x\n", __func__, - pdata->lcd_dev, info->state); - - /* No need to lock */ - hdmi->info = info; + dev_dbg(hdmi->dev, "%s(%p): state %x\n", __func__, hdmi, + hdmi->hp_state); /* * hp_state can be set to @@ -1021,56 +999,30 @@ static void sh_hdmi_display_on(void *arg, struct fb_info *info) * HDMI_HOTPLUG_CONNECTED: on monitor plug-in * HDMI_HOTPLUG_EDID_DONE: on EDID read completion */ - switch (hdmi->hp_state) { - case HDMI_HOTPLUG_EDID_DONE: + if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { /* PS mode d->e. All functions are active */ hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL); dev_dbg(hdmi->dev, "HDMI running\n"); - break; - case HDMI_HOTPLUG_DISCONNECTED: - info->state = FBINFO_STATE_SUSPENDED; - default: - hdmi->var = ch->display_var; } + + return hdmi->hp_state == HDMI_HOTPLUG_DISCONNECTED + ? SH_MOBILE_LCDC_DISPLAY_DISCONNECTED + : SH_MOBILE_LCDC_DISPLAY_CONNECTED; } -/* locking: called with info->lock held */ -static void sh_hdmi_display_off(void *arg) +static void sh_hdmi_display_off(struct sh_mobile_lcdc_entity *entity) { - struct sh_hdmi *hdmi = arg; - struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; + struct sh_hdmi *hdmi = entity_to_sh_hdmi(entity); - dev_dbg(hdmi->dev, "%s(%p)\n", __func__, pdata->lcd_dev); + dev_dbg(hdmi->dev, "%s(%p)\n", __func__, hdmi); /* PS mode e->a */ hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL); } -static bool sh_hdmi_must_reconfigure(struct sh_hdmi *hdmi) -{ - struct fb_info *info = hdmi->info; - struct sh_mobile_lcdc_chan *ch = info->par; - struct fb_var_screeninfo *new_var = &hdmi->var, *old_var = &ch->display_var; - struct fb_videomode mode1, mode2; - - fb_var_to_videomode(&mode1, old_var); - fb_var_to_videomode(&mode2, new_var); - - dev_dbg(info->dev, "Old %ux%u, new %ux%u\n", - mode1.xres, mode1.yres, mode2.xres, mode2.yres); - - if (fb_mode_is_equal(&mode1, &mode2)) { - /* It can be a different monitor with an equal video-mode */ - old_var->width = new_var->width; - old_var->height = new_var->height; - return false; - } - - dev_dbg(info->dev, "Switching %u -> %u lines\n", - mode1.yres, mode2.yres); - *old_var = *new_var; - - return true; -} +static const struct sh_mobile_lcdc_entity_ops sh_hdmi_ops = { + .display_on = sh_hdmi_display_on, + .display_off = sh_hdmi_display_off, +}; /** * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock @@ -1111,20 +1063,11 @@ static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long hdmi_rate, static void sh_hdmi_edid_work_fn(struct work_struct *work) { struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work); - struct fb_info *info; - struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; - struct sh_mobile_lcdc_chan *ch; + struct sh_mobile_lcdc_chan *ch = hdmi->entity.lcdc; int ret; - dev_dbg(hdmi->dev, "%s(%p): begin, hotplug status %d\n", __func__, - pdata->lcd_dev, hdmi->hp_state); - - if (!pdata->lcd_dev) - return; - - mutex_lock(&hdmi->mutex); - - info = hdmi->info; + dev_dbg(hdmi->dev, "%s(%p): begin, hotplug status %d\n", __func__, hdmi, + hdmi->hp_state); if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED) { unsigned long parent_rate = 0, hdmi_rate; @@ -1145,103 +1088,32 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) /* Switched to another (d) power-save mode */ msleep(10); - if (!info) - goto out; - - ch = info->par; - - if (lock_fb_info(info)) { - console_lock(); - - /* HDMI plug in */ - if (!sh_hdmi_must_reconfigure(hdmi) && - info->state == FBINFO_STATE_RUNNING) { - /* - * First activation with the default monitor - just turn - * on, if we run a resume here, the logo disappears - */ - info->var.width = hdmi->var.width; - info->var.height = hdmi->var.height; - sh_hdmi_display_on(hdmi, info); - } else { - /* New monitor or have to wake up */ - fb_set_suspend(info, 0); - } - - console_unlock(); - unlock_fb_info(info); - } + if (ch && ch->notify) + ch->notify(ch, SH_MOBILE_LCDC_EVENT_DISPLAY_CONNECT, + &hdmi->mode, &hdmi->monspec); } else { - ret = 0; - if (!info) - goto out; - hdmi->monspec.modedb_len = 0; fb_destroy_modedb(hdmi->monspec.modedb); hdmi->monspec.modedb = NULL; - if (lock_fb_info(info)) { - console_lock(); + if (ch && ch->notify) + ch->notify(ch, SH_MOBILE_LCDC_EVENT_DISPLAY_DISCONNECT, + NULL, NULL); - /* HDMI disconnect */ - fb_set_suspend(info, 1); - - console_unlock(); - unlock_fb_info(info); - } + ret = 0; } out: if (ret < 0 && ret != -EAGAIN) hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; - mutex_unlock(&hdmi->mutex); - dev_dbg(hdmi->dev, "%s(%p): end\n", __func__, pdata->lcd_dev); -} - -static int sh_hdmi_notify(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct fb_event *event = data; - struct fb_info *info = event->info; - struct sh_mobile_lcdc_chan *ch = info->par; - struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; - struct sh_hdmi *hdmi = board_cfg->board_data; - - if (!hdmi || nb != &hdmi->notifier || hdmi->info != info) - return NOTIFY_DONE; - - switch(action) { - case FB_EVENT_FB_REGISTERED: - /* Unneeded, activation taken care by sh_hdmi_display_on() */ - break; - case FB_EVENT_FB_UNREGISTERED: - /* - * We are called from unregister_framebuffer() with the - * info->lock held. This is bad for us, because we can race with - * the scheduled work, which has to call fb_set_suspend(), which - * takes info->lock internally, so, sh_hdmi_edid_work_fn() - * cannot take and hold info->lock for the whole function - * duration. Using an additional lock creates a classical AB-BA - * lock up. Therefore, we have to release the info->lock - * temporarily, synchronise with the work queue and re-acquire - * the info->lock. - */ - unlock_fb_info(info); - mutex_lock(&hdmi->mutex); - hdmi->info = NULL; - mutex_unlock(&hdmi->mutex); - lock_fb_info(info); - return NOTIFY_OK; - } - return NOTIFY_DONE; + dev_dbg(hdmi->dev, "%s(%p): end\n", __func__, hdmi); } static int __init sh_hdmi_probe(struct platform_device *pdev) { struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - struct sh_mobile_lcdc_board_cfg *board_cfg; int irq = platform_get_irq(pdev, 0), ret; struct sh_hdmi *hdmi; long rate; @@ -1255,9 +1127,9 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) return -ENOMEM; } - mutex_init(&hdmi->mutex); - hdmi->dev = &pdev->dev; + hdmi->entity.owner = THIS_MODULE; + hdmi->entity.ops = &sh_hdmi_ops; hdmi->hdmi_clk = clk_get(&pdev->dev, "ick"); if (IS_ERR(hdmi->hdmi_clk)) { @@ -1297,14 +1169,7 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) goto emap; } - platform_set_drvdata(pdev, hdmi); - - /* Set up LCDC callbacks */ - board_cfg = &pdata->lcd_chan->board_cfg; - board_cfg->owner = THIS_MODULE; - board_cfg->board_data = hdmi; - board_cfg->display_on = sh_hdmi_display_on; - board_cfg->display_off = sh_hdmi_display_off; + platform_set_drvdata(pdev, &hdmi->entity); INIT_DELAYED_WORK(&hdmi->edid_work, sh_hdmi_edid_work_fn); @@ -1329,9 +1194,6 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) goto ecodec; } - hdmi->notifier.notifier_call = sh_hdmi_notify; - fb_register_client(&hdmi->notifier); - return 0; ecodec: @@ -1347,7 +1209,6 @@ ereqreg: erate: clk_put(hdmi->hdmi_clk); egetclk: - mutex_destroy(&hdmi->mutex); kfree(hdmi); return ret; @@ -1355,21 +1216,12 @@ egetclk: static int __exit sh_hdmi_remove(struct platform_device *pdev) { - struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; - struct sh_hdmi *hdmi = platform_get_drvdata(pdev); + struct sh_hdmi *hdmi = entity_to_sh_hdmi(platform_get_drvdata(pdev)); struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - struct sh_mobile_lcdc_board_cfg *board_cfg = &pdata->lcd_chan->board_cfg; int irq = platform_get_irq(pdev, 0); snd_soc_unregister_codec(&pdev->dev); - fb_unregister_client(&hdmi->notifier); - - board_cfg->display_on = NULL; - board_cfg->display_off = NULL; - board_cfg->board_data = NULL; - board_cfg->owner = NULL; - /* No new work will be scheduled, wait for running ISR */ free_irq(irq, hdmi); /* Wait for already scheduled work */ @@ -1380,7 +1232,6 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev) clk_put(hdmi->hdmi_clk); iounmap(hdmi->base); release_mem_region(res->start, resource_size(res)); - mutex_destroy(&hdmi->mutex); kfree(hdmi); return 0; diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index aac5b369d73c..7a0b301587f6 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -8,26 +8,27 @@ * for more details. */ -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/mm.h> +#include <linux/atomic.h> +#include <linux/backlight.h> #include <linux/clk.h> -#include <linux/pm_runtime.h> -#include <linux/platform_device.h> +#include <linux/console.h> #include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/init.h> #include <linux/interrupt.h> -#include <linux/videodev2.h> -#include <linux/vmalloc.h> #include <linux/ioctl.h> -#include <linux/slab.h> -#include <linux/console.h> -#include <linux/backlight.h> -#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/mm.h> #include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/videodev2.h> +#include <linux/vmalloc.h> + #include <video/sh_mobile_lcdc.h> #include <video/sh_mobile_meram.h> -#include <linux/atomic.h> #include "sh_mobile_lcdcfb.h" @@ -37,6 +38,24 @@ #define MAX_XRES 1920 #define MAX_YRES 1080 +struct sh_mobile_lcdc_priv { + void __iomem *base; + int irq; + atomic_t hw_usecnt; + struct device *dev; + struct clk *dot_clk; + unsigned long lddckr; + struct sh_mobile_lcdc_chan ch[2]; + struct notifier_block notifier; + int started; + int forced_fourcc; /* 2 channel LCDC must share fourcc setting */ + struct sh_mobile_meram_info *meram_dev; +}; + +/* ----------------------------------------------------------------------------- + * Registers access + */ + static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { [LDDCKPAT1R] = 0x400, [LDDCKPAT2R] = 0x404, @@ -75,38 +94,6 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = { [LDPMR] = 0x63c, }; -static const struct fb_videomode default_720p = { - .name = "HDMI 720p", - .xres = 1280, - .yres = 720, - - .left_margin = 220, - .right_margin = 110, - .hsync_len = 40, - - .upper_margin = 20, - .lower_margin = 5, - .vsync_len = 5, - - .pixclock = 13468, - .refresh = 60, - .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT, -}; - -struct sh_mobile_lcdc_priv { - void __iomem *base; - int irq; - atomic_t hw_usecnt; - struct device *dev; - struct clk *dot_clk; - unsigned long lddckr; - struct sh_mobile_lcdc_chan ch[2]; - struct notifier_block notifier; - int started; - int forced_fourcc; /* 2 channel LCDC must share fourcc setting */ - struct sh_mobile_meram_info *meram_dev; -}; - static bool banked(int reg_nr) { switch (reg_nr) { @@ -127,6 +114,11 @@ static bool banked(int reg_nr) return false; } +static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan) +{ + return chan->cfg->chan == LCDC_CHAN_SUBLCD; +} + static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan, int reg_nr, unsigned long data) { @@ -169,11 +161,72 @@ static void lcdc_wait_bit(struct sh_mobile_lcdc_priv *priv, cpu_relax(); } -static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan) +/* ----------------------------------------------------------------------------- + * Clock management + */ + +static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) +{ + if (atomic_inc_and_test(&priv->hw_usecnt)) { + if (priv->dot_clk) + clk_enable(priv->dot_clk); + pm_runtime_get_sync(priv->dev); + if (priv->meram_dev && priv->meram_dev->pdev) + pm_runtime_get_sync(&priv->meram_dev->pdev->dev); + } +} + +static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) { - return chan->cfg.chan == LCDC_CHAN_SUBLCD; + if (atomic_sub_return(1, &priv->hw_usecnt) == -1) { + if (priv->meram_dev && priv->meram_dev->pdev) + pm_runtime_put_sync(&priv->meram_dev->pdev->dev); + pm_runtime_put(priv->dev); + if (priv->dot_clk) + clk_disable(priv->dot_clk); + } } +static int sh_mobile_lcdc_setup_clocks(struct sh_mobile_lcdc_priv *priv, + int clock_source) +{ + struct clk *clk; + char *str; + + switch (clock_source) { + case LCDC_CLK_BUS: + str = "bus_clk"; + priv->lddckr = LDDCKR_ICKSEL_BUS; + break; + case LCDC_CLK_PERIPHERAL: + str = "peripheral_clk"; + priv->lddckr = LDDCKR_ICKSEL_MIPI; + break; + case LCDC_CLK_EXTERNAL: + str = NULL; + priv->lddckr = LDDCKR_ICKSEL_HDMI; + break; + default: + return -EINVAL; + } + + if (str == NULL) + return 0; + + clk = clk_get(priv->dev, str); + if (IS_ERR(clk)) { + dev_err(priv->dev, "cannot get dot clock %s\n", str); + return PTR_ERR(clk); + } + + priv->dot_clk = clk; + return 0; +} + +/* ----------------------------------------------------------------------------- + * Display, panel and deferred I/O + */ + static void lcdc_sys_write_index(void *handle, unsigned long data) { struct sh_mobile_lcdc_chan *ch = handle; @@ -216,74 +269,11 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = { lcdc_sys_read_data, }; -static int sh_mobile_format_fourcc(const struct fb_var_screeninfo *var) -{ - if (var->grayscale > 1) - return var->grayscale; - - switch (var->bits_per_pixel) { - case 16: - return V4L2_PIX_FMT_RGB565; - case 24: - return V4L2_PIX_FMT_BGR24; - case 32: - return V4L2_PIX_FMT_BGR32; - default: - return 0; - } -} - -static int sh_mobile_format_is_fourcc(const struct fb_var_screeninfo *var) -{ - return var->grayscale > 1; -} - -static bool sh_mobile_format_is_yuv(const struct fb_var_screeninfo *var) -{ - if (var->grayscale <= 1) - return false; - - switch (var->grayscale) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - case V4L2_PIX_FMT_NV24: - case V4L2_PIX_FMT_NV42: - return true; - - default: - return false; - } -} - -static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) -{ - if (atomic_inc_and_test(&priv->hw_usecnt)) { - if (priv->dot_clk) - clk_enable(priv->dot_clk); - pm_runtime_get_sync(priv->dev); - if (priv->meram_dev && priv->meram_dev->pdev) - pm_runtime_get_sync(&priv->meram_dev->pdev->dev); - } -} - -static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) -{ - if (atomic_sub_return(1, &priv->hw_usecnt) == -1) { - if (priv->meram_dev && priv->meram_dev->pdev) - pm_runtime_put_sync(&priv->meram_dev->pdev->dev); - pm_runtime_put(priv->dev); - if (priv->dot_clk) - clk_disable(priv->dot_clk); - } -} - static int sh_mobile_lcdc_sginit(struct fb_info *info, struct list_head *pagelist) { struct sh_mobile_lcdc_chan *ch = info->par; - unsigned int nr_pages_max = info->fix.smem_len >> PAGE_SHIFT; + unsigned int nr_pages_max = ch->fb_size >> PAGE_SHIFT; struct page *page; int nr_pages = 0; @@ -299,7 +289,7 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info, struct list_head *pagelist) { struct sh_mobile_lcdc_chan *ch = info->par; - struct sh_mobile_lcdc_board_cfg *bcfg = &ch->cfg.board_cfg; + const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg; /* enable clocks before accessing hardware */ sh_mobile_lcdc_clk_on(ch->lcdc); @@ -323,16 +313,15 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info, unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist); /* trigger panel update */ - dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); - if (bcfg->start_transfer) - bcfg->start_transfer(bcfg->board_data, ch, - &sh_mobile_lcdc_sys_bus_ops); + dma_map_sg(ch->lcdc->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); + if (panel->start_transfer) + panel->start_transfer(ch, &sh_mobile_lcdc_sys_bus_ops); lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG); - dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); + dma_unmap_sg(ch->lcdc->dev, ch->sglist, nr_pages, + DMA_TO_DEVICE); } else { - if (bcfg->start_transfer) - bcfg->start_transfer(bcfg->board_data, ch, - &sh_mobile_lcdc_sys_bus_ops); + if (panel->start_transfer) + panel->start_transfer(ch, &sh_mobile_lcdc_sys_bus_ops); lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG); } } @@ -345,6 +334,217 @@ static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info) schedule_delayed_work(&info->deferred_work, fbdefio->delay); } +static void sh_mobile_lcdc_display_on(struct sh_mobile_lcdc_chan *ch) +{ + const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg; + + if (ch->tx_dev) { + int ret; + + ret = ch->tx_dev->ops->display_on(ch->tx_dev); + if (ret < 0) + return; + + if (ret == SH_MOBILE_LCDC_DISPLAY_DISCONNECTED) + ch->info->state = FBINFO_STATE_SUSPENDED; + } + + /* HDMI must be enabled before LCDC configuration */ + if (panel->display_on) + panel->display_on(); +} + +static void sh_mobile_lcdc_display_off(struct sh_mobile_lcdc_chan *ch) +{ + const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg; + + if (panel->display_off) + panel->display_off(); + + if (ch->tx_dev) + ch->tx_dev->ops->display_off(ch->tx_dev); +} + +static bool +sh_mobile_lcdc_must_reconfigure(struct sh_mobile_lcdc_chan *ch, + const struct fb_videomode *new_mode) +{ + dev_dbg(ch->info->dev, "Old %ux%u, new %ux%u\n", + ch->display.mode.xres, ch->display.mode.yres, + new_mode->xres, new_mode->yres); + + /* It can be a different monitor with an equal video-mode */ + if (fb_mode_is_equal(&ch->display.mode, new_mode)) + return false; + + dev_dbg(ch->info->dev, "Switching %u -> %u lines\n", + ch->display.mode.yres, new_mode->yres); + ch->display.mode = *new_mode; + + return true; +} + +static int sh_mobile_check_var(struct fb_var_screeninfo *var, + struct fb_info *info); + +static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch, + enum sh_mobile_lcdc_entity_event event, + const struct fb_videomode *mode, + const struct fb_monspecs *monspec) +{ + struct fb_info *info = ch->info; + struct fb_var_screeninfo var; + int ret = 0; + + switch (event) { + case SH_MOBILE_LCDC_EVENT_DISPLAY_CONNECT: + /* HDMI plug in */ + if (lock_fb_info(info)) { + console_lock(); + + ch->display.width = monspec->max_x * 10; + ch->display.height = monspec->max_y * 10; + + if (!sh_mobile_lcdc_must_reconfigure(ch, mode) && + info->state == FBINFO_STATE_RUNNING) { + /* First activation with the default monitor. + * Just turn on, if we run a resume here, the + * logo disappears. + */ + info->var.width = monspec->max_x * 10; + info->var.height = monspec->max_y * 10; + sh_mobile_lcdc_display_on(ch); + } else { + /* New monitor or have to wake up */ + fb_set_suspend(info, 0); + } + + console_unlock(); + unlock_fb_info(info); + } + break; + + case SH_MOBILE_LCDC_EVENT_DISPLAY_DISCONNECT: + /* HDMI disconnect */ + if (lock_fb_info(info)) { + console_lock(); + fb_set_suspend(info, 1); + console_unlock(); + unlock_fb_info(info); + } + break; + + case SH_MOBILE_LCDC_EVENT_DISPLAY_MODE: + /* Validate a proposed new mode */ + fb_videomode_to_var(&var, mode); + var.bits_per_pixel = info->var.bits_per_pixel; + var.grayscale = info->var.grayscale; + ret = sh_mobile_check_var(&var, info); + break; + } + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Format helpers + */ + +struct sh_mobile_lcdc_format_info { + u32 fourcc; + unsigned int bpp; + bool yuv; + u32 lddfr; +}; + +static const struct sh_mobile_lcdc_format_info sh_mobile_format_infos[] = { + { + .fourcc = V4L2_PIX_FMT_RGB565, + .bpp = 16, + .yuv = false, + .lddfr = LDDFR_PKF_RGB16, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, + .bpp = 24, + .yuv = false, + .lddfr = LDDFR_PKF_RGB24, + }, { + .fourcc = V4L2_PIX_FMT_BGR32, + .bpp = 32, + .yuv = false, + .lddfr = LDDFR_PKF_ARGB32, + }, { + .fourcc = V4L2_PIX_FMT_NV12, + .bpp = 12, + .yuv = true, + .lddfr = LDDFR_CC | LDDFR_YF_420, + }, { + .fourcc = V4L2_PIX_FMT_NV21, + .bpp = 12, + .yuv = true, + .lddfr = LDDFR_CC | LDDFR_YF_420, + }, { + .fourcc = V4L2_PIX_FMT_NV16, + .bpp = 16, + .yuv = true, + .lddfr = LDDFR_CC | LDDFR_YF_422, + }, { + .fourcc = V4L2_PIX_FMT_NV61, + .bpp = 16, + .yuv = true, + .lddfr = LDDFR_CC | LDDFR_YF_422, + }, { + .fourcc = V4L2_PIX_FMT_NV24, + .bpp = 24, + .yuv = true, + .lddfr = LDDFR_CC | LDDFR_YF_444, + }, { + .fourcc = V4L2_PIX_FMT_NV42, + .bpp = 24, + .yuv = true, + .lddfr = LDDFR_CC | LDDFR_YF_444, + }, +}; + +static const struct sh_mobile_lcdc_format_info * +sh_mobile_format_info(u32 fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sh_mobile_format_infos); ++i) { + if (sh_mobile_format_infos[i].fourcc == fourcc) + return &sh_mobile_format_infos[i]; + } + + return NULL; +} + +static int sh_mobile_format_fourcc(const struct fb_var_screeninfo *var) +{ + if (var->grayscale > 1) + return var->grayscale; + + switch (var->bits_per_pixel) { + case 16: + return V4L2_PIX_FMT_RGB565; + case 24: + return V4L2_PIX_FMT_BGR24; + case 32: + return V4L2_PIX_FMT_BGR32; + default: + return 0; + } +} + +static int sh_mobile_format_is_fourcc(const struct fb_var_screeninfo *var) +{ + return var->grayscale > 1; +} + +/* ----------------------------------------------------------------------------- + * Start, stop and IRQ + */ + static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data) { struct sh_mobile_lcdc_priv *priv = data; @@ -385,6 +585,26 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data) return IRQ_HANDLED; } +static int sh_mobile_wait_for_vsync(struct sh_mobile_lcdc_chan *ch) +{ + unsigned long ldintr; + int ret; + + /* Enable VSync End interrupt and be careful not to acknowledge any + * pending interrupt. + */ + ldintr = lcdc_read(ch->lcdc, _LDINTR); + ldintr |= LDINTR_VEE | LDINTR_STATUS_MASK; + lcdc_write(ch->lcdc, _LDINTR, ldintr); + + ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion, + msecs_to_jiffies(100)); + if (!ret) + return -ETIMEDOUT; + + return 0; +} + static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv, int start) { @@ -416,53 +636,52 @@ static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv, static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) { - struct fb_var_screeninfo *var = &ch->info->var, *display_var = &ch->display_var; + const struct fb_var_screeninfo *var = &ch->info->var; + const struct fb_videomode *mode = &ch->display.mode; unsigned long h_total, hsync_pos, display_h_total; u32 tmp; tmp = ch->ldmt1r_value; tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : LDMT1R_VPOL; tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : LDMT1R_HPOL; - tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? LDMT1R_DWPOL : 0; - tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? LDMT1R_DIPOL : 0; - tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? LDMT1R_DAPOL : 0; - tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? LDMT1R_HSCNT : 0; - tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? LDMT1R_DWCNT : 0; + tmp |= (ch->cfg->flags & LCDC_FLAGS_DWPOL) ? LDMT1R_DWPOL : 0; + tmp |= (ch->cfg->flags & LCDC_FLAGS_DIPOL) ? LDMT1R_DIPOL : 0; + tmp |= (ch->cfg->flags & LCDC_FLAGS_DAPOL) ? LDMT1R_DAPOL : 0; + tmp |= (ch->cfg->flags & LCDC_FLAGS_HSCNT) ? LDMT1R_HSCNT : 0; + tmp |= (ch->cfg->flags & LCDC_FLAGS_DWCNT) ? LDMT1R_DWCNT : 0; lcdc_write_chan(ch, LDMT1R, tmp); /* setup SYS bus */ - lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r); - lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r); + lcdc_write_chan(ch, LDMT2R, ch->cfg->sys_bus_cfg.ldmt2r); + lcdc_write_chan(ch, LDMT3R, ch->cfg->sys_bus_cfg.ldmt3r); /* horizontal configuration */ - h_total = display_var->xres + display_var->hsync_len + - display_var->left_margin + display_var->right_margin; + h_total = mode->xres + mode->hsync_len + mode->left_margin + + mode->right_margin; tmp = h_total / 8; /* HTCN */ - tmp |= (min(display_var->xres, var->xres) / 8) << 16; /* HDCN */ + tmp |= (min(mode->xres, ch->xres) / 8) << 16; /* HDCN */ lcdc_write_chan(ch, LDHCNR, tmp); - hsync_pos = display_var->xres + display_var->right_margin; + hsync_pos = mode->xres + mode->right_margin; tmp = hsync_pos / 8; /* HSYNP */ - tmp |= (display_var->hsync_len / 8) << 16; /* HSYNW */ + tmp |= (mode->hsync_len / 8) << 16; /* HSYNW */ lcdc_write_chan(ch, LDHSYNR, tmp); /* vertical configuration */ - tmp = display_var->yres + display_var->vsync_len + - display_var->upper_margin + display_var->lower_margin; /* VTLN */ - tmp |= min(display_var->yres, var->yres) << 16; /* VDLN */ + tmp = mode->yres + mode->vsync_len + mode->upper_margin + + mode->lower_margin; /* VTLN */ + tmp |= min(mode->yres, ch->yres) << 16; /* VDLN */ lcdc_write_chan(ch, LDVLNR, tmp); - tmp = display_var->yres + display_var->lower_margin; /* VSYNP */ - tmp |= display_var->vsync_len << 16; /* VSYNW */ + tmp = mode->yres + mode->lower_margin; /* VSYNP */ + tmp |= mode->vsync_len << 16; /* VSYNW */ lcdc_write_chan(ch, LDVSYNR, tmp); /* Adjust horizontal synchronisation for HDMI */ - display_h_total = display_var->xres + display_var->hsync_len + - display_var->left_margin + display_var->right_margin; - tmp = ((display_var->xres & 7) << 24) | - ((display_h_total & 7) << 16) | - ((display_var->hsync_len & 7) << 8) | - (hsync_pos & 7); + display_h_total = mode->xres + mode->hsync_len + mode->left_margin + + mode->right_margin; + tmp = ((mode->xres & 7) << 24) | ((display_h_total & 7) << 16) + | ((mode->hsync_len & 7) << 8) | (hsync_pos & 7); lcdc_write_chan(ch, LDHAJR, tmp); } @@ -498,7 +717,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) /* Power supply */ lcdc_write_chan(ch, LDPMR, 0); - m = ch->cfg.clock_divider; + m = ch->cfg->clock_divider; if (!m) continue; @@ -525,32 +744,10 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) sh_mobile_lcdc_geometry(ch); - switch (sh_mobile_format_fourcc(&ch->info->var)) { - case V4L2_PIX_FMT_RGB565: - tmp = LDDFR_PKF_RGB16; - break; - case V4L2_PIX_FMT_BGR24: - tmp = LDDFR_PKF_RGB24; - break; - case V4L2_PIX_FMT_BGR32: - tmp = LDDFR_PKF_ARGB32; - break; - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - tmp = LDDFR_CC | LDDFR_YF_420; - break; - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - tmp = LDDFR_CC | LDDFR_YF_422; - break; - case V4L2_PIX_FMT_NV24: - case V4L2_PIX_FMT_NV42: - tmp = LDDFR_CC | LDDFR_YF_444; - break; - } + tmp = ch->format->lddfr; - if (sh_mobile_format_is_yuv(&ch->info->var)) { - switch (ch->info->var.colorspace) { + if (ch->format->yuv) { + switch (ch->colorspace) { case V4L2_COLORSPACE_REC709: tmp |= LDDFR_CF1; break; @@ -563,7 +760,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) lcdc_write_chan(ch, LDDFR, tmp); lcdc_write_chan(ch, LDMLSR, ch->pitch); lcdc_write_chan(ch, LDSA1R, ch->base_addr_y); - if (sh_mobile_format_is_yuv(&ch->info->var)) + if (ch->format->yuv) lcdc_write_chan(ch, LDSA2R, ch->base_addr_c); /* When using deferred I/O mode, configure the LCDC for one-shot @@ -571,7 +768,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) * continuous read mode. */ if (ch->ldmt1r_value & LDMT1R_IFM && - ch->cfg.sys_bus_cfg.deferred_io_msec) { + ch->cfg->sys_bus_cfg.deferred_io_msec) { lcdc_write_chan(ch, LDSM1R, LDSM1R_OS); lcdc_write(priv, _LDINTR, LDINTR_FE); } else { @@ -580,7 +777,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) } /* Word and long word swap. */ - switch (sh_mobile_format_fourcc(&priv->ch[0].info->var)) { + switch (priv->ch[0].format->fourcc) { case V4L2_PIX_FMT_RGB565: case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_NV61: @@ -609,7 +806,6 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) { struct sh_mobile_meram_info *mdev = priv->meram_dev; - struct sh_mobile_lcdc_board_cfg *board_cfg; struct sh_mobile_lcdc_chan *ch; unsigned long tmp; int ret; @@ -626,15 +822,15 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) lcdc_wait_bit(priv, _LDCNT2R, LDCNT2R_BR, 0); for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { - ch = &priv->ch[k]; + const struct sh_mobile_lcdc_panel_cfg *panel; + ch = &priv->ch[k]; if (!ch->enabled) continue; - board_cfg = &ch->cfg.board_cfg; - if (board_cfg->setup_sys) { - ret = board_cfg->setup_sys(board_cfg->board_data, ch, - &sh_mobile_lcdc_sys_bus_ops); + panel = &ch->cfg->panel_cfg; + if (panel->setup_sys) { + ret = panel->setup_sys(ch, &sh_mobile_lcdc_sys_bus_ops); if (ret) return ret; } @@ -642,33 +838,30 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) /* Compute frame buffer base address and pitch for each channel. */ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { - struct sh_mobile_meram_cfg *cfg; int pixelformat; + void *meram; ch = &priv->ch[k]; if (!ch->enabled) continue; - ch->base_addr_y = ch->info->fix.smem_start; - ch->base_addr_c = ch->base_addr_y - + ch->info->var.xres - * ch->info->var.yres_virtual; - ch->pitch = ch->info->fix.line_length; + ch->base_addr_y = ch->dma_handle; + ch->base_addr_c = ch->base_addr_y + ch->xres * ch->yres_virtual; /* Enable MERAM if possible. */ - cfg = ch->cfg.meram_cfg; - if (mdev == NULL || mdev->ops == NULL || cfg == NULL) + if (mdev == NULL || mdev->ops == NULL || + ch->cfg->meram_cfg == NULL) continue; /* we need to de-init configured ICBs before we can * re-initialize them. */ - if (ch->meram_enabled) { - mdev->ops->meram_unregister(mdev, cfg); - ch->meram_enabled = 0; + if (ch->meram) { + mdev->ops->meram_unregister(mdev, ch->meram); + ch->meram = NULL; } - switch (sh_mobile_format_fourcc(&ch->info->var)) { + switch (ch->format->fourcc) { case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_NV16: @@ -687,13 +880,15 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) break; } - ret = mdev->ops->meram_register(mdev, cfg, ch->pitch, - ch->info->var.yres, pixelformat, - ch->base_addr_y, ch->base_addr_c, - &ch->base_addr_y, &ch->base_addr_c, + meram = mdev->ops->meram_register(mdev, ch->cfg->meram_cfg, + ch->pitch, ch->yres, pixelformat, &ch->pitch); - if (!ret) - ch->meram_enabled = 1; + if (!IS_ERR(meram)) { + mdev->ops->meram_update(mdev, meram, + ch->base_addr_y, ch->base_addr_c, + &ch->base_addr_y, &ch->base_addr_c); + ch->meram = meram; + } } /* Start the LCDC. */ @@ -707,7 +902,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) if (!ch->enabled) continue; - tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; + tmp = ch->cfg->sys_bus_cfg.deferred_io_msec; if (ch->ldmt1r_value & LDMT1R_IFM && tmp) { ch->defio.deferred_io = sh_mobile_lcdc_deferred_io; ch->defio.delay = msecs_to_jiffies(tmp); @@ -715,11 +910,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) fb_deferred_io_init(ch->info); } - board_cfg = &ch->cfg.board_cfg; - if (board_cfg->display_on && try_module_get(board_cfg->owner)) { - board_cfg->display_on(board_cfg->board_data, ch->info); - module_put(board_cfg->owner); - } + sh_mobile_lcdc_display_on(ch); if (ch->bl) { ch->bl->props.power = FB_BLANK_UNBLANK; @@ -733,7 +924,6 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) { struct sh_mobile_lcdc_chan *ch; - struct sh_mobile_lcdc_board_cfg *board_cfg; int k; /* clean up deferred io and ask board code to disable panel */ @@ -760,20 +950,14 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) backlight_update_status(ch->bl); } - board_cfg = &ch->cfg.board_cfg; - if (board_cfg->display_off && try_module_get(board_cfg->owner)) { - board_cfg->display_off(board_cfg->board_data); - module_put(board_cfg->owner); - } + sh_mobile_lcdc_display_off(ch); /* disable the meram */ - if (ch->meram_enabled) { - struct sh_mobile_meram_cfg *cfg; + if (ch->meram) { struct sh_mobile_meram_info *mdev; - cfg = ch->cfg.meram_cfg; mdev = priv->meram_dev; - mdev->ops->meram_unregister(mdev, cfg); - ch->meram_enabled = 0; + mdev->ops->meram_unregister(mdev, ch->meram); + ch->meram = 0; } } @@ -790,86 +974,9 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) sh_mobile_lcdc_clk_off(priv); } -static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch) -{ - int interface_type = ch->cfg.interface_type; - - switch (interface_type) { - case RGB8: - case RGB9: - case RGB12A: - case RGB12B: - case RGB16: - case RGB18: - case RGB24: - case SYS8A: - case SYS8B: - case SYS8C: - case SYS8D: - case SYS9: - case SYS12: - case SYS16A: - case SYS16B: - case SYS16C: - case SYS18: - case SYS24: - break; - default: - return -EINVAL; - } - - /* SUBLCD only supports SYS interface */ - if (lcdc_chan_is_sublcd(ch)) { - if (!(interface_type & LDMT1R_IFM)) - return -EINVAL; - - interface_type &= ~LDMT1R_IFM; - } - - ch->ldmt1r_value = interface_type; - return 0; -} - -static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev, - int clock_source, - struct sh_mobile_lcdc_priv *priv) -{ - char *str; - - switch (clock_source) { - case LCDC_CLK_BUS: - str = "bus_clk"; - priv->lddckr = LDDCKR_ICKSEL_BUS; - break; - case LCDC_CLK_PERIPHERAL: - str = "peripheral_clk"; - priv->lddckr = LDDCKR_ICKSEL_MIPI; - break; - case LCDC_CLK_EXTERNAL: - str = NULL; - priv->lddckr = LDDCKR_ICKSEL_HDMI; - break; - default: - return -EINVAL; - } - - if (str) { - priv->dot_clk = clk_get(&pdev->dev, str); - if (IS_ERR(priv->dot_clk)) { - dev_err(&pdev->dev, "cannot get dot clock %s\n", str); - return PTR_ERR(priv->dot_clk); - } - } - - /* Runtime PM support involves two step for this driver: - * 1) Enable Runtime PM - * 2) Force Runtime PM Resume since hardware is accessed from probe() - */ - priv->dev = &pdev->dev; - pm_runtime_enable(priv->dev); - pm_runtime_resume(priv->dev); - return 0; -} +/* ----------------------------------------------------------------------------- + * Frame buffer operations + */ static int sh_mobile_lcdc_setcolreg(u_int regno, u_int red, u_int green, u_int blue, @@ -936,14 +1043,12 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var, unsigned long new_pan_offset; unsigned long base_addr_y, base_addr_c; unsigned long c_offset; - bool yuv = sh_mobile_format_is_yuv(&info->var); - if (!yuv) - new_pan_offset = var->yoffset * info->fix.line_length - + var->xoffset * (info->var.bits_per_pixel / 8); + if (!ch->format->yuv) + new_pan_offset = var->yoffset * ch->pitch + + var->xoffset * (ch->format->bpp / 8); else - new_pan_offset = var->yoffset * info->fix.line_length - + var->xoffset; + new_pan_offset = var->yoffset * ch->pitch + var->xoffset; if (new_pan_offset == ch->pan_offset) return 0; /* No change, do nothing */ @@ -952,39 +1057,33 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var, /* Set the source address for the next refresh */ base_addr_y = ch->dma_handle + new_pan_offset; - if (yuv) { + if (ch->format->yuv) { /* Set y offset */ - c_offset = var->yoffset * info->fix.line_length - * (info->var.bits_per_pixel - 8) / 8; - base_addr_c = ch->dma_handle - + info->var.xres * info->var.yres_virtual + c_offset = var->yoffset * ch->pitch + * (ch->format->bpp - 8) / 8; + base_addr_c = ch->dma_handle + ch->xres * ch->yres_virtual + c_offset; /* Set x offset */ - if (sh_mobile_format_fourcc(&info->var) == V4L2_PIX_FMT_NV24) + if (ch->format->fourcc == V4L2_PIX_FMT_NV24) base_addr_c += 2 * var->xoffset; else base_addr_c += var->xoffset; } - if (ch->meram_enabled) { - struct sh_mobile_meram_cfg *cfg; + if (ch->meram) { struct sh_mobile_meram_info *mdev; - int ret; - cfg = ch->cfg.meram_cfg; mdev = priv->meram_dev; - ret = mdev->ops->meram_update(mdev, cfg, + mdev->ops->meram_update(mdev, ch->meram, base_addr_y, base_addr_c, &base_addr_y, &base_addr_c); - if (ret) - return ret; } ch->base_addr_y = base_addr_y; ch->base_addr_c = base_addr_c; lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y); - if (yuv) + if (ch->format->yuv) lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c); if (lcdc_chan_is_sublcd(ch)) @@ -999,27 +1098,6 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var, return 0; } -static int sh_mobile_wait_for_vsync(struct fb_info *info) -{ - struct sh_mobile_lcdc_chan *ch = info->par; - unsigned long ldintr; - int ret; - - /* Enable VSync End interrupt and be careful not to acknowledge any - * pending interrupt. - */ - ldintr = lcdc_read(ch->lcdc, _LDINTR); - ldintr |= LDINTR_VEE | LDINTR_STATUS_MASK; - lcdc_write(ch->lcdc, _LDINTR, ldintr); - - ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion, - msecs_to_jiffies(100)); - if (!ret) - return -ETIMEDOUT; - - return 0; -} - static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { @@ -1027,7 +1105,7 @@ static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd, switch (cmd) { case FBIO_WAITFORVSYNC: - retval = sh_mobile_wait_for_vsync(info); + retval = sh_mobile_wait_for_vsync(info->par); break; default: @@ -1040,7 +1118,8 @@ static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd, static void sh_mobile_fb_reconfig(struct fb_info *info) { struct sh_mobile_lcdc_chan *ch = info->par; - struct fb_videomode mode1, mode2; + struct fb_var_screeninfo var; + struct fb_videomode mode; struct fb_event event; int evnt = FB_EVENT_MODE_CHANGE_ALL; @@ -1048,14 +1127,19 @@ static void sh_mobile_fb_reconfig(struct fb_info *info) /* More framebuffer users are active */ return; - fb_var_to_videomode(&mode1, &ch->display_var); - fb_var_to_videomode(&mode2, &info->var); + fb_var_to_videomode(&mode, &info->var); - if (fb_mode_is_equal(&mode1, &mode2)) + if (fb_mode_is_equal(&ch->display.mode, &mode)) return; /* Display has been re-plugged, framebuffer is free now, reconfigure */ - if (fb_set_var(info, &ch->display_var) < 0) + var = info->var; + fb_videomode_to_var(&var, &ch->display.mode); + var.width = ch->display.width; + var.height = ch->display.height; + var.activate = FB_ACTIVATE_NOW; + + if (fb_set_var(info, &var) < 0) /* Couldn't reconfigure, hopefully, can continue as before */ return; @@ -1065,7 +1149,7 @@ static void sh_mobile_fb_reconfig(struct fb_info *info) * user event, we have to call the chain ourselves. */ event.info = info; - event.data = &mode1; + event.data = &ch->display.mode; fb_notifier_call_chain(evnt, &event); } @@ -1124,8 +1208,8 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in * distance between two modes is defined as the size of the * non-overlapping parts of the two rectangles. */ - for (i = 0; i < ch->cfg.num_cfg; ++i) { - const struct fb_videomode *mode = &ch->cfg.lcd_cfg[i]; + for (i = 0; i < ch->cfg->num_modes; ++i) { + const struct fb_videomode *mode = &ch->cfg->lcd_modes[i]; unsigned int dist; /* We can only round up. */ @@ -1144,7 +1228,7 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in } /* If no available mode can be used, return an error. */ - if (ch->cfg.num_cfg != 0) { + if (ch->cfg->num_modes != 0) { if (best_dist == (unsigned int)-1) return -EINVAL; @@ -1161,32 +1245,17 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in var->yres_virtual = var->yres; if (sh_mobile_format_is_fourcc(var)) { - switch (var->grayscale) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - var->bits_per_pixel = 12; - break; - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - var->bits_per_pixel = 16; - break; - case V4L2_PIX_FMT_BGR24: - case V4L2_PIX_FMT_NV24: - case V4L2_PIX_FMT_NV42: - var->bits_per_pixel = 24; - break; - case V4L2_PIX_FMT_BGR32: - var->bits_per_pixel = 32; - break; - default: + const struct sh_mobile_lcdc_format_info *format; + + format = sh_mobile_format_info(var->grayscale); + if (format == NULL) return -EINVAL; - } + var->bits_per_pixel = format->bpp; /* Default to RGB and JPEG color-spaces for RGB and YUV formats * respectively. */ - if (!sh_mobile_format_is_yuv(var)) + if (!format->yuv) var->colorspace = V4L2_COLORSPACE_SRGB; else if (var->colorspace != V4L2_COLORSPACE_REC709) var->colorspace = V4L2_COLORSPACE_JPEG; @@ -1246,22 +1315,28 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in static int sh_mobile_set_par(struct fb_info *info) { struct sh_mobile_lcdc_chan *ch = info->par; - u32 line_length = info->fix.line_length; int ret; sh_mobile_lcdc_stop(ch->lcdc); - if (sh_mobile_format_is_yuv(&info->var)) - info->fix.line_length = info->var.xres; + ch->format = sh_mobile_format_info(sh_mobile_format_fourcc(&info->var)); + ch->colorspace = info->var.colorspace; + + ch->xres = info->var.xres; + ch->xres_virtual = info->var.xres_virtual; + ch->yres = info->var.yres; + ch->yres_virtual = info->var.yres_virtual; + + if (ch->format->yuv) + ch->pitch = info->var.xres; else - info->fix.line_length = info->var.xres - * info->var.bits_per_pixel / 8; + ch->pitch = info->var.xres * ch->format->bpp / 8; ret = sh_mobile_lcdc_start(ch->lcdc); - if (ret < 0) { + if (ret < 0) dev_err(info->dev, "%s: unable to restart LCDC\n", __func__); - info->fix.line_length = line_length; - } + + info->fix.line_length = ch->pitch; if (sh_mobile_format_is_fourcc(&info->var)) { info->fix.type = FB_TYPE_FOURCC; @@ -1290,8 +1365,8 @@ static int sh_mobile_lcdc_blank(int blank, struct fb_info *info) /* blank the screen? */ if (blank > FB_BLANK_UNBLANK && ch->blank_status == FB_BLANK_UNBLANK) { struct fb_fillrect rect = { - .width = info->var.xres, - .height = info->var.yres, + .width = ch->xres, + .height = ch->yres, }; sh_mobile_lcdc_fillrect(info, &rect); } @@ -1307,8 +1382,8 @@ static int sh_mobile_lcdc_blank(int blank, struct fb_info *info) * mode will reenable the clocks and update the screen in time, * so it does not need this. */ if (!info->fbdefio) { - sh_mobile_wait_for_vsync(info); - sh_mobile_wait_for_vsync(info); + sh_mobile_wait_for_vsync(ch); + sh_mobile_wait_for_vsync(ch); } sh_mobile_lcdc_clk_off(p); } @@ -1334,25 +1409,161 @@ static struct fb_ops sh_mobile_lcdc_ops = { .fb_set_par = sh_mobile_set_par, }; +static void +sh_mobile_lcdc_channel_fb_unregister(struct sh_mobile_lcdc_chan *ch) +{ + if (ch->info && ch->info->dev) + unregister_framebuffer(ch->info); +} + +static int __devinit +sh_mobile_lcdc_channel_fb_register(struct sh_mobile_lcdc_chan *ch) +{ + struct fb_info *info = ch->info; + int ret; + + if (info->fbdefio) { + ch->sglist = vmalloc(sizeof(struct scatterlist) * + ch->fb_size >> PAGE_SHIFT); + if (!ch->sglist) { + dev_err(ch->lcdc->dev, "cannot allocate sglist\n"); + return -ENOMEM; + } + } + + info->bl_dev = ch->bl; + + ret = register_framebuffer(info); + if (ret < 0) + return ret; + + dev_info(ch->lcdc->dev, "registered %s/%s as %dx%d %dbpp.\n", + dev_name(ch->lcdc->dev), (ch->cfg->chan == LCDC_CHAN_MAINLCD) ? + "mainlcd" : "sublcd", info->var.xres, info->var.yres, + info->var.bits_per_pixel); + + /* deferred io mode: disable clock to save power */ + if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED) + sh_mobile_lcdc_clk_off(ch->lcdc); + + return ret; +} + +static void +sh_mobile_lcdc_channel_fb_cleanup(struct sh_mobile_lcdc_chan *ch) +{ + struct fb_info *info = ch->info; + + if (!info || !info->device) + return; + + if (ch->sglist) + vfree(ch->sglist); + + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); +} + +static int __devinit +sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch, + const struct fb_videomode *mode, + unsigned int num_modes) +{ + struct sh_mobile_lcdc_priv *priv = ch->lcdc; + struct fb_var_screeninfo *var; + struct fb_info *info; + int ret; + + /* Allocate and initialize the frame buffer device. Create the modes + * list and allocate the color map. + */ + info = framebuffer_alloc(0, priv->dev); + if (info == NULL) { + dev_err(priv->dev, "unable to allocate fb_info\n"); + return -ENOMEM; + } + + ch->info = info; + + info->flags = FBINFO_FLAG_DEFAULT; + info->fbops = &sh_mobile_lcdc_ops; + info->device = priv->dev; + info->screen_base = ch->fb_mem; + info->pseudo_palette = &ch->pseudo_palette; + info->par = ch; + + fb_videomode_to_modelist(mode, num_modes, &info->modelist); + + ret = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0); + if (ret < 0) { + dev_err(priv->dev, "unable to allocate cmap\n"); + return ret; + } + + /* Initialize fixed screen information. Restrict pan to 2 lines steps + * for NV12 and NV21. + */ + info->fix = sh_mobile_lcdc_fix; + info->fix.smem_start = ch->dma_handle; + info->fix.smem_len = ch->fb_size; + info->fix.line_length = ch->pitch; + + if (ch->format->yuv) + info->fix.visual = FB_VISUAL_FOURCC; + else + info->fix.visual = FB_VISUAL_TRUECOLOR; + + if (ch->format->fourcc == V4L2_PIX_FMT_NV12 || + ch->format->fourcc == V4L2_PIX_FMT_NV21) + info->fix.ypanstep = 2; + + /* Initialize variable screen information using the first mode as + * default. The default Y virtual resolution is twice the panel size to + * allow for double-buffering. + */ + var = &info->var; + fb_videomode_to_var(var, mode); + var->width = ch->cfg->panel_cfg.width; + var->height = ch->cfg->panel_cfg.height; + var->yres_virtual = var->yres * 2; + var->activate = FB_ACTIVATE_NOW; + + /* Use the legacy API by default for RGB formats, and the FOURCC API + * for YUV formats. + */ + if (!ch->format->yuv) + var->bits_per_pixel = ch->format->bpp; + else + var->grayscale = ch->format->fourcc; + + ret = sh_mobile_check_var(var, info); + if (ret) + return ret; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Backlight + */ + static int sh_mobile_lcdc_update_bl(struct backlight_device *bdev) { struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev); - struct sh_mobile_lcdc_board_cfg *cfg = &ch->cfg.board_cfg; int brightness = bdev->props.brightness; if (bdev->props.power != FB_BLANK_UNBLANK || bdev->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) brightness = 0; - return cfg->set_brightness(cfg->board_data, brightness); + return ch->cfg->bl_info.set_brightness(brightness); } static int sh_mobile_lcdc_get_brightness(struct backlight_device *bdev) { struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev); - struct sh_mobile_lcdc_board_cfg *cfg = &ch->cfg.board_cfg; - return cfg->get_brightness(cfg->board_data); + return ch->cfg->bl_info.get_brightness(); } static int sh_mobile_lcdc_check_fb(struct backlight_device *bdev, @@ -1373,7 +1584,7 @@ static struct backlight_device *sh_mobile_lcdc_bl_probe(struct device *parent, { struct backlight_device *bl; - bl = backlight_device_register(ch->cfg.bl_info.name, parent, ch, + bl = backlight_device_register(ch->cfg->bl_info.name, parent, ch, &sh_mobile_lcdc_bl_ops, NULL); if (IS_ERR(bl)) { dev_err(parent, "unable to register backlight device: %ld\n", @@ -1381,7 +1592,7 @@ static struct backlight_device *sh_mobile_lcdc_bl_probe(struct device *parent, return NULL; } - bl->props.max_brightness = ch->cfg.bl_info.max_brightness; + bl->props.max_brightness = ch->cfg->bl_info.max_brightness; bl->props.brightness = bl->props.max_brightness; backlight_update_status(bl); @@ -1393,6 +1604,10 @@ static void sh_mobile_lcdc_bl_remove(struct backlight_device *bdev) backlight_device_unregister(bdev); } +/* ----------------------------------------------------------------------------- + * Power management + */ + static int sh_mobile_lcdc_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -1436,6 +1651,10 @@ static const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = { .runtime_resume = sh_mobile_lcdc_runtime_resume, }; +/* ----------------------------------------------------------------------------- + * Framebuffer notifier + */ + /* locking: called with info->lock held */ static int sh_mobile_lcdc_notify(struct notifier_block *nb, unsigned long action, void *data) @@ -1443,7 +1662,6 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb, struct fb_event *event = data; struct fb_info *info = event->info; struct sh_mobile_lcdc_chan *ch = info->par; - struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; if (&ch->lcdc->notifier != nb) return NOTIFY_DONE; @@ -1453,10 +1671,7 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb, switch(action) { case FB_EVENT_SUSPEND: - if (board_cfg->display_off && try_module_get(board_cfg->owner)) { - board_cfg->display_off(board_cfg->board_data); - module_put(board_cfg->owner); - } + sh_mobile_lcdc_display_off(ch); sh_mobile_lcdc_stop(ch->lcdc); break; case FB_EVENT_RESUME: @@ -1464,47 +1679,60 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb, sh_mobile_fb_reconfig(info); mutex_unlock(&ch->open_lock); - /* HDMI must be enabled before LCDC configuration */ - if (board_cfg->display_on && try_module_get(board_cfg->owner)) { - board_cfg->display_on(board_cfg->board_data, info); - module_put(board_cfg->owner); - } - + sh_mobile_lcdc_display_on(ch); sh_mobile_lcdc_start(ch->lcdc); } return NOTIFY_OK; } +/* ----------------------------------------------------------------------------- + * Probe/remove and driver init/exit + */ + +static const struct fb_videomode default_720p __devinitconst = { + .name = "HDMI 720p", + .xres = 1280, + .yres = 720, + + .left_margin = 220, + .right_margin = 110, + .hsync_len = 40, + + .upper_margin = 20, + .lower_margin = 5, + .vsync_len = 5, + + .pixclock = 13468, + .refresh = 60, + .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT, +}; + static int sh_mobile_lcdc_remove(struct platform_device *pdev) { struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); - struct fb_info *info; int i; fb_unregister_client(&priv->notifier); for (i = 0; i < ARRAY_SIZE(priv->ch); i++) - if (priv->ch[i].info && priv->ch[i].info->dev) - unregister_framebuffer(priv->ch[i].info); + sh_mobile_lcdc_channel_fb_unregister(&priv->ch[i]); sh_mobile_lcdc_stop(priv); for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { - info = priv->ch[i].info; + struct sh_mobile_lcdc_chan *ch = &priv->ch[i]; - if (!info || !info->device) - continue; + if (ch->tx_dev) { + ch->tx_dev->lcdc = NULL; + module_put(ch->cfg->tx_dev->dev.driver->owner); + } - if (priv->ch[i].sglist) - vfree(priv->ch[i].sglist); + sh_mobile_lcdc_channel_fb_cleanup(ch); - if (info->screen_base) - dma_free_coherent(&pdev->dev, info->fix.smem_len, - info->screen_base, - priv->ch[i].dma_handle); - fb_dealloc_cmap(&info->cmap); - framebuffer_release(info); + if (ch->fb_mem) + dma_free_coherent(&pdev->dev, ch->fb_size, + ch->fb_mem, ch->dma_handle); } for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { @@ -1512,11 +1740,10 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev) sh_mobile_lcdc_bl_remove(priv->ch[i].bl); } - if (priv->dot_clk) + if (priv->dot_clk) { + pm_runtime_disable(&pdev->dev); clk_put(priv->dot_clk); - - if (priv->dev) - pm_runtime_disable(priv->dev); + } if (priv->base) iounmap(priv->base); @@ -1527,49 +1754,82 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev) return 0; } -static int __devinit sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch, - struct device *dev) +static int __devinit sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch) { - struct sh_mobile_lcdc_chan_cfg *cfg = &ch->cfg; + int interface_type = ch->cfg->interface_type; + + switch (interface_type) { + case RGB8: + case RGB9: + case RGB12A: + case RGB12B: + case RGB16: + case RGB18: + case RGB24: + case SYS8A: + case SYS8B: + case SYS8C: + case SYS8D: + case SYS9: + case SYS12: + case SYS16A: + case SYS16B: + case SYS16C: + case SYS18: + case SYS24: + break; + default: + return -EINVAL; + } + + /* SUBLCD only supports SYS interface */ + if (lcdc_chan_is_sublcd(ch)) { + if (!(interface_type & LDMT1R_IFM)) + return -EINVAL; + + interface_type &= ~LDMT1R_IFM; + } + + ch->ldmt1r_value = interface_type; + return 0; +} + +static int __devinit +sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv, + struct sh_mobile_lcdc_chan *ch) +{ + const struct sh_mobile_lcdc_format_info *format; + const struct sh_mobile_lcdc_chan_cfg *cfg = ch->cfg; const struct fb_videomode *max_mode; const struct fb_videomode *mode; - struct fb_var_screeninfo *var; - struct fb_info *info; + unsigned int num_modes; unsigned int max_size; - int num_cfg; - void *buf; - int ret; - int i; + unsigned int i; mutex_init(&ch->open_lock); + ch->notify = sh_mobile_lcdc_display_notify; - /* Allocate the frame buffer device. */ - ch->info = framebuffer_alloc(0, dev); - if (!ch->info) { - dev_err(dev, "unable to allocate fb_info\n"); - return -ENOMEM; + /* Validate the format. */ + format = sh_mobile_format_info(cfg->fourcc); + if (format == NULL) { + dev_err(priv->dev, "Invalid FOURCC %08x.\n", cfg->fourcc); + return -EINVAL; } - info = ch->info; - info->fbops = &sh_mobile_lcdc_ops; - info->par = ch; - info->pseudo_palette = &ch->pseudo_palette; - info->flags = FBINFO_FLAG_DEFAULT; - /* Iterate through the modes to validate them and find the highest * resolution. */ max_mode = NULL; max_size = 0; - for (i = 0, mode = cfg->lcd_cfg; i < cfg->num_cfg; i++, mode++) { + for (i = 0, mode = cfg->lcd_modes; i < cfg->num_modes; i++, mode++) { unsigned int size = mode->yres * mode->xres; /* NV12/NV21 buffers must have even number of lines */ if ((cfg->fourcc == V4L2_PIX_FMT_NV12 || cfg->fourcc == V4L2_PIX_FMT_NV21) && (mode->yres & 0x1)) { - dev_err(dev, "yres must be multiple of 2 for YCbCr420 " - "mode.\n"); + dev_err(priv->dev, "yres must be multiple of 2 for " + "YCbCr420 mode.\n"); return -EINVAL; } @@ -1582,93 +1842,59 @@ static int __devinit sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch, if (!max_size) max_size = MAX_XRES * MAX_YRES; else - dev_dbg(dev, "Found largest videomode %ux%u\n", + dev_dbg(priv->dev, "Found largest videomode %ux%u\n", max_mode->xres, max_mode->yres); - /* Create the mode list. */ - if (cfg->lcd_cfg == NULL) { + if (cfg->lcd_modes == NULL) { mode = &default_720p; - num_cfg = 1; + num_modes = 1; } else { - mode = cfg->lcd_cfg; - num_cfg = cfg->num_cfg; + mode = cfg->lcd_modes; + num_modes = cfg->num_modes; } - fb_videomode_to_modelist(mode, num_cfg, &info->modelist); + /* Use the first mode as default. */ + ch->format = format; + ch->xres = mode->xres; + ch->xres_virtual = mode->xres; + ch->yres = mode->yres; + ch->yres_virtual = mode->yres * 2; - /* Initialize variable screen information using the first mode as - * default. The default Y virtual resolution is twice the panel size to - * allow for double-buffering. - */ - var = &info->var; - fb_videomode_to_var(var, mode); - var->width = cfg->lcd_size_cfg.width; - var->height = cfg->lcd_size_cfg.height; - var->yres_virtual = var->yres * 2; - var->activate = FB_ACTIVATE_NOW; - - switch (cfg->fourcc) { - case V4L2_PIX_FMT_RGB565: - var->bits_per_pixel = 16; - break; - case V4L2_PIX_FMT_BGR24: - var->bits_per_pixel = 24; - break; - case V4L2_PIX_FMT_BGR32: - var->bits_per_pixel = 32; - break; - default: - var->grayscale = cfg->fourcc; - break; + if (!format->yuv) { + ch->colorspace = V4L2_COLORSPACE_SRGB; + ch->pitch = ch->xres * format->bpp / 8; + } else { + ch->colorspace = V4L2_COLORSPACE_REC709; + ch->pitch = ch->xres; } - /* Make sure the memory size check won't fail. smem_len is initialized - * later based on var. - */ - info->fix.smem_len = UINT_MAX; - ret = sh_mobile_check_var(var, info); - if (ret) - return ret; - - max_size = max_size * var->bits_per_pixel / 8 * 2; + ch->display.width = cfg->panel_cfg.width; + ch->display.height = cfg->panel_cfg.height; + ch->display.mode = *mode; - /* Allocate frame buffer memory and color map. */ - buf = dma_alloc_coherent(dev, max_size, &ch->dma_handle, GFP_KERNEL); - if (!buf) { - dev_err(dev, "unable to allocate buffer\n"); + /* Allocate frame buffer memory. */ + ch->fb_size = max_size * format->bpp / 8 * 2; + ch->fb_mem = dma_alloc_coherent(priv->dev, ch->fb_size, &ch->dma_handle, + GFP_KERNEL); + if (ch->fb_mem == NULL) { + dev_err(priv->dev, "unable to allocate buffer\n"); return -ENOMEM; } - ret = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0); - if (ret < 0) { - dev_err(dev, "unable to allocate cmap\n"); - dma_free_coherent(dev, max_size, buf, ch->dma_handle); - return ret; - } - - /* Initialize fixed screen information. Restrict pan to 2 lines steps - * for NV12 and NV21. - */ - info->fix = sh_mobile_lcdc_fix; - info->fix.smem_start = ch->dma_handle; - info->fix.smem_len = max_size; - if (cfg->fourcc == V4L2_PIX_FMT_NV12 || - cfg->fourcc == V4L2_PIX_FMT_NV21) - info->fix.ypanstep = 2; - - if (sh_mobile_format_is_yuv(var)) { - info->fix.line_length = var->xres; - info->fix.visual = FB_VISUAL_FOURCC; - } else { - info->fix.line_length = var->xres * var->bits_per_pixel / 8; - info->fix.visual = FB_VISUAL_TRUECOLOR; + /* Initialize the transmitter device if present. */ + if (cfg->tx_dev) { + if (!cfg->tx_dev->dev.driver || + !try_module_get(cfg->tx_dev->dev.driver->owner)) { + dev_warn(priv->dev, + "unable to get transmitter device\n"); + return -EINVAL; + } + ch->tx_dev = platform_get_drvdata(cfg->tx_dev); + ch->tx_dev->lcdc = ch; + ch->tx_dev->def_mode = *mode; } - info->screen_base = buf; - info->device = dev; - ch->display_var = *var; - - return 0; + return sh_mobile_lcdc_channel_fb_init(ch, mode, num_modes); } static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) @@ -1698,6 +1924,8 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) return -ENOMEM; } + priv->dev = &pdev->dev; + priv->meram_dev = pdata->meram_dev; platform_set_drvdata(pdev, priv); error = request_irq(i, sh_mobile_lcdc_irq, 0, @@ -1714,7 +1942,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) struct sh_mobile_lcdc_chan *ch = priv->ch + num_channels; ch->lcdc = priv; - memcpy(&ch->cfg, &pdata->ch[i], sizeof(pdata->ch[i])); + ch->cfg = &pdata->ch[i]; error = sh_mobile_lcdc_check_interface(ch); if (error) { @@ -1726,7 +1954,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) ch->pan_offset = 0; /* probe the backlight is there is one defined */ - if (ch->cfg.bl_info.max_brightness) + if (ch->cfg->bl_info.max_brightness) ch->bl = sh_mobile_lcdc_bl_probe(&pdev->dev, ch); switch (pdata->ch[i].chan) { @@ -1757,18 +1985,19 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) if (!priv->base) goto err1; - error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv); + error = sh_mobile_lcdc_setup_clocks(priv, pdata->clock_source); if (error) { dev_err(&pdev->dev, "unable to setup clocks\n"); goto err1; } - priv->meram_dev = pdata->meram_dev; + /* Enable runtime PM. */ + pm_runtime_enable(&pdev->dev); for (i = 0; i < num_channels; i++) { struct sh_mobile_lcdc_chan *ch = priv->ch + i; - error = sh_mobile_lcdc_channel_init(ch, &pdev->dev); + error = sh_mobile_lcdc_channel_init(priv, ch); if (error) goto err1; } @@ -1781,31 +2010,10 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) for (i = 0; i < num_channels; i++) { struct sh_mobile_lcdc_chan *ch = priv->ch + i; - struct fb_info *info = ch->info; - - if (info->fbdefio) { - ch->sglist = vmalloc(sizeof(struct scatterlist) * - info->fix.smem_len >> PAGE_SHIFT); - if (!ch->sglist) { - dev_err(&pdev->dev, "cannot allocate sglist\n"); - goto err1; - } - } - - info->bl_dev = ch->bl; - error = register_framebuffer(info); - if (error < 0) + error = sh_mobile_lcdc_channel_fb_register(ch); + if (error) goto err1; - - dev_info(info->dev, "registered %s/%s as %dx%d %dbpp.\n", - pdev->name, (ch->cfg.chan == LCDC_CHAN_MAINLCD) ? - "mainlcd" : "sublcd", info->var.xres, info->var.yres, - info->var.bits_per_pixel); - - /* deferred io mode: disable clock to save power */ - if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED) - sh_mobile_lcdc_clk_off(priv); } /* Failure ignored */ diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h index a58a0f38848b..da1c26e78a57 100644 --- a/drivers/video/sh_mobile_lcdcfb.h +++ b/drivers/video/sh_mobile_lcdcfb.h @@ -14,9 +14,35 @@ enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, #define PALETTE_NR 16 -struct sh_mobile_lcdc_priv; -struct fb_info; struct backlight_device; +struct fb_info; +struct module; +struct sh_mobile_lcdc_chan; +struct sh_mobile_lcdc_entity; +struct sh_mobile_lcdc_format_info; +struct sh_mobile_lcdc_priv; + +#define SH_MOBILE_LCDC_DISPLAY_DISCONNECTED 0 +#define SH_MOBILE_LCDC_DISPLAY_CONNECTED 1 + +struct sh_mobile_lcdc_entity_ops { + /* Display */ + int (*display_on)(struct sh_mobile_lcdc_entity *entity); + void (*display_off)(struct sh_mobile_lcdc_entity *entity); +}; + +enum sh_mobile_lcdc_entity_event { + SH_MOBILE_LCDC_EVENT_DISPLAY_CONNECT, + SH_MOBILE_LCDC_EVENT_DISPLAY_DISCONNECT, + SH_MOBILE_LCDC_EVENT_DISPLAY_MODE, +}; + +struct sh_mobile_lcdc_entity { + struct module *owner; + const struct sh_mobile_lcdc_entity_ops *ops; + struct sh_mobile_lcdc_chan *lcdc; + struct fb_videomode def_mode; +}; /* * struct sh_mobile_lcdc_chan - LCDC display channel @@ -27,29 +53,57 @@ struct backlight_device; */ struct sh_mobile_lcdc_chan { struct sh_mobile_lcdc_priv *lcdc; + struct sh_mobile_lcdc_entity *tx_dev; + const struct sh_mobile_lcdc_chan_cfg *cfg; + unsigned long *reg_offs; unsigned long ldmt1r_value; unsigned long enabled; /* ME and SE in LDCNT2R */ - struct sh_mobile_lcdc_chan_cfg cfg; - u32 pseudo_palette[PALETTE_NR]; - struct fb_info *info; - struct backlight_device *bl; + void *meram; + + struct mutex open_lock; /* protects the use counter */ + int use_count; + + void *fb_mem; + unsigned long fb_size; + dma_addr_t dma_handle; - struct fb_deferred_io defio; - struct scatterlist *sglist; - unsigned long frame_end; unsigned long pan_offset; + + unsigned long frame_end; wait_queue_head_t frame_end_wait; struct completion vsync_completion; - struct fb_var_screeninfo display_var; - int use_count; - int blank_status; - struct mutex open_lock; /* protects the use counter */ - int meram_enabled; + + const struct sh_mobile_lcdc_format_info *format; + u32 colorspace; + unsigned int xres; + unsigned int xres_virtual; + unsigned int yres; + unsigned int yres_virtual; + unsigned int pitch; unsigned long base_addr_y; unsigned long base_addr_c; - unsigned int pitch; + + int (*notify)(struct sh_mobile_lcdc_chan *ch, + enum sh_mobile_lcdc_entity_event event, + const struct fb_videomode *mode, + const struct fb_monspecs *monspec); + + /* Backlight */ + struct backlight_device *bl; + + /* FB */ + struct fb_info *info; + u32 pseudo_palette[PALETTE_NR]; + struct { + unsigned int width; + unsigned int height; + struct fb_videomode mode; + } display; + struct fb_deferred_io defio; + struct scatterlist *sglist; + int blank_status; }; #endif diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c index f45d83ecfd21..82ba830bf95d 100644 --- a/drivers/video/sh_mobile_meram.c +++ b/drivers/video/sh_mobile_meram.c @@ -9,16 +9,22 @@ * for more details. */ +#include <linux/device.h> +#include <linux/err.h> +#include <linux/genalloc.h> +#include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/device.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> -#include <linux/io.h> #include <linux/slab.h> -#include <linux/platform_device.h> + #include <video/sh_mobile_meram.h> -/* meram registers */ +/* ----------------------------------------------------------------------------- + * MERAM registers + */ + #define MEVCR1 0x4 #define MEVCR1_RST (1 << 31) #define MEVCR1_WD (1 << 30) @@ -81,16 +87,14 @@ ((yszm1) << MExxBSIZE_YSZM1_SHIFT) | \ ((xszm1) << MExxBSIZE_XSZM1_SHIFT)) -#define SH_MOBILE_MERAM_ICB_NUM 32 - -static unsigned long common_regs[] = { +static const unsigned long common_regs[] = { MEVCR1, MEQSEL1, MEQSEL2, }; -#define CMN_REGS_SIZE ARRAY_SIZE(common_regs) +#define MERAM_REGS_SIZE ARRAY_SIZE(common_regs) -static unsigned long icb_regs[] = { +static const unsigned long icb_regs[] = { MExxCTL, MExxBSIZE, MExxMNCF, @@ -100,216 +104,269 @@ static unsigned long icb_regs[] = { }; #define ICB_REGS_SIZE ARRAY_SIZE(icb_regs) +/* + * sh_mobile_meram_icb - MERAM ICB information + * @regs: Registers cache + * @index: ICB index + * @offset: MERAM block offset + * @size: MERAM block size in KiB + * @cache_unit: Bytes to cache per ICB + * @pixelformat: Video pixel format of the data stored in the ICB + * @current_reg: Which of Start Address Register A (0) or B (1) is in use + */ +struct sh_mobile_meram_icb { + unsigned long regs[ICB_REGS_SIZE]; + unsigned int index; + unsigned long offset; + unsigned int size; + + unsigned int cache_unit; + unsigned int pixelformat; + unsigned int current_reg; +}; + +#define MERAM_ICB_NUM 32 + +struct sh_mobile_meram_fb_plane { + struct sh_mobile_meram_icb *marker; + struct sh_mobile_meram_icb *cache; +}; + +struct sh_mobile_meram_fb_cache { + unsigned int nplanes; + struct sh_mobile_meram_fb_plane planes[2]; +}; + +/* + * sh_mobile_meram_priv - MERAM device + * @base: Registers base address + * @meram: MERAM physical address + * @regs: Registers cache + * @lock: Protects used_icb and icbs + * @used_icb: Bitmask of used ICBs + * @icbs: ICBs + * @pool: Allocation pool to manage the MERAM + */ struct sh_mobile_meram_priv { - void __iomem *base; - struct mutex lock; - unsigned long used_icb; - int used_meram_cache_regions; - unsigned long used_meram_cache[SH_MOBILE_MERAM_ICB_NUM]; - unsigned long cmn_saved_regs[CMN_REGS_SIZE]; - unsigned long icb_saved_regs[ICB_REGS_SIZE * SH_MOBILE_MERAM_ICB_NUM]; + void __iomem *base; + unsigned long meram; + unsigned long regs[MERAM_REGS_SIZE]; + + struct mutex lock; + unsigned long used_icb; + struct sh_mobile_meram_icb icbs[MERAM_ICB_NUM]; + + struct gen_pool *pool; }; /* settings */ -#define MERAM_SEC_LINE 15 -#define MERAM_LINE_WIDTH 2048 +#define MERAM_GRANULARITY 1024 +#define MERAM_SEC_LINE 15 +#define MERAM_LINE_WIDTH 2048 -/* - * MERAM/ICB access functions +/* ----------------------------------------------------------------------------- + * Registers access */ #define MERAM_ICB_OFFSET(base, idx, off) ((base) + (off) + (idx) * 0x20) -static inline void meram_write_icb(void __iomem *base, int idx, int off, - unsigned long val) +static inline void meram_write_icb(void __iomem *base, unsigned int idx, + unsigned int off, unsigned long val) { iowrite32(val, MERAM_ICB_OFFSET(base, idx, off)); } -static inline unsigned long meram_read_icb(void __iomem *base, int idx, int off) +static inline unsigned long meram_read_icb(void __iomem *base, unsigned int idx, + unsigned int off) { return ioread32(MERAM_ICB_OFFSET(base, idx, off)); } -static inline void meram_write_reg(void __iomem *base, int off, - unsigned long val) +static inline void meram_write_reg(void __iomem *base, unsigned int off, + unsigned long val) { iowrite32(val, base + off); } -static inline unsigned long meram_read_reg(void __iomem *base, int off) +static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off) { return ioread32(base + off); } -/* - * register ICB - */ - -#define MERAM_CACHE_START(p) ((p) >> 16) -#define MERAM_CACHE_END(p) ((p) & 0xffff) -#define MERAM_CACHE_SET(o, s) ((((o) & 0xffff) << 16) | \ - (((o) + (s) - 1) & 0xffff)) - -/* - * check if there's no overlaps in MERAM allocation. +/* ----------------------------------------------------------------------------- + * Allocation */ -static inline int meram_check_overlap(struct sh_mobile_meram_priv *priv, - struct sh_mobile_meram_icb *new) +/* Allocate ICBs and MERAM for a plane. */ +static int __meram_alloc(struct sh_mobile_meram_priv *priv, + struct sh_mobile_meram_fb_plane *plane, + size_t size) { - int i; - int used_start, used_end, meram_start, meram_end; + unsigned long mem; + unsigned long idx; - /* valid ICB? */ - if (new->marker_icb & ~0x1f || new->cache_icb & ~0x1f) - return 1; + idx = find_first_zero_bit(&priv->used_icb, 28); + if (idx == 28) + return -ENOMEM; + plane->cache = &priv->icbs[idx]; - if (test_bit(new->marker_icb, &priv->used_icb) || - test_bit(new->cache_icb, &priv->used_icb)) - return 1; + idx = find_next_zero_bit(&priv->used_icb, 32, 28); + if (idx == 32) + return -ENOMEM; + plane->marker = &priv->icbs[idx]; - for (i = 0; i < priv->used_meram_cache_regions; i++) { - used_start = MERAM_CACHE_START(priv->used_meram_cache[i]); - used_end = MERAM_CACHE_END(priv->used_meram_cache[i]); - meram_start = new->meram_offset; - meram_end = new->meram_offset + new->meram_size; + mem = gen_pool_alloc(priv->pool, size * 1024); + if (mem == 0) + return -ENOMEM; - if ((meram_start >= used_start && meram_start < used_end) || - (meram_end > used_start && meram_end < used_end)) - return 1; - } + __set_bit(plane->marker->index, &priv->used_icb); + __set_bit(plane->cache->index, &priv->used_icb); + + plane->marker->offset = mem - priv->meram; + plane->marker->size = size; return 0; } -/* - * mark the specified ICB as used - */ +/* Free ICBs and MERAM for a plane. */ +static void __meram_free(struct sh_mobile_meram_priv *priv, + struct sh_mobile_meram_fb_plane *plane) +{ + gen_pool_free(priv->pool, priv->meram + plane->marker->offset, + plane->marker->size * 1024); -static inline void meram_mark(struct sh_mobile_meram_priv *priv, - struct sh_mobile_meram_icb *new) + __clear_bit(plane->marker->index, &priv->used_icb); + __clear_bit(plane->cache->index, &priv->used_icb); +} + +/* Is this a YCbCr(NV12, NV16 or NV24) colorspace? */ +static int is_nvcolor(int cspace) { - int n; + if (cspace == SH_MOBILE_MERAM_PF_NV || + cspace == SH_MOBILE_MERAM_PF_NV24) + return 1; + return 0; +} - if (new->marker_icb < 0 || new->cache_icb < 0) - return; +/* Allocate memory for the ICBs and mark them as used. */ +static struct sh_mobile_meram_fb_cache * +meram_alloc(struct sh_mobile_meram_priv *priv, + const struct sh_mobile_meram_cfg *cfg, + int pixelformat) +{ + struct sh_mobile_meram_fb_cache *cache; + unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1; + int ret; - __set_bit(new->marker_icb, &priv->used_icb); - __set_bit(new->cache_icb, &priv->used_icb); + if (cfg->icb[0].meram_size == 0) + return ERR_PTR(-EINVAL); - n = priv->used_meram_cache_regions; + if (nplanes == 2 && cfg->icb[1].meram_size == 0) + return ERR_PTR(-EINVAL); - priv->used_meram_cache[n] = MERAM_CACHE_SET(new->meram_offset, - new->meram_size); + cache = kzalloc(sizeof(*cache), GFP_KERNEL); + if (cache == NULL) + return ERR_PTR(-ENOMEM); - priv->used_meram_cache_regions++; -} + cache->nplanes = nplanes; -/* - * unmark the specified ICB as used - */ + ret = __meram_alloc(priv, &cache->planes[0], cfg->icb[0].meram_size); + if (ret < 0) + goto error; -static inline void meram_unmark(struct sh_mobile_meram_priv *priv, - struct sh_mobile_meram_icb *icb) -{ - int i; - unsigned long pattern; - - if (icb->marker_icb < 0 || icb->cache_icb < 0) - return; - - __clear_bit(icb->marker_icb, &priv->used_icb); - __clear_bit(icb->cache_icb, &priv->used_icb); - - pattern = MERAM_CACHE_SET(icb->meram_offset, icb->meram_size); - for (i = 0; i < priv->used_meram_cache_regions; i++) { - if (priv->used_meram_cache[i] == pattern) { - while (i < priv->used_meram_cache_regions - 1) { - priv->used_meram_cache[i] = - priv->used_meram_cache[i + 1] ; - i++; - } - priv->used_meram_cache[i] = 0; - priv->used_meram_cache_regions--; - break; - } + cache->planes[0].marker->current_reg = 1; + cache->planes[0].marker->pixelformat = pixelformat; + + if (cache->nplanes == 1) + return cache; + + ret = __meram_alloc(priv, &cache->planes[1], cfg->icb[1].meram_size); + if (ret < 0) { + __meram_free(priv, &cache->planes[0]); + goto error; } + + return cache; + +error: + kfree(cache); + return ERR_PTR(-ENOMEM); } -/* - * is this a YCbCr(NV12, NV16 or NV24) colorspace - */ -static inline int is_nvcolor(int cspace) +/* Unmark the specified ICB as used. */ +static void meram_free(struct sh_mobile_meram_priv *priv, + struct sh_mobile_meram_fb_cache *cache) { - if (cspace == SH_MOBILE_MERAM_PF_NV || - cspace == SH_MOBILE_MERAM_PF_NV24) - return 1; - return 0; + __meram_free(priv, &cache->planes[0]); + if (cache->nplanes == 2) + __meram_free(priv, &cache->planes[1]); + + kfree(cache); } -/* - * set the next address to fetch - */ -static inline void meram_set_next_addr(struct sh_mobile_meram_priv *priv, - struct sh_mobile_meram_cfg *cfg, - unsigned long base_addr_y, - unsigned long base_addr_c) +/* Set the next address to fetch. */ +static void meram_set_next_addr(struct sh_mobile_meram_priv *priv, + struct sh_mobile_meram_fb_cache *cache, + unsigned long base_addr_y, + unsigned long base_addr_c) { + struct sh_mobile_meram_icb *icb = cache->planes[0].marker; unsigned long target; - target = (cfg->current_reg) ? MExxSARA : MExxSARB; - cfg->current_reg ^= 1; + icb->current_reg ^= 1; + target = icb->current_reg ? MExxSARB : MExxSARA; /* set the next address to fetch */ - meram_write_icb(priv->base, cfg->icb[0].cache_icb, target, + meram_write_icb(priv->base, cache->planes[0].cache->index, target, base_addr_y); - meram_write_icb(priv->base, cfg->icb[0].marker_icb, target, - base_addr_y + cfg->icb[0].cache_unit); - - if (is_nvcolor(cfg->pixelformat)) { - meram_write_icb(priv->base, cfg->icb[1].cache_icb, target, - base_addr_c); - meram_write_icb(priv->base, cfg->icb[1].marker_icb, target, - base_addr_c + cfg->icb[1].cache_unit); + meram_write_icb(priv->base, cache->planes[0].marker->index, target, + base_addr_y + cache->planes[0].marker->cache_unit); + + if (cache->nplanes == 2) { + meram_write_icb(priv->base, cache->planes[1].cache->index, + target, base_addr_c); + meram_write_icb(priv->base, cache->planes[1].marker->index, + target, base_addr_c + + cache->planes[1].marker->cache_unit); } } -/* - * get the next ICB address - */ -static inline void meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata, - struct sh_mobile_meram_cfg *cfg, - unsigned long *icb_addr_y, - unsigned long *icb_addr_c) +/* Get the next ICB address. */ +static void +meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata, + struct sh_mobile_meram_fb_cache *cache, + unsigned long *icb_addr_y, unsigned long *icb_addr_c) { + struct sh_mobile_meram_icb *icb = cache->planes[0].marker; unsigned long icb_offset; if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0) - icb_offset = 0x80000000 | (cfg->current_reg << 29); + icb_offset = 0x80000000 | (icb->current_reg << 29); else - icb_offset = 0xc0000000 | (cfg->current_reg << 23); + icb_offset = 0xc0000000 | (icb->current_reg << 23); - *icb_addr_y = icb_offset | (cfg->icb[0].marker_icb << 24); - if (is_nvcolor(cfg->pixelformat)) - *icb_addr_c = icb_offset | (cfg->icb[1].marker_icb << 24); + *icb_addr_y = icb_offset | (cache->planes[0].marker->index << 24); + if (cache->nplanes == 2) + *icb_addr_c = icb_offset + | (cache->planes[1].marker->index << 24); } #define MERAM_CALC_BYTECOUNT(x, y) \ (((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1)) -/* - * initialize MERAM - */ - +/* Initialize MERAM. */ static int meram_init(struct sh_mobile_meram_priv *priv, - struct sh_mobile_meram_icb *icb, - int xres, int yres, int *out_pitch) + struct sh_mobile_meram_fb_plane *plane, + unsigned int xres, unsigned int yres, + unsigned int *out_pitch) { + struct sh_mobile_meram_icb *marker = plane->marker; unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres); unsigned long bnm; - int lcdc_pitch, xpitch, line_cnt; - int save_lines; + unsigned int lcdc_pitch; + unsigned int xpitch; + unsigned int line_cnt; + unsigned int save_lines; /* adjust pitch to 1024, 2048, 4096 or 8192 */ lcdc_pitch = (xres - 1) | 1023; @@ -322,13 +379,13 @@ static int meram_init(struct sh_mobile_meram_priv *priv, lcdc_pitch = xpitch = MERAM_LINE_WIDTH; line_cnt = total_byte_count >> 11; *out_pitch = xres; - save_lines = (icb->meram_size / 16 / MERAM_SEC_LINE); + save_lines = plane->marker->size / 16 / MERAM_SEC_LINE; save_lines *= MERAM_SEC_LINE; } else { xpitch = xres; line_cnt = yres; *out_pitch = lcdc_pitch; - save_lines = icb->meram_size / (lcdc_pitch >> 10) / 2; + save_lines = plane->marker->size / (lcdc_pitch >> 10) / 2; save_lines &= 0xff; } bnm = (save_lines - 1) << 16; @@ -336,19 +393,20 @@ static int meram_init(struct sh_mobile_meram_priv *priv, /* TODO: we better to check if we have enough MERAM buffer size */ /* set up ICB */ - meram_write_icb(priv->base, icb->cache_icb, MExxBSIZE, + meram_write_icb(priv->base, plane->cache->index, MExxBSIZE, MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1)); - meram_write_icb(priv->base, icb->marker_icb, MExxBSIZE, + meram_write_icb(priv->base, plane->marker->index, MExxBSIZE, MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1)); - meram_write_icb(priv->base, icb->cache_icb, MExxMNCF, bnm); - meram_write_icb(priv->base, icb->marker_icb, MExxMNCF, bnm); + meram_write_icb(priv->base, plane->cache->index, MExxMNCF, bnm); + meram_write_icb(priv->base, plane->marker->index, MExxMNCF, bnm); - meram_write_icb(priv->base, icb->cache_icb, MExxSBSIZE, xpitch); - meram_write_icb(priv->base, icb->marker_icb, MExxSBSIZE, xpitch); + meram_write_icb(priv->base, plane->cache->index, MExxSBSIZE, xpitch); + meram_write_icb(priv->base, plane->marker->index, MExxSBSIZE, xpitch); /* save a cache unit size */ - icb->cache_unit = xres * save_lines; + plane->cache->cache_unit = xres * save_lines; + plane->marker->cache_unit = xres * save_lines; /* * Set MERAM for framebuffer @@ -356,13 +414,13 @@ static int meram_init(struct sh_mobile_meram_priv *priv, * we also chain the cache_icb and the marker_icb. * we also split the allocated MERAM buffer between two ICBs. */ - meram_write_icb(priv->base, icb->cache_icb, MExxCTL, - MERAM_MExxCTL_VAL(icb->marker_icb, icb->meram_offset) | - MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM | + meram_write_icb(priv->base, plane->cache->index, MExxCTL, + MERAM_MExxCTL_VAL(plane->marker->index, marker->offset) + | MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM | MExxCTL_MD_FB); - meram_write_icb(priv->base, icb->marker_icb, MExxCTL, - MERAM_MExxCTL_VAL(icb->cache_icb, icb->meram_offset + - icb->meram_size / 2) | + meram_write_icb(priv->base, plane->marker->index, MExxCTL, + MERAM_MExxCTL_VAL(plane->cache->index, marker->offset + + plane->marker->size / 2) | MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM | MExxCTL_MD_FB); @@ -370,239 +428,175 @@ static int meram_init(struct sh_mobile_meram_priv *priv, } static void meram_deinit(struct sh_mobile_meram_priv *priv, - struct sh_mobile_meram_icb *icb) + struct sh_mobile_meram_fb_plane *plane) { /* disable ICB */ - meram_write_icb(priv->base, icb->cache_icb, MExxCTL, + meram_write_icb(priv->base, plane->cache->index, MExxCTL, MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF); - meram_write_icb(priv->base, icb->marker_icb, MExxCTL, + meram_write_icb(priv->base, plane->marker->index, MExxCTL, MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF); - icb->cache_unit = 0; + + plane->cache->cache_unit = 0; + plane->marker->cache_unit = 0; } -/* - * register the ICB +/* ----------------------------------------------------------------------------- + * Registration/unregistration */ -static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata, - struct sh_mobile_meram_cfg *cfg, - int xres, int yres, int pixelformat, - unsigned long base_addr_y, - unsigned long base_addr_c, - unsigned long *icb_addr_y, - unsigned long *icb_addr_c, - int *pitch) +static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata, + const struct sh_mobile_meram_cfg *cfg, + unsigned int xres, unsigned int yres, + unsigned int pixelformat, + unsigned int *pitch) { - struct platform_device *pdev; - struct sh_mobile_meram_priv *priv; - int n, out_pitch; - int error = 0; - - if (!pdata || !pdata->priv || !pdata->pdev || !cfg) - return -EINVAL; + struct sh_mobile_meram_fb_cache *cache; + struct sh_mobile_meram_priv *priv = pdata->priv; + struct platform_device *pdev = pdata->pdev; + unsigned int out_pitch; if (pixelformat != SH_MOBILE_MERAM_PF_NV && pixelformat != SH_MOBILE_MERAM_PF_NV24 && pixelformat != SH_MOBILE_MERAM_PF_RGB) - return -EINVAL; - - priv = pdata->priv; - pdev = pdata->pdev; + return ERR_PTR(-EINVAL); - dev_dbg(&pdev->dev, "registering %dx%d (%s) (y=%08lx, c=%08lx)", - xres, yres, (!pixelformat) ? "yuv" : "rgb", - base_addr_y, base_addr_c); + dev_dbg(&pdev->dev, "registering %dx%d (%s)", xres, yres, + !pixelformat ? "yuv" : "rgb"); /* we can't handle wider than 8192px */ if (xres > 8192) { dev_err(&pdev->dev, "width exceeding the limit (> 8192)."); - return -EINVAL; - } - - /* do we have at least one ICB config? */ - if (cfg->icb[0].marker_icb < 0 || cfg->icb[0].cache_icb < 0) { - dev_err(&pdev->dev, "at least one ICB is required."); - return -EINVAL; + return ERR_PTR(-EINVAL); } mutex_lock(&priv->lock); - if (priv->used_meram_cache_regions + 2 > SH_MOBILE_MERAM_ICB_NUM) { - dev_err(&pdev->dev, "no more ICB available."); - error = -EINVAL; - goto err; - } - - /* make sure that there's no overlaps */ - if (meram_check_overlap(priv, &cfg->icb[0])) { - dev_err(&pdev->dev, "conflicting config detected."); - error = -EINVAL; + /* We now register the ICBs and allocate the MERAM regions. */ + cache = meram_alloc(priv, cfg, pixelformat); + if (IS_ERR(cache)) { + dev_err(&pdev->dev, "MERAM allocation failed (%ld).", + PTR_ERR(cache)); goto err; } - n = 1; - - /* do the same if we have the second ICB set */ - if (cfg->icb[1].marker_icb >= 0 && cfg->icb[1].cache_icb >= 0) { - if (meram_check_overlap(priv, &cfg->icb[1])) { - dev_err(&pdev->dev, "conflicting config detected."); - error = -EINVAL; - goto err; - } - n = 2; - } - - if (is_nvcolor(pixelformat) && n != 2) { - dev_err(&pdev->dev, "requires two ICB sets for planar Y/C."); - error = -EINVAL; - goto err; - } - - /* we now register the ICB */ - cfg->pixelformat = pixelformat; - meram_mark(priv, &cfg->icb[0]); - if (is_nvcolor(pixelformat)) - meram_mark(priv, &cfg->icb[1]); /* initialize MERAM */ - meram_init(priv, &cfg->icb[0], xres, yres, &out_pitch); + meram_init(priv, &cache->planes[0], xres, yres, &out_pitch); *pitch = out_pitch; if (pixelformat == SH_MOBILE_MERAM_PF_NV) - meram_init(priv, &cfg->icb[1], xres, (yres + 1) / 2, + meram_init(priv, &cache->planes[1], xres, (yres + 1) / 2, &out_pitch); else if (pixelformat == SH_MOBILE_MERAM_PF_NV24) - meram_init(priv, &cfg->icb[1], 2 * xres, (yres + 1) / 2, + meram_init(priv, &cache->planes[1], 2 * xres, (yres + 1) / 2, &out_pitch); - cfg->current_reg = 1; - meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c); - meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c); - - dev_dbg(&pdev->dev, "registered - can access via y=%08lx, c=%08lx", - *icb_addr_y, *icb_addr_c); - err: mutex_unlock(&priv->lock); - return error; + return cache; } -static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, - struct sh_mobile_meram_cfg *cfg) +static void +sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, void *data) { - struct sh_mobile_meram_priv *priv; - - if (!pdata || !pdata->priv || !cfg) - return -EINVAL; - - priv = pdata->priv; + struct sh_mobile_meram_fb_cache *cache = data; + struct sh_mobile_meram_priv *priv = pdata->priv; mutex_lock(&priv->lock); - /* deinit & unmark */ - if (is_nvcolor(cfg->pixelformat)) { - meram_deinit(priv, &cfg->icb[1]); - meram_unmark(priv, &cfg->icb[1]); - } - meram_deinit(priv, &cfg->icb[0]); - meram_unmark(priv, &cfg->icb[0]); + /* deinit & free */ + meram_deinit(priv, &cache->planes[0]); + if (cache->nplanes == 2) + meram_deinit(priv, &cache->planes[1]); - mutex_unlock(&priv->lock); + meram_free(priv, cache); - return 0; + mutex_unlock(&priv->lock); } -static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, - struct sh_mobile_meram_cfg *cfg, - unsigned long base_addr_y, - unsigned long base_addr_c, - unsigned long *icb_addr_y, - unsigned long *icb_addr_c) +static void +sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data, + unsigned long base_addr_y, unsigned long base_addr_c, + unsigned long *icb_addr_y, unsigned long *icb_addr_c) { - struct sh_mobile_meram_priv *priv; - - if (!pdata || !pdata->priv || !cfg) - return -EINVAL; - - priv = pdata->priv; + struct sh_mobile_meram_fb_cache *cache = data; + struct sh_mobile_meram_priv *priv = pdata->priv; mutex_lock(&priv->lock); - meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c); - meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c); + meram_set_next_addr(priv, cache, base_addr_y, base_addr_c); + meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c); mutex_unlock(&priv->lock); - - return 0; } -static int sh_mobile_meram_runtime_suspend(struct device *dev) +static struct sh_mobile_meram_ops sh_mobile_meram_ops = { + .module = THIS_MODULE, + .meram_register = sh_mobile_meram_register, + .meram_unregister = sh_mobile_meram_unregister, + .meram_update = sh_mobile_meram_update, +}; + +/* ----------------------------------------------------------------------------- + * Power management + */ + +static int sh_mobile_meram_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev); - int k, j; + unsigned int i, j; - for (k = 0; k < CMN_REGS_SIZE; k++) - priv->cmn_saved_regs[k] = meram_read_reg(priv->base, - common_regs[k]); + for (i = 0; i < MERAM_REGS_SIZE; i++) + priv->regs[i] = meram_read_reg(priv->base, common_regs[i]); - for (j = 0; j < 32; j++) { - if (!test_bit(j, &priv->used_icb)) + for (i = 0; i < 32; i++) { + if (!test_bit(i, &priv->used_icb)) continue; - for (k = 0; k < ICB_REGS_SIZE; k++) { - priv->icb_saved_regs[j * ICB_REGS_SIZE + k] = - meram_read_icb(priv->base, j, icb_regs[k]); + for (j = 0; j < ICB_REGS_SIZE; j++) { + priv->icbs[i].regs[j] = + meram_read_icb(priv->base, i, icb_regs[j]); /* Reset ICB on resume */ - if (icb_regs[k] == MExxCTL) - priv->icb_saved_regs[j * ICB_REGS_SIZE + k] |= + if (icb_regs[j] == MExxCTL) + priv->icbs[i].regs[j] |= MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF; } } return 0; } -static int sh_mobile_meram_runtime_resume(struct device *dev) +static int sh_mobile_meram_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev); - int k, j; + unsigned int i, j; - for (j = 0; j < 32; j++) { - if (!test_bit(j, &priv->used_icb)) + for (i = 0; i < 32; i++) { + if (!test_bit(i, &priv->used_icb)) continue; - for (k = 0; k < ICB_REGS_SIZE; k++) { - meram_write_icb(priv->base, j, icb_regs[k], - priv->icb_saved_regs[j * ICB_REGS_SIZE + k]); - } + for (j = 0; j < ICB_REGS_SIZE; j++) + meram_write_icb(priv->base, i, icb_regs[j], + priv->icbs[i].regs[j]); } - for (k = 0; k < CMN_REGS_SIZE; k++) - meram_write_reg(priv->base, common_regs[k], - priv->cmn_saved_regs[k]); + for (i = 0; i < MERAM_REGS_SIZE; i++) + meram_write_reg(priv->base, common_regs[i], priv->regs[i]); return 0; } -static const struct dev_pm_ops sh_mobile_meram_dev_pm_ops = { - .runtime_suspend = sh_mobile_meram_runtime_suspend, - .runtime_resume = sh_mobile_meram_runtime_resume, -}; - -static struct sh_mobile_meram_ops sh_mobile_meram_ops = { - .module = THIS_MODULE, - .meram_register = sh_mobile_meram_register, - .meram_unregister = sh_mobile_meram_unregister, - .meram_update = sh_mobile_meram_update, -}; +static UNIVERSAL_DEV_PM_OPS(sh_mobile_meram_dev_pm_ops, + sh_mobile_meram_suspend, + sh_mobile_meram_resume, NULL); -/* - * initialize MERAM +/* ----------------------------------------------------------------------------- + * Probe/remove and driver init/exit */ -static int sh_mobile_meram_remove(struct platform_device *pdev); - static int __devinit sh_mobile_meram_probe(struct platform_device *pdev) { struct sh_mobile_meram_priv *priv; struct sh_mobile_meram_info *pdata = pdev->dev.platform_data; - struct resource *res; + struct resource *regs; + struct resource *meram; + unsigned int i; int error; if (!pdata) { @@ -610,8 +604,9 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + meram = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (regs == NULL || meram == NULL) { dev_err(&pdev->dev, "cannot get platform resources\n"); return -ENOENT; } @@ -622,32 +617,74 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev) return -ENOMEM; } - platform_set_drvdata(pdev, priv); - - /* initialize private data */ + /* Initialize private data. */ mutex_init(&priv->lock); - priv->base = ioremap_nocache(res->start, resource_size(res)); + priv->used_icb = pdata->reserved_icbs; + + for (i = 0; i < MERAM_ICB_NUM; ++i) + priv->icbs[i].index = i; + + pdata->ops = &sh_mobile_meram_ops; + pdata->priv = priv; + pdata->pdev = pdev; + + /* Request memory regions and remap the registers. */ + if (!request_mem_region(regs->start, resource_size(regs), pdev->name)) { + dev_err(&pdev->dev, "MERAM registers region already claimed\n"); + error = -EBUSY; + goto err_req_regs; + } + + if (!request_mem_region(meram->start, resource_size(meram), + pdev->name)) { + dev_err(&pdev->dev, "MERAM memory region already claimed\n"); + error = -EBUSY; + goto err_req_meram; + } + + priv->base = ioremap_nocache(regs->start, resource_size(regs)); if (!priv->base) { dev_err(&pdev->dev, "ioremap failed\n"); error = -EFAULT; - goto err; + goto err_ioremap; } - pdata->ops = &sh_mobile_meram_ops; - pdata->priv = priv; - pdata->pdev = pdev; + + priv->meram = meram->start; + + /* Create and initialize the MERAM memory pool. */ + priv->pool = gen_pool_create(ilog2(MERAM_GRANULARITY), -1); + if (priv->pool == NULL) { + error = -ENOMEM; + goto err_genpool; + } + + error = gen_pool_add(priv->pool, meram->start, resource_size(meram), + -1); + if (error < 0) + goto err_genpool; /* initialize ICB addressing mode */ if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1) meram_write_reg(priv->base, MEVCR1, MEVCR1_AMD1); + platform_set_drvdata(pdev, priv); pm_runtime_enable(&pdev->dev); dev_info(&pdev->dev, "sh_mobile_meram initialized."); return 0; -err: - sh_mobile_meram_remove(pdev); +err_genpool: + if (priv->pool) + gen_pool_destroy(priv->pool); + iounmap(priv->base); +err_ioremap: + release_mem_region(meram->start, resource_size(meram)); +err_req_meram: + release_mem_region(regs->start, resource_size(regs)); +err_req_regs: + mutex_destroy(&priv->lock); + kfree(priv); return error; } @@ -656,11 +693,16 @@ err: static int sh_mobile_meram_remove(struct platform_device *pdev) { struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev); + struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *meram = platform_get_resource(pdev, IORESOURCE_MEM, 1); pm_runtime_disable(&pdev->dev); - if (priv->base) - iounmap(priv->base); + gen_pool_destroy(priv->pool); + + iounmap(priv->base); + release_mem_region(meram->start, resource_size(meram)); + release_mem_region(regs->start, resource_size(regs)); mutex_destroy(&priv->lock); diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c index a40c05ebbdc2..a159b63e18b9 100644 --- a/drivers/video/udlfb.c +++ b/drivers/video/udlfb.c @@ -72,6 +72,7 @@ MODULE_DEVICE_TABLE(usb, id_table); static bool console = 1; /* Allow fbcon to open framebuffer */ static bool fb_defio = 1; /* Detect mmap writes using page faults */ static bool shadow = 1; /* Optionally disable shadow framebuffer */ +static int pixel_limit; /* Optionally force a pixel resolution limit */ /* dlfb keeps a list of urbs for efficient bulk transfers */ static void dlfb_urb_completion(struct urb *urb); @@ -918,10 +919,6 @@ static void dlfb_free(struct kref *kref) { struct dlfb_data *dev = container_of(kref, struct dlfb_data, kref); - /* this function will wait for all in-flight urbs to complete */ - if (dev->urbs.count > 0) - dlfb_free_urb_list(dev); - if (dev->backing_buffer) vfree(dev->backing_buffer); @@ -940,35 +937,42 @@ static void dlfb_release_urb_work(struct work_struct *work) up(&unode->dev->urbs.limit_sem); } -static void dlfb_free_framebuffer_work(struct work_struct *work) +static void dlfb_free_framebuffer(struct dlfb_data *dev) { - struct dlfb_data *dev = container_of(work, struct dlfb_data, - free_framebuffer_work.work); struct fb_info *info = dev->info; - int node = info->node; - unregister_framebuffer(info); + if (info) { + int node = info->node; - if (info->cmap.len != 0) - fb_dealloc_cmap(&info->cmap); - if (info->monspecs.modedb) - fb_destroy_modedb(info->monspecs.modedb); - if (info->screen_base) - vfree(info->screen_base); + unregister_framebuffer(info); - fb_destroy_modelist(&info->modelist); + if (info->cmap.len != 0) + fb_dealloc_cmap(&info->cmap); + if (info->monspecs.modedb) + fb_destroy_modedb(info->monspecs.modedb); + if (info->screen_base) + vfree(info->screen_base); - dev->info = 0; + fb_destroy_modelist(&info->modelist); - /* Assume info structure is freed after this point */ - framebuffer_release(info); + dev->info = NULL; - pr_warn("fb_info for /dev/fb%d has been freed\n", node); + /* Assume info structure is freed after this point */ + framebuffer_release(info); + + pr_warn("fb_info for /dev/fb%d has been freed\n", node); + } /* ref taken in probe() as part of registering framebfufer */ kref_put(&dev->kref, dlfb_free); } +static void dlfb_free_framebuffer_work(struct work_struct *work) +{ + struct dlfb_data *dev = container_of(work, struct dlfb_data, + free_framebuffer_work.work); + dlfb_free_framebuffer(dev); +} /* * Assumes caller is holding info->lock mutex (for open and release at least) */ @@ -1012,7 +1016,8 @@ static int dlfb_is_valid_mode(struct fb_videomode *mode, return 0; } - pr_info("%dx%d valid mode\n", mode->xres, mode->yres); + pr_info("%dx%d @ %d Hz valid mode\n", mode->xres, mode->yres, + mode->refresh); return 1; } @@ -1427,19 +1432,22 @@ static ssize_t edid_store( struct device *fbdev = container_of(kobj, struct device, kobj); struct fb_info *fb_info = dev_get_drvdata(fbdev); struct dlfb_data *dev = fb_info->par; + int ret; /* We only support write of entire EDID at once, no offset*/ if ((src_size != EDID_LENGTH) || (src_off != 0)) - return 0; + return -EINVAL; - dlfb_setup_modes(dev, fb_info, src, src_size); + ret = dlfb_setup_modes(dev, fb_info, src, src_size); + if (ret) + return ret; - if (dev->edid && (memcmp(src, dev->edid, src_size) == 0)) { - pr_info("sysfs written EDID is new default\n"); - dlfb_ops_set_par(fb_info); - return src_size; - } else - return 0; + if (!dev->edid || memcmp(src, dev->edid, src_size)) + return -EINVAL; + + pr_info("sysfs written EDID is new default\n"); + dlfb_ops_set_par(fb_info); + return src_size; } static ssize_t metrics_reset_store(struct device *fbdev, @@ -1537,7 +1545,7 @@ static int dlfb_parse_vendor_descriptor(struct dlfb_data *dev, u8 length; u16 key; - key = *((u16 *) desc); + key = le16_to_cpu(*((u16 *) desc)); desc += sizeof(u16); length = *desc; desc++; @@ -1570,14 +1578,15 @@ success: kfree(buf); return true; } + +static void dlfb_init_framebuffer_work(struct work_struct *work); + static int dlfb_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct usb_device *usbdev; struct dlfb_data *dev = 0; - struct fb_info *info = 0; int retval = -ENOMEM; - int i; /* usb initialization */ @@ -1589,9 +1598,7 @@ static int dlfb_usb_probe(struct usb_interface *interface, goto error; } - /* we need to wait for both usb and fbdev to spin down on disconnect */ kref_init(&dev->kref); /* matching kref_put in usb .disconnect fn */ - kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */ dev->udev = usbdev; dev->gdev = &usbdev->dev; /* our generic struct device * */ @@ -1613,16 +1620,53 @@ static int dlfb_usb_probe(struct usb_interface *interface, goto error; } + if (pixel_limit) { + pr_warn("DL chip limit of %d overriden" + " by module param to %d\n", + dev->sku_pixel_limit, pixel_limit); + dev->sku_pixel_limit = pixel_limit; + } + + if (!dlfb_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) { retval = -ENOMEM; pr_err("dlfb_alloc_urb_list failed\n"); goto error; } + kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */ + /* We don't register a new USB class. Our client interface is fbdev */ + /* Workitem keep things fast & simple during USB enumeration */ + INIT_DELAYED_WORK(&dev->init_framebuffer_work, + dlfb_init_framebuffer_work); + schedule_delayed_work(&dev->init_framebuffer_work, 0); + + return 0; + +error: + if (dev) { + + kref_put(&dev->kref, dlfb_free); /* ref for framebuffer */ + kref_put(&dev->kref, dlfb_free); /* last ref from kref_init */ + + /* dev has been deallocated. Do not dereference */ + } + + return retval; +} + +static void dlfb_init_framebuffer_work(struct work_struct *work) +{ + struct dlfb_data *dev = container_of(work, struct dlfb_data, + init_framebuffer_work.work); + struct fb_info *info; + int retval; + int i; + /* allocates framebuffer driver structure, not framebuffer memory */ - info = framebuffer_alloc(0, &interface->dev); + info = framebuffer_alloc(0, dev->gdev); if (!info) { retval = -ENOMEM; pr_err("framebuffer_alloc failed\n"); @@ -1668,15 +1712,13 @@ static int dlfb_usb_probe(struct usb_interface *interface, for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) { retval = device_create_file(info->dev, &fb_device_attrs[i]); if (retval) { - pr_err("device_create_file failed %d\n", retval); - goto err_del_attrs; + pr_warn("device_create_file failed %d\n", retval); } } retval = device_create_bin_file(info->dev, &edid_attr); if (retval) { - pr_err("device_create_bin_file failed %d\n", retval); - goto err_del_attrs; + pr_warn("device_create_bin_file failed %d\n", retval); } pr_info("DisplayLink USB device /dev/fb%d attached. %dx%d resolution." @@ -1684,38 +1726,10 @@ static int dlfb_usb_probe(struct usb_interface *interface, info->var.xres, info->var.yres, ((dev->backing_buffer) ? info->fix.smem_len * 2 : info->fix.smem_len) >> 10); - return 0; - -err_del_attrs: - for (i -= 1; i >= 0; i--) - device_remove_file(info->dev, &fb_device_attrs[i]); + return; error: - if (dev) { - - if (info) { - if (info->cmap.len != 0) - fb_dealloc_cmap(&info->cmap); - if (info->monspecs.modedb) - fb_destroy_modedb(info->monspecs.modedb); - if (info->screen_base) - vfree(info->screen_base); - - fb_destroy_modelist(&info->modelist); - - framebuffer_release(info); - } - - if (dev->backing_buffer) - vfree(dev->backing_buffer); - - kref_put(&dev->kref, dlfb_free); /* ref for framebuffer */ - kref_put(&dev->kref, dlfb_free); /* last ref from kref_init */ - - /* dev has been deallocated. Do not dereference */ - } - - return retval; + dlfb_free_framebuffer(dev); } static void dlfb_usb_disconnect(struct usb_interface *interface) @@ -1735,12 +1749,20 @@ static void dlfb_usb_disconnect(struct usb_interface *interface) /* When non-active we'll update virtual framebuffer, but no new urbs */ atomic_set(&dev->usb_active, 0); - /* remove udlfb's sysfs interfaces */ - for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) - device_remove_file(info->dev, &fb_device_attrs[i]); - device_remove_bin_file(info->dev, &edid_attr); - unlink_framebuffer(info); + /* this function will wait for all in-flight urbs to complete */ + dlfb_free_urb_list(dev); + + if (info) { + /* remove udlfb's sysfs interfaces */ + for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) + device_remove_file(info->dev, &fb_device_attrs[i]); + device_remove_bin_file(info->dev, &edid_attr); + unlink_framebuffer(info); + } + usb_set_intfdata(interface, NULL); + dev->udev = NULL; + dev->gdev = NULL; /* if clients still have us open, will be freed on last close */ if (dev->fb_count == 0) @@ -1806,12 +1828,12 @@ static void dlfb_free_urb_list(struct dlfb_data *dev) int ret; unsigned long flags; - pr_notice("Waiting for completes and freeing all render urbs\n"); + pr_notice("Freeing all render urbs\n"); /* keep waiting and freeing, until we've got 'em all */ while (count--) { - /* Getting interrupted means a leak, but ok at shutdown*/ + /* Getting interrupted means a leak, but ok at disconnect */ ret = down_interruptible(&dev->urbs.limit_sem); if (ret) break; @@ -1833,6 +1855,7 @@ static void dlfb_free_urb_list(struct dlfb_data *dev) kfree(node); } + dev->urbs.count = 0; } static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size) @@ -1948,6 +1971,9 @@ MODULE_PARM_DESC(fb_defio, "Page fault detection of mmap writes"); module_param(shadow, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); MODULE_PARM_DESC(shadow, "Shadow vid mem. Disable to save mem but lose perf"); +module_param(pixel_limit, int, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); +MODULE_PARM_DESC(pixel_limit, "Force limit on max mode (in x*y pixels)"); + MODULE_AUTHOR("Roberto De Ioris <roberto@unbit.it>, " "Jaya Kumar <jayakumar.lkml@gmail.com>, " "Bernie Thompson <bernie@plugable.com>"); diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c index e7f69ef572dc..260cca7ddb41 100644 --- a/drivers/video/uvesafb.c +++ b/drivers/video/uvesafb.c @@ -121,7 +121,7 @@ static int uvesafb_helper_start(void) NULL, }; - return call_usermodehelper(v86d_path, argv, envp, 1); + return call_usermodehelper(v86d_path, argv, envp, UMH_WAIT_PROC); } /* @@ -362,7 +362,7 @@ static u8 *uvesafb_vbe_state_save(struct uvesafb_par *par) state = kmalloc(par->vbe_state_size, GFP_KERNEL); if (!state) - return NULL; + return ERR_PTR(-ENOMEM); task = uvesafb_prep(); if (!task) { @@ -1172,9 +1172,17 @@ static int uvesafb_open(struct fb_info *info, int user) { struct uvesafb_par *par = info->par; int cnt = atomic_read(&par->ref_count); + u8 *buf = NULL; - if (!cnt && par->vbe_state_size) - par->vbe_state_orig = uvesafb_vbe_state_save(par); + if (!cnt && par->vbe_state_size) { + buf = uvesafb_vbe_state_save(par); + if (IS_ERR(buf)) { + printk(KERN_WARNING "uvesafb: save hardware state" + "failed, error code is %ld!\n", PTR_ERR(buf)); + } else { + par->vbe_state_orig = buf; + } + } atomic_inc(&par->ref_count); return 0; diff --git a/drivers/video/via/Makefile b/drivers/video/via/Makefile index 5108136e8776..159f26e6adb5 100644 --- a/drivers/video/via/Makefile +++ b/drivers/video/via/Makefile @@ -6,4 +6,7 @@ obj-$(CONFIG_FB_VIA) += viafb.o viafb-y :=viafbdev.o hw.o via_i2c.o dvi.o lcd.o ioctl.o accel.o \ via_utility.o vt1636.o global.o tblDPASetting.o viamode.o \ - via-core.o via-gpio.o via_modesetting.o via_clock.o + via-core.o via-gpio.o via_modesetting.o via_clock.o \ + via_aux.o via_aux_edid.o via_aux_vt1636.o via_aux_vt1632.o \ + via_aux_vt1631.o via_aux_vt1625.o via_aux_vt1622.o via_aux_vt1621.o \ + via_aux_sii164.o via_aux_ch7301.o diff --git a/drivers/video/via/chip.h b/drivers/video/via/chip.h index 3ebf20c06eef..d32a5076c20f 100644 --- a/drivers/video/via/chip.h +++ b/drivers/video/via/chip.h @@ -146,9 +146,6 @@ struct tmds_setting_information { struct lvds_setting_information { int iga_path; - int h_active; - int v_active; - int bpp; int lcd_panel_hres; int lcd_panel_vres; int display_method; diff --git a/drivers/video/via/dvi.c b/drivers/video/via/dvi.c index 9138e517267c..6be72f0ba21d 100644 --- a/drivers/video/via/dvi.c +++ b/drivers/video/via/dvi.c @@ -172,10 +172,11 @@ static int tmds_register_read_bytes(int index, u8 *buff, int buff_len) } /* DVI Set Mode */ -void viafb_dvi_set_mode(const struct fb_var_screeninfo *var, int iga) +void viafb_dvi_set_mode(const struct fb_var_screeninfo *var, + u16 cxres, u16 cyres, int iga) { struct fb_var_screeninfo dvi_var = *var; - struct crt_mode_table *rb_mode; + const struct fb_videomode *rb_mode; int maxPixelClock; maxPixelClock = viaparinfo->shared->tmds_setting_info.max_pixel_clock; @@ -185,7 +186,7 @@ void viafb_dvi_set_mode(const struct fb_var_screeninfo *var, int iga) viafb_fill_var_timing_info(&dvi_var, rb_mode); } - viafb_fill_crtc_timing(&dvi_var, iga); + viafb_fill_crtc_timing(&dvi_var, cxres, cyres, iga); } /* Sense DVI Connector */ diff --git a/drivers/video/via/dvi.h b/drivers/video/via/dvi.h index e2116aaf797a..db757850c216 100644 --- a/drivers/video/via/dvi.h +++ b/drivers/video/via/dvi.h @@ -59,6 +59,7 @@ void viafb_dvi_enable(void); bool __devinit viafb_tmds_trasmitter_identify(void); void __devinit viafb_init_dvi_size(struct tmds_chip_information *tmds_chip, struct tmds_setting_information *tmds_setting); -void viafb_dvi_set_mode(const struct fb_var_screeninfo *var, int iga); +void viafb_dvi_set_mode(const struct fb_var_screeninfo *var, + u16 cxres, u16 cyres, int iga); #endif /* __DVI_H__ */ diff --git a/drivers/video/via/hw.c b/drivers/video/via/hw.c index 8497727d66de..898590db5e14 100644 --- a/drivers/video/via/hw.c +++ b/drivers/video/via/hw.c @@ -1467,28 +1467,32 @@ void viafb_set_vclock(u32 clk, int set_iga) via_write_misc_reg_mask(0x0C, 0x0C); /* select external clock */ } -static struct display_timing var_to_timing(const struct fb_var_screeninfo *var) +struct display_timing var_to_timing(const struct fb_var_screeninfo *var, + u16 cxres, u16 cyres) { struct display_timing timing; + u16 dx = (var->xres - cxres) / 2, dy = (var->yres - cyres) / 2; - timing.hor_addr = var->xres; - timing.hor_sync_start = timing.hor_addr + var->right_margin; + timing.hor_addr = cxres; + timing.hor_sync_start = timing.hor_addr + var->right_margin + dx; timing.hor_sync_end = timing.hor_sync_start + var->hsync_len; - timing.hor_total = timing.hor_sync_end + var->left_margin; - timing.hor_blank_start = timing.hor_addr; - timing.hor_blank_end = timing.hor_total; - timing.ver_addr = var->yres; - timing.ver_sync_start = timing.ver_addr + var->lower_margin; + timing.hor_total = timing.hor_sync_end + var->left_margin + dx; + timing.hor_blank_start = timing.hor_addr + dx; + timing.hor_blank_end = timing.hor_total - dx; + timing.ver_addr = cyres; + timing.ver_sync_start = timing.ver_addr + var->lower_margin + dy; timing.ver_sync_end = timing.ver_sync_start + var->vsync_len; - timing.ver_total = timing.ver_sync_end + var->upper_margin; - timing.ver_blank_start = timing.ver_addr; - timing.ver_blank_end = timing.ver_total; + timing.ver_total = timing.ver_sync_end + var->upper_margin + dy; + timing.ver_blank_start = timing.ver_addr + dy; + timing.ver_blank_end = timing.ver_total - dy; return timing; } -void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, int iga) +void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, + u16 cxres, u16 cyres, int iga) { - struct display_timing crt_reg = var_to_timing(var); + struct display_timing crt_reg = var_to_timing(var, + cxres ? cxres : var->xres, cyres ? cyres : var->yres); if (iga == IGA1) via_set_primary_timing(&crt_reg); @@ -1526,13 +1530,6 @@ void viafb_update_device_setting(int hres, int vres, int bpp, int flag) if (flag == 0) { viaparinfo->tmds_setting_info->h_active = hres; viaparinfo->tmds_setting_info->v_active = vres; - - viaparinfo->lvds_setting_info->h_active = hres; - viaparinfo->lvds_setting_info->v_active = vres; - viaparinfo->lvds_setting_info->bpp = bpp; - viaparinfo->lvds_setting_info2->h_active = hres; - viaparinfo->lvds_setting_info2->v_active = vres; - viaparinfo->lvds_setting_info2->bpp = bpp; } else { if (viaparinfo->tmds_setting_info->iga_path == IGA2) { @@ -1540,16 +1537,6 @@ void viafb_update_device_setting(int hres, int vres, int bpp, int flag) viaparinfo->tmds_setting_info->v_active = vres; } - if (viaparinfo->lvds_setting_info->iga_path == IGA2) { - viaparinfo->lvds_setting_info->h_active = hres; - viaparinfo->lvds_setting_info->v_active = vres; - viaparinfo->lvds_setting_info->bpp = bpp; - } - if (IGA2 == viaparinfo->lvds_setting_info2->iga_path) { - viaparinfo->lvds_setting_info2->h_active = hres; - viaparinfo->lvds_setting_info2->v_active = vres; - viaparinfo->lvds_setting_info2->bpp = bpp; - } } } @@ -1758,13 +1745,13 @@ static void set_display_channel(void) } } -static u8 get_sync(struct fb_info *info) +static u8 get_sync(struct fb_var_screeninfo *var) { u8 polarity = 0; - if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT)) + if (!(var->sync & FB_SYNC_HOR_HIGH_ACT)) polarity |= VIA_HSYNC_NEGATIVE; - if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT)) + if (!(var->sync & FB_SYNC_VERT_HIGH_ACT)) polarity |= VIA_VSYNC_NEGATIVE; return polarity; } @@ -1844,9 +1831,9 @@ static void hw_init(void) load_fix_bit_crtc_reg(); } -int viafb_setmode(int video_bpp, int video_bpp1) +int viafb_setmode(void) { - int j; + int j, cxres = 0, cyres = 0; int port; u32 devices = viaparinfo->shared->iga1_devices | viaparinfo->shared->iga2_devices; @@ -1895,6 +1882,8 @@ int viafb_setmode(int video_bpp, int video_bpp1) } else if (viafb_SAMM_ON) { viafb_fill_var_timing_info(&var2, viafb_get_best_mode( viafb_second_xres, viafb_second_yres, viafb_refresh1)); + cxres = viafbinfo->var.xres; + cyres = viafbinfo->var.yres; var2.bits_per_pixel = viafbinfo->var.bits_per_pixel; } @@ -1902,9 +1891,9 @@ int viafb_setmode(int video_bpp, int video_bpp1) if (viafb_CRT_ON) { if (viaparinfo->shared->iga2_devices & VIA_CRT && viafb_SAMM_ON) - viafb_fill_crtc_timing(&var2, IGA2); + viafb_fill_crtc_timing(&var2, cxres, cyres, IGA2); else - viafb_fill_crtc_timing(&viafbinfo->var, + viafb_fill_crtc_timing(&viafbinfo->var, 0, 0, (viaparinfo->shared->iga1_devices & VIA_CRT) ? IGA1 : IGA2); @@ -1922,17 +1911,17 @@ int viafb_setmode(int video_bpp, int video_bpp1) if (viafb_DVI_ON) { if (viaparinfo->shared->tmds_setting_info.iga_path == IGA2 && viafb_SAMM_ON) - viafb_dvi_set_mode(&var2, IGA2); + viafb_dvi_set_mode(&var2, cxres, cyres, IGA2); else - viafb_dvi_set_mode(&viafbinfo->var, + viafb_dvi_set_mode(&viafbinfo->var, 0, 0, viaparinfo->tmds_setting_info->iga_path); } if (viafb_LCD_ON) { if (viafb_SAMM_ON && (viaparinfo->lvds_setting_info->iga_path == IGA2)) { - viaparinfo->lvds_setting_info->bpp = video_bpp1; - viafb_lcd_set_mode(viaparinfo->lvds_setting_info, + viafb_lcd_set_mode(&var2, cxres, cyres, + viaparinfo->lvds_setting_info, &viaparinfo->chip_info->lvds_chip_info); } else { /* IGA1 doesn't have LCD scaling, so set it center. */ @@ -1940,16 +1929,16 @@ int viafb_setmode(int video_bpp, int video_bpp1) viaparinfo->lvds_setting_info->display_method = LCD_CENTERING; } - viaparinfo->lvds_setting_info->bpp = video_bpp; - viafb_lcd_set_mode(viaparinfo->lvds_setting_info, + viafb_lcd_set_mode(&viafbinfo->var, 0, 0, + viaparinfo->lvds_setting_info, &viaparinfo->chip_info->lvds_chip_info); } } if (viafb_LCD2_ON) { if (viafb_SAMM_ON && (viaparinfo->lvds_setting_info2->iga_path == IGA2)) { - viaparinfo->lvds_setting_info2->bpp = video_bpp1; - viafb_lcd_set_mode(viaparinfo->lvds_setting_info2, + viafb_lcd_set_mode(&var2, cxres, cyres, + viaparinfo->lvds_setting_info2, &viaparinfo->chip_info->lvds_chip_info2); } else { /* IGA1 doesn't have LCD scaling, so set it center. */ @@ -1957,8 +1946,8 @@ int viafb_setmode(int video_bpp, int video_bpp1) viaparinfo->lvds_setting_info2->display_method = LCD_CENTERING; } - viaparinfo->lvds_setting_info2->bpp = video_bpp; - viafb_lcd_set_mode(viaparinfo->lvds_setting_info2, + viafb_lcd_set_mode(&viafbinfo->var, 0, 0, + viaparinfo->lvds_setting_info2, &viaparinfo->chip_info->lvds_chip_info2); } } @@ -1971,7 +1960,7 @@ int viafb_setmode(int video_bpp, int video_bpp1) if (!viafb_hotplug) { viafb_hotplug_Xres = viafbinfo->var.xres; viafb_hotplug_Yres = viafbinfo->var.yres; - viafb_hotplug_bpp = video_bpp; + viafb_hotplug_bpp = viafbinfo->var.bits_per_pixel; viafb_hotplug_refresh = viafb_refresh; if (viafb_DVI_ON) @@ -1980,13 +1969,13 @@ int viafb_setmode(int video_bpp, int video_bpp1) viafb_DeviceStatus = CRT_Device; } device_on(); - if (!viafb_dual_fb) - via_set_sync_polarity(devices, get_sync(viafbinfo)); + if (!viafb_SAMM_ON) + via_set_sync_polarity(devices, get_sync(&viafbinfo->var)); else { via_set_sync_polarity(viaparinfo->shared->iga1_devices, - get_sync(viafbinfo)); + get_sync(&viafbinfo->var)); via_set_sync_polarity(viaparinfo->shared->iga2_devices, - get_sync(viafbinfo1)); + get_sync(&var2)); } clock.set_engine_pll_state(VIA_STATE_ON); @@ -2023,20 +2012,20 @@ int viafb_setmode(int video_bpp, int video_bpp1) int viafb_get_refresh(int hres, int vres, u32 long_refresh) { - struct crt_mode_table *best; + const struct fb_videomode *best; best = viafb_get_best_mode(hres, vres, long_refresh); if (!best) return 60; - if (abs(best->refresh_rate - long_refresh) > 3) { + if (abs(best->refresh - long_refresh) > 3) { if (hres == 1200 && vres == 900) return 49; /* OLPC DCON only supports 50 Hz */ else return 60; } - return best->refresh_rate; + return best->refresh; } static void device_off(void) @@ -2129,26 +2118,17 @@ void viafb_set_dpa_gfx(int output_interface, struct GFX_DPA_SETTING\ } } -/*According var's xres, yres fill var's other timing information*/ void viafb_fill_var_timing_info(struct fb_var_screeninfo *var, - struct crt_mode_table *mode) + const struct fb_videomode *mode) { - struct display_timing crt_reg; - - crt_reg = mode->crtc; - var->pixclock = 1000000000 / (crt_reg.hor_total * crt_reg.ver_total) - * 1000 / mode->refresh_rate; - var->left_margin = - crt_reg.hor_total - (crt_reg.hor_sync_start + crt_reg.hor_sync_end); - var->right_margin = crt_reg.hor_sync_start - crt_reg.hor_addr; - var->hsync_len = crt_reg.hor_sync_end; - var->upper_margin = - crt_reg.ver_total - (crt_reg.ver_sync_start + crt_reg.ver_sync_end); - var->lower_margin = crt_reg.ver_sync_start - crt_reg.ver_addr; - var->vsync_len = crt_reg.ver_sync_end; - var->sync = 0; - if (mode->h_sync_polarity == POSITIVE) - var->sync |= FB_SYNC_HOR_HIGH_ACT; - if (mode->v_sync_polarity == POSITIVE) - var->sync |= FB_SYNC_VERT_HIGH_ACT; + var->pixclock = mode->pixclock; + var->xres = mode->xres; + var->yres = mode->yres; + var->left_margin = mode->left_margin; + var->right_margin = mode->right_margin; + var->hsync_len = mode->hsync_len; + var->upper_margin = mode->upper_margin; + var->lower_margin = mode->lower_margin; + var->vsync_len = mode->vsync_len; + var->sync = mode->sync; } diff --git a/drivers/video/via/hw.h b/drivers/video/via/hw.h index 4db5b6e8d8d0..6be243cfc823 100644 --- a/drivers/video/via/hw.h +++ b/drivers/video/via/hw.h @@ -637,7 +637,10 @@ extern int viafb_LCD_ON; extern int viafb_DVI_ON; extern int viafb_hotplug; -void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, int iga); +struct display_timing var_to_timing(const struct fb_var_screeninfo *var, + u16 cxres, u16 cyres); +void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, + u16 cxres, u16 cyres, int iga); void viafb_set_vclock(u32 CLK, int set_iga); void viafb_load_reg(int timing_value, int viafb_load_reg_num, struct io_register *reg, @@ -657,9 +660,9 @@ void viafb_load_FIFO_reg(int set_iga, int hor_active, int ver_active); void viafb_set_dpa_gfx(int output_interface, struct GFX_DPA_SETTING\ *p_gfx_dpa_setting); -int viafb_setmode(int video_bpp, int video_bpp1); +int viafb_setmode(void); void viafb_fill_var_timing_info(struct fb_var_screeninfo *var, - struct crt_mode_table *mode); + const struct fb_videomode *mode); void __devinit viafb_init_chip_info(int chip_type); void __devinit viafb_init_dac(int set_iga); int viafb_get_refresh(int hres, int vres, u32 float_refresh); diff --git a/drivers/video/via/lcd.c b/drivers/video/via/lcd.c index 5f3b4e394e82..165037910536 100644 --- a/drivers/video/via/lcd.c +++ b/drivers/video/via/lcd.c @@ -53,10 +53,6 @@ static void __devinit fp_id_to_vindex(int panel_id); static int lvds_register_read(int index); static void load_lcd_scaling(int set_hres, int set_vres, int panel_hres, int panel_vres); -static void via_pitch_alignment_patch_lcd( - struct lvds_setting_information *plvds_setting_info, - struct lvds_chip_information - *plvds_chip_info); static void lcd_patch_skew_dvp0(struct lvds_setting_information *plvds_setting_info, struct lvds_chip_information *plvds_chip_info); @@ -79,9 +75,6 @@ static void check_diport_of_integrated_lvds( struct lvds_chip_information *plvds_chip_info, struct lvds_setting_information *plvds_setting_info); -static struct display_timing lcd_centering_timging(struct display_timing - mode_crt_reg, - struct display_timing panel_crt_reg); static inline bool check_lvds_chip(int device_id_subaddr, int device_id) { @@ -454,20 +447,17 @@ static void load_lcd_scaling(int set_hres, int set_vres, int panel_hres, } } -static void via_pitch_alignment_patch_lcd( - struct lvds_setting_information *plvds_setting_info, - struct lvds_chip_information - *plvds_chip_info) +static void via_pitch_alignment_patch_lcd(int iga_path, int hres, int bpp) { unsigned char cr13, cr35, cr65, cr66, cr67; unsigned long dwScreenPitch = 0; unsigned long dwPitch; - dwPitch = plvds_setting_info->h_active * (plvds_setting_info->bpp >> 3); + dwPitch = hres * (bpp >> 3); if (dwPitch & 0x1F) { dwScreenPitch = ((dwPitch + 31) & ~31) >> 3; - if (plvds_setting_info->iga_path == IGA2) { - if (plvds_setting_info->bpp > 8) { + if (iga_path == IGA2) { + if (bpp > 8) { cr66 = (unsigned char)(dwScreenPitch & 0xFF); viafb_write_reg(CR66, VIACR, cr66); cr67 = viafb_read_reg(VIACR, CR67) & 0xFC; @@ -485,7 +475,7 @@ static void via_pitch_alignment_patch_lcd( cr65 += 2; viafb_write_reg(CR65, VIACR, cr65); } else { - if (plvds_setting_info->bpp > 8) { + if (bpp > 8) { cr13 = (unsigned char)(dwScreenPitch & 0xFF); viafb_write_reg(CR13, VIACR, cr13); cr35 = viafb_read_reg(VIACR, CR35) & 0x1F; @@ -548,49 +538,45 @@ static void lcd_patch_skew(struct lvds_setting_information } /* LCD Set Mode */ -void viafb_lcd_set_mode(struct lvds_setting_information *plvds_setting_info, +void viafb_lcd_set_mode(const struct fb_var_screeninfo *var, u16 cxres, + u16 cyres, struct lvds_setting_information *plvds_setting_info, struct lvds_chip_information *plvds_chip_info) { int set_iga = plvds_setting_info->iga_path; - int mode_bpp = plvds_setting_info->bpp; - int set_hres = plvds_setting_info->h_active; - int set_vres = plvds_setting_info->v_active; + int mode_bpp = var->bits_per_pixel; + int set_hres = cxres ? cxres : var->xres; + int set_vres = cyres ? cyres : var->yres; int panel_hres = plvds_setting_info->lcd_panel_hres; int panel_vres = plvds_setting_info->lcd_panel_vres; u32 clock; - struct display_timing mode_crt_reg, panel_crt_reg, timing; - struct crt_mode_table *mode_crt_table, *panel_crt_table; + struct display_timing timing; + struct fb_var_screeninfo panel_var; + const struct fb_videomode *mode_crt_table, *panel_crt_table; DEBUG_MSG(KERN_INFO "viafb_lcd_set_mode!!\n"); /* Get mode table */ mode_crt_table = viafb_get_best_mode(set_hres, set_vres, 60); - mode_crt_reg = mode_crt_table->crtc; /* Get panel table Pointer */ panel_crt_table = viafb_get_best_mode(panel_hres, panel_vres, 60); - panel_crt_reg = panel_crt_table->crtc; + viafb_fill_var_timing_info(&panel_var, panel_crt_table); DEBUG_MSG(KERN_INFO "bellow viafb_lcd_set_mode!!\n"); if (VT1636_LVDS == plvds_chip_info->lvds_chip_name) viafb_init_lvds_vt1636(plvds_setting_info, plvds_chip_info); - clock = panel_crt_reg.hor_total * panel_crt_reg.ver_total - * panel_crt_table->refresh_rate; + clock = PICOS2KHZ(panel_crt_table->pixclock) * 1000; plvds_setting_info->vclk = clock; if (set_iga == IGA2 && (set_hres < panel_hres || set_vres < panel_vres) && plvds_setting_info->display_method == LCD_EXPANDSION) { - timing = panel_crt_reg; + timing = var_to_timing(&panel_var, panel_hres, panel_vres); load_lcd_scaling(set_hres, set_vres, panel_hres, panel_vres); } else { - timing = lcd_centering_timging(mode_crt_reg, panel_crt_reg); + timing = var_to_timing(&panel_var, set_hres, set_vres); if (set_iga == IGA2) /* disable scaling */ via_write_reg_mask(VIACR, 0x79, 0x00, BIT0 + BIT1 + BIT2); } - timing.hor_blank_end += timing.hor_blank_start; - timing.hor_sync_end += timing.hor_sync_start; - timing.ver_blank_end += timing.ver_blank_start; - timing.ver_sync_end += timing.ver_sync_start; if (set_iga == IGA1) via_set_primary_timing(&timing); else if (set_iga == IGA2) @@ -613,7 +599,8 @@ void viafb_lcd_set_mode(struct lvds_setting_information *plvds_setting_info, viafb_write_reg_mask(CR6A, VIACR, 0x01, BIT0); /* Patch for non 32bit alignment mode */ - via_pitch_alignment_patch_lcd(plvds_setting_info, plvds_chip_info); + via_pitch_alignment_patch_lcd(plvds_setting_info->iga_path, set_hres, + var->bits_per_pixel); } static void integrated_lvds_disable(struct lvds_setting_information @@ -973,37 +960,6 @@ void __devinit viafb_init_lvds_output_interface(struct lvds_chip_information } } -static struct display_timing lcd_centering_timging(struct display_timing - mode_crt_reg, - struct display_timing panel_crt_reg) -{ - struct display_timing crt_reg; - - crt_reg.hor_total = panel_crt_reg.hor_total; - crt_reg.hor_addr = mode_crt_reg.hor_addr; - crt_reg.hor_blank_start = - (panel_crt_reg.hor_addr - mode_crt_reg.hor_addr) / 2 + - crt_reg.hor_addr; - crt_reg.hor_blank_end = panel_crt_reg.hor_blank_end; - crt_reg.hor_sync_start = - (panel_crt_reg.hor_sync_start - - panel_crt_reg.hor_blank_start) + crt_reg.hor_blank_start; - crt_reg.hor_sync_end = panel_crt_reg.hor_sync_end; - - crt_reg.ver_total = panel_crt_reg.ver_total; - crt_reg.ver_addr = mode_crt_reg.ver_addr; - crt_reg.ver_blank_start = - (panel_crt_reg.ver_addr - mode_crt_reg.ver_addr) / 2 + - crt_reg.ver_addr; - crt_reg.ver_blank_end = panel_crt_reg.ver_blank_end; - crt_reg.ver_sync_start = - (panel_crt_reg.ver_sync_start - - panel_crt_reg.ver_blank_start) + crt_reg.ver_blank_start; - crt_reg.ver_sync_end = panel_crt_reg.ver_sync_end; - - return crt_reg; -} - bool viafb_lcd_get_mobile_state(bool *mobile) { unsigned char __iomem *romptr, *tableptr, *biosptr; diff --git a/drivers/video/via/lcd.h b/drivers/video/via/lcd.h index 77ca7b862e68..8f3e4e06156c 100644 --- a/drivers/video/via/lcd.h +++ b/drivers/video/via/lcd.h @@ -76,7 +76,8 @@ void __devinit viafb_init_lvds_output_interface(struct lvds_chip_information *plvds_chip_info, struct lvds_setting_information *plvds_setting_info); -void viafb_lcd_set_mode(struct lvds_setting_information *plvds_setting_info, +void viafb_lcd_set_mode(const struct fb_var_screeninfo *var, u16 cxres, + u16 cyres, struct lvds_setting_information *plvds_setting_info, struct lvds_chip_information *plvds_chip_info); bool __devinit viafb_lvds_trasmitter_identify(void); void viafb_init_lvds_output_interface(struct lvds_chip_information diff --git a/drivers/video/via/share.h b/drivers/video/via/share.h index c01c1c162726..3158dfc90bed 100644 --- a/drivers/video/via/share.h +++ b/drivers/video/via/share.h @@ -283,337 +283,6 @@ #define HW_LAYOUT_LCD1_LCD2 0x04 #define HW_LAYOUT_LCD_EXTERNAL_LCD2 0x10 -/* Definition Refresh Rate */ -#define REFRESH_49 49 -#define REFRESH_50 50 -#define REFRESH_60 60 -#define REFRESH_75 75 -#define REFRESH_85 85 -#define REFRESH_100 100 -#define REFRESH_120 120 - -/* Definition Sync Polarity*/ -#define NEGATIVE 1 -#define POSITIVE 0 - -/*480x640@60 Sync Polarity (GTF) -*/ -#define M480X640_R60_HSP NEGATIVE -#define M480X640_R60_VSP POSITIVE - -/*640x480@60 Sync Polarity (VESA Mode) -*/ -#define M640X480_R60_HSP NEGATIVE -#define M640X480_R60_VSP NEGATIVE - -/*640x480@75 Sync Polarity (VESA Mode) -*/ -#define M640X480_R75_HSP NEGATIVE -#define M640X480_R75_VSP NEGATIVE - -/*640x480@85 Sync Polarity (VESA Mode) -*/ -#define M640X480_R85_HSP NEGATIVE -#define M640X480_R85_VSP NEGATIVE - -/*640x480@100 Sync Polarity (GTF Mode) -*/ -#define M640X480_R100_HSP NEGATIVE -#define M640X480_R100_VSP POSITIVE - -/*640x480@120 Sync Polarity (GTF Mode) -*/ -#define M640X480_R120_HSP NEGATIVE -#define M640X480_R120_VSP POSITIVE - -/*720x480@60 Sync Polarity (GTF Mode) -*/ -#define M720X480_R60_HSP NEGATIVE -#define M720X480_R60_VSP POSITIVE - -/*720x576@60 Sync Polarity (GTF Mode) -*/ -#define M720X576_R60_HSP NEGATIVE -#define M720X576_R60_VSP POSITIVE - -/*800x600@60 Sync Polarity (VESA Mode) -*/ -#define M800X600_R60_HSP POSITIVE -#define M800X600_R60_VSP POSITIVE - -/*800x600@75 Sync Polarity (VESA Mode) -*/ -#define M800X600_R75_HSP POSITIVE -#define M800X600_R75_VSP POSITIVE - -/*800x600@85 Sync Polarity (VESA Mode) -*/ -#define M800X600_R85_HSP POSITIVE -#define M800X600_R85_VSP POSITIVE - -/*800x600@100 Sync Polarity (GTF Mode) -*/ -#define M800X600_R100_HSP NEGATIVE -#define M800X600_R100_VSP POSITIVE - -/*800x600@120 Sync Polarity (GTF Mode) -*/ -#define M800X600_R120_HSP NEGATIVE -#define M800X600_R120_VSP POSITIVE - -/*800x480@60 Sync Polarity (CVT Mode) -*/ -#define M800X480_R60_HSP NEGATIVE -#define M800X480_R60_VSP POSITIVE - -/*848x480@60 Sync Polarity (CVT Mode) -*/ -#define M848X480_R60_HSP NEGATIVE -#define M848X480_R60_VSP POSITIVE - -/*852x480@60 Sync Polarity (GTF Mode) -*/ -#define M852X480_R60_HSP NEGATIVE -#define M852X480_R60_VSP POSITIVE - -/*1024x512@60 Sync Polarity (GTF Mode) -*/ -#define M1024X512_R60_HSP NEGATIVE -#define M1024X512_R60_VSP POSITIVE - -/*1024x600@60 Sync Polarity (GTF Mode) -*/ -#define M1024X600_R60_HSP NEGATIVE -#define M1024X600_R60_VSP POSITIVE - -/*1024x768@60 Sync Polarity (VESA Mode) -*/ -#define M1024X768_R60_HSP NEGATIVE -#define M1024X768_R60_VSP NEGATIVE - -/*1024x768@75 Sync Polarity (VESA Mode) -*/ -#define M1024X768_R75_HSP POSITIVE -#define M1024X768_R75_VSP POSITIVE - -/*1024x768@85 Sync Polarity (VESA Mode) -*/ -#define M1024X768_R85_HSP POSITIVE -#define M1024X768_R85_VSP POSITIVE - -/*1024x768@100 Sync Polarity (GTF Mode) -*/ -#define M1024X768_R100_HSP NEGATIVE -#define M1024X768_R100_VSP POSITIVE - -/*1152x864@75 Sync Polarity (VESA Mode) -*/ -#define M1152X864_R75_HSP POSITIVE -#define M1152X864_R75_VSP POSITIVE - -/*1280x720@60 Sync Polarity (GTF Mode) -*/ -#define M1280X720_R60_HSP NEGATIVE -#define M1280X720_R60_VSP POSITIVE - -/* 1280x768@50 Sync Polarity (GTF Mode) */ -#define M1280X768_R50_HSP NEGATIVE -#define M1280X768_R50_VSP POSITIVE - -/*1280x768@60 Sync Polarity (GTF Mode) -*/ -#define M1280X768_R60_HSP NEGATIVE -#define M1280X768_R60_VSP POSITIVE - -/*1280x800@60 Sync Polarity (CVT Mode) -*/ -#define M1280X800_R60_HSP NEGATIVE -#define M1280X800_R60_VSP POSITIVE - -/*1280x960@60 Sync Polarity (VESA Mode) -*/ -#define M1280X960_R60_HSP POSITIVE -#define M1280X960_R60_VSP POSITIVE - -/*1280x1024@60 Sync Polarity (VESA Mode) -*/ -#define M1280X1024_R60_HSP POSITIVE -#define M1280X1024_R60_VSP POSITIVE - -/* 1360x768@60 Sync Polarity (CVT Mode) */ -#define M1360X768_R60_HSP POSITIVE -#define M1360X768_R60_VSP POSITIVE - -/* 1360x768@60 Sync Polarity (CVT Reduce Blanking Mode) */ -#define M1360X768_RB_R60_HSP POSITIVE -#define M1360X768_RB_R60_VSP NEGATIVE - -/* 1368x768@50 Sync Polarity (GTF Mode) */ -#define M1368X768_R50_HSP NEGATIVE -#define M1368X768_R50_VSP POSITIVE - -/* 1368x768@60 Sync Polarity (VESA Mode) */ -#define M1368X768_R60_HSP NEGATIVE -#define M1368X768_R60_VSP POSITIVE - -/*1280x1024@75 Sync Polarity (VESA Mode) -*/ -#define M1280X1024_R75_HSP POSITIVE -#define M1280X1024_R75_VSP POSITIVE - -/*1280x1024@85 Sync Polarity (VESA Mode) -*/ -#define M1280X1024_R85_HSP POSITIVE -#define M1280X1024_R85_VSP POSITIVE - -/*1440x1050@60 Sync Polarity (GTF Mode) -*/ -#define M1440X1050_R60_HSP NEGATIVE -#define M1440X1050_R60_VSP POSITIVE - -/*1600x1200@60 Sync Polarity (VESA Mode) -*/ -#define M1600X1200_R60_HSP POSITIVE -#define M1600X1200_R60_VSP POSITIVE - -/*1600x1200@75 Sync Polarity (VESA Mode) -*/ -#define M1600X1200_R75_HSP POSITIVE -#define M1600X1200_R75_VSP POSITIVE - -/* 1680x1050@60 Sync Polarity (CVT Mode) */ -#define M1680x1050_R60_HSP NEGATIVE -#define M1680x1050_R60_VSP NEGATIVE - -/* 1680x1050@60 Sync Polarity (CVT Reduce Blanking Mode) */ -#define M1680x1050_RB_R60_HSP POSITIVE -#define M1680x1050_RB_R60_VSP NEGATIVE - -/* 1680x1050@75 Sync Polarity (CVT Mode) */ -#define M1680x1050_R75_HSP NEGATIVE -#define M1680x1050_R75_VSP POSITIVE - -/*1920x1080@60 Sync Polarity (CVT Mode) -*/ -#define M1920X1080_R60_HSP NEGATIVE -#define M1920X1080_R60_VSP POSITIVE - -/* 1920x1080@60 Sync Polarity (CVT Reduce Blanking Mode) */ -#define M1920X1080_RB_R60_HSP POSITIVE -#define M1920X1080_RB_R60_VSP NEGATIVE - -/*1920x1440@60 Sync Polarity (VESA Mode) -*/ -#define M1920X1440_R60_HSP NEGATIVE -#define M1920X1440_R60_VSP POSITIVE - -/*1920x1440@75 Sync Polarity (VESA Mode) -*/ -#define M1920X1440_R75_HSP NEGATIVE -#define M1920X1440_R75_VSP POSITIVE - -#if 0 -/* 1400x1050@60 Sync Polarity (VESA Mode) */ -#define M1400X1050_R60_HSP NEGATIVE -#define M1400X1050_R60_VSP NEGATIVE -#endif - -/* 1400x1050@60 Sync Polarity (CVT Mode) */ -#define M1400X1050_R60_HSP NEGATIVE -#define M1400X1050_R60_VSP POSITIVE - -/* 1400x1050@60 Sync Polarity (CVT Reduce Blanking Mode) */ -#define M1400X1050_RB_R60_HSP POSITIVE -#define M1400X1050_RB_R60_VSP NEGATIVE - -/* 1400x1050@75 Sync Polarity (CVT Mode) */ -#define M1400X1050_R75_HSP NEGATIVE -#define M1400X1050_R75_VSP POSITIVE - -/* 960x600@60 Sync Polarity (CVT Mode) */ -#define M960X600_R60_HSP NEGATIVE -#define M960X600_R60_VSP POSITIVE - -/* 1000x600@60 Sync Polarity (GTF Mode) */ -#define M1000X600_R60_HSP NEGATIVE -#define M1000X600_R60_VSP POSITIVE - -/* 1024x576@60 Sync Polarity (GTF Mode) */ -#define M1024X576_R60_HSP NEGATIVE -#define M1024X576_R60_VSP POSITIVE - -/*1024x600@60 Sync Polarity (GTF Mode)*/ -#define M1024X600_R60_HSP NEGATIVE -#define M1024X600_R60_VSP POSITIVE - -/* 1088x612@60 Sync Polarity (CVT Mode) */ -#define M1088X612_R60_HSP NEGATIVE -#define M1088X612_R60_VSP POSITIVE - -/* 1152x720@60 Sync Polarity (CVT Mode) */ -#define M1152X720_R60_HSP NEGATIVE -#define M1152X720_R60_VSP POSITIVE - -/* 1200x720@60 Sync Polarity (GTF Mode) */ -#define M1200X720_R60_HSP NEGATIVE -#define M1200X720_R60_VSP POSITIVE - -/* 1200x900@60 Sync Polarity (DCON) */ -#define M1200X900_R60_HSP POSITIVE -#define M1200X900_R60_VSP POSITIVE - -/* 1280x600@60 Sync Polarity (GTF Mode) */ -#define M1280x600_R60_HSP NEGATIVE -#define M1280x600_R60_VSP POSITIVE - -/* 1280x720@50 Sync Polarity (GTF Mode) */ -#define M1280X720_R50_HSP NEGATIVE -#define M1280X720_R50_VSP POSITIVE - -/* 1440x900@60 Sync Polarity (CVT Mode) */ -#define M1440X900_R60_HSP NEGATIVE -#define M1440X900_R60_VSP POSITIVE - -/* 1440x900@75 Sync Polarity (CVT Mode) */ -#define M1440X900_R75_HSP NEGATIVE -#define M1440X900_R75_VSP POSITIVE - -/* 1440x900@60 Sync Polarity (CVT Reduce Blanking Mode) */ -#define M1440X900_RB_R60_HSP POSITIVE -#define M1440X900_RB_R60_VSP NEGATIVE - -/* 1600x900@60 Sync Polarity (CVT Mode) */ -#define M1600X900_R60_HSP NEGATIVE -#define M1600X900_R60_VSP POSITIVE - -/* 1600x900@60 Sync Polarity (CVT Reduce Blanking Mode) */ -#define M1600X900_RB_R60_HSP POSITIVE -#define M1600X900_RB_R60_VSP NEGATIVE - -/* 1600x1024@60 Sync Polarity (GTF Mode) */ -#define M1600X1024_R60_HSP NEGATIVE -#define M1600X1024_R60_VSP POSITIVE - -/* 1792x1344@60 Sync Polarity (DMT Mode) */ -#define M1792x1344_R60_HSP NEGATIVE -#define M1792x1344_R60_VSP POSITIVE - -/* 1856x1392@60 Sync Polarity (DMT Mode) */ -#define M1856x1392_R60_HSP NEGATIVE -#define M1856x1392_R60_VSP POSITIVE - -/* 1920x1200@60 Sync Polarity (CVT Mode) */ -#define M1920X1200_R60_HSP NEGATIVE -#define M1920X1200_R60_VSP POSITIVE - -/* 1920x1200@60 Sync Polarity (CVT Reduce Blanking Mode) */ -#define M1920X1200_RB_R60_HSP POSITIVE -#define M1920X1200_RB_R60_VSP NEGATIVE - -/* 2048x1536@60 Sync Polarity (CVT Mode) */ -#define M2048x1536_R60_HSP NEGATIVE -#define M2048x1536_R60_VSP POSITIVE - /* Definition CRTC Timing Index */ #define H_TOTAL_INDEX 0 #define H_ADDR_INDEX 1 diff --git a/drivers/video/via/via_aux.c b/drivers/video/via/via_aux.c new file mode 100644 index 000000000000..4a0a55cdac3d --- /dev/null +++ b/drivers/video/via/via_aux.c @@ -0,0 +1,88 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * infrastructure for devices connected via I2C + */ + +#include <linux/slab.h> +#include "via_aux.h" + + +struct via_aux_bus *via_aux_probe(struct i2c_adapter *adap) +{ + struct via_aux_bus *bus; + + if (!adap) + return NULL; + + bus = kmalloc(sizeof(*bus), GFP_KERNEL); + if (!bus) + return NULL; + + bus->adap = adap; + INIT_LIST_HEAD(&bus->drivers); + + via_aux_edid_probe(bus); + via_aux_vt1636_probe(bus); + via_aux_vt1632_probe(bus); + via_aux_vt1631_probe(bus); + via_aux_vt1625_probe(bus); + via_aux_vt1622_probe(bus); + via_aux_vt1621_probe(bus); + via_aux_sii164_probe(bus); + via_aux_ch7301_probe(bus); + + return bus; +} + +void via_aux_free(struct via_aux_bus *bus) +{ + struct via_aux_drv *pos, *n; + + if (!bus) + return; + + list_for_each_entry_safe(pos, n, &bus->drivers, chain) { + if (pos->cleanup) + pos->cleanup(pos); + + list_del(&pos->chain); + kfree(pos->data); + kfree(pos); + } + + kfree(bus); +} + +const struct fb_videomode *via_aux_get_preferred_mode(struct via_aux_bus *bus) +{ + struct via_aux_drv *pos; + const struct fb_videomode *mode = NULL; + + if (!bus) + return NULL; + + list_for_each_entry(pos, &bus->drivers, chain) { + if (pos->get_preferred_mode) + mode = pos->get_preferred_mode(pos); + } + + return mode; +} diff --git a/drivers/video/via/via_aux.h b/drivers/video/via/via_aux.h new file mode 100644 index 000000000000..a8de3f038cea --- /dev/null +++ b/drivers/video/via/via_aux.h @@ -0,0 +1,93 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * infrastructure for devices connected via I2C + */ + +#ifndef __VIA_AUX_H__ +#define __VIA_AUX_H__ + + +#include <linux/list.h> +#include <linux/i2c.h> +#include <linux/fb.h> + + +struct via_aux_bus { + struct i2c_adapter *adap; /* the I2C device to access the bus */ + struct list_head drivers; /* drivers for devices on this bus */ +}; + +struct via_aux_drv { + struct list_head chain; /* chain to support multiple drivers */ + + struct via_aux_bus *bus; /* the I2C bus used */ + u8 addr; /* the I2C slave address */ + + const char *name; /* human readable name of the driver */ + void *data; /* private data of this driver */ + + void (*cleanup)(struct via_aux_drv *drv); + const struct fb_videomode* (*get_preferred_mode) + (struct via_aux_drv *drv); +}; + + +struct via_aux_bus *via_aux_probe(struct i2c_adapter *adap); +void via_aux_free(struct via_aux_bus *bus); +const struct fb_videomode *via_aux_get_preferred_mode(struct via_aux_bus *bus); + + +static inline bool via_aux_add(struct via_aux_drv *drv) +{ + struct via_aux_drv *data = kmalloc(sizeof(*data), GFP_KERNEL); + + if (!data) + return false; + + *data = *drv; + list_add_tail(&data->chain, &data->bus->drivers); + return true; +} + +static inline bool via_aux_read(struct via_aux_drv *drv, u8 start, u8 *buf, + u8 len) +{ + struct i2c_msg msg[2] = { + {.addr = drv->addr, .flags = 0, .len = 1, .buf = &start}, + {.addr = drv->addr, .flags = I2C_M_RD, .len = len, .buf = buf} }; + + return i2c_transfer(drv->bus->adap, msg, 2) == 2; +} + + +/* probe functions of existing drivers - should only be called in via_aux.c */ +void via_aux_ch7301_probe(struct via_aux_bus *bus); +void via_aux_edid_probe(struct via_aux_bus *bus); +void via_aux_sii164_probe(struct via_aux_bus *bus); +void via_aux_vt1636_probe(struct via_aux_bus *bus); +void via_aux_vt1632_probe(struct via_aux_bus *bus); +void via_aux_vt1631_probe(struct via_aux_bus *bus); +void via_aux_vt1625_probe(struct via_aux_bus *bus); +void via_aux_vt1622_probe(struct via_aux_bus *bus); +void via_aux_vt1621_probe(struct via_aux_bus *bus); + + +#endif /* __VIA_AUX_H__ */ diff --git a/drivers/video/via/via_aux_ch7301.c b/drivers/video/via/via_aux_ch7301.c new file mode 100644 index 000000000000..1cbe5037a6b0 --- /dev/null +++ b/drivers/video/via/via_aux_ch7301.c @@ -0,0 +1,50 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * driver for Chrontel CH7301 DVI Transmitter + */ + +#include <linux/slab.h> +#include "via_aux.h" + + +static const char *name = "CH7301 DVI Transmitter"; + + +static void probe(struct via_aux_bus *bus, u8 addr) +{ + struct via_aux_drv drv = { + .bus = bus, + .addr = addr, + .name = name}; + u8 tmp; + + if (!via_aux_read(&drv, 0x4B, &tmp, 1) || tmp != 0x17) + return; + + printk(KERN_INFO "viafb: Found %s at address 0x%x\n", name, addr); + via_aux_add(&drv); +} + +void via_aux_ch7301_probe(struct via_aux_bus *bus) +{ + probe(bus, 0x75); + probe(bus, 0x76); +} diff --git a/drivers/video/via/via_aux_edid.c b/drivers/video/via/via_aux_edid.c new file mode 100644 index 000000000000..754d4509033f --- /dev/null +++ b/drivers/video/via/via_aux_edid.c @@ -0,0 +1,100 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * generic EDID driver + */ + +#include <linux/slab.h> +#include <linux/fb.h> +#include "via_aux.h" +#include "../edid.h" + + +static const char *name = "EDID"; + + +static void query_edid(struct via_aux_drv *drv) +{ + struct fb_monspecs *spec = drv->data; + unsigned char edid[EDID_LENGTH]; + bool valid = false; + + if (spec) { + fb_destroy_modedb(spec->modedb); + } else { + spec = kmalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return; + } + + spec->version = spec->revision = 0; + if (via_aux_read(drv, 0x00, edid, EDID_LENGTH)) { + fb_edid_to_monspecs(edid, spec); + valid = spec->version || spec->revision; + } + + if (!valid) { + kfree(spec); + spec = NULL; + } else + printk(KERN_DEBUG "EDID: %s %s\n", spec->manufacturer, spec->monitor); + + drv->data = spec; +} + +static const struct fb_videomode *get_preferred_mode(struct via_aux_drv *drv) +{ + struct fb_monspecs *spec = drv->data; + int i; + + if (!spec || !spec->modedb || !(spec->misc & FB_MISC_1ST_DETAIL)) + return NULL; + + for (i = 0; i < spec->modedb_len; i++) { + if (spec->modedb[i].flag & FB_MODE_IS_FIRST && + spec->modedb[i].flag & FB_MODE_IS_DETAILED) + return &spec->modedb[i]; + } + + return NULL; +} + +static void cleanup(struct via_aux_drv *drv) +{ + struct fb_monspecs *spec = drv->data; + + if (spec) + fb_destroy_modedb(spec->modedb); +} + +void via_aux_edid_probe(struct via_aux_bus *bus) +{ + struct via_aux_drv drv = { + .bus = bus, + .addr = 0x50, + .name = name, + .cleanup = cleanup, + .get_preferred_mode = get_preferred_mode}; + + query_edid(&drv); + + /* as EDID devices can be connected/disconnected just add the driver */ + via_aux_add(&drv); +} diff --git a/drivers/video/via/via_aux_sii164.c b/drivers/video/via/via_aux_sii164.c new file mode 100644 index 000000000000..ca1b35f033b1 --- /dev/null +++ b/drivers/video/via/via_aux_sii164.c @@ -0,0 +1,54 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * driver for Silicon Image SiI 164 PanelLink Transmitter + */ + +#include <linux/slab.h> +#include "via_aux.h" + + +static const char *name = "SiI 164 PanelLink Transmitter"; + + +static void probe(struct via_aux_bus *bus, u8 addr) +{ + struct via_aux_drv drv = { + .bus = bus, + .addr = addr, + .name = name}; + /* check vendor id and device id */ + const u8 id[] = {0x01, 0x00, 0x06, 0x00}, len = ARRAY_SIZE(id); + u8 tmp[len]; + + if (!via_aux_read(&drv, 0x00, tmp, len) || memcmp(id, tmp, len)) + return; + + printk(KERN_INFO "viafb: Found %s at address 0x%x\n", name, addr); + via_aux_add(&drv); +} + +void via_aux_sii164_probe(struct via_aux_bus *bus) +{ + u8 i; + + for (i = 0x38; i <= 0x3F; i++) + probe(bus, i); +} diff --git a/drivers/video/via/via_aux_vt1621.c b/drivers/video/via/via_aux_vt1621.c new file mode 100644 index 000000000000..38eca8479898 --- /dev/null +++ b/drivers/video/via/via_aux_vt1621.c @@ -0,0 +1,44 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * driver for VIA VT1621(M) TV Encoder + */ + +#include <linux/slab.h> +#include "via_aux.h" + + +static const char *name = "VT1621(M) TV Encoder"; + + +void via_aux_vt1621_probe(struct via_aux_bus *bus) +{ + struct via_aux_drv drv = { + .bus = bus, + .addr = 0x20, + .name = name}; + u8 tmp; + + if (!via_aux_read(&drv, 0x1B, &tmp, 1) || tmp != 0x02) + return; + + printk(KERN_INFO "viafb: Found %s\n", name); + via_aux_add(&drv); +} diff --git a/drivers/video/via/via_aux_vt1622.c b/drivers/video/via/via_aux_vt1622.c new file mode 100644 index 000000000000..8c79c68ba683 --- /dev/null +++ b/drivers/video/via/via_aux_vt1622.c @@ -0,0 +1,50 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * driver for VIA VT1622(M) Digital TV Encoder + */ + +#include <linux/slab.h> +#include "via_aux.h" + + +static const char *name = "VT1622(M) Digital TV Encoder"; + + +static void probe(struct via_aux_bus *bus, u8 addr) +{ + struct via_aux_drv drv = { + .bus = bus, + .addr = addr, + .name = name}; + u8 tmp; + + if (!via_aux_read(&drv, 0x1B, &tmp, 1) || tmp != 0x03) + return; + + printk(KERN_INFO "viafb: Found %s at address 0x%x\n", name, addr); + via_aux_add(&drv); +} + +void via_aux_vt1622_probe(struct via_aux_bus *bus) +{ + probe(bus, 0x20); + probe(bus, 0x21); +} diff --git a/drivers/video/via/via_aux_vt1625.c b/drivers/video/via/via_aux_vt1625.c new file mode 100644 index 000000000000..03eb30165d36 --- /dev/null +++ b/drivers/video/via/via_aux_vt1625.c @@ -0,0 +1,50 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * driver for VIA VT1625(M) HDTV Encoder + */ + +#include <linux/slab.h> +#include "via_aux.h" + + +static const char *name = "VT1625(M) HDTV Encoder"; + + +static void probe(struct via_aux_bus *bus, u8 addr) +{ + struct via_aux_drv drv = { + .bus = bus, + .addr = addr, + .name = name}; + u8 tmp; + + if (!via_aux_read(&drv, 0x1B, &tmp, 1) || tmp != 0x50) + return; + + printk(KERN_INFO "viafb: Found %s at address 0x%x\n", name, addr); + via_aux_add(&drv); +} + +void via_aux_vt1625_probe(struct via_aux_bus *bus) +{ + probe(bus, 0x20); + probe(bus, 0x21); +} diff --git a/drivers/video/via/via_aux_vt1631.c b/drivers/video/via/via_aux_vt1631.c new file mode 100644 index 000000000000..06e742f1f723 --- /dev/null +++ b/drivers/video/via/via_aux_vt1631.c @@ -0,0 +1,46 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * driver for VIA VT1631 LVDS Transmitter + */ + +#include <linux/slab.h> +#include "via_aux.h" + + +static const char *name = "VT1631 LVDS Transmitter"; + + +void via_aux_vt1631_probe(struct via_aux_bus *bus) +{ + struct via_aux_drv drv = { + .bus = bus, + .addr = 0x38, + .name = name}; + /* check vendor id and device id */ + const u8 id[] = {0x06, 0x11, 0x91, 0x31}, len = ARRAY_SIZE(id); + u8 tmp[len]; + + if (!via_aux_read(&drv, 0x00, tmp, len) || memcmp(id, tmp, len)) + return; + + printk(KERN_INFO "viafb: Found %s\n", name); + via_aux_add(&drv); +} diff --git a/drivers/video/via/via_aux_vt1632.c b/drivers/video/via/via_aux_vt1632.c new file mode 100644 index 000000000000..d24f4cd97401 --- /dev/null +++ b/drivers/video/via/via_aux_vt1632.c @@ -0,0 +1,54 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * driver for VIA VT1632 DVI Transmitter + */ + +#include <linux/slab.h> +#include "via_aux.h" + + +static const char *name = "VT1632 DVI Transmitter"; + + +static void probe(struct via_aux_bus *bus, u8 addr) +{ + struct via_aux_drv drv = { + .bus = bus, + .addr = addr, + .name = name}; + /* check vendor id and device id */ + const u8 id[] = {0x06, 0x11, 0x92, 0x31}, len = ARRAY_SIZE(id); + u8 tmp[len]; + + if (!via_aux_read(&drv, 0x00, tmp, len) || memcmp(id, tmp, len)) + return; + + printk(KERN_INFO "viafb: Found %s at address 0x%x\n", name, addr); + via_aux_add(&drv); +} + +void via_aux_vt1632_probe(struct via_aux_bus *bus) +{ + u8 i; + + for (i = 0x08; i <= 0x0F; i++) + probe(bus, i); +} diff --git a/drivers/video/via/via_aux_vt1636.c b/drivers/video/via/via_aux_vt1636.c new file mode 100644 index 000000000000..9e015c101d4d --- /dev/null +++ b/drivers/video/via/via_aux_vt1636.c @@ -0,0 +1,46 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * driver for VIA VT1636 LVDS Transmitter + */ + +#include <linux/slab.h> +#include "via_aux.h" + + +static const char *name = "VT1636 LVDS Transmitter"; + + +void via_aux_vt1636_probe(struct via_aux_bus *bus) +{ + struct via_aux_drv drv = { + .bus = bus, + .addr = 0x40, + .name = name}; + /* check vendor id and device id */ + const u8 id[] = {0x06, 0x11, 0x45, 0x33}, len = ARRAY_SIZE(id); + u8 tmp[len]; + + if (!via_aux_read(&drv, 0x00, tmp, len) || memcmp(id, tmp, len)) + return; + + printk(KERN_INFO "viafb: Found %s\n", name); + via_aux_add(&drv); +} diff --git a/drivers/video/via/via_i2c.c b/drivers/video/via/via_i2c.c index 78f1405dbab7..dd53058bbbb7 100644 --- a/drivers/video/via/via_i2c.c +++ b/drivers/video/via/via_i2c.c @@ -51,7 +51,7 @@ static void via_i2c_setscl(void *data, int state) val |= 0x01; break; case VIA_PORT_GPIO: - val |= 0x80; + val |= 0x82; break; default: printk(KERN_ERR "viafb_i2c: specify wrong i2c type.\n"); @@ -67,6 +67,9 @@ static int via_i2c_getscl(void *data) int ret = 0; spin_lock_irqsave(&i2c_vdev->reg_lock, flags); + if (adap_data->type == VIA_PORT_GPIO) + via_write_reg_mask(adap_data->io_port, adap_data->ioport_index, + 0, 0x80); if (via_read_reg(adap_data->io_port, adap_data->ioport_index) & 0x08) ret = 1; spin_unlock_irqrestore(&i2c_vdev->reg_lock, flags); @@ -80,6 +83,9 @@ static int via_i2c_getsda(void *data) int ret = 0; spin_lock_irqsave(&i2c_vdev->reg_lock, flags); + if (adap_data->type == VIA_PORT_GPIO) + via_write_reg_mask(adap_data->io_port, adap_data->ioport_index, + 0, 0x40); if (via_read_reg(adap_data->io_port, adap_data->ioport_index) & 0x04) ret = 1; spin_unlock_irqrestore(&i2c_vdev->reg_lock, flags); @@ -103,7 +109,7 @@ static void via_i2c_setsda(void *data, int state) val |= 0x01; break; case VIA_PORT_GPIO: - val |= 0x40; + val |= 0x42; break; default: printk(KERN_ERR "viafb_i2c: specify wrong i2c type.\n"); diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index a13c258bd32f..0c8837565bc7 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -24,6 +24,7 @@ #include <linux/slab.h> #include <linux/stat.h> #include <linux/via-core.h> +#include <linux/via_i2c.h> #include <asm/olpc.h> #define _MASTER_FILE @@ -286,26 +287,22 @@ static int viafb_set_par(struct fb_info *info) viafb_second_yres, viafb_bpp1, 1); } - refresh = viafb_get_refresh(info->var.xres, info->var.yres, - get_var_refresh(&info->var)); - if (viafb_get_best_mode(viafbinfo->var.xres, viafbinfo->var.yres, - refresh)) { - if (viafb_dual_fb && viapar->iga_path == IGA2) { - viafb_bpp1 = info->var.bits_per_pixel; - viafb_refresh1 = refresh; - } else { - viafb_bpp = info->var.bits_per_pixel; - viafb_refresh = refresh; - } - - if (info->var.accel_flags & FB_ACCELF_TEXT) - info->flags &= ~FBINFO_HWACCEL_DISABLED; - else - info->flags |= FBINFO_HWACCEL_DISABLED; - viafb_setmode(info->var.bits_per_pixel, viafb_bpp1); - viafb_pan_display(&info->var, info); + refresh = get_var_refresh(&info->var); + if (viafb_dual_fb && viapar->iga_path == IGA2) { + viafb_bpp1 = info->var.bits_per_pixel; + viafb_refresh1 = refresh; + } else { + viafb_bpp = info->var.bits_per_pixel; + viafb_refresh = refresh; } + if (info->var.accel_flags & FB_ACCELF_TEXT) + info->flags &= ~FBINFO_HWACCEL_DISABLED; + else + info->flags |= FBINFO_HWACCEL_DISABLED; + viafb_setmode(); + viafb_pan_display(&info->var, info); + return 0; } @@ -1670,12 +1667,23 @@ static void viafb_remove_proc(struct viafb_shared *shared) } #undef IS_VT1636 -static int parse_mode(const char *str, u32 *xres, u32 *yres) +static int parse_mode(const char *str, u32 devices, u32 *xres, u32 *yres) { + const struct fb_videomode *mode = NULL; char *ptr; if (!str) { - if (machine_is_olpc()) { + if (devices == VIA_CRT) + mode = via_aux_get_preferred_mode( + viaparinfo->shared->i2c_26); + else if (devices == VIA_DVP1) + mode = via_aux_get_preferred_mode( + viaparinfo->shared->i2c_31); + + if (mode) { + *xres = mode->xres; + *yres = mode->yres; + } else if (machine_is_olpc()) { *xres = 1200; *yres = 900; } else { @@ -1729,6 +1737,31 @@ static struct viafb_pm_hooks viafb_fb_pm_hooks = { #endif +static void __devinit i2c_bus_probe(struct viafb_shared *shared) +{ + /* should be always CRT */ + printk(KERN_INFO "viafb: Probing I2C bus 0x26\n"); + shared->i2c_26 = via_aux_probe(viafb_find_i2c_adapter(VIA_PORT_26)); + + /* seems to be usually DVP1 */ + printk(KERN_INFO "viafb: Probing I2C bus 0x31\n"); + shared->i2c_31 = via_aux_probe(viafb_find_i2c_adapter(VIA_PORT_31)); + + /* FIXME: what is this? */ + if (!machine_is_olpc()) { + printk(KERN_INFO "viafb: Probing I2C bus 0x2C\n"); + shared->i2c_2C = via_aux_probe(viafb_find_i2c_adapter(VIA_PORT_2C)); + } + + printk(KERN_INFO "viafb: Finished I2C bus probing"); +} + +static void i2c_bus_free(struct viafb_shared *shared) +{ + via_aux_free(shared->i2c_26); + via_aux_free(shared->i2c_31); + via_aux_free(shared->i2c_2C); +} int __devinit via_fb_pci_probe(struct viafb_dev *vdev) { @@ -1762,6 +1795,7 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev) &viaparinfo->shared->lvds_setting_info2; viaparinfo->chip_info = &viaparinfo->shared->chip_info; + i2c_bus_probe(viaparinfo->shared); if (viafb_dual_fb) viafb_SAMM_ON = 1; parse_lcd_port(); @@ -1804,10 +1838,11 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev) viafb_second_size * 1024 * 1024; } - parse_mode(viafb_mode, &default_xres, &default_yres); + parse_mode(viafb_mode, viaparinfo->shared->iga1_devices, + &default_xres, &default_yres); if (viafb_SAMM_ON == 1) - parse_mode(viafb_mode1, &viafb_second_xres, - &viafb_second_yres); + parse_mode(viafb_mode1, viaparinfo->shared->iga2_devices, + &viafb_second_xres, &viafb_second_yres); default_var.xres = default_xres; default_var.yres = default_yres; @@ -1915,6 +1950,7 @@ out_fb1_release: if (viafbinfo1) framebuffer_release(viafbinfo1); out_fb_release: + i2c_bus_free(viaparinfo->shared); framebuffer_release(viafbinfo); return rc; } @@ -1927,6 +1963,7 @@ void __devexit via_fb_pci_remove(struct pci_dev *pdev) if (viafb_dual_fb) unregister_framebuffer(viafbinfo1); viafb_remove_proc(viaparinfo->shared); + i2c_bus_free(viaparinfo->shared); framebuffer_release(viafbinfo); if (viafb_dual_fb) framebuffer_release(viafbinfo1); @@ -2033,9 +2070,9 @@ int __init viafb_init(void) if (r < 0) return r; #endif - if (parse_mode(viafb_mode, &dummy_x, &dummy_y) + if (parse_mode(viafb_mode, 0, &dummy_x, &dummy_y) || !viafb_get_best_mode(dummy_x, dummy_y, viafb_refresh) - || parse_mode(viafb_mode1, &dummy_x, &dummy_y) + || parse_mode(viafb_mode1, 0, &dummy_x, &dummy_y) || !viafb_get_best_mode(dummy_x, dummy_y, viafb_refresh1) || viafb_bpp < 0 || viafb_bpp > 32 || viafb_bpp1 < 0 || viafb_bpp1 > 32 diff --git a/drivers/video/via/viafbdev.h b/drivers/video/via/viafbdev.h index d9440635d1d4..f6b2ddf56e94 100644 --- a/drivers/video/via/viafbdev.h +++ b/drivers/video/via/viafbdev.h @@ -26,6 +26,7 @@ #include <linux/fb.h> #include <linux/spinlock.h> +#include "via_aux.h" #include "ioctl.h" #include "share.h" #include "chip.h" @@ -48,6 +49,11 @@ struct viafb_shared { struct proc_dir_entry *iga2_proc_entry; struct viafb_dev *vdev; /* Global dev info */ + /* I2C busses that may have auxiliary devices */ + struct via_aux_bus *i2c_26; + struct via_aux_bus *i2c_31; + struct via_aux_bus *i2c_2C; + /* All the information will be needed to set engine */ struct tmds_setting_information tmds_setting_info; struct lvds_setting_information lvds_setting_info; diff --git a/drivers/video/via/viamode.c b/drivers/video/via/viamode.c index 0911cac1b2ff..0666ab01cf4a 100644 --- a/drivers/video/via/viamode.c +++ b/drivers/video/via/viamode.c @@ -268,591 +268,78 @@ struct VPITTable VPIT = { /* Mode Table */ /********************/ -/* 480x640 */ -static struct crt_mode_table CRTM480x640[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M480X640_R60_HSP, M480X640_R60_VSP, - {624, 480, 480, 144, 504, 48, 663, 640, 640, 23, 641, 3} } /* GTF*/ -}; - -/* 640x480*/ -static struct crt_mode_table CRTM640x480[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M640X480_R60_HSP, M640X480_R60_VSP, - {800, 640, 640, 160, 656, 96, 525, 480, 480, 45, 490, 2} }, - {REFRESH_75, M640X480_R75_HSP, M640X480_R75_VSP, - {840, 640, 640, 200, 656, 64, 500, 480, 480, 20, 481, 3} }, - {REFRESH_85, M640X480_R85_HSP, M640X480_R85_VSP, - {832, 640, 640, 192, 696, 56, 509, 480, 480, 29, 481, 3} }, - {REFRESH_100, M640X480_R100_HSP, M640X480_R100_VSP, - {848, 640, 640, 208, 680, 64, 509, 480, 480, 29, 481, 3} }, /*GTF*/ - {REFRESH_120, M640X480_R120_HSP, M640X480_R120_VSP, - {848, 640, 640, 208, 680, 64, 515, 480, 480, 35, 481, 3} } /*GTF*/ -}; - -/*720x480 (GTF)*/ -static struct crt_mode_table CRTM720x480[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M720X480_R60_HSP, M720X480_R60_VSP, - {896, 720, 720, 176, 736, 72, 497, 480, 480, 17, 481, 3} } - -}; - -/*720x576 (GTF)*/ -static struct crt_mode_table CRTM720x576[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M720X576_R60_HSP, M720X576_R60_VSP, - {912, 720, 720, 192, 744, 72, 597, 576, 576, 21, 577, 3} } -}; - -/* 800x480 (CVT) */ -static struct crt_mode_table CRTM800x480[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M800X480_R60_HSP, M800X480_R60_VSP, - {992, 800, 800, 192, 824, 72, 500, 480, 480, 20, 483, 7} } -}; - -/* 800x600*/ -static struct crt_mode_table CRTM800x600[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M800X600_R60_HSP, M800X600_R60_VSP, - {1056, 800, 800, 256, 840, 128, 628, 600, 600, 28, 601, 4} }, - {REFRESH_75, M800X600_R75_HSP, M800X600_R75_VSP, - {1056, 800, 800, 256, 816, 80, 625, 600, 600, 25, 601, 3} }, - {REFRESH_85, M800X600_R85_HSP, M800X600_R85_VSP, - {1048, 800, 800, 248, 832, 64, 631, 600, 600, 31, 601, 3} }, - {REFRESH_100, M800X600_R100_HSP, M800X600_R100_VSP, - {1072, 800, 800, 272, 848, 88, 636, 600, 600, 36, 601, 3} }, - {REFRESH_120, M800X600_R120_HSP, M800X600_R120_VSP, - {1088, 800, 800, 288, 856, 88, 643, 600, 600, 43, 601, 3} } -}; - -/* 848x480 (CVT) */ -static struct crt_mode_table CRTM848x480[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M848X480_R60_HSP, M848X480_R60_VSP, - {1056, 848, 848, 208, 872, 80, 500, 480, 480, 20, 483, 5} } -}; - -/*856x480 (GTF) convert to 852x480*/ -static struct crt_mode_table CRTM852x480[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M852X480_R60_HSP, M852X480_R60_VSP, - {1064, 856, 856, 208, 872, 88, 497, 480, 480, 17, 481, 3} } -}; - -/*1024x512 (GTF)*/ -static struct crt_mode_table CRTM1024x512[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1024X512_R60_HSP, M1024X512_R60_VSP, - {1296, 1024, 1024, 272, 1056, 104, 531, 512, 512, 19, 513, 3} } - -}; - -/* 1024x600*/ -static struct crt_mode_table CRTM1024x600[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1024X600_R60_HSP, M1024X600_R60_VSP, - {1312, 1024, 1024, 288, 1064, 104, 622, 600, 600, 22, 601, 3} }, -}; - -/* 1024x768*/ -static struct crt_mode_table CRTM1024x768[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1024X768_R60_HSP, M1024X768_R60_VSP, - {1344, 1024, 1024, 320, 1048, 136, 806, 768, 768, 38, 771, 6} }, - {REFRESH_75, M1024X768_R75_HSP, M1024X768_R75_VSP, - {1312, 1024, 1024, 288, 1040, 96, 800, 768, 768, 32, 769, 3} }, - {REFRESH_85, M1024X768_R85_HSP, M1024X768_R85_VSP, - {1376, 1024, 1024, 352, 1072, 96, 808, 768, 768, 40, 769, 3} }, - {REFRESH_100, M1024X768_R100_HSP, M1024X768_R100_VSP, - {1392, 1024, 1024, 368, 1096, 112, 814, 768, 768, 46, 769, 3} } -}; - -/* 1152x864*/ -static struct crt_mode_table CRTM1152x864[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_75, M1152X864_R75_HSP, M1152X864_R75_VSP, - {1600, 1152, 1152, 448, 1216, 128, 900, 864, 864, 36, 865, 3} } - -}; - -/* 1280x720 (HDMI 720P)*/ -static struct crt_mode_table CRTM1280x720[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1280X720_R60_HSP, M1280X720_R60_VSP, - {1648, 1280, 1280, 368, 1392, 40, 750, 720, 720, 30, 725, 5} }, - {REFRESH_50, M1280X720_R50_HSP, M1280X720_R50_VSP, - {1632, 1280, 1280, 352, 1328, 128, 741, 720, 720, 21, 721, 3} } -}; - -/*1280x768 (GTF)*/ -static struct crt_mode_table CRTM1280x768[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1280X768_R60_HSP, M1280X768_R60_VSP, - {1680, 1280, 1280, 400, 1344, 136, 795, 768, 768, 27, 769, 3} }, - {REFRESH_50, M1280X768_R50_HSP, M1280X768_R50_VSP, - {1648, 1280, 1280, 368, 1336, 128, 791, 768, 768, 23, 769, 3} } -}; - -/* 1280x800 (CVT) */ -static struct crt_mode_table CRTM1280x800[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1280X800_R60_HSP, M1280X800_R60_VSP, - {1680, 1280, 1280, 400, 1352, 128, 831, 800, 800, 31, 803, 6} } -}; - -/*1280x960*/ -static struct crt_mode_table CRTM1280x960[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1280X960_R60_HSP, M1280X960_R60_VSP, - {1800, 1280, 1280, 520, 1376, 112, 1000, 960, 960, 40, 961, 3} } -}; - -/* 1280x1024*/ -static struct crt_mode_table CRTM1280x1024[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1280X1024_R60_HSP, M1280X1024_R60_VSP, - {1688, 1280, 1280, 408, 1328, 112, 1066, 1024, 1024, 42, 1025, - 3} }, - {REFRESH_75, M1280X1024_R75_HSP, M1280X1024_R75_VSP, - {1688, 1280, 1280, 408, 1296, 144, 1066, 1024, 1024, 42, 1025, - 3} }, - {REFRESH_85, M1280X1024_R85_HSP, M1280X1024_R85_VSP, - {1728, 1280, 1280, 448, 1344, 160, 1072, 1024, 1024, 48, 1025, 3} } -}; - -/* 1368x768 (GTF) */ -static struct crt_mode_table CRTM1368x768[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1368X768_R60_HSP, M1368X768_R60_VSP, - {1800, 1368, 1368, 432, 1440, 144, 795, 768, 768, 27, 769, 3} } -}; - -/*1440x1050 (GTF)*/ -static struct crt_mode_table CRTM1440x1050[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1440X1050_R60_HSP, M1440X1050_R60_VSP, - {1936, 1440, 1440, 496, 1536, 152, 1077, 1040, 1040, 37, 1041, 3} } -}; - -/* 1600x1200*/ -static struct crt_mode_table CRTM1600x1200[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1600X1200_R60_HSP, M1600X1200_R60_VSP, - {2160, 1600, 1600, 560, 1664, 192, 1250, 1200, 1200, 50, 1201, - 3} }, - {REFRESH_75, M1600X1200_R75_HSP, M1600X1200_R75_VSP, - {2160, 1600, 1600, 560, 1664, 192, 1250, 1200, 1200, 50, 1201, 3} } - -}; - -/* 1680x1050 (CVT) */ -static struct crt_mode_table CRTM1680x1050[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1680x1050_R60_HSP, M1680x1050_R60_VSP, - {2240, 1680, 1680, 560, 1784, 176, 1089, 1050, 1050, 39, 1053, - 6} }, - {REFRESH_75, M1680x1050_R75_HSP, M1680x1050_R75_VSP, - {2272, 1680, 1680, 592, 1800, 176, 1099, 1050, 1050, 49, 1053, 6} } -}; - -/* 1680x1050 (CVT Reduce Blanking) */ -static struct crt_mode_table CRTM1680x1050_RB[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1680x1050_RB_R60_HSP, M1680x1050_RB_R60_VSP, - {1840, 1680, 1680, 160, 1728, 32, 1080, 1050, 1050, 30, 1053, 6} } -}; - -/* 1920x1080 (CVT)*/ -static struct crt_mode_table CRTM1920x1080[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1920X1080_R60_HSP, M1920X1080_R60_VSP, - {2576, 1920, 1920, 656, 2048, 200, 1120, 1080, 1080, 40, 1083, 5} } -}; - -/* 1920x1080 (CVT with Reduce Blanking) */ -static struct crt_mode_table CRTM1920x1080_RB[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1920X1080_RB_R60_HSP, M1920X1080_RB_R60_VSP, - {2080, 1920, 1920, 160, 1968, 32, 1111, 1080, 1080, 31, 1083, 5} } -}; - -/* 1920x1440*/ -static struct crt_mode_table CRTM1920x1440[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1920X1440_R60_HSP, M1920X1440_R60_VSP, - {2600, 1920, 1920, 680, 2048, 208, 1500, 1440, 1440, 60, 1441, - 3} }, - {REFRESH_75, M1920X1440_R75_HSP, M1920X1440_R75_VSP, - {2640, 1920, 1920, 720, 2064, 224, 1500, 1440, 1440, 60, 1441, 3} } -}; - -/* 1400x1050 (CVT) */ -static struct crt_mode_table CRTM1400x1050[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1400X1050_R60_HSP, M1400X1050_R60_VSP, - {1864, 1400, 1400, 464, 1488, 144, 1089, 1050, 1050, 39, 1053, - 4} }, - {REFRESH_75, M1400X1050_R75_HSP, M1400X1050_R75_VSP, - {1896, 1400, 1400, 496, 1504, 144, 1099, 1050, 1050, 49, 1053, 4} } -}; - -/* 1400x1050 (CVT Reduce Blanking) */ -static struct crt_mode_table CRTM1400x1050_RB[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1400X1050_RB_R60_HSP, M1400X1050_RB_R60_VSP, - {1560, 1400, 1400, 160, 1448, 32, 1080, 1050, 1050, 30, 1053, 4} } -}; - -/* 960x600 (CVT) */ -static struct crt_mode_table CRTM960x600[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M960X600_R60_HSP, M960X600_R60_VSP, - {1216, 960, 960, 256, 992, 96, 624, 600, 600, 24, 603, 6} } -}; - -/* 1000x600 (GTF) */ -static struct crt_mode_table CRTM1000x600[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1000X600_R60_HSP, M1000X600_R60_VSP, - {1288, 1000, 1000, 288, 1040, 104, 622, 600, 600, 22, 601, 3} } -}; - -/* 1024x576 (GTF) */ -static struct crt_mode_table CRTM1024x576[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1024X576_R60_HSP, M1024X576_R60_VSP, - {1312, 1024, 1024, 288, 1064, 104, 597, 576, 576, 21, 577, 3} } -}; - -/* 1088x612 (CVT) */ -static struct crt_mode_table CRTM1088x612[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1088X612_R60_HSP, M1088X612_R60_VSP, - {1392, 1088, 1088, 304, 1136, 104, 636, 612, 612, 24, 615, 5} } -}; - -/* 1152x720 (CVT) */ -static struct crt_mode_table CRTM1152x720[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1152X720_R60_HSP, M1152X720_R60_VSP, - {1488, 1152, 1152, 336, 1208, 112, 748, 720, 720, 28, 723, 6} } -}; - -/* 1200x720 (GTF) */ -static struct crt_mode_table CRTM1200x720[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1200X720_R60_HSP, M1200X720_R60_VSP, - {1568, 1200, 1200, 368, 1256, 128, 746, 720, 720, 26, 721, 3} } -}; - -/* 1200x900 (DCON) */ -static struct crt_mode_table DCON1200x900[] = { - /* r_rate, hsp, vsp */ - {REFRESH_49, M1200X900_R60_HSP, M1200X900_R60_VSP, - /* The correct htotal is 1240, but this doesn't raster on VX855. */ - /* Via suggested changing to a multiple of 16, hence 1264. */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {1264, 1200, 1200, 64, 1211, 32, 912, 900, 900, 12, 901, 10} } -}; - -/* 1280x600 (GTF) */ -static struct crt_mode_table CRTM1280x600[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1280x600_R60_HSP, M1280x600_R60_VSP, - {1648, 1280, 1280, 368, 1336, 128, 622, 600, 600, 22, 601, 3} } -}; - -/* 1360x768 (CVT) */ -static struct crt_mode_table CRTM1360x768[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1360X768_R60_HSP, M1360X768_R60_VSP, - {1776, 1360, 1360, 416, 1432, 136, 798, 768, 768, 30, 771, 5} } -}; - -/* 1360x768 (CVT Reduce Blanking) */ -static struct crt_mode_table CRTM1360x768_RB[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1360X768_RB_R60_HSP, M1360X768_RB_R60_VSP, - {1520, 1360, 1360, 160, 1408, 32, 790, 768, 768, 22, 771, 5} } -}; - -/* 1366x768 (GTF) */ -static struct crt_mode_table CRTM1366x768[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1368X768_R60_HSP, M1368X768_R60_VSP, - {1800, 1368, 1368, 432, 1440, 144, 795, 768, 768, 27, 769, 3} }, - {REFRESH_50, M1368X768_R50_HSP, M1368X768_R50_VSP, - {1768, 1368, 1368, 400, 1424, 144, 791, 768, 768, 23, 769, 3} } -}; - -/* 1440x900 (CVT) */ -static struct crt_mode_table CRTM1440x900[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1440X900_R60_HSP, M1440X900_R60_VSP, - {1904, 1440, 1440, 464, 1520, 152, 934, 900, 900, 34, 903, 6} }, - {REFRESH_75, M1440X900_R75_HSP, M1440X900_R75_VSP, - {1936, 1440, 1440, 496, 1536, 152, 942, 900, 900, 42, 903, 6} } -}; - -/* 1440x900 (CVT Reduce Blanking) */ -static struct crt_mode_table CRTM1440x900_RB[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1440X900_RB_R60_HSP, M1440X900_RB_R60_VSP, - {1600, 1440, 1440, 160, 1488, 32, 926, 900, 900, 26, 903, 6} } -}; - -/* 1600x900 (CVT) */ -static struct crt_mode_table CRTM1600x900[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1600X900_R60_HSP, M1600X900_R60_VSP, - {2112, 1600, 1600, 512, 1688, 168, 934, 900, 900, 34, 903, 5} } -}; - -/* 1600x900 (CVT Reduce Blanking) */ -static struct crt_mode_table CRTM1600x900_RB[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1600X900_RB_R60_HSP, M1600X900_RB_R60_VSP, - {1760, 1600, 1600, 160, 1648, 32, 926, 900, 900, 26, 903, 5} } -}; - -/* 1600x1024 (GTF) */ -static struct crt_mode_table CRTM1600x1024[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1600X1024_R60_HSP, M1600X1024_R60_VSP, - {2144, 1600, 1600, 544, 1704, 168, 1060, 1024, 1024, 36, 1025, 3} } -}; - -/* 1792x1344 (DMT) */ -static struct crt_mode_table CRTM1792x1344[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1792x1344_R60_HSP, M1792x1344_R60_VSP, - {2448, 1792, 1792, 656, 1920, 200, 1394, 1344, 1344, 50, 1345, 3} } -}; - -/* 1856x1392 (DMT) */ -static struct crt_mode_table CRTM1856x1392[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1856x1392_R60_HSP, M1856x1392_R60_VSP, - {2528, 1856, 1856, 672, 1952, 224, 1439, 1392, 1392, 47, 1393, 3} } -}; - -/* 1920x1200 (CVT) */ -static struct crt_mode_table CRTM1920x1200[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1920X1200_R60_HSP, M1920X1200_R60_VSP, - {2592, 1920, 1920, 672, 2056, 200, 1245, 1200, 1200, 45, 1203, 6} } -}; - -/* 1920x1200 (CVT with Reduce Blanking) */ -static struct crt_mode_table CRTM1920x1200_RB[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1920X1200_RB_R60_HSP, M1920X1200_RB_R60_VSP, - {2080, 1920, 1920, 160, 1968, 32, 1235, 1200, 1200, 35, 1203, 6} } -}; - -/* 2048x1536 (CVT) */ -static struct crt_mode_table CRTM2048x1536[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M2048x1536_R60_HSP, M2048x1536_R60_VSP, - {2800, 2048, 2048, 752, 2200, 224, 1592, 1536, 1536, 56, 1539, 4} } -}; - -static struct VideoModeTable viafb_modes[] = { - /* Display : 480x640 (GTF) */ - {CRTM480x640, ARRAY_SIZE(CRTM480x640)}, - - /* Display : 640x480 */ - {CRTM640x480, ARRAY_SIZE(CRTM640x480)}, - - /* Display : 720x480 (GTF) */ - {CRTM720x480, ARRAY_SIZE(CRTM720x480)}, - - /* Display : 720x576 (GTF) */ - {CRTM720x576, ARRAY_SIZE(CRTM720x576)}, - - /* Display : 800x600 */ - {CRTM800x600, ARRAY_SIZE(CRTM800x600)}, - - /* Display : 800x480 (CVT) */ - {CRTM800x480, ARRAY_SIZE(CRTM800x480)}, - - /* Display : 848x480 (CVT) */ - {CRTM848x480, ARRAY_SIZE(CRTM848x480)}, - - /* Display : 852x480 (GTF) */ - {CRTM852x480, ARRAY_SIZE(CRTM852x480)}, - - /* Display : 1024x512 (GTF) */ - {CRTM1024x512, ARRAY_SIZE(CRTM1024x512)}, - - /* Display : 1024x600 */ - {CRTM1024x600, ARRAY_SIZE(CRTM1024x600)}, - - /* Display : 1024x768 */ - {CRTM1024x768, ARRAY_SIZE(CRTM1024x768)}, - - /* Display : 1152x864 */ - {CRTM1152x864, ARRAY_SIZE(CRTM1152x864)}, - - /* Display : 1280x768 (GTF) */ - {CRTM1280x768, ARRAY_SIZE(CRTM1280x768)}, - - /* Display : 960x600 (CVT) */ - {CRTM960x600, ARRAY_SIZE(CRTM960x600)}, - - /* Display : 1000x600 (GTF) */ - {CRTM1000x600, ARRAY_SIZE(CRTM1000x600)}, - - /* Display : 1024x576 (GTF) */ - {CRTM1024x576, ARRAY_SIZE(CRTM1024x576)}, - - /* Display : 1088x612 (GTF) */ - {CRTM1088x612, ARRAY_SIZE(CRTM1088x612)}, - - /* Display : 1152x720 (CVT) */ - {CRTM1152x720, ARRAY_SIZE(CRTM1152x720)}, - - /* Display : 1200x720 (GTF) */ - {CRTM1200x720, ARRAY_SIZE(CRTM1200x720)}, - - /* Display : 1200x900 (DCON) */ - {DCON1200x900, ARRAY_SIZE(DCON1200x900)}, - - /* Display : 1280x600 (GTF) */ - {CRTM1280x600, ARRAY_SIZE(CRTM1280x600)}, - - /* Display : 1280x800 (CVT) */ - {CRTM1280x800, ARRAY_SIZE(CRTM1280x800)}, - - /* Display : 1280x960 */ - {CRTM1280x960, ARRAY_SIZE(CRTM1280x960)}, - - /* Display : 1280x1024 */ - {CRTM1280x1024, ARRAY_SIZE(CRTM1280x1024)}, - - /* Display : 1360x768 (CVT) */ - {CRTM1360x768, ARRAY_SIZE(CRTM1360x768)}, - - /* Display : 1366x768 */ - {CRTM1366x768, ARRAY_SIZE(CRTM1366x768)}, - - /* Display : 1368x768 (GTF) */ - {CRTM1368x768, ARRAY_SIZE(CRTM1368x768)}, - - /* Display : 1440x900 (CVT) */ - {CRTM1440x900, ARRAY_SIZE(CRTM1440x900)}, - - /* Display : 1440x1050 (GTF) */ - {CRTM1440x1050, ARRAY_SIZE(CRTM1440x1050)}, - - /* Display : 1600x900 (CVT) */ - {CRTM1600x900, ARRAY_SIZE(CRTM1600x900)}, - - /* Display : 1600x1024 (GTF) */ - {CRTM1600x1024, ARRAY_SIZE(CRTM1600x1024)}, - - /* Display : 1600x1200 */ - {CRTM1600x1200, ARRAY_SIZE(CRTM1600x1200)}, - - /* Display : 1680x1050 (CVT) */ - {CRTM1680x1050, ARRAY_SIZE(CRTM1680x1050)}, - - /* Display : 1792x1344 (DMT) */ - {CRTM1792x1344, ARRAY_SIZE(CRTM1792x1344)}, - - /* Display : 1856x1392 (DMT) */ - {CRTM1856x1392, ARRAY_SIZE(CRTM1856x1392)}, - - /* Display : 1920x1440 */ - {CRTM1920x1440, ARRAY_SIZE(CRTM1920x1440)}, - - /* Display : 2048x1536 */ - {CRTM2048x1536, ARRAY_SIZE(CRTM2048x1536)}, - - /* Display : 1280x720 */ - {CRTM1280x720, ARRAY_SIZE(CRTM1280x720)}, - - /* Display : 1920x1080 (CVT) */ - {CRTM1920x1080, ARRAY_SIZE(CRTM1920x1080)}, - - /* Display : 1920x1200 (CVT) */ - {CRTM1920x1200, ARRAY_SIZE(CRTM1920x1200)}, - - /* Display : 1400x1050 (CVT) */ - {CRTM1400x1050, ARRAY_SIZE(CRTM1400x1050)} -}; - -static struct VideoModeTable viafb_rb_modes[] = { - /* Display : 1360x768 (CVT Reduce Blanking) */ - {CRTM1360x768_RB, ARRAY_SIZE(CRTM1360x768_RB)}, - - /* Display : 1440x900 (CVT Reduce Blanking) */ - {CRTM1440x900_RB, ARRAY_SIZE(CRTM1440x900_RB)}, - - /* Display : 1400x1050 (CVT Reduce Blanking) */ - {CRTM1400x1050_RB, ARRAY_SIZE(CRTM1400x1050_RB)}, - - /* Display : 1600x900 (CVT Reduce Blanking) */ - {CRTM1600x900_RB, ARRAY_SIZE(CRTM1600x900_RB)}, - - /* Display : 1680x1050 (CVT Reduce Blanking) */ - {CRTM1680x1050_RB, ARRAY_SIZE(CRTM1680x1050_RB)}, - - /* Display : 1920x1080 (CVT Reduce Blanking) */ - {CRTM1920x1080_RB, ARRAY_SIZE(CRTM1920x1080_RB)}, - - /* Display : 1920x1200 (CVT Reduce Blanking) */ - {CRTM1920x1200_RB, ARRAY_SIZE(CRTM1920x1200_RB)} -}; +static const struct fb_videomode viafb_modes[] = { + {NULL, 60, 480, 640, 40285, 72, 24, 19, 1, 48, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 640, 480, 39682, 48, 16, 33, 10, 96, 2, 0, 0, 0}, + {NULL, 75, 640, 480, 31746, 120, 16, 16, 1, 64, 3, 0, 0, 0}, + {NULL, 85, 640, 480, 27780, 80, 56, 25, 1, 56, 3, 0, 0, 0}, + {NULL, 100, 640, 480, 23167, 104, 40, 25, 1, 64, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 120, 640, 480, 19081, 104, 40, 31, 1, 64, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 720, 480, 37426, 88, 16, 13, 1, 72, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 720, 576, 30611, 96, 24, 17, 1, 72, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 800, 600, 25131, 88, 40, 23, 1, 128, 4, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 75, 800, 600, 20202, 160, 16, 21, 1, 80, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 85, 800, 600, 17790, 152, 32, 27, 1, 64, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 100, 800, 600, 14667, 136, 48, 32, 1, 88, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 120, 800, 600, 11911, 144, 56, 39, 1, 88, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 800, 480, 33602, 96, 24, 10, 3, 72, 7, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 848, 480, 31565, 104, 24, 12, 3, 80, 5, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 856, 480, 31517, 104, 16, 13, 1, 88, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1024, 512, 24218, 136, 32, 15, 1, 104, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1024, 600, 20423, 144, 40, 18, 1, 104, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1024, 768, 15385, 160, 24, 29, 3, 136, 6, 0, 0, 0}, + {NULL, 75, 1024, 768, 12703, 176, 16, 28, 1, 96, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 85, 1024, 768, 10581, 208, 48, 36, 1, 96, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 100, 1024, 768, 8825, 184, 72, 42, 1, 112, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 75, 1152, 864, 9259, 256, 64, 32, 1, 128, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1280, 768, 12478, 200, 64, 23, 1, 136, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 50, 1280, 768, 15342, 184, 56, 19, 1, 128, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 960, 600, 21964, 128, 32, 15, 3, 96, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1000, 600, 20803, 144, 40, 18, 1, 104, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1024, 576, 21278, 144, 40, 17, 1, 104, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1088, 612, 18825, 152, 48, 16, 3, 104, 5, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1152, 720, 14974, 168, 56, 19, 3, 112, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1200, 720, 14248, 184, 56, 22, 1, 128, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 49, 1200, 900, 17703, 21, 11, 1, 1, 32, 10, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1280, 600, 16259, 184, 56, 18, 1, 128, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1280, 800, 11938, 200, 72, 22, 3, 128, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1280, 960, 9259, 312, 96, 36, 1, 112, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1280, 1024, 9262, 248, 48, 38, 1, 112, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 75, 1280, 1024, 7409, 248, 16, 38, 1, 144, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 85, 1280, 1024, 6351, 224, 64, 44, 1, 160, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1360, 768, 11759, 208, 72, 22, 3, 136, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1368, 768, 11646, 216, 72, 23, 1, 144, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 50, 1368, 768, 14301, 200, 56, 19, 1, 144, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1368, 768, 11646, 216, 72, 23, 1, 144, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1440, 900, 9372, 232, 80, 25, 3, 152, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 75, 1440, 900, 7311, 248, 96, 33, 3, 152, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1440, 1040, 7993, 248, 96, 33, 1, 152, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1600, 900, 8449, 256, 88, 26, 3, 168, 5, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1600, 1024, 7333, 272, 104, 32, 1, 168, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1680, 1050, 6832, 280, 104, 30, 3, 176, 6, 0, 0, 0}, + {NULL, 75, 1680, 1050, 5339, 296, 120, 40, 3, 176, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1792, 1344, 4883, 328, 128, 46, 1, 200, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1856, 1392, 4581, 352, 96, 43, 1, 224, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1920, 1440, 4273, 344, 128, 56, 1, 208, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 75, 1920, 1440, 3367, 352, 144, 56, 1, 224, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 2048, 1536, 3738, 376, 152, 49, 3, 224, 4, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1280, 720, 13484, 216, 112, 20, 5, 40, 5, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 50, 1280, 720, 16538, 176, 48, 17, 1, 128, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1920, 1080, 5776, 328, 128, 32, 3, 200, 5, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1920, 1200, 5164, 336, 136, 36, 3, 200, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1400, 1050, 8210, 232, 88, 32, 3, 144, 4, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 75, 1400, 1050, 6398, 248, 104, 42, 3, 144, 4, FB_SYNC_VERT_HIGH_ACT, 0, 0} }; + +static const struct fb_videomode viafb_rb_modes[] = { + {NULL, 60, 1360, 768, 13879, 80, 48, 14, 3, 32, 5, FB_SYNC_HOR_HIGH_ACT, 0, 0}, + {NULL, 60, 1440, 900, 11249, 80, 48, 17, 3, 32, 6, FB_SYNC_HOR_HIGH_ACT, 0, 0}, + {NULL, 60, 1400, 1050, 9892, 80, 48, 23, 3, 32, 4, FB_SYNC_HOR_HIGH_ACT, 0, 0}, + {NULL, 60, 1600, 900, 10226, 80, 48, 18, 3, 32, 5, FB_SYNC_HOR_HIGH_ACT, 0, 0}, + {NULL, 60, 1680, 1050, 8387, 80, 48, 21, 3, 32, 6, FB_SYNC_HOR_HIGH_ACT, 0, 0}, + {NULL, 60, 1920, 1080, 7212, 80, 48, 23, 3, 32, 5, FB_SYNC_HOR_HIGH_ACT, 0, 0}, + {NULL, 60, 1920, 1200, 6488, 80, 48, 26, 3, 32, 6, FB_SYNC_HOR_HIGH_ACT, 0, 0} }; int NUM_TOTAL_CN400_ModeXregs = ARRAY_SIZE(CN400_ModeXregs); int NUM_TOTAL_CN700_ModeXregs = ARRAY_SIZE(CN700_ModeXregs); @@ -863,56 +350,34 @@ int NUM_TOTAL_CLE266_ModeXregs = ARRAY_SIZE(CLE266_ModeXregs); int NUM_TOTAL_PATCH_MODE = ARRAY_SIZE(res_patch_table); -static struct VideoModeTable *get_modes(struct VideoModeTable *vmt, int n, - int hres, int vres) -{ - int i; - - for (i = 0; i < n; i++) - if (vmt[i].mode_array && - vmt[i].crtc[0].crtc.hor_addr == hres && - vmt[i].crtc[0].crtc.ver_addr == vres) - return &viafb_modes[i]; - - return NULL; -} - -static struct crt_mode_table *get_best_mode(struct VideoModeTable *vmt, - int refresh) +static const struct fb_videomode *get_best_mode( + const struct fb_videomode *modes, int n, + int hres, int vres, int refresh) { - struct crt_mode_table *best; + const struct fb_videomode *best = NULL; int i; - if (!vmt) - return NULL; + for (i = 0; i < n; i++) { + if (modes[i].xres != hres || modes[i].yres != vres) + continue; - best = &vmt->crtc[0]; - for (i = 1; i < vmt->mode_array; i++) { - if (abs(vmt->crtc[i].refresh_rate - refresh) - < abs(best->refresh_rate - refresh)) - best = &vmt->crtc[i]; + if (!best || abs(modes[i].refresh - refresh) < + abs(best->refresh - refresh)) + best = &modes[i]; } return best; } -static struct VideoModeTable *viafb_get_mode(int hres, int vres) -{ - return get_modes(viafb_modes, ARRAY_SIZE(viafb_modes), hres, vres); -} - -struct crt_mode_table *viafb_get_best_mode(int hres, int vres, int refresh) +const struct fb_videomode *viafb_get_best_mode(int hres, int vres, int refresh) { - return get_best_mode(viafb_get_mode(hres, vres), refresh); + return get_best_mode(viafb_modes, ARRAY_SIZE(viafb_modes), + hres, vres, refresh); } -static struct VideoModeTable *viafb_get_rb_mode(int hres, int vres) -{ - return get_modes(viafb_rb_modes, ARRAY_SIZE(viafb_rb_modes), hres, - vres); -} - -struct crt_mode_table *viafb_get_best_rb_mode(int hres, int vres, int refresh) +const struct fb_videomode *viafb_get_best_rb_mode(int hres, int vres, + int refresh) { - return get_best_mode(viafb_get_rb_mode(hres, vres), refresh); + return get_best_mode(viafb_rb_modes, ARRAY_SIZE(viafb_rb_modes), + hres, vres, refresh); } diff --git a/drivers/video/via/viamode.h b/drivers/video/via/viamode.h index 5917a2b00e1b..dd19106698e7 100644 --- a/drivers/video/via/viamode.h +++ b/drivers/video/via/viamode.h @@ -31,11 +31,6 @@ struct VPITTable { unsigned char AR[StdAR]; }; -struct VideoModeTable { - struct crt_mode_table *crtc; - int mode_array; -}; - struct patch_table { int table_length; struct io_reg *io_reg_table; @@ -60,7 +55,9 @@ extern struct io_reg PM1024x768[]; extern struct patch_table res_patch_table[]; extern struct VPITTable VPIT; -struct crt_mode_table *viafb_get_best_mode(int hres, int vres, int refresh); -struct crt_mode_table *viafb_get_best_rb_mode(int hres, int vres, int refresh); +const struct fb_videomode *viafb_get_best_mode(int hres, int vres, + int refresh); +const struct fb_videomode *viafb_get_best_rb_mode(int hres, int vres, + int refresh); #endif /* __VIAMODE_H__ */ diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 7e9e8f4d8f0c..0e7366dfc901 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -170,7 +170,7 @@ config HAVE_S3C2410_WATCHDOG config S3C2410_WATCHDOG tristate "S3C2410 Watchdog" - depends on ARCH_S3C2410 || HAVE_S3C2410_WATCHDOG + depends on HAVE_S3C2410_WATCHDOG select WATCHDOG_CORE help Watchdog timer block in the Samsung SoCs. This will reboot diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c index b3046dc4b56c..7ceefd29ae14 100644 --- a/drivers/watchdog/at91rm9200_wdt.c +++ b/drivers/watchdog/at91rm9200_wdt.c @@ -51,7 +51,7 @@ static unsigned long at91wdt_busy; */ static inline void at91_wdt_stop(void) { - at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN); + at91_st_write(AT91_ST_WDMR, AT91_ST_EXTEN); } /* @@ -59,9 +59,9 @@ static inline void at91_wdt_stop(void) */ static inline void at91_wdt_start(void) { - at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN | + at91_st_write(AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN | (((65536 * wdt_time) >> 8) & AT91_ST_WDV)); - at91_sys_write(AT91_ST_CR, AT91_ST_WDRST); + at91_st_write(AT91_ST_CR, AT91_ST_WDRST); } /* @@ -69,7 +69,7 @@ static inline void at91_wdt_start(void) */ static inline void at91_wdt_reload(void) { - at91_sys_write(AT91_ST_CR, AT91_ST_WDRST); + at91_st_write(AT91_ST_CR, AT91_ST_WDRST); } /* ......................................................................... */ diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c index 726b7df61fd0..09cd888db619 100644 --- a/drivers/watchdog/ep93xx_wdt.c +++ b/drivers/watchdog/ep93xx_wdt.c @@ -23,6 +23,7 @@ * - Add a few missing ioctls */ +#include <linux/platform_device.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/miscdevice.h> @@ -30,7 +31,6 @@ #include <linux/timer.h> #include <linux/uaccess.h> #include <linux/io.h> -#include <mach/hardware.h> #define WDT_VERSION "0.3" #define PFX "ep93xx_wdt: " @@ -41,6 +41,7 @@ static int nowayout = WATCHDOG_NOWAYOUT; static int timeout = WDT_TIMEOUT; +static void __iomem *mmio_base; static struct timer_list timer; static unsigned long next_heartbeat; static unsigned long wdt_status; @@ -49,26 +50,25 @@ static unsigned long boot_status; #define WDT_IN_USE 0 #define WDT_OK_TO_CLOSE 1 -#define EP93XX_WDT_REG(x) (EP93XX_WATCHDOG_BASE + (x)) -#define EP93XX_WDT_WATCHDOG EP93XX_WDT_REG(0x00) -#define EP93XX_WDT_WDSTATUS EP93XX_WDT_REG(0x04) +#define EP93XX_WATCHDOG 0x00 +#define EP93XX_WDSTATUS 0x04 /* reset the wdt every ~200ms */ #define WDT_INTERVAL (HZ/5) static void wdt_enable(void) { - __raw_writew(0xaaaa, EP93XX_WDT_WATCHDOG); + writel(0xaaaa, mmio_base + EP93XX_WATCHDOG); } static void wdt_disable(void) { - __raw_writew(0xaa55, EP93XX_WDT_WATCHDOG); + writel(0xaa55, mmio_base + EP93XX_WATCHDOG); } static inline void wdt_ping(void) { - __raw_writew(0x5555, EP93XX_WDT_WATCHDOG); + writel(0x5555, mmio_base + EP93XX_WATCHDOG); } static void wdt_startup(void) @@ -206,18 +206,32 @@ static void ep93xx_timer_ping(unsigned long data) mod_timer(&timer, jiffies + WDT_INTERVAL); } -static int __init ep93xx_wdt_init(void) +static int __devinit ep93xx_wdt_probe(struct platform_device *pdev) { + struct resource *res; + unsigned long val; int err; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), pdev->name)) + return -EBUSY; + + mmio_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!mmio_base) + return -ENXIO; + err = misc_register(&ep93xx_wdt_miscdev); - boot_status = __raw_readl(EP93XX_WDT_WATCHDOG) & 0x01 ? 1 : 0; + val = readl(mmio_base + EP93XX_WATCHDOG); + boot_status = val & 0x01 ? 1 : 0; printk(KERN_INFO PFX "EP93XX watchdog, driver version " WDT_VERSION "%s\n", - (__raw_readl(EP93XX_WDT_WATCHDOG) & 0x08) - ? " (nCS1 disable detected)" : ""); + (val & 0x08) ? " (nCS1 disable detected)" : ""); if (timeout < 1 || timeout > 3600) { timeout = WDT_TIMEOUT; @@ -230,14 +244,23 @@ static int __init ep93xx_wdt_init(void) return err; } -static void __exit ep93xx_wdt_exit(void) +static int __devexit ep93xx_wdt_remove(struct platform_device *pdev) { wdt_shutdown(); misc_deregister(&ep93xx_wdt_miscdev); + return 0; } -module_init(ep93xx_wdt_init); -module_exit(ep93xx_wdt_exit); +static struct platform_driver ep93xx_wdt_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ep93xx-wdt", + }, + .probe = ep93xx_wdt_probe, + .remove = __devexit_p(ep93xx_wdt_remove), +}; + +module_platform_driver(ep93xx_wdt_driver); module_param(nowayout, int, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c index eef1524ae52e..3ff9e47bd218 100644 --- a/drivers/watchdog/sp805_wdt.c +++ b/drivers/watchdog/sp805_wdt.c @@ -370,17 +370,7 @@ static struct amba_driver sp805_wdt_driver = { .remove = __devexit_p(sp805_wdt_remove), }; -static int __init sp805_wdt_init(void) -{ - return amba_driver_register(&sp805_wdt_driver); -} -module_init(sp805_wdt_init); - -static void __exit sp805_wdt_exit(void) -{ - amba_driver_unregister(&sp805_wdt_driver); -} -module_exit(sp805_wdt_exit); +module_amba_driver(sp805_wdt_driver); module_param(nowayout, int, 0); MODULE_PARM_DESC(nowayout, diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index 648bcd4195c5..94243136f6bf 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -180,9 +180,8 @@ config XEN_PRIVCMD config XEN_ACPI_PROCESSOR tristate "Xen ACPI processor" - depends on XEN && X86 && ACPI_PROCESSOR - default y if (X86_ACPI_CPUFREQ = y || X86_POWERNOW_K8 = y) - default m if (X86_ACPI_CPUFREQ = m || X86_POWERNOW_K8 = m) + depends on XEN && X86 && ACPI_PROCESSOR && CPU_FREQ + default m help This ACPI processor uploads Power Management information to the Xen hypervisor. diff --git a/drivers/xen/events.c b/drivers/xen/events.c index e5e5812a1014..4b33acd8ed4e 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -37,6 +37,7 @@ #include <asm/idle.h> #include <asm/io_apic.h> #include <asm/sync_bitops.h> +#include <asm/xen/page.h> #include <asm/xen/pci.h> #include <asm/xen/hypercall.h> #include <asm/xen/hypervisor.h> @@ -109,6 +110,8 @@ struct irq_info { #define PIRQ_SHAREABLE (1 << 1) static int *evtchn_to_irq; +static unsigned long *pirq_eoi_map; +static bool (*pirq_needs_eoi)(unsigned irq); static DEFINE_PER_CPU(unsigned long [NR_EVENT_CHANNELS/BITS_PER_LONG], cpu_evtchn_mask); @@ -269,10 +272,14 @@ static unsigned int cpu_from_evtchn(unsigned int evtchn) return ret; } -static bool pirq_needs_eoi(unsigned irq) +static bool pirq_check_eoi_map(unsigned irq) { - struct irq_info *info = info_for_irq(irq); + return test_bit(irq, pirq_eoi_map); +} +static bool pirq_needs_eoi_flag(unsigned irq) +{ + struct irq_info *info = info_for_irq(irq); BUG_ON(info->type != IRQT_PIRQ); return info->u.pirq.flags & PIRQ_NEEDS_EOI; @@ -1768,7 +1775,7 @@ void xen_callback_vector(void) {} void __init xen_init_IRQ(void) { - int i; + int i, rc; evtchn_to_irq = kcalloc(NR_EVENT_CHANNELS, sizeof(*evtchn_to_irq), GFP_KERNEL); @@ -1782,6 +1789,8 @@ void __init xen_init_IRQ(void) for (i = 0; i < NR_EVENT_CHANNELS; i++) mask_evtchn(i); + pirq_needs_eoi = pirq_needs_eoi_flag; + if (xen_hvm_domain()) { xen_callback_vector(); native_init_IRQ(); @@ -1789,8 +1798,19 @@ void __init xen_init_IRQ(void) * __acpi_register_gsi can point at the right function */ pci_xen_hvm_init(); } else { + struct physdev_pirq_eoi_gmfn eoi_gmfn; + irq_ctx_init(smp_processor_id()); if (xen_initial_domain()) pci_xen_initial_domain(); + + pirq_eoi_map = (void *)__get_free_page(GFP_KERNEL|__GFP_ZERO); + eoi_gmfn.gmfn = virt_to_mfn(pirq_eoi_map); + rc = HYPERVISOR_physdev_op(PHYSDEVOP_pirq_eoi_gmfn_v2, &eoi_gmfn); + if (rc != 0) { + free_page((unsigned long) pirq_eoi_map); + pirq_eoi_map = NULL; + } else + pirq_needs_eoi = pirq_check_eoi_map; } } diff --git a/drivers/xen/platform-pci.c b/drivers/xen/platform-pci.c index 319dd0a94d51..2389e581e23c 100644 --- a/drivers/xen/platform-pci.c +++ b/drivers/xen/platform-pci.c @@ -186,11 +186,6 @@ static struct pci_driver platform_driver = { static int __init platform_pci_module_init(void) { - /* no unplug has been done, IGNORE hasn't been specified: just - * return now */ - if (!xen_platform_pci_unplug) - return -ENODEV; - return pci_register_driver(&platform_driver); } diff --git a/drivers/xen/tmem.c b/drivers/xen/tmem.c index 17d9e37beba4..dcb79521e6c8 100644 --- a/drivers/xen/tmem.c +++ b/drivers/xen/tmem.c @@ -9,7 +9,6 @@ #include <linux/types.h> #include <linux/init.h> #include <linux/pagemap.h> -#include <linux/module.h> #include <linux/cleancache.h> /* temporary ifdef until include/linux/frontswap.h is upstream */ @@ -128,15 +127,13 @@ static int xen_tmem_flush_object(u32 pool_id, struct tmem_oid oid) return xen_tmem_op(TMEM_FLUSH_OBJECT, pool_id, oid, 0, 0, 0, 0, 0); } -int tmem_enabled __read_mostly; -EXPORT_SYMBOL(tmem_enabled); +bool __read_mostly tmem_enabled = false; static int __init enable_tmem(char *s) { - tmem_enabled = 1; + tmem_enabled = true; return 1; } - __setup("tmem", enable_tmem); #ifdef CONFIG_CLEANCACHE @@ -229,17 +226,16 @@ static int tmem_cleancache_init_shared_fs(char *uuid, size_t pagesize) return xen_tmem_new_pool(shared_uuid, TMEM_POOL_SHARED, pagesize); } -static int use_cleancache = 1; +static bool __initdata use_cleancache = true; static int __init no_cleancache(char *s) { - use_cleancache = 0; + use_cleancache = false; return 1; } - __setup("nocleancache", no_cleancache); -static struct cleancache_ops tmem_cleancache_ops = { +static struct cleancache_ops __initdata tmem_cleancache_ops = { .put_page = tmem_cleancache_put_page, .get_page = tmem_cleancache_get_page, .invalidate_page = tmem_cleancache_flush_page, @@ -356,17 +352,16 @@ static void tmem_frontswap_init(unsigned ignored) xen_tmem_new_pool(private, TMEM_POOL_PERSIST, PAGE_SIZE); } -static int __initdata use_frontswap = 1; +static bool __initdata use_frontswap = true; static int __init no_frontswap(char *s) { - use_frontswap = 0; + use_frontswap = false; return 1; } - __setup("nofrontswap", no_frontswap); -static struct frontswap_ops tmem_frontswap_ops = { +static struct frontswap_ops __initdata tmem_frontswap_ops = { .put_page = tmem_frontswap_put_page, .get_page = tmem_frontswap_get_page, .invalidate_page = tmem_frontswap_flush_page, diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c index 5c2be963aa18..174b5653cd8a 100644 --- a/drivers/xen/xen-acpi-processor.c +++ b/drivers/xen/xen-acpi-processor.c @@ -501,11 +501,11 @@ static int __init xen_acpi_processor_init(void) perf = per_cpu_ptr(acpi_perf_data, i); rc = acpi_processor_register_performance(perf, i); - if (WARN_ON(rc)) + if (rc) goto err_out; } rc = acpi_processor_notify_smm(THIS_MODULE); - if (WARN_ON(rc)) + if (rc) goto err_unregister; for_each_possible_cpu(i) { |