diff options
Diffstat (limited to 'drivers')
273 files changed, 12760 insertions, 2874 deletions
diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 5b73f6a2cd8..831883b7d6c 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -233,6 +233,9 @@ static void acpi_ac_notify(acpi_handle handle, u32 event, void *data) device = ac->device; switch (event) { + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); case ACPI_AC_NOTIFY_STATUS: case ACPI_NOTIFY_BUS_CHECK: case ACPI_NOTIFY_DEVICE_CHECK: @@ -244,11 +247,6 @@ static void acpi_ac_notify(acpi_handle handle, u32 event, void *data) #ifdef CONFIG_ACPI_SYSFS_POWER kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE); #endif - break; - default: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Unsupported event [0x%x]\n", event)); - break; } return; diff --git a/drivers/acpi/bay.c b/drivers/acpi/bay.c index 26038c2a2a7..61b6c5beb2d 100644 --- a/drivers/acpi/bay.c +++ b/drivers/acpi/bay.c @@ -377,6 +377,9 @@ static int __init bay_init(void) INIT_LIST_HEAD(&drive_bays); + if (acpi_disabled) + return -ENODEV; + /* look for dockable drive bays */ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, find_bay, &bays, NULL); diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 96c542f7fde..bb7c51f712b 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -917,6 +917,9 @@ static int __init dock_init(void) dock_station = NULL; + if (acpi_disabled) + return 0; + /* look for a dock station */ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, find_dock, &num, NULL); diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 2808dc60fd6..9b227d4dc9c 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -333,6 +333,9 @@ static int __init acpi_rtc_init(void) { struct device *dev = get_rtc_dev(); + if (acpi_disabled) + return 0; + if (dev) { rtc_wake_setup(); rtc_info.wake_on = rtc_wake_on; diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index c3b0cd88d09..495c63a3e0a 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -36,9 +36,8 @@ static int acpi_sleep_prepare(u32 acpi_state) if (!acpi_wakeup_address) { return -EFAULT; } - acpi_set_firmware_waking_vector((acpi_physical_address) - virt_to_phys((void *) - acpi_wakeup_address)); + acpi_set_firmware_waking_vector( + (acpi_physical_address)acpi_wakeup_address); } ACPI_FLUSH_CPU_CACHE(); diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c index 224c57c0338..4ebbba2b6b1 100644 --- a/drivers/acpi/sleep/proc.c +++ b/drivers/acpi/sleep/proc.c @@ -315,8 +315,11 @@ acpi_system_write_alarm(struct file *file, cmos_bcd_write(day, acpi_gbl_FADT.day_alarm, rtc_control); if (acpi_gbl_FADT.month_alarm) cmos_bcd_write(mo, acpi_gbl_FADT.month_alarm, rtc_control); - if (acpi_gbl_FADT.century) + if (acpi_gbl_FADT.century) { + if (adjust) + yr += cmos_bcd_read(acpi_gbl_FADT.century, rtc_control) * 100; cmos_bcd_write(yr / 100, acpi_gbl_FADT.century, rtc_control); + } /* enable the rtc alarm interrupt */ rtc_control |= RTC_AIE; CMOS_WRITE(rtc_control, RTC_CONTROL); diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 5e5dda3a302..d089c4519d4 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -1713,7 +1713,8 @@ acpi_video_bus_get_devices(struct acpi_video_bus *video, status = acpi_video_bus_get_one_device(dev, video); if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Cant attach device")); + ACPI_DEBUG_PRINT((ACPI_DB_WARN, + "Cant attach device")); continue; } } diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 9bf2986a278..ae8494944c4 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -651,9 +651,17 @@ config PATA_WINBOND_VLB Support for the Winbond W83759A controller on Vesa Local Bus systems. +config HAVE_PATA_PLATFORM + bool + help + This is an internal configuration node for any machine that + uses pata-platform driver to enable the relevant driver in the + configuration structure without having to submit endless patches + to update the PATA_PLATFORM entry. + config PATA_PLATFORM tristate "Generic platform device PATA support" - depends on EMBEDDED || ARCH_RPC || PPC + depends on EMBEDDED || ARCH_RPC || PPC || HAVE_PATA_PLATFORM help This option enables support for generic directly connected ATA devices commonly found on embedded systems. diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 966ab401e52..5e6468a7ca4 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -90,6 +90,7 @@ enum { board_ahci_mv = 4, board_ahci_sb700 = 5, board_ahci_mcp65 = 6, + board_ahci_nopmp = 7, /* global controller registers */ HOST_CAP = 0x00, /* host capabilities */ @@ -401,6 +402,14 @@ static const struct ata_port_info ahci_port_info[] = { .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, + /* board_ahci_nopmp */ + { + AHCI_HFLAGS (AHCI_HFLAG_NO_PMP), + .flags = AHCI_FLAG_COMMON, + .pio_mask = 0x1f, /* pio0-4 */ + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_ops, + }, }; static const struct pci_device_id ahci_pci_tbl[] = { @@ -525,9 +534,9 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(NVIDIA, 0x0bc7), board_ahci }, /* MCP7B */ /* SiS */ - { PCI_VDEVICE(SI, 0x1184), board_ahci }, /* SiS 966 */ - { PCI_VDEVICE(SI, 0x1185), board_ahci }, /* SiS 966 */ - { PCI_VDEVICE(SI, 0x0186), board_ahci }, /* SiS 968 */ + { PCI_VDEVICE(SI, 0x1184), board_ahci_nopmp }, /* SiS 966 */ + { PCI_VDEVICE(SI, 0x1185), board_ahci_nopmp }, /* SiS 968 */ + { PCI_VDEVICE(SI, 0x0186), board_ahci_nopmp }, /* SiS 968 */ /* Marvell */ { PCI_VDEVICE(MARVELL, 0x6145), board_ahci_mv }, /* 6145 */ @@ -653,6 +662,14 @@ static void ahci_save_initial_config(struct pci_dev *pdev, cap &= ~HOST_CAP_PMP; } + if (pdev->vendor == PCI_VENDOR_ID_JMICRON && pdev->device == 0x2361 && + port_map != 1) { + dev_printk(KERN_INFO, &pdev->dev, + "JMB361 has only one port, port_map 0x%x -> 0x%x\n", + port_map, 1); + port_map = 1; + } + /* * Temporary Marvell 6145 hack: PATA port presence * is asserted through the standard AHCI port @@ -1760,7 +1777,7 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance) struct ahci_host_priv *hpriv; unsigned int i, handled = 0; void __iomem *mmio; - u32 irq_stat, irq_ack = 0; + u32 irq_stat, irq_masked; VPRINTK("ENTER\n"); @@ -1769,16 +1786,17 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance) /* sigh. 0xffffffff is a valid return from h/w */ irq_stat = readl(mmio + HOST_IRQ_STAT); - irq_stat &= hpriv->port_map; if (!irq_stat) return IRQ_NONE; + irq_masked = irq_stat & hpriv->port_map; + spin_lock(&host->lock); for (i = 0; i < host->n_ports; i++) { struct ata_port *ap; - if (!(irq_stat & (1 << i))) + if (!(irq_masked & (1 << i))) continue; ap = host->ports[i]; @@ -1792,14 +1810,20 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance) "interrupt on disabled port %u\n", i); } - irq_ack |= (1 << i); - } - - if (irq_ack) { - writel(irq_ack, mmio + HOST_IRQ_STAT); handled = 1; } + /* HOST_IRQ_STAT behaves as level triggered latch meaning that + * it should be cleared after all the port events are cleared; + * otherwise, it will raise a spurious interrupt after each + * valid one. Please read section 10.6.2 of ahci 1.1 for more + * information. + * + * Also, use the unmasked value to clear interrupt as spurious + * pending event on a dummy port might cause screaming IRQ. + */ + writel(irq_stat, mmio + HOST_IRQ_STAT); + spin_unlock(&host->lock); VPRINTK("EXIT\n"); diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index 81b7ae37695..a90ae03f56b 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -1043,6 +1043,13 @@ static int piix_broken_suspend(void) }, }, { + .ident = "TECRA M4", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "TECRA M4"), + }, + }, + { .ident = "TECRA M5", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index cc816ca623d..303fc0d2b97 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4297,7 +4297,7 @@ void ata_sg_clean(struct ata_queued_cmd *qc) } /** - * ata_check_atapi_dma - Check whether ATAPI DMA can be supported + * atapi_check_dma - Check whether ATAPI DMA can be supported * @qc: Metadata associated with taskfile to check * * Allow low-level driver to filter ATA PACKET commands, returning @@ -4310,7 +4310,7 @@ void ata_sg_clean(struct ata_queued_cmd *qc) * RETURNS: 0 when ATAPI DMA can be used * nonzero otherwise */ -int ata_check_atapi_dma(struct ata_queued_cmd *qc) +int atapi_check_dma(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 2e6e1622dc6..57a43649a46 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -2343,8 +2343,8 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc) { struct scsi_cmnd *scmd = qc->scsicmd; struct ata_device *dev = qc->dev; - int using_pio = (dev->flags & ATA_DFLAG_PIO); int nodata = (scmd->sc_data_direction == DMA_NONE); + int using_pio = !nodata && (dev->flags & ATA_DFLAG_PIO); unsigned int nbytes; memset(qc->cdb, 0, dev->cdb_len); @@ -2362,7 +2362,7 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc) ata_qc_set_pc_nbytes(qc); /* check whether ATAPI DMA is safe */ - if (!using_pio && ata_check_atapi_dma(qc)) + if (!nodata && !using_pio && atapi_check_dma(qc)) using_pio = 1; /* Some controller variants snoop this value for Packet @@ -2402,13 +2402,11 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc) qc->tf.lbam = (nbytes & 0xFF); qc->tf.lbah = (nbytes >> 8); - if (using_pio || nodata) { - /* no data, or PIO data xfer */ - if (nodata) - qc->tf.protocol = ATAPI_PROT_NODATA; - else - qc->tf.protocol = ATAPI_PROT_PIO; - } else { + if (nodata) + qc->tf.protocol = ATAPI_PROT_NODATA; + else if (using_pio) + qc->tf.protocol = ATAPI_PROT_PIO; + else { /* DMA data xfer */ qc->tf.protocol = ATAPI_PROT_DMA; qc->tf.feature |= ATAPI_PKT_DMA; diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 215d18672a5..c0908c22548 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -1094,6 +1094,7 @@ static void ata_hsm_qc_complete(struct ata_queued_cmd *qc, int in_wq) int ata_sff_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc, u8 status, int in_wq) { + struct ata_eh_info *ehi = &ap->link.eh_info; unsigned long flags = 0; int poll_next; @@ -1125,9 +1126,12 @@ fsm_start: if (likely(status & (ATA_ERR | ATA_DF))) /* device stops HSM for abort/error */ qc->err_mask |= AC_ERR_DEV; - else + else { /* HSM violation. Let EH handle this */ + ata_ehi_push_desc(ehi, + "ST_FIRST: !(DRQ|ERR|DF)"); qc->err_mask |= AC_ERR_HSM; + } ap->hsm_task_state = HSM_ST_ERR; goto fsm_start; @@ -1146,9 +1150,9 @@ fsm_start: * the CDB. */ if (!(qc->dev->horkage & ATA_HORKAGE_STUCK_ERR)) { - ata_port_printk(ap, KERN_WARNING, - "DRQ=1 with device error, " - "dev_stat 0x%X\n", status); + ata_ehi_push_desc(ehi, "ST_FIRST: " + "DRQ=1 with device error, " + "dev_stat 0x%X", status); qc->err_mask |= AC_ERR_HSM; ap->hsm_task_state = HSM_ST_ERR; goto fsm_start; @@ -1205,9 +1209,9 @@ fsm_start: * let the EH abort the command or reset the device. */ if (unlikely(status & (ATA_ERR | ATA_DF))) { - ata_port_printk(ap, KERN_WARNING, "DRQ=1 with " - "device error, dev_stat 0x%X\n", - status); + ata_ehi_push_desc(ehi, "ST-ATAPI: " + "DRQ=1 with device error, " + "dev_stat 0x%X", status); qc->err_mask |= AC_ERR_HSM; ap->hsm_task_state = HSM_ST_ERR; goto fsm_start; @@ -1226,13 +1230,17 @@ fsm_start: if (likely(status & (ATA_ERR | ATA_DF))) /* device stops HSM for abort/error */ qc->err_mask |= AC_ERR_DEV; - else + else { /* HSM violation. Let EH handle this. * Phantom devices also trigger this * condition. Mark hint. */ + ata_ehi_push_desc(ehi, "ST-ATA: " + "DRQ=1 with device error, " + "dev_stat 0x%X", status); qc->err_mask |= AC_ERR_HSM | AC_ERR_NODEV_HINT; + } ap->hsm_task_state = HSM_ST_ERR; goto fsm_start; @@ -1257,8 +1265,12 @@ fsm_start: status = ata_wait_idle(ap); } - if (status & (ATA_BUSY | ATA_DRQ)) + if (status & (ATA_BUSY | ATA_DRQ)) { + ata_ehi_push_desc(ehi, "ST-ATA: " + "BUSY|DRQ persists on ERR|DF, " + "dev_stat 0x%X", status); qc->err_mask |= AC_ERR_HSM; + } /* ata_pio_sectors() might change the * state to HSM_ST_LAST. so, the state diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 4514283937e..1cf803adbc9 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -106,7 +106,7 @@ extern void ata_sg_clean(struct ata_queued_cmd *qc); extern void ata_qc_free(struct ata_queued_cmd *qc); extern void ata_qc_issue(struct ata_queued_cmd *qc); extern void __ata_qc_complete(struct ata_queued_cmd *qc); -extern int ata_check_atapi_dma(struct ata_queued_cmd *qc); +extern int atapi_check_dma(struct ata_queued_cmd *qc); extern void swap_buf_le16(u16 *buf, unsigned int buf_words); extern void ata_dev_init(struct ata_device *dev); extern void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp); diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c index 3d39f9dfec5..41b4361bbf6 100644 --- a/drivers/ata/pata_pcmcia.c +++ b/drivers/ata/pata_pcmcia.c @@ -414,6 +414,7 @@ static struct pcmcia_device_id pcmcia_devices[] = { PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCIDE", 0x547e66dc, 0x5c5ab149), PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCIDEII", 0x547e66dc, 0xb3662674), PCMCIA_DEVICE_PROD_ID12("LOOKMEET", "CBIDE2 ", 0xe37be2b5, 0x8671043b), + PCMCIA_DEVICE_PROD_ID12("M-Systems", "CF300", 0x7ed2ad87, 0x7e9e78ee), PCMCIA_DEVICE_PROD_ID12("M-Systems", "CF500", 0x7ed2ad87, 0x7a13045c), PCMCIA_DEVICE_PROD_ID2("NinjaATA-", 0xebe0bd79), PCMCIA_DEVICE_PROD_ID12("PCMCIA", "CD-ROM", 0x281f1c5d, 0x66536591), @@ -424,6 +425,7 @@ static struct pcmcia_device_id pcmcia_devices[] = { PCMCIA_DEVICE_PROD_ID12("SMI VENDOR", "SMI PRODUCT", 0x30896c92, 0x703cc5f6), PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "MK2001MPL", 0xb4585a1a, 0x3489e003), PCMCIA_DEVICE_PROD_ID1("TRANSCEND 512M ", 0xd0909443), + PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS1GCF45", 0x709b1bf1, 0xf68b6f32), PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS1GCF80", 0x709b1bf1, 0x2a54d4b1), PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS2GCF120", 0x709b1bf1, 0x969aa4f2), PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS4GCF120", 0x709b1bf1, 0xf54a91c8), diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 60391e9a84d..ad169ffbc4c 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -1322,6 +1322,9 @@ static int mv_port_start(struct ata_port *ap) goto out_port_free_dma_mem; memset(pp->crpb, 0, MV_CRPB_Q_SZ); + /* 6041/6081 Rev. "C0" (and newer) are okay with async notify */ + if (hpriv->hp_flags & MV_HP_ERRATA_60X1C0) + ap->flags |= ATA_FLAG_AN; /* * For GEN_I, there's no NCQ, so we only allocate a single sg_tbl. * For later hardware, we need one unique sg_tbl per NCQ tag. @@ -1592,6 +1595,24 @@ static unsigned int mv_qc_issue(struct ata_queued_cmd *qc) if ((qc->tf.protocol != ATA_PROT_DMA) && (qc->tf.protocol != ATA_PROT_NCQ)) { + static int limit_warnings = 10; + /* + * Errata SATA#16, SATA#24: warn if multiple DRQs expected. + * + * Someday, we might implement special polling workarounds + * for these, but it all seems rather unnecessary since we + * normally use only DMA for commands which transfer more + * than a single block of data. + * + * Much of the time, this could just work regardless. + * So for now, just log the incident, and allow the attempt. + */ + if (limit_warnings > 0 && (qc->nbytes / qc->sect_size) > 1) { + --limit_warnings; + ata_link_printk(qc->dev->link, KERN_WARNING, DRV_NAME + ": attempting PIO w/multiple DRQ: " + "this may fail due to h/w errata\n"); + } /* * We're about to send a non-EDMA capable command to the * port. Turn off EDMA so there won't be problems accessing diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index 8ee6b5b4ede..84ffcc26a74 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -370,6 +370,7 @@ static const struct pci_device_id sil24_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x3124), BID_SIL3124 }, { PCI_VDEVICE(CMD, 0x3132), BID_SIL3132 }, { PCI_VDEVICE(CMD, 0x0242), BID_SIL3132 }, + { PCI_VDEVICE(CMD, 0x0244), BID_SIL3132 }, { PCI_VDEVICE(CMD, 0x3131), BID_SIL3131 }, { PCI_VDEVICE(CMD, 0x3531), BID_SIL3131 }, diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c index f277cea904c..db529b84994 100644 --- a/drivers/ata/sata_uli.c +++ b/drivers/ata/sata_uli.c @@ -83,6 +83,7 @@ static struct ata_port_operations uli_ops = { .inherits = &ata_bmdma_port_ops, .scr_read = uli_scr_read, .scr_write = uli_scr_write, + .hardreset = ATA_OP_NULL, }; static const struct ata_port_info uli_port_info = { diff --git a/drivers/atm/he.c b/drivers/atm/he.c index ffc4a5a4194..ea495b21f91 100644 --- a/drivers/atm/he.c +++ b/drivers/atm/he.c @@ -1542,7 +1542,8 @@ he_start(struct atm_dev *dev) /* initialize framer */ #ifdef CONFIG_ATM_HE_USE_SUNI - suni_init(he_dev->atm_dev); + if (he_isMM(he_dev)) + suni_init(he_dev->atm_dev); if (he_dev->atm_dev->phy && he_dev->atm_dev->phy->start) he_dev->atm_dev->phy->start(he_dev->atm_dev); #endif /* CONFIG_ATM_HE_USE_SUNI */ @@ -1554,6 +1555,7 @@ he_start(struct atm_dev *dev) val = he_phy_get(he_dev->atm_dev, SUNI_TPOP_APM); val = (val & ~SUNI_TPOP_APM_S) | (SUNI_TPOP_S_SDH << SUNI_TPOP_APM_S_SHIFT); he_phy_put(he_dev->atm_dev, val, SUNI_TPOP_APM); + he_phy_put(he_dev->atm_dev, SUNI_TACP_IUCHP_CLP, SUNI_TACP_IUCHP); } /* 5.1.12 enable transmit and receive */ @@ -2844,10 +2846,15 @@ he_ioctl(struct atm_dev *atm_dev, unsigned int cmd, void __user *arg) if (copy_from_user(®, arg, sizeof(struct he_ioctl_reg))) return -EFAULT; - + spin_lock_irqsave(&he_dev->global_lock, flags); switch (reg.type) { case HE_REGTYPE_PCI: + if (reg.addr < 0 || reg.addr >= HE_REGMAP_SIZE) { + err = -EINVAL; + break; + } + reg.val = he_readl(he_dev, reg.addr); break; case HE_REGTYPE_RCM: diff --git a/drivers/atm/he.h b/drivers/atm/he.h index fe6cd15a78a..b87d6ccabac 100644 --- a/drivers/atm/he.h +++ b/drivers/atm/he.h @@ -267,13 +267,7 @@ struct he_dev { char prod_id[30]; char mac_addr[6]; - int media; /* - * 0x26 = HE155 MM - * 0x27 = HE622 MM - * 0x46 = HE155 SM - * 0x47 = HE622 SM - */ - + int media; unsigned int vcibits, vpibits; unsigned int cells_per_row; @@ -392,6 +386,7 @@ struct he_vcc #define HE_DEV(dev) ((struct he_dev *) (dev)->dev_data) #define he_is622(dev) ((dev)->media & 0x1) +#define he_isMM(dev) ((dev)->media & 0x20) #define HE_REGMAP_SIZE 0x100000 @@ -876,8 +871,8 @@ struct he_vcc #define M_SN 0x3a /* integer */ #define MEDIA 0x3e /* integer */ #define HE155MM 0x26 -#define HE155SM 0x27 -#define HE622MM 0x46 +#define HE622MM 0x27 +#define HE155SM 0x46 #define HE622SM 0x47 #define MAC_ADDR 0x42 /* char[] */ diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c index 5c28ca7380f..139fce6968a 100644 --- a/drivers/atm/iphase.c +++ b/drivers/atm/iphase.c @@ -2562,17 +2562,11 @@ static int __devinit ia_start(struct atm_dev *dev) error = suni_init(dev); if (error) goto err_free_rx; - /* - * Enable interrupt on loss of signal - * SUNI_RSOP_CIE - 0x10 - * SUNI_RSOP_CIE_LOSE - 0x04 - */ - ia_phy_put(dev, ia_phy_get(dev, 0x10) | 0x04, 0x10); -#ifndef MODULE - error = dev->phy->start(dev); - if (error) - goto err_free_rx; -#endif + if (dev->phy->start) { + error = dev->phy->start(dev); + if (error) + goto err_free_rx; + } /* Get iadev->carrier_detect status */ IaFrontEndIntr(iadev); } @@ -3198,6 +3192,8 @@ static int __devinit ia_init_one(struct pci_dev *pdev, IF_INIT(printk("dev_id = 0x%x iadev->LineRate = %d \n", (u32)dev, iadev->LineRate);) + pci_set_drvdata(pdev, dev); + ia_dev[iadev_count] = iadev; _ia_dev[iadev_count] = dev; iadev_count++; @@ -3219,8 +3215,6 @@ static int __devinit ia_init_one(struct pci_dev *pdev, iadev->next_board = ia_boards; ia_boards = dev; - pci_set_drvdata(pdev, dev); - return 0; err_out_deregister_dev: @@ -3238,9 +3232,14 @@ static void __devexit ia_remove_one(struct pci_dev *pdev) struct atm_dev *dev = pci_get_drvdata(pdev); IADEV *iadev = INPH_IA_DEV(dev); - ia_phy_put(dev, ia_phy_get(dev,0x10) & ~(0x4), 0x10); + /* Disable phy interrupts */ + ia_phy_put(dev, ia_phy_get(dev, SUNI_RSOP_CIE) & ~(SUNI_RSOP_CIE_LOSE), + SUNI_RSOP_CIE); udelay(1); + if (dev->phy && dev->phy->stop) + dev->phy->stop(dev); + /* De-register device */ free_irq(iadev->irq, dev); iadev_count--; diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig index 043353bd060..14b9d5f4c20 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -64,7 +64,7 @@ config KS0108_DELAY Amount of time the ks0108 should wait between each control write to the parallel port. - If your driver seems to miss random writings, increment this. + If your LCD seems to miss random writings, increment this. If you don't know what I'm talking about, ignore it. diff --git a/drivers/auxdisplay/cfag12864b.c b/drivers/auxdisplay/cfag12864b.c index 80bb0610538..683509f013a 100644 --- a/drivers/auxdisplay/cfag12864b.c +++ b/drivers/auxdisplay/cfag12864b.c @@ -5,7 +5,7 @@ * License: GPLv2 * Depends: ks0108 * - * Author: Copyright (C) Miguel Ojeda Sandonis <maxextreme@gmail.com> + * Author: Copyright (C) Miguel Ojeda Sandonis * Date: 2006-10-31 * * This program is free software; you can redistribute it and/or modify @@ -398,5 +398,5 @@ module_init(cfag12864b_init); module_exit(cfag12864b_exit); MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Miguel Ojeda Sandonis <maxextreme@gmail.com>"); +MODULE_AUTHOR("Miguel Ojeda Sandonis <miguel.ojeda.sandonis@gmail.com>"); MODULE_DESCRIPTION("cfag12864b LCD driver"); diff --git a/drivers/auxdisplay/cfag12864bfb.c b/drivers/auxdisplay/cfag12864bfb.c index 307c190699e..fe3a865be4e 100644 --- a/drivers/auxdisplay/cfag12864bfb.c +++ b/drivers/auxdisplay/cfag12864bfb.c @@ -5,7 +5,7 @@ * License: GPLv2 * Depends: cfag12864b * - * Author: Copyright (C) Miguel Ojeda Sandonis <maxextreme@gmail.com> + * Author: Copyright (C) Miguel Ojeda Sandonis * Date: 2006-10-31 * * This program is free software; you can redistribute it and/or modify @@ -186,5 +186,5 @@ module_init(cfag12864bfb_init); module_exit(cfag12864bfb_exit); MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Miguel Ojeda Sandonis <maxextreme@gmail.com>"); +MODULE_AUTHOR("Miguel Ojeda Sandonis <miguel.ojeda.sandonis@gmail.com>"); MODULE_DESCRIPTION("cfag12864b LCD framebuffer driver"); diff --git a/drivers/auxdisplay/ks0108.c b/drivers/auxdisplay/ks0108.c index e6c3646ef18..5b93852392b 100644 --- a/drivers/auxdisplay/ks0108.c +++ b/drivers/auxdisplay/ks0108.c @@ -5,7 +5,7 @@ * License: GPLv2 * Depends: parport * - * Author: Copyright (C) Miguel Ojeda Sandonis <maxextreme@gmail.com> + * Author: Copyright (C) Miguel Ojeda Sandonis * Date: 2006-10-31 * * This program is free software; you can redistribute it and/or modify @@ -173,6 +173,6 @@ module_init(ks0108_init); module_exit(ks0108_exit); MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Miguel Ojeda Sandonis <maxextreme@gmail.com>"); +MODULE_AUTHOR("Miguel Ojeda Sandonis <miguel.ojeda.sandonis@gmail.com>"); MODULE_DESCRIPTION("ks0108 LCD Controller driver"); diff --git a/drivers/base/node.c b/drivers/base/node.c index 39f3d1b3a21..0f867a08333 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -84,8 +84,8 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf) nid, K(i.totalram), nid, K(i.freeram), nid, K(i.totalram - i.freeram), - nid, node_page_state(nid, NR_ACTIVE), - nid, node_page_state(nid, NR_INACTIVE), + nid, K(node_page_state(nid, NR_ACTIVE)), + nid, K(node_page_state(nid, NR_INACTIVE)), #ifdef CONFIG_HIGHMEM nid, K(i.totalhigh), nid, K(i.freehigh), diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c index 2b4b392dcbc..87a7f1d0257 100644 --- a/drivers/base/power/trace.c +++ b/drivers/base/power/trace.c @@ -153,7 +153,7 @@ EXPORT_SYMBOL(set_trace_device); * it's not any guarantee, but it's a high _likelihood_ that * the match is valid). */ -void generate_resume_trace(void *tracedata, unsigned int user) +void generate_resume_trace(const void *tracedata, unsigned int user) { unsigned short lineno = *(unsigned short *)tracedata; const char *file = *(const char **)(tracedata + 2); diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 5f1e1cc6165..d81632cd7d0 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -106,35 +106,34 @@ MODULE_DEVICE_TABLE(pci, cciss_pci_device_id); /* board_id = Subsystem Device ID & Vendor ID * product = Marketing Name for the board * access = Address of the struct of function pointers - * nr_cmds = Number of commands supported by controller */ static struct board_type products[] = { - {0x40700E11, "Smart Array 5300", &SA5_access, 512}, - {0x40800E11, "Smart Array 5i", &SA5B_access, 512}, - {0x40820E11, "Smart Array 532", &SA5B_access, 512}, - {0x40830E11, "Smart Array 5312", &SA5B_access, 512}, - {0x409A0E11, "Smart Array 641", &SA5_access, 512}, - {0x409B0E11, "Smart Array 642", &SA5_access, 512}, - {0x409C0E11, "Smart Array 6400", &SA5_access, 512}, - {0x409D0E11, "Smart Array 6400 EM", &SA5_access, 512}, - {0x40910E11, "Smart Array 6i", &SA5_access, 512}, - {0x3225103C, "Smart Array P600", &SA5_access, 512}, - {0x3223103C, "Smart Array P800", &SA5_access, 512}, - {0x3234103C, "Smart Array P400", &SA5_access, 512}, - {0x3235103C, "Smart Array P400i", &SA5_access, 512}, - {0x3211103C, "Smart Array E200i", &SA5_access, 120}, - {0x3212103C, "Smart Array E200", &SA5_access, 120}, - {0x3213103C, "Smart Array E200i", &SA5_access, 120}, - {0x3214103C, "Smart Array E200i", &SA5_access, 120}, - {0x3215103C, "Smart Array E200i", &SA5_access, 120}, - {0x3237103C, "Smart Array E500", &SA5_access, 512}, - {0x323D103C, "Smart Array P700m", &SA5_access, 512}, - {0x3241103C, "Smart Array P212", &SA5_access, 384}, - {0x3243103C, "Smart Array P410", &SA5_access, 384}, - {0x3245103C, "Smart Array P410i", &SA5_access, 384}, - {0x3247103C, "Smart Array P411", &SA5_access, 384}, - {0x3249103C, "Smart Array P812", &SA5_access, 384}, - {0xFFFF103C, "Unknown Smart Array", &SA5_access, 120}, + {0x40700E11, "Smart Array 5300", &SA5_access}, + {0x40800E11, "Smart Array 5i", &SA5B_access}, + {0x40820E11, "Smart Array 532", &SA5B_access}, + {0x40830E11, "Smart Array 5312", &SA5B_access}, + {0x409A0E11, "Smart Array 641", &SA5_access}, + {0x409B0E11, "Smart Array 642", &SA5_access}, + {0x409C0E11, "Smart Array 6400", &SA5_access}, + {0x409D0E11, "Smart Array 6400 EM", &SA5_access}, + {0x40910E11, "Smart Array 6i", &SA5_access}, + {0x3225103C, "Smart Array P600", &SA5_access}, + {0x3223103C, "Smart Array P800", &SA5_access}, + {0x3234103C, "Smart Array P400", &SA5_access}, + {0x3235103C, "Smart Array P400i", &SA5_access}, + {0x3211103C, "Smart Array E200i", &SA5_access}, + {0x3212103C, "Smart Array E200", &SA5_access}, + {0x3213103C, "Smart Array E200i", &SA5_access}, + {0x3214103C, "Smart Array E200i", &SA5_access}, + {0x3215103C, "Smart Array E200i", &SA5_access}, + {0x3237103C, "Smart Array E500", &SA5_access}, + {0x323D103C, "Smart Array P700m", &SA5_access}, + {0x3241103C, "Smart Array P212", &SA5_access}, + {0x3243103C, "Smart Array P410", &SA5_access}, + {0x3245103C, "Smart Array P410i", &SA5_access}, + {0x3247103C, "Smart Array P411", &SA5_access}, + {0x3249103C, "Smart Array P812", &SA5_access}, + {0xFFFF103C, "Unknown Smart Array", &SA5_access}, }; /* How long to wait (in milliseconds) for board to go into simple mode */ @@ -3086,11 +3085,20 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) print_cfg_table(c->cfgtable); #endif /* CCISS_DEBUG */ + /* Some controllers support Zero Memory Raid (ZMR). + * When configured in ZMR mode the number of supported + * commands drops to 64. So instead of just setting an + * arbitrary value we make the driver a little smarter. + * We read the config table to tell us how many commands + * are supported on the controller then subtract 4 to + * leave a little room for ioctl calls. + */ + c->max_commands = readl(&(c->cfgtable->CmdsOutMax)); for (i = 0; i < ARRAY_SIZE(products); i++) { if (board_id == products[i].board_id) { c->product_name = products[i].product_name; c->access = *(products[i].access); - c->nr_cmds = products[i].nr_cmds; + c->nr_cmds = c->max_commands - 4; break; } } @@ -3110,7 +3118,7 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) if (subsystem_vendor_id == PCI_VENDOR_ID_HP) { c->product_name = products[i-1].product_name; c->access = *(products[i-1].access); - c->nr_cmds = products[i-1].nr_cmds; + c->nr_cmds = c->max_commands - 4; printk(KERN_WARNING "cciss: This is an unknown " "Smart Array controller.\n" "cciss: Please update to the latest driver " @@ -3546,6 +3554,10 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, for (j = 0; j <= hba[i]->highest_lun; j++) add_disk(hba[i]->gendisk[j]); + /* we must register the controller even if no disks exist */ + if (hba[i]->highest_lun == -1) + add_disk(hba[i]->gendisk[0]); + return 1; clean4: diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 99e6a406efb..81e14bea54b 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -99,8 +99,8 @@ struct agp_bridge_driver { const void *aperture_sizes; int num_aperture_sizes; enum aper_size_type size_type; - int cant_use_aperture; - int needs_scratch_page; + bool cant_use_aperture; + bool needs_scratch_page; const struct gatt_mask *masks; int (*fetch_size)(void); int (*configure)(void); @@ -278,7 +278,7 @@ void agp_generic_destroy_page(void *addr, int flags); void agp_free_key(int key); int agp_num_entries(void); u32 agp_collect_device_status(struct agp_bridge_data *bridge, u32 mode, u32 command); -void agp_device_command(u32 command, int agp_v3); +void agp_device_command(u32 command, bool agp_v3); int agp_3_5_enable(struct agp_bridge_data *bridge); void global_cache_flush(void); void get_agp_version(struct agp_bridge_data *bridge); diff --git a/drivers/char/agp/alpha-agp.c b/drivers/char/agp/alpha-agp.c index e77c17838c8..5da89f6c6c2 100644 --- a/drivers/char/agp/alpha-agp.c +++ b/drivers/char/agp/alpha-agp.c @@ -80,7 +80,7 @@ static void alpha_core_agp_enable(struct agp_bridge_data *bridge, u32 mode) agp->mode.bits.enable = 1; agp->ops->configure(agp); - agp_device_command(agp->mode.lw, 0); + agp_device_command(agp->mode.lw, false); } static int alpha_core_agp_insert_memory(struct agp_memory *mem, off_t pg_start, @@ -126,7 +126,7 @@ struct agp_bridge_driver alpha_core_agp_driver = { .aperture_sizes = alpha_core_agp_sizes, .num_aperture_sizes = 1, .size_type = FIXED_APER_SIZE, - .cant_use_aperture = 1, + .cant_use_aperture = true, .masks = NULL, .fetch_size = alpha_core_agp_fetch_size, diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c index 96bdb9296b0..39a0718bc61 100644 --- a/drivers/char/agp/amd-k7-agp.c +++ b/drivers/char/agp/amd-k7-agp.c @@ -314,9 +314,9 @@ static int amd_insert_memory(struct agp_memory *mem, off_t pg_start, int type) j++; } - if (mem->is_flushed == FALSE) { + if (!mem->is_flushed) { global_cache_flush(); - mem->is_flushed = TRUE; + mem->is_flushed = true; } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index d8200ac8f8c..481ffe87c71 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -16,28 +16,9 @@ #include <asm/page.h> /* PAGE_SIZE */ #include <asm/e820.h> #include <asm/k8.h> +#include <asm/gart.h> #include "agp.h" -/* PTE bits. */ -#define GPTE_VALID 1 -#define GPTE_COHERENT 2 - -/* Aperture control register bits. */ -#define GARTEN (1<<0) -#define DISGARTCPU (1<<4) -#define DISGARTIO (1<<5) - -/* GART cache control register bits. */ -#define INVGART (1<<0) -#define GARTPTEERR (1<<1) - -/* K8 On-cpu GART registers */ -#define AMD64_GARTAPERTURECTL 0x90 -#define AMD64_GARTAPERTUREBASE 0x94 -#define AMD64_GARTTABLEBASE 0x98 -#define AMD64_GARTCACHECTL 0x9c -#define AMD64_GARTEN (1<<0) - /* NVIDIA K8 registers */ #define NVIDIA_X86_64_0_APBASE 0x10 #define NVIDIA_X86_64_1_APBASE1 0x50 @@ -90,9 +71,9 @@ static int amd64_insert_memory(struct agp_memory *mem, off_t pg_start, int type) j++; } - if (mem->is_flushed == FALSE) { + if (!mem->is_flushed) { global_cache_flush(); - mem->is_flushed = TRUE; + mem->is_flushed = true; } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { @@ -165,29 +146,18 @@ static int amd64_fetch_size(void) * In a multiprocessor x86-64 system, this function gets * called once for each CPU. */ -static u64 amd64_configure (struct pci_dev *hammer, u64 gatt_table) +static u64 amd64_configure(struct pci_dev *hammer, u64 gatt_table) { u64 aperturebase; u32 tmp; - u64 addr, aper_base; + u64 aper_base; /* Address to map to */ - pci_read_config_dword (hammer, AMD64_GARTAPERTUREBASE, &tmp); + pci_read_config_dword(hammer, AMD64_GARTAPERTUREBASE, &tmp); aperturebase = tmp << 25; aper_base = (aperturebase & PCI_BASE_ADDRESS_MEM_MASK); - /* address of the mappings table */ - addr = (u64) gatt_table; - addr >>= 12; - tmp = (u32) addr<<4; - tmp &= ~0xf; - pci_write_config_dword (hammer, AMD64_GARTTABLEBASE, tmp); - - /* Enable GART translation for this hammer. */ - pci_read_config_dword(hammer, AMD64_GARTAPERTURECTL, &tmp); - tmp |= GARTEN; - tmp &= ~(DISGARTCPU | DISGARTIO); - pci_write_config_dword(hammer, AMD64_GARTAPERTURECTL, tmp); + enable_gart_translation(hammer, gatt_table); return aper_base; } @@ -226,9 +196,9 @@ static void amd64_cleanup(void) for (i = 0; i < num_k8_northbridges; i++) { struct pci_dev *dev = k8_northbridges[i]; /* disable gart translation */ - pci_read_config_dword (dev, AMD64_GARTAPERTURECTL, &tmp); + pci_read_config_dword(dev, AMD64_GARTAPERTURECTL, &tmp); tmp &= ~AMD64_GARTEN; - pci_write_config_dword (dev, AMD64_GARTAPERTURECTL, tmp); + pci_write_config_dword(dev, AMD64_GARTAPERTURECTL, tmp); } } @@ -258,24 +228,10 @@ static const struct agp_bridge_driver amd_8151_driver = { }; /* Some basic sanity checks for the aperture. */ -static int __devinit aperture_valid(u64 aper, u32 size) +static int __devinit agp_aperture_valid(u64 aper, u32 size) { - if (aper == 0) { - printk(KERN_ERR PFX "No aperture\n"); - return 0; - } - if (size < 32*1024*1024) { - printk(KERN_ERR PFX "Aperture too small (%d MB)\n", size>>20); - return 0; - } - if ((u64)aper + size > 0x100000000ULL) { - printk(KERN_ERR PFX "Aperture out of bounds\n"); + if (!aperture_valid(aper, size, 32*1024*1024)) return 0; - } - if (e820_any_mapped(aper, aper + size, E820_RAM)) { - printk(KERN_ERR PFX "Aperture pointing to RAM\n"); - return 0; - } /* Request the Aperture. This catches cases when someone else already put a mapping in there - happens with some very broken BIOS @@ -308,11 +264,11 @@ static __devinit int fix_northbridge(struct pci_dev *nb, struct pci_dev *agp, u32 nb_order, nb_base; u16 apsize; - pci_read_config_dword(nb, 0x90, &nb_order); + pci_read_config_dword(nb, AMD64_GARTAPERTURECTL, &nb_order); nb_order = (nb_order >> 1) & 7; - pci_read_config_dword(nb, 0x94, &nb_base); + pci_read_config_dword(nb, AMD64_GARTAPERTUREBASE, &nb_base); nb_aper = nb_base << 25; - if (aperture_valid(nb_aper, (32*1024*1024)<<nb_order)) { + if (agp_aperture_valid(nb_aper, (32*1024*1024)<<nb_order)) { return 0; } @@ -331,12 +287,23 @@ static __devinit int fix_northbridge(struct pci_dev *nb, struct pci_dev *agp, pci_read_config_dword(agp, 0x10, &aper_low); pci_read_config_dword(agp, 0x14, &aper_hi); aper = (aper_low & ~((1<<22)-1)) | ((u64)aper_hi << 32); + + /* + * On some sick chips APSIZE is 0. This means it wants 4G + * so let double check that order, and lets trust the AMD NB settings + */ + if (order >=0 && aper + (32ULL<<(20 + order)) > 0x100000000ULL) { + printk(KERN_INFO "Aperture size %u MB is not right, using settings from NB\n", + 32 << order); + order = nb_order; + } + printk(KERN_INFO PFX "Aperture from AGP @ %Lx size %u MB\n", aper, 32 << order); - if (order < 0 || !aperture_valid(aper, (32*1024*1024)<<order)) + if (order < 0 || !agp_aperture_valid(aper, (32*1024*1024)<<order)) return -1; - pci_write_config_dword(nb, 0x90, order << 1); - pci_write_config_dword(nb, 0x94, aper >> 25); + pci_write_config_dword(nb, AMD64_GARTAPERTURECTL, order << 1); + pci_write_config_dword(nb, AMD64_GARTAPERTUREBASE, aper >> 25); return 0; } diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c index 07b4d8ff56e..3a4566c0d84 100644 --- a/drivers/char/agp/ati-agp.c +++ b/drivers/char/agp/ati-agp.c @@ -287,10 +287,10 @@ static int ati_insert_memory(struct agp_memory * mem, j++; } - if (mem->is_flushed == FALSE) { + if (!mem->is_flushed) { /*CACHE_FLUSH(); */ global_cache_flush(); - mem->is_flushed = TRUE; + mem->is_flushed = true; } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index b1bdd015165..1ec87104e68 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -188,10 +188,10 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) err_out: if (bridge->driver->needs_scratch_page) { - bridge->driver->agp_destroy_page(gart_to_virt(bridge->scratch_page_real), - AGP_PAGE_DESTROY_UNMAP); - bridge->driver->agp_destroy_page(gart_to_virt(bridge->scratch_page_real), - AGP_PAGE_DESTROY_FREE); + void *va = gart_to_virt(bridge->scratch_page_real); + + bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP); + bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE); } if (got_gatt) bridge->driver->free_gatt_table(bridge); @@ -215,10 +215,10 @@ static void agp_backend_cleanup(struct agp_bridge_data *bridge) if (bridge->driver->agp_destroy_page && bridge->driver->needs_scratch_page) { - bridge->driver->agp_destroy_page(gart_to_virt(bridge->scratch_page_real), - AGP_PAGE_DESTROY_UNMAP); - bridge->driver->agp_destroy_page(gart_to_virt(bridge->scratch_page_real), - AGP_PAGE_DESTROY_FREE); + void *va = gart_to_virt(bridge->scratch_page_real); + + bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP); + bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE); } } diff --git a/drivers/char/agp/compat_ioctl.c b/drivers/char/agp/compat_ioctl.c index 39275794fe6..58c57cb2518 100644 --- a/drivers/char/agp/compat_ioctl.c +++ b/drivers/char/agp/compat_ioctl.c @@ -214,7 +214,7 @@ long compat_agp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ret_val = -EINVAL; goto ioctl_out; } - if ((agp_fe.backend_acquired != TRUE) && + if ((agp_fe.backend_acquired != true) && (cmd != AGPIOC_ACQUIRE32)) { ret_val = -EBUSY; goto ioctl_out; diff --git a/drivers/char/agp/efficeon-agp.c b/drivers/char/agp/efficeon-agp.c index cac0009cebc..8ca6f262ef8 100644 --- a/drivers/char/agp/efficeon-agp.c +++ b/drivers/char/agp/efficeon-agp.c @@ -249,9 +249,9 @@ static int efficeon_insert_memory(struct agp_memory * mem, off_t pg_start, int t if (type != 0 || mem->type != 0) return -EINVAL; - if (mem->is_flushed == FALSE) { + if (!mem->is_flushed) { global_cache_flush(); - mem->is_flushed = TRUE; + mem->is_flushed = true; } last_page = NULL; @@ -329,7 +329,7 @@ static const struct agp_bridge_driver efficeon_driver = { .free_gatt_table = efficeon_free_gatt_table, .insert_memory = efficeon_insert_memory, .remove_memory = efficeon_remove_memory, - .cant_use_aperture = 0, // 1 might be faster? + .cant_use_aperture = false, // true might be faster? // Generic .alloc_by_type = agp_generic_alloc_by_type, diff --git a/drivers/char/agp/frontend.c b/drivers/char/agp/frontend.c index 857b26227d8..e6cb1ab03e0 100644 --- a/drivers/char/agp/frontend.c +++ b/drivers/char/agp/frontend.c @@ -395,7 +395,7 @@ static int agp_remove_controller(struct agp_controller *controller) if (agp_fe.current_controller == controller) { agp_fe.current_controller = NULL; - agp_fe.backend_acquired = FALSE; + agp_fe.backend_acquired = false; agp_backend_release(agp_bridge); } kfree(controller); @@ -443,7 +443,7 @@ static void agp_controller_release_current(struct agp_controller *controller, } agp_fe.current_controller = NULL; - agp_fe.used_by_controller = FALSE; + agp_fe.used_by_controller = false; agp_backend_release(agp_bridge); } @@ -573,7 +573,7 @@ static int agp_mmap(struct file *file, struct vm_area_struct *vma) mutex_lock(&(agp_fe.agp_mutex)); - if (agp_fe.backend_acquired != TRUE) + if (agp_fe.backend_acquired != true) goto out_eperm; if (!(test_bit(AGP_FF_IS_VALID, &priv->access_flags))) @@ -768,7 +768,7 @@ int agpioc_acquire_wrap(struct agp_file_private *priv) atomic_inc(&agp_bridge->agp_in_use); - agp_fe.backend_acquired = TRUE; + agp_fe.backend_acquired = true; controller = agp_find_controller_by_pid(priv->my_pid); @@ -778,7 +778,7 @@ int agpioc_acquire_wrap(struct agp_file_private *priv) controller = agp_create_controller(priv->my_pid); if (controller == NULL) { - agp_fe.backend_acquired = FALSE; + agp_fe.backend_acquired = false; agp_backend_release(agp_bridge); return -ENOMEM; } @@ -981,7 +981,7 @@ static long agp_ioctl(struct file *file, ret_val = -EINVAL; goto ioctl_out; } - if ((agp_fe.backend_acquired != TRUE) && + if ((agp_fe.backend_acquired != true) && (cmd != AGPIOC_ACQUIRE)) { ret_val = -EBUSY; goto ioctl_out; diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 7fc0c99a3a5..564daaa6c7d 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -96,13 +96,13 @@ EXPORT_SYMBOL(agp_flush_chipset); void agp_alloc_page_array(size_t size, struct agp_memory *mem) { mem->memory = NULL; - mem->vmalloc_flag = 0; + mem->vmalloc_flag = false; if (size <= 2*PAGE_SIZE) mem->memory = kmalloc(size, GFP_KERNEL | __GFP_NORETRY); if (mem->memory == NULL) { mem->memory = vmalloc(size); - mem->vmalloc_flag = 1; + mem->vmalloc_flag = true; } } EXPORT_SYMBOL(agp_alloc_page_array); @@ -188,7 +188,7 @@ void agp_free_memory(struct agp_memory *curr) if (curr == NULL) return; - if (curr->is_bound == TRUE) + if (curr->is_bound) agp_unbind_memory(curr); if (curr->type >= AGP_USER_TYPES) { @@ -202,10 +202,13 @@ void agp_free_memory(struct agp_memory *curr) } if (curr->page_count != 0) { for (i = 0; i < curr->page_count; i++) { - curr->bridge->driver->agp_destroy_page(gart_to_virt(curr->memory[i]), AGP_PAGE_DESTROY_UNMAP); + curr->memory[i] = (unsigned long)gart_to_virt(curr->memory[i]); + curr->bridge->driver->agp_destroy_page((void *)curr->memory[i], + AGP_PAGE_DESTROY_UNMAP); } for (i = 0; i < curr->page_count; i++) { - curr->bridge->driver->agp_destroy_page(gart_to_virt(curr->memory[i]), AGP_PAGE_DESTROY_FREE); + curr->bridge->driver->agp_destroy_page((void *)curr->memory[i], + AGP_PAGE_DESTROY_FREE); } } agp_free_key(curr->key); @@ -411,20 +414,20 @@ int agp_bind_memory(struct agp_memory *curr, off_t pg_start) if (curr == NULL) return -EINVAL; - if (curr->is_bound == TRUE) { + if (curr->is_bound) { printk(KERN_INFO PFX "memory %p is already bound!\n", curr); return -EINVAL; } - if (curr->is_flushed == FALSE) { + if (!curr->is_flushed) { curr->bridge->driver->cache_flush(); - curr->is_flushed = TRUE; + curr->is_flushed = true; } ret_val = curr->bridge->driver->insert_memory(curr, pg_start, curr->type); if (ret_val != 0) return ret_val; - curr->is_bound = TRUE; + curr->is_bound = true; curr->pg_start = pg_start; return 0; } @@ -446,7 +449,7 @@ int agp_unbind_memory(struct agp_memory *curr) if (curr == NULL) return -EINVAL; - if (curr->is_bound != TRUE) { + if (!curr->is_bound) { printk(KERN_INFO PFX "memory %p was not bound!\n", curr); return -EINVAL; } @@ -456,7 +459,7 @@ int agp_unbind_memory(struct agp_memory *curr) if (ret_val != 0) return ret_val; - curr->is_bound = FALSE; + curr->is_bound = false; curr->pg_start = 0; return 0; } @@ -754,7 +757,7 @@ u32 agp_collect_device_status(struct agp_bridge_data *bridge, u32 requested_mode EXPORT_SYMBOL(agp_collect_device_status); -void agp_device_command(u32 bridge_agpstat, int agp_v3) +void agp_device_command(u32 bridge_agpstat, bool agp_v3) { struct pci_dev *device = NULL; int mode; @@ -818,7 +821,7 @@ void agp_generic_enable(struct agp_bridge_data *bridge, u32 requested_mode) /* If we have 3.5, we can do the isoch stuff. */ if (bridge->minor_version >= 5) agp_3_5_enable(bridge); - agp_device_command(bridge_agpstat, TRUE); + agp_device_command(bridge_agpstat, true); return; } else { /* Disable calibration cycle in RX91<1> when not in AGP3.0 mode of operation.*/ @@ -835,7 +838,7 @@ void agp_generic_enable(struct agp_bridge_data *bridge, u32 requested_mode) } /* AGP v<3 */ - agp_device_command(bridge_agpstat, FALSE); + agp_device_command(bridge_agpstat, false); } EXPORT_SYMBOL(agp_generic_enable); @@ -1083,9 +1086,9 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) j++; } - if (mem->is_flushed == FALSE) { + if (!mem->is_flushed) { bridge->driver->cache_flush(); - mem->is_flushed = TRUE; + mem->is_flushed = true; } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c index cbb0444467b..80d7317f85c 100644 --- a/drivers/char/agp/hp-agp.c +++ b/drivers/char/agp/hp-agp.c @@ -353,9 +353,9 @@ hp_zx1_insert_memory (struct agp_memory *mem, off_t pg_start, int type) j++; } - if (mem->is_flushed == FALSE) { + if (!mem->is_flushed) { global_cache_flush(); - mem->is_flushed = TRUE; + mem->is_flushed = true; } for (i = 0, j = io_pg_start; i < mem->page_count; i++) { @@ -437,7 +437,7 @@ const struct agp_bridge_driver hp_zx1_driver = { .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, .agp_type_to_mask_type = agp_generic_type_to_mask_type, - .cant_use_aperture = 1, + .cant_use_aperture = true, }; static int __init diff --git a/drivers/char/agp/i460-agp.c b/drivers/char/agp/i460-agp.c index 76f581c85a7..e587eebebc6 100644 --- a/drivers/char/agp/i460-agp.c +++ b/drivers/char/agp/i460-agp.c @@ -580,7 +580,7 @@ const struct agp_bridge_driver intel_i460_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_type_to_mask_type = agp_generic_type_to_mask_type, - .cant_use_aperture = 1, + .cant_use_aperture = true, }; static int __devinit agp_intel_i460_probe(struct pci_dev *pdev, diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index eeea50a1d22..df702642ab8 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -34,6 +34,12 @@ #define PCI_DEVICE_ID_INTEL_Q33_IG 0x29D2 #define PCI_DEVICE_ID_INTEL_IGD_HB 0x2A40 #define PCI_DEVICE_ID_INTEL_IGD_IG 0x2A42 +#define PCI_DEVICE_ID_INTEL_IGD_E_HB 0x2E00 +#define PCI_DEVICE_ID_INTEL_IGD_E_IG 0x2E02 +#define PCI_DEVICE_ID_INTEL_Q45_HB 0x2E10 +#define PCI_DEVICE_ID_INTEL_Q45_IG 0x2E12 +#define PCI_DEVICE_ID_INTEL_G45_HB 0x2E20 +#define PCI_DEVICE_ID_INTEL_G45_IG 0x2E22 /* cover 915 and 945 variants */ #define IS_I915 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_E7221_HB || \ @@ -55,6 +61,10 @@ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q35_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q33_HB) +#define IS_G4X (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGD_E_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q45_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G45_HB) + extern int agp_memory_reserved; @@ -80,8 +90,13 @@ extern int agp_memory_reserved; #define I915_PTEADDR 0x1C #define I915_GMCH_GMS_STOLEN_48M (0x6 << 4) #define I915_GMCH_GMS_STOLEN_64M (0x7 << 4) -#define G33_GMCH_GMS_STOLEN_128M (0x8 << 4) -#define G33_GMCH_GMS_STOLEN_256M (0x9 << 4) +#define G33_GMCH_GMS_STOLEN_128M (0x8 << 4) +#define G33_GMCH_GMS_STOLEN_256M (0x9 << 4) +#define INTEL_GMCH_GMS_STOLEN_96M (0xa << 4) +#define INTEL_GMCH_GMS_STOLEN_160M (0xb << 4) +#define INTEL_GMCH_GMS_STOLEN_224M (0xc << 4) +#define INTEL_GMCH_GMS_STOLEN_352M (0xd << 4) + #define I915_IFPADDR 0x60 /* Intel 965G registers */ @@ -325,7 +340,7 @@ static int intel_i810_insert_entries(struct agp_memory *mem, off_t pg_start, out: ret = 0; out_err: - mem->is_flushed = 1; + mem->is_flushed = true; return ret; } @@ -418,9 +433,11 @@ static void intel_i810_free_by_type(struct agp_memory *curr) if (curr->page_count == 4) i8xx_destroy_pages(gart_to_virt(curr->memory[0])); else { - agp_bridge->driver->agp_destroy_page(gart_to_virt(curr->memory[0]), + void *va = gart_to_virt(curr->memory[0]); + + agp_bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP); - agp_bridge->driver->agp_destroy_page(gart_to_virt(curr->memory[0]), + agp_bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE); } agp_free_page_array(curr); @@ -504,6 +521,10 @@ static void intel_i830_init_gtt_entries(void) size = 512; } size += 4; + } else if (IS_G4X) { + /* On 4 series hardware, GTT stolen is separate from graphics + * stolen, ignore it in stolen gtt entries counting */ + size = 0; } else { /* On previous hardware, the GTT size was just what was * required to map the aperture. @@ -552,30 +573,54 @@ static void intel_i830_init_gtt_entries(void) break; case I915_GMCH_GMS_STOLEN_48M: /* Check it's really I915G */ - if (IS_I915 || IS_I965 || IS_G33) + if (IS_I915 || IS_I965 || IS_G33 || IS_G4X) gtt_entries = MB(48) - KB(size); else gtt_entries = 0; break; case I915_GMCH_GMS_STOLEN_64M: /* Check it's really I915G */ - if (IS_I915 || IS_I965 || IS_G33) + if (IS_I915 || IS_I965 || IS_G33 || IS_G4X) gtt_entries = MB(64) - KB(size); else gtt_entries = 0; break; case G33_GMCH_GMS_STOLEN_128M: - if (IS_G33) + if (IS_G33 || IS_I965 || IS_G4X) gtt_entries = MB(128) - KB(size); else gtt_entries = 0; break; case G33_GMCH_GMS_STOLEN_256M: - if (IS_G33) + if (IS_G33 || IS_I965 || IS_G4X) gtt_entries = MB(256) - KB(size); else gtt_entries = 0; break; + case INTEL_GMCH_GMS_STOLEN_96M: + if (IS_I965 || IS_G4X) + gtt_entries = MB(96) - KB(size); + else + gtt_entries = 0; + break; + case INTEL_GMCH_GMS_STOLEN_160M: + if (IS_I965 || IS_G4X) + gtt_entries = MB(160) - KB(size); + else + gtt_entries = 0; + break; + case INTEL_GMCH_GMS_STOLEN_224M: + if (IS_I965 || IS_G4X) + gtt_entries = MB(224) - KB(size); + else + gtt_entries = 0; + break; + case INTEL_GMCH_GMS_STOLEN_352M: + if (IS_I965 || IS_G4X) + gtt_entries = MB(352) - KB(size); + else + gtt_entries = 0; + break; default: gtt_entries = 0; break; @@ -793,7 +838,7 @@ static int intel_i830_insert_entries(struct agp_memory *mem, off_t pg_start, out: ret = 0; out_err: - mem->is_flushed = 1; + mem->is_flushed = true; return ret; } @@ -903,7 +948,7 @@ static void intel_i9xx_setup_flush(void) intel_private.ifp_resource.flags = IORESOURCE_MEM; /* Setup chipset flush for 915 */ - if (IS_I965 || IS_G33) { + if (IS_I965 || IS_G33 || IS_G4X) { intel_i965_g33_setup_chipset_flush(); } else { intel_i915_setup_chipset_flush(); @@ -1020,7 +1065,7 @@ static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, out: ret = 0; out_err: - mem->is_flushed = 1; + mem->is_flushed = true; return ret; } @@ -1134,53 +1179,64 @@ static unsigned long intel_i965_mask_memory(struct agp_bridge_data *bridge, return addr | bridge->driver->masks[type].mask; } +static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size) +{ + switch (agp_bridge->dev->device) { + case PCI_DEVICE_ID_INTEL_IGD_HB: + case PCI_DEVICE_ID_INTEL_IGD_E_HB: + case PCI_DEVICE_ID_INTEL_Q45_HB: + case PCI_DEVICE_ID_INTEL_G45_HB: + *gtt_offset = *gtt_size = MB(2); + break; + default: + *gtt_offset = *gtt_size = KB(512); + } +} + /* The intel i965 automatically initializes the agp aperture during POST. * Use the memory already set aside for in the GTT. */ static int intel_i965_create_gatt_table(struct agp_bridge_data *bridge) { - int page_order; - struct aper_size_info_fixed *size; - int num_entries; - u32 temp; - int gtt_offset, gtt_size; + int page_order; + struct aper_size_info_fixed *size; + int num_entries; + u32 temp; + int gtt_offset, gtt_size; - size = agp_bridge->current_size; - page_order = size->page_order; - num_entries = size->num_entries; - agp_bridge->gatt_table_real = NULL; + size = agp_bridge->current_size; + page_order = size->page_order; + num_entries = size->num_entries; + agp_bridge->gatt_table_real = NULL; - pci_read_config_dword(intel_private.pcidev, I915_MMADDR, &temp); + pci_read_config_dword(intel_private.pcidev, I915_MMADDR, &temp); - temp &= 0xfff00000; + temp &= 0xfff00000; - if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGD_HB) - gtt_offset = gtt_size = MB(2); - else - gtt_offset = gtt_size = KB(512); + intel_i965_get_gtt_range(>t_offset, >t_size); - intel_private.gtt = ioremap((temp + gtt_offset) , gtt_size); + intel_private.gtt = ioremap((temp + gtt_offset) , gtt_size); - if (!intel_private.gtt) - return -ENOMEM; + if (!intel_private.gtt) + return -ENOMEM; - intel_private.registers = ioremap(temp, 128 * 4096); - if (!intel_private.registers) { + intel_private.registers = ioremap(temp, 128 * 4096); + if (!intel_private.registers) { iounmap(intel_private.gtt); return -ENOMEM; } - temp = readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000; - global_cache_flush(); /* FIXME: ? */ + temp = readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000; + global_cache_flush(); /* FIXME: ? */ - /* we have to call this as early as possible after the MMIO base address is known */ - intel_i830_init_gtt_entries(); + /* we have to call this as early as possible after the MMIO base address is known */ + intel_i830_init_gtt_entries(); - agp_bridge->gatt_table = NULL; + agp_bridge->gatt_table = NULL; - agp_bridge->gatt_bus_addr = temp; + agp_bridge->gatt_bus_addr = temp; - return 0; + return 0; } @@ -1656,7 +1712,7 @@ static const struct agp_bridge_driver intel_810_driver = { .aperture_sizes = intel_i810_sizes, .size_type = FIXED_APER_SIZE, .num_aperture_sizes = 2, - .needs_scratch_page = TRUE, + .needs_scratch_page = true, .configure = intel_i810_configure, .fetch_size = intel_i810_fetch_size, .cleanup = intel_i810_cleanup, @@ -1697,7 +1753,7 @@ static const struct agp_bridge_driver intel_815_driver = { .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, - .agp_type_to_mask_type = agp_generic_type_to_mask_type, + .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; static const struct agp_bridge_driver intel_830_driver = { @@ -1705,7 +1761,7 @@ static const struct agp_bridge_driver intel_830_driver = { .aperture_sizes = intel_i830_sizes, .size_type = FIXED_APER_SIZE, .num_aperture_sizes = 4, - .needs_scratch_page = TRUE, + .needs_scratch_page = true, .configure = intel_i830_configure, .fetch_size = intel_i830_fetch_size, .cleanup = intel_i830_cleanup, @@ -1876,7 +1932,7 @@ static const struct agp_bridge_driver intel_915_driver = { .aperture_sizes = intel_i830_sizes, .size_type = FIXED_APER_SIZE, .num_aperture_sizes = 4, - .needs_scratch_page = TRUE, + .needs_scratch_page = true, .configure = intel_i915_configure, .fetch_size = intel_i9xx_fetch_size, .cleanup = intel_i915_cleanup, @@ -1898,28 +1954,28 @@ static const struct agp_bridge_driver intel_915_driver = { }; static const struct agp_bridge_driver intel_i965_driver = { - .owner = THIS_MODULE, - .aperture_sizes = intel_i830_sizes, - .size_type = FIXED_APER_SIZE, - .num_aperture_sizes = 4, - .needs_scratch_page = TRUE, - .configure = intel_i915_configure, - .fetch_size = intel_i9xx_fetch_size, - .cleanup = intel_i915_cleanup, - .tlb_flush = intel_i810_tlbflush, - .mask_memory = intel_i965_mask_memory, - .masks = intel_i810_masks, - .agp_enable = intel_i810_agp_enable, - .cache_flush = global_cache_flush, - .create_gatt_table = intel_i965_create_gatt_table, - .free_gatt_table = intel_i830_free_gatt_table, - .insert_memory = intel_i915_insert_entries, - .remove_memory = intel_i915_remove_entries, - .alloc_by_type = intel_i830_alloc_by_type, - .free_by_type = intel_i810_free_by_type, - .agp_alloc_page = agp_generic_alloc_page, - .agp_destroy_page = agp_generic_destroy_page, - .agp_type_to_mask_type = intel_i830_type_to_mask_type, + .owner = THIS_MODULE, + .aperture_sizes = intel_i830_sizes, + .size_type = FIXED_APER_SIZE, + .num_aperture_sizes = 4, + .needs_scratch_page = true, + .configure = intel_i915_configure, + .fetch_size = intel_i9xx_fetch_size, + .cleanup = intel_i915_cleanup, + .tlb_flush = intel_i810_tlbflush, + .mask_memory = intel_i965_mask_memory, + .masks = intel_i810_masks, + .agp_enable = intel_i810_agp_enable, + .cache_flush = global_cache_flush, + .create_gatt_table = intel_i965_create_gatt_table, + .free_gatt_table = intel_i830_free_gatt_table, + .insert_memory = intel_i915_insert_entries, + .remove_memory = intel_i915_remove_entries, + .alloc_by_type = intel_i830_alloc_by_type, + .free_by_type = intel_i810_free_by_type, + .agp_alloc_page = agp_generic_alloc_page, + .agp_destroy_page = agp_generic_destroy_page, + .agp_type_to_mask_type = intel_i830_type_to_mask_type, .chipset_flush = intel_i915_chipset_flush, }; @@ -1948,28 +2004,28 @@ static const struct agp_bridge_driver intel_7505_driver = { }; static const struct agp_bridge_driver intel_g33_driver = { - .owner = THIS_MODULE, - .aperture_sizes = intel_i830_sizes, - .size_type = FIXED_APER_SIZE, - .num_aperture_sizes = 4, - .needs_scratch_page = TRUE, - .configure = intel_i915_configure, - .fetch_size = intel_i9xx_fetch_size, - .cleanup = intel_i915_cleanup, - .tlb_flush = intel_i810_tlbflush, - .mask_memory = intel_i965_mask_memory, - .masks = intel_i810_masks, - .agp_enable = intel_i810_agp_enable, - .cache_flush = global_cache_flush, - .create_gatt_table = intel_i915_create_gatt_table, - .free_gatt_table = intel_i830_free_gatt_table, - .insert_memory = intel_i915_insert_entries, - .remove_memory = intel_i915_remove_entries, - .alloc_by_type = intel_i830_alloc_by_type, - .free_by_type = intel_i810_free_by_type, - .agp_alloc_page = agp_generic_alloc_page, - .agp_destroy_page = agp_generic_destroy_page, - .agp_type_to_mask_type = intel_i830_type_to_mask_type, + .owner = THIS_MODULE, + .aperture_sizes = intel_i830_sizes, + .size_type = FIXED_APER_SIZE, + .num_aperture_sizes = 4, + .needs_scratch_page = true, + .configure = intel_i915_configure, + .fetch_size = intel_i9xx_fetch_size, + .cleanup = intel_i915_cleanup, + .tlb_flush = intel_i810_tlbflush, + .mask_memory = intel_i965_mask_memory, + .masks = intel_i810_masks, + .agp_enable = intel_i810_agp_enable, + .cache_flush = global_cache_flush, + .create_gatt_table = intel_i915_create_gatt_table, + .free_gatt_table = intel_i830_free_gatt_table, + .insert_memory = intel_i915_insert_entries, + .remove_memory = intel_i915_remove_entries, + .alloc_by_type = intel_i830_alloc_by_type, + .free_by_type = intel_i810_free_by_type, + .agp_alloc_page = agp_generic_alloc_page, + .agp_destroy_page = agp_generic_destroy_page, + .agp_type_to_mask_type = intel_i830_type_to_mask_type, .chipset_flush = intel_i915_chipset_flush, }; @@ -2063,6 +2119,12 @@ static const struct intel_driver_description { NULL, &intel_g33_driver }, { PCI_DEVICE_ID_INTEL_IGD_HB, PCI_DEVICE_ID_INTEL_IGD_IG, 0, "Intel Integrated Graphics Device", NULL, &intel_i965_driver }, + { PCI_DEVICE_ID_INTEL_IGD_E_HB, PCI_DEVICE_ID_INTEL_IGD_E_IG, 0, + "Intel Integrated Graphics Device", NULL, &intel_i965_driver }, + { PCI_DEVICE_ID_INTEL_Q45_HB, PCI_DEVICE_ID_INTEL_Q45_IG, 0, + "Q45/Q43", NULL, &intel_i965_driver }, + { PCI_DEVICE_ID_INTEL_G45_HB, PCI_DEVICE_ID_INTEL_G45_IG, 0, + "G45/G43", NULL, &intel_i965_driver }, { 0, 0, 0, NULL, NULL, NULL } }; @@ -2254,6 +2316,9 @@ static struct pci_device_id agp_intel_pci_table[] = { ID(PCI_DEVICE_ID_INTEL_Q35_HB), ID(PCI_DEVICE_ID_INTEL_Q33_HB), ID(PCI_DEVICE_ID_INTEL_IGD_HB), + ID(PCI_DEVICE_ID_INTEL_IGD_E_HB), + ID(PCI_DEVICE_ID_INTEL_Q45_HB), + ID(PCI_DEVICE_ID_INTEL_G45_HB), { } }; diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c index 225ed2a53d4..eaceb61ba2d 100644 --- a/drivers/char/agp/nvidia-agp.c +++ b/drivers/char/agp/nvidia-agp.c @@ -214,9 +214,9 @@ static int nvidia_insert_memory(struct agp_memory *mem, off_t pg_start, int type return -EBUSY; } - if (mem->is_flushed == FALSE) { + if (!mem->is_flushed) { global_cache_flush(); - mem->is_flushed = TRUE; + mem->is_flushed = true; } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c index 2939e3570f9..8c42dcc5958 100644 --- a/drivers/char/agp/parisc-agp.c +++ b/drivers/char/agp/parisc-agp.c @@ -141,9 +141,9 @@ parisc_agp_insert_memory(struct agp_memory *mem, off_t pg_start, int type) j++; } - if (mem->is_flushed == FALSE) { + if (!mem->is_flushed) { global_cache_flush(); - mem->is_flushed = TRUE; + mem->is_flushed = true; } for (i = 0, j = io_pg_start; i < mem->page_count; i++) { @@ -226,7 +226,7 @@ static const struct agp_bridge_driver parisc_agp_driver = { .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, .agp_type_to_mask_type = agp_generic_type_to_mask_type, - .cant_use_aperture = 1, + .cant_use_aperture = true, }; static int __init diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c index 98cf8abb3e5..b972d83bb1b 100644 --- a/drivers/char/agp/sgi-agp.c +++ b/drivers/char/agp/sgi-agp.c @@ -182,9 +182,9 @@ static int sgi_tioca_insert_memory(struct agp_memory *mem, off_t pg_start, j++; } - if (mem->is_flushed == FALSE) { + if (!mem->is_flushed) { bridge->driver->cache_flush(); - mem->is_flushed = TRUE; + mem->is_flushed = true; } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { @@ -264,8 +264,8 @@ const struct agp_bridge_driver sgi_tioca_driver = { .agp_alloc_page = sgi_tioca_alloc_page, .agp_destroy_page = agp_generic_destroy_page, .agp_type_to_mask_type = agp_generic_type_to_mask_type, - .cant_use_aperture = 1, - .needs_scratch_page = 0, + .cant_use_aperture = true, + .needs_scratch_page = false, .num_aperture_sizes = 1, }; diff --git a/drivers/char/agp/sworks-agp.c b/drivers/char/agp/sworks-agp.c index e08934e58f3..0e054c13449 100644 --- a/drivers/char/agp/sworks-agp.c +++ b/drivers/char/agp/sworks-agp.c @@ -339,9 +339,9 @@ static int serverworks_insert_memory(struct agp_memory *mem, j++; } - if (mem->is_flushed == FALSE) { + if (!mem->is_flushed) { global_cache_flush(); - mem->is_flushed = TRUE; + mem->is_flushed = true; } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { @@ -412,7 +412,7 @@ static void serverworks_agp_enable(struct agp_bridge_data *bridge, u32 mode) bridge->capndx + PCI_AGP_COMMAND, command); - agp_device_command(command, 0); + agp_device_command(command, false); } static const struct agp_bridge_driver sworks_driver = { diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c index 42c0a600b1a..d2fa3cfca02 100644 --- a/drivers/char/agp/uninorth-agp.c +++ b/drivers/char/agp/uninorth-agp.c @@ -281,10 +281,10 @@ static void uninorth_agp_enable(struct agp_bridge_data *bridge, u32 mode) if (uninorth_rev >= 0x30) { /* This is an AGP V3 */ - agp_device_command(command, (status & AGPSTAT_MODE_3_0)); + agp_device_command(command, (status & AGPSTAT_MODE_3_0) != 0); } else { /* AGP V2 */ - agp_device_command(command, 0); + agp_device_command(command, false); } uninorth_tlbflush(NULL); @@ -511,7 +511,7 @@ const struct agp_bridge_driver uninorth_agp_driver = { .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, .agp_type_to_mask_type = agp_generic_type_to_mask_type, - .cant_use_aperture = 1, + .cant_use_aperture = true, }; const struct agp_bridge_driver u3_agp_driver = { @@ -536,8 +536,8 @@ const struct agp_bridge_driver u3_agp_driver = { .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, .agp_type_to_mask_type = agp_generic_type_to_mask_type, - .cant_use_aperture = 1, - .needs_scratch_page = 1, + .cant_use_aperture = true, + .needs_scratch_page = true, }; static struct agp_device_ids uninorth_agp_device_ids[] __devinitdata = { diff --git a/drivers/char/agp/via-agp.c b/drivers/char/agp/via-agp.c index 0ecc54d327b..7b36476dff4 100644 --- a/drivers/char/agp/via-agp.c +++ b/drivers/char/agp/via-agp.c @@ -389,11 +389,20 @@ static struct agp_device_ids via_agp_device_ids[] __devinitdata = .device_id = PCI_DEVICE_ID_VIA_VT3324, .chipset_name = "CX700", }, - /* VT3336 */ + /* VT3336 - this is a chipset for AMD Athlon/K8 CPU. Due to K8's unique + * architecture, the AGP resource and behavior are different from + * the traditional AGP which resides only in chipset. AGP is used + * by 3D driver which wasn't available for the VT3336 and VT3364 + * generation until now. Unfortunately, by testing, VT3364 works + * but VT3336 doesn't. - explaination from via, just leave this as + * as a placeholder to avoid future patches adding it back in. + */ +#if 0 { .device_id = PCI_DEVICE_ID_VIA_VT3336, .chipset_name = "VT3336", }, +#endif /* P4M890 */ { .device_id = PCI_DEVICE_ID_VIA_P4M890, @@ -546,8 +555,8 @@ static const struct pci_device_id agp_via_pci_table[] = { ID(PCI_DEVICE_ID_VIA_3296_0), ID(PCI_DEVICE_ID_VIA_P4M800CE), ID(PCI_DEVICE_ID_VIA_VT3324), - ID(PCI_DEVICE_ID_VIA_VT3336), ID(PCI_DEVICE_ID_VIA_P4M890), + ID(PCI_DEVICE_ID_VIA_VT3364), { } }; diff --git a/drivers/char/drm/ati_pcigart.c b/drivers/char/drm/ati_pcigart.c index b710426bab3..c533d0c9ec6 100644 --- a/drivers/char/drm/ati_pcigart.c +++ b/drivers/char/drm/ati_pcigart.c @@ -76,7 +76,7 @@ int drm_ati_pcigart_cleanup(struct drm_device *dev, struct drm_ati_pcigart_info for (i = 0; i < pages; i++) { if (!entry->busaddr[i]) break; - pci_unmap_single(dev->pdev, entry->busaddr[i], + pci_unmap_page(dev->pdev, entry->busaddr[i], PAGE_SIZE, PCI_DMA_TODEVICE); } @@ -137,10 +137,8 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga for (i = 0; i < pages; i++) { /* we need to support large memory configurations */ - entry->busaddr[i] = pci_map_single(dev->pdev, - page_address(entry-> - pagelist[i]), - PAGE_SIZE, PCI_DMA_TODEVICE); + entry->busaddr[i] = pci_map_page(dev->pdev, entry->pagelist[i], + 0, PAGE_SIZE, PCI_DMA_TODEVICE); if (entry->busaddr[i] == 0) { DRM_ERROR("unable to map PCIGART pages!\n"); drm_ati_pcigart_cleanup(dev, gart_info); diff --git a/drivers/char/drm/drm.h b/drivers/char/drm/drm.h index 3a05c6d5ebe..38d3c6b8276 100644 --- a/drivers/char/drm/drm.h +++ b/drivers/char/drm/drm.h @@ -628,7 +628,7 @@ struct drm_set_version { #define DRM_IOCTL_AGP_BIND DRM_IOW( 0x36, struct drm_agp_binding) #define DRM_IOCTL_AGP_UNBIND DRM_IOW( 0x37, struct drm_agp_binding) -#define DRM_IOCTL_SG_ALLOC DRM_IOW( 0x38, struct drm_scatter_gather) +#define DRM_IOCTL_SG_ALLOC DRM_IOWR(0x38, struct drm_scatter_gather) #define DRM_IOCTL_SG_FREE DRM_IOW( 0x39, struct drm_scatter_gather) #define DRM_IOCTL_WAIT_VBLANK DRM_IOWR(0x3a, union drm_wait_vblank) diff --git a/drivers/char/drm/drm_drv.c b/drivers/char/drm/drm_drv.c index fc54140551a..564138714bb 100644 --- a/drivers/char/drm/drm_drv.c +++ b/drivers/char/drm/drm_drv.c @@ -470,17 +470,18 @@ int drm_ioctl(struct inode *inode, struct file *filp, if ((nr >= DRM_COMMAND_BASE) && (nr < DRM_COMMAND_END) && (nr < DRM_COMMAND_BASE + dev->driver->num_ioctls)) ioctl = &dev->driver->ioctls[nr - DRM_COMMAND_BASE]; - else if ((nr >= DRM_COMMAND_END) || (nr < DRM_COMMAND_BASE)) + else if ((nr >= DRM_COMMAND_END) || (nr < DRM_COMMAND_BASE)) { ioctl = &drm_ioctls[nr]; - else + cmd = ioctl->cmd; + } else goto err_i1; + /* Do not trust userspace, use our own definition */ func = ioctl->func; /* is there a local override? */ if ((nr == DRM_IOCTL_NR(DRM_IOCTL_DMA)) && dev->driver->dma_ioctl) func = dev->driver->dma_ioctl; - if (!func) { DRM_DEBUG("no function\n"); retcode = -EINVAL; diff --git a/drivers/char/drm/drm_pciids.h b/drivers/char/drm/drm_pciids.h index a6a499f97e2..135bd19499f 100644 --- a/drivers/char/drm/drm_pciids.h +++ b/drivers/char/drm/drm_pciids.h @@ -103,20 +103,18 @@ {0x1002, 0x5653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x5834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP}, \ {0x1002, 0x5835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \ - {0x1002, 0x5954, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \ - {0x1002, 0x5955, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \ - {0x1002, 0x5974, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \ - {0x1002, 0x5975, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \ + {0x1002, 0x5954, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \ + {0x1002, 0x5955, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \ + {0x1002, 0x5974, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \ + {0x1002, 0x5975, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \ {0x1002, 0x5960, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5961, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5962, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5964, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5965, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \ - {0x1002, 0x5a41, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \ - {0x1002, 0x5a42, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \ - {0x1002, 0x5a61, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \ - {0x1002, 0x5a62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \ + {0x1002, 0x5a61, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \ + {0x1002, 0x5a62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \ {0x1002, 0x5b60, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \ {0x1002, 0x5b62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \ {0x1002, 0x5b63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \ @@ -411,4 +409,7 @@ {0x8086, 0x2a02, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0x8086, 0x2a12, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0x8086, 0x2a42, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x2e02, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x2e12, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x2e22, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0, 0, 0} diff --git a/drivers/char/drm/i915_drv.c b/drivers/char/drm/i915_drv.c index e8f3d682e3b..93aed1c38bd 100644 --- a/drivers/char/drm/i915_drv.c +++ b/drivers/char/drm/i915_drv.c @@ -389,6 +389,7 @@ static int i915_resume(struct drm_device *dev) pci_restore_state(dev->pdev); if (pci_enable_device(dev->pdev)) return -1; + pci_set_master(dev->pdev); pci_write_config_byte(dev->pdev, LBB, dev_priv->saveLBB); diff --git a/drivers/char/drm/i915_drv.h b/drivers/char/drm/i915_drv.h index 1b20f7c0639..d7326d92a23 100644 --- a/drivers/char/drm/i915_drv.h +++ b/drivers/char/drm/i915_drv.h @@ -1112,12 +1112,19 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); (dev)->pci_device == 0x29A2 || \ (dev)->pci_device == 0x2A02 || \ (dev)->pci_device == 0x2A12 || \ - (dev)->pci_device == 0x2A42) + (dev)->pci_device == 0x2A42 || \ + (dev)->pci_device == 0x2E02 || \ + (dev)->pci_device == 0x2E12 || \ + (dev)->pci_device == 0x2E22) #define IS_I965GM(dev) ((dev)->pci_device == 0x2A02) #define IS_IGD_GM(dev) ((dev)->pci_device == 0x2A42) +#define IS_G4X(dev) ((dev)->pci_device == 0x2E02 || \ + (dev)->pci_device == 0x2E12 || \ + (dev)->pci_device == 0x2E22) + #define IS_G33(dev) ((dev)->pci_device == 0x29C2 || \ (dev)->pci_device == 0x29B2 || \ (dev)->pci_device == 0x29D2) @@ -1128,7 +1135,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); #define IS_MOBILE(dev) (IS_I830(dev) || IS_I85X(dev) || IS_I915GM(dev) || \ IS_I945GM(dev) || IS_I965GM(dev) || IS_IGD_GM(dev)) -#define I915_NEED_GFX_HWS(dev) (IS_G33(dev) || IS_IGD_GM(dev)) +#define I915_NEED_GFX_HWS(dev) (IS_G33(dev) || IS_IGD_GM(dev) || IS_G4X(dev)) #define PRIMARY_RINGBUFFER_SIZE (128*1024) diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c index f7f16e7a8bf..df036118b8b 100644 --- a/drivers/char/drm/i915_irq.c +++ b/drivers/char/drm/i915_irq.c @@ -62,11 +62,11 @@ static void i915_vblank_tasklet(struct drm_device *dev) u32 ropcpp = (0xcc << 16) | ((cpp - 1) << 24); RING_LOCALS; - if (sarea_priv->front_tiled) { + if (IS_I965G(dev) && sarea_priv->front_tiled) { cmd |= XY_SRC_COPY_BLT_DST_TILED; dst_pitch >>= 2; } - if (sarea_priv->back_tiled) { + if (IS_I965G(dev) && sarea_priv->back_tiled) { cmd |= XY_SRC_COPY_BLT_SRC_TILED; src_pitch >>= 2; } diff --git a/drivers/char/drm/r300_cmdbuf.c b/drivers/char/drm/r300_cmdbuf.c index f535812e405..702df45320f 100644 --- a/drivers/char/drm/r300_cmdbuf.c +++ b/drivers/char/drm/r300_cmdbuf.c @@ -189,18 +189,12 @@ void r300_init_reg_flags(struct drm_device *dev) ADD_RANGE(R300_RE_CULL_CNTL, 1); ADD_RANGE(0x42C0, 2); ADD_RANGE(R300_RS_CNTL_0, 2); - ADD_RANGE(R300_RS_INTERP_0, 8); - ADD_RANGE(R300_RS_ROUTE_0, 8); - ADD_RANGE(0x43A4, 2); + + ADD_RANGE(R300_SC_HYPERZ, 2); ADD_RANGE(0x43E8, 1); - ADD_RANGE(R300_PFS_CNTL_0, 3); - ADD_RANGE(R300_PFS_NODE_0, 4); - ADD_RANGE(R300_PFS_TEXI_0, 64); + ADD_RANGE(0x46A4, 5); - ADD_RANGE(R300_PFS_INSTR0_0, 64); - ADD_RANGE(R300_PFS_INSTR1_0, 64); - ADD_RANGE(R300_PFS_INSTR2_0, 64); - ADD_RANGE(R300_PFS_INSTR3_0, 64); + ADD_RANGE(R300_RE_FOG_STATE, 1); ADD_RANGE(R300_FOG_COLOR_R, 3); ADD_RANGE(R300_PP_ALPHA_TEST, 2); @@ -215,14 +209,12 @@ void r300_init_reg_flags(struct drm_device *dev) ADD_RANGE(0x4E50, 9); ADD_RANGE(0x4E88, 1); ADD_RANGE(0x4EA0, 2); - ADD_RANGE(R300_RB3D_ZSTENCIL_CNTL_0, 3); - ADD_RANGE(R300_RB3D_ZSTENCIL_FORMAT, 4); - ADD_RANGE_MARK(R300_RB3D_DEPTHOFFSET, 1, MARK_CHECK_OFFSET); /* check offset */ - ADD_RANGE(R300_RB3D_DEPTHPITCH, 1); - ADD_RANGE(0x4F28, 1); - ADD_RANGE(0x4F30, 2); - ADD_RANGE(0x4F44, 1); - ADD_RANGE(0x4F54, 1); + ADD_RANGE(R300_ZB_CNTL, 3); + ADD_RANGE(R300_ZB_FORMAT, 4); + ADD_RANGE_MARK(R300_ZB_DEPTHOFFSET, 1, MARK_CHECK_OFFSET); /* check offset */ + ADD_RANGE(R300_ZB_DEPTHPITCH, 1); + ADD_RANGE(R300_ZB_DEPTHCLEARVALUE, 1); + ADD_RANGE(R300_ZB_ZMASK_OFFSET, 13); ADD_RANGE(R300_TX_FILTER_0, 16); ADD_RANGE(R300_TX_FILTER1_0, 16); @@ -235,13 +227,32 @@ void r300_init_reg_flags(struct drm_device *dev) ADD_RANGE(R300_TX_BORDER_COLOR_0, 16); /* Sporadic registers used as primitives are emitted */ - ADD_RANGE(R300_RB3D_ZCACHE_CTLSTAT, 1); + ADD_RANGE(R300_ZB_ZCACHE_CTLSTAT, 1); ADD_RANGE(R300_RB3D_DSTCACHE_CTLSTAT, 1); ADD_RANGE(R300_VAP_INPUT_ROUTE_0_0, 8); ADD_RANGE(R300_VAP_INPUT_ROUTE_1_0, 8); if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV515) { - ADD_RANGE(0x4074, 16); + ADD_RANGE(R500_VAP_INDEX_OFFSET, 1); + ADD_RANGE(R500_US_CONFIG, 2); + ADD_RANGE(R500_US_CODE_ADDR, 3); + ADD_RANGE(R500_US_FC_CTRL, 1); + ADD_RANGE(R500_RS_IP_0, 16); + ADD_RANGE(R500_RS_INST_0, 16); + ADD_RANGE(R500_RB3D_COLOR_CLEAR_VALUE_AR, 2); + ADD_RANGE(R500_RB3D_CONSTANT_COLOR_AR, 2); + ADD_RANGE(R500_ZB_FIFO_SIZE, 2); + } else { + ADD_RANGE(R300_PFS_CNTL_0, 3); + ADD_RANGE(R300_PFS_NODE_0, 4); + ADD_RANGE(R300_PFS_TEXI_0, 64); + ADD_RANGE(R300_PFS_INSTR0_0, 64); + ADD_RANGE(R300_PFS_INSTR1_0, 64); + ADD_RANGE(R300_PFS_INSTR2_0, 64); + ADD_RANGE(R300_PFS_INSTR3_0, 64); + ADD_RANGE(R300_RS_INTERP_0, 8); + ADD_RANGE(R300_RS_ROUTE_0, 8); + } } @@ -707,8 +718,9 @@ static __inline__ void r300_pacify(drm_radeon_private_t *dev_priv) BEGIN_RING(6); OUT_RING(CP_PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); OUT_RING(R300_RB3D_DSTCACHE_UNKNOWN_0A); - OUT_RING(CP_PACKET0(R300_RB3D_ZCACHE_CTLSTAT, 0)); - OUT_RING(R300_RB3D_ZCACHE_UNKNOWN_03); + OUT_RING(CP_PACKET0(R300_ZB_ZCACHE_CTLSTAT, 0)); + OUT_RING(R300_ZB_ZCACHE_CTLSTAT_ZC_FLUSH_FLUSH_AND_FREE| + R300_ZB_ZCACHE_CTLSTAT_ZC_FREE_FREE); OUT_RING(CP_PACKET3(RADEON_CP_NOP, 0)); OUT_RING(0x0); ADVANCE_RING(); @@ -829,6 +841,54 @@ static int r300_scratch(drm_radeon_private_t *dev_priv, } /** + * Uploads user-supplied vertex program instructions or parameters onto + * the graphics card. + * Called by r300_do_cp_cmdbuf. + */ +static inline int r300_emit_r500fp(drm_radeon_private_t *dev_priv, + drm_radeon_kcmd_buffer_t *cmdbuf, + drm_r300_cmd_header_t header) +{ + int sz; + int addr; + int type; + int clamp; + int stride; + RING_LOCALS; + + sz = header.r500fp.count; + /* address is 9 bits 0 - 8, bit 1 of flags is part of address */ + addr = ((header.r500fp.adrhi_flags & 1) << 8) | header.r500fp.adrlo; + + type = !!(header.r500fp.adrhi_flags & R500FP_CONSTANT_TYPE); + clamp = !!(header.r500fp.adrhi_flags & R500FP_CONSTANT_CLAMP); + + addr |= (type << 16); + addr |= (clamp << 17); + + stride = type ? 4 : 6; + + DRM_DEBUG("r500fp %d %d type: %d\n", sz, addr, type); + if (!sz) + return 0; + if (sz * stride * 4 > cmdbuf->bufsz) + return -EINVAL; + + BEGIN_RING(3 + sz * stride); + OUT_RING_REG(R500_GA_US_VECTOR_INDEX, addr); + OUT_RING(CP_PACKET0_TABLE(R500_GA_US_VECTOR_DATA, sz * stride - 1)); + OUT_RING_TABLE((int *)cmdbuf->buf, sz * stride); + + ADVANCE_RING(); + + cmdbuf->buf += sz * stride * 4; + cmdbuf->bufsz -= sz * stride * 4; + + return 0; +} + + +/** * Parses and validates a user-supplied command buffer and emits appropriate * commands on the DMA ring buffer. * Called by the ioctl handler function radeon_cp_cmdbuf. @@ -963,6 +1023,19 @@ int r300_do_cp_cmdbuf(struct drm_device *dev, } break; + case R300_CMD_R500FP: + if ((dev_priv->flags & RADEON_FAMILY_MASK) < CHIP_RV515) { + DRM_ERROR("Calling r500 command on r300 card\n"); + ret = -EINVAL; + goto cleanup; + } + DRM_DEBUG("R300_CMD_R500FP\n"); + ret = r300_emit_r500fp(dev_priv, cmdbuf, header); + if (ret) { + DRM_ERROR("r300_emit_r500fp failed\n"); + goto cleanup; + } + break; default: DRM_ERROR("bad cmd_type %i at %p\n", header.header.cmd_type, diff --git a/drivers/char/drm/r300_reg.h b/drivers/char/drm/r300_reg.h index 8f664af9c4a..a6802f26afc 100644 --- a/drivers/char/drm/r300_reg.h +++ b/drivers/char/drm/r300_reg.h @@ -702,6 +702,27 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. # define R300_RS_ROUTE_1_UNKNOWN11 (1 << 11) /* END: Rasterization / Interpolators - many guesses */ +/* Hierarchical Z Enable */ +#define R300_SC_HYPERZ 0x43a4 +# define R300_SC_HYPERZ_DISABLE (0 << 0) +# define R300_SC_HYPERZ_ENABLE (1 << 0) +# define R300_SC_HYPERZ_MIN (0 << 1) +# define R300_SC_HYPERZ_MAX (1 << 1) +# define R300_SC_HYPERZ_ADJ_256 (0 << 2) +# define R300_SC_HYPERZ_ADJ_128 (1 << 2) +# define R300_SC_HYPERZ_ADJ_64 (2 << 2) +# define R300_SC_HYPERZ_ADJ_32 (3 << 2) +# define R300_SC_HYPERZ_ADJ_16 (4 << 2) +# define R300_SC_HYPERZ_ADJ_8 (5 << 2) +# define R300_SC_HYPERZ_ADJ_4 (6 << 2) +# define R300_SC_HYPERZ_ADJ_2 (7 << 2) +# define R300_SC_HYPERZ_HZ_Z0MIN_NO (0 << 5) +# define R300_SC_HYPERZ_HZ_Z0MIN (1 << 5) +# define R300_SC_HYPERZ_HZ_Z0MAX_NO (0 << 6) +# define R300_SC_HYPERZ_HZ_Z0MAX (1 << 6) + +#define R300_SC_EDGERULE 0x43a8 + /* BEGIN: Scissors and cliprects */ /* There are four clipping rectangles. Their corner coordinates are inclusive. @@ -1346,7 +1367,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. /* Guess by Vladimir. * Set to 0A before 3D operations, set to 02 afterwards. */ -#define R300_RB3D_DSTCACHE_CTLSTAT 0x4E4C +/*#define R300_RB3D_DSTCACHE_CTLSTAT 0x4E4C*/ # define R300_RB3D_DSTCACHE_UNKNOWN_02 0x00000002 # define R300_RB3D_DSTCACHE_UNKNOWN_0A 0x0000000A @@ -1355,19 +1376,14 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. * for this. * Bit (1<<8) is the "test" bit. so plain write is 6 - vd */ -#define R300_RB3D_ZSTENCIL_CNTL_0 0x4F00 -# define R300_RB3D_Z_DISABLED_1 0x00000010 -# define R300_RB3D_Z_DISABLED_2 0x00000014 -# define R300_RB3D_Z_TEST 0x00000012 -# define R300_RB3D_Z_TEST_AND_WRITE 0x00000016 -# define R300_RB3D_Z_WRITE_ONLY 0x00000006 - -# define R300_RB3D_Z_TEST 0x00000012 -# define R300_RB3D_Z_TEST_AND_WRITE 0x00000016 -# define R300_RB3D_Z_WRITE_ONLY 0x00000006 -# define R300_RB3D_STENCIL_ENABLE 0x00000001 - -#define R300_RB3D_ZSTENCIL_CNTL_1 0x4F04 +#define R300_ZB_CNTL 0x4F00 +# define R300_STENCIL_ENABLE (1 << 0) +# define R300_Z_ENABLE (1 << 1) +# define R300_Z_WRITE_ENABLE (1 << 2) +# define R300_Z_SIGNED_COMPARE (1 << 3) +# define R300_STENCIL_FRONT_BACK (1 << 4) + +#define R300_ZB_ZSTENCILCNTL 0x4f04 /* functions */ # define R300_ZS_NEVER 0 # define R300_ZS_LESS 1 @@ -1387,52 +1403,166 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. # define R300_ZS_INVERT 5 # define R300_ZS_INCR_WRAP 6 # define R300_ZS_DECR_WRAP 7 +# define R300_Z_FUNC_SHIFT 0 /* front and back refer to operations done for front and back faces, i.e. separate stencil function support */ -# define R300_RB3D_ZS1_DEPTH_FUNC_SHIFT 0 -# define R300_RB3D_ZS1_FRONT_FUNC_SHIFT 3 -# define R300_RB3D_ZS1_FRONT_FAIL_OP_SHIFT 6 -# define R300_RB3D_ZS1_FRONT_ZPASS_OP_SHIFT 9 -# define R300_RB3D_ZS1_FRONT_ZFAIL_OP_SHIFT 12 -# define R300_RB3D_ZS1_BACK_FUNC_SHIFT 15 -# define R300_RB3D_ZS1_BACK_FAIL_OP_SHIFT 18 -# define R300_RB3D_ZS1_BACK_ZPASS_OP_SHIFT 21 -# define R300_RB3D_ZS1_BACK_ZFAIL_OP_SHIFT 24 - -#define R300_RB3D_ZSTENCIL_CNTL_2 0x4F08 -# define R300_RB3D_ZS2_STENCIL_REF_SHIFT 0 -# define R300_RB3D_ZS2_STENCIL_MASK 0xFF -# define R300_RB3D_ZS2_STENCIL_MASK_SHIFT 8 -# define R300_RB3D_ZS2_STENCIL_WRITE_MASK_SHIFT 16 +# define R300_S_FRONT_FUNC_SHIFT 3 +# define R300_S_FRONT_SFAIL_OP_SHIFT 6 +# define R300_S_FRONT_ZPASS_OP_SHIFT 9 +# define R300_S_FRONT_ZFAIL_OP_SHIFT 12 +# define R300_S_BACK_FUNC_SHIFT 15 +# define R300_S_BACK_SFAIL_OP_SHIFT 18 +# define R300_S_BACK_ZPASS_OP_SHIFT 21 +# define R300_S_BACK_ZFAIL_OP_SHIFT 24 + +#define R300_ZB_STENCILREFMASK 0x4f08 +# define R300_STENCILREF_SHIFT 0 +# define R300_STENCILREF_MASK 0x000000ff +# define R300_STENCILMASK_SHIFT 8 +# define R300_STENCILMASK_MASK 0x0000ff00 +# define R300_STENCILWRITEMASK_SHIFT 16 +# define R300_STENCILWRITEMASK_MASK 0x00ff0000 /* gap */ -#define R300_RB3D_ZSTENCIL_FORMAT 0x4F10 -# define R300_DEPTH_FORMAT_16BIT_INT_Z (0 << 0) -# define R300_DEPTH_FORMAT_24BIT_INT_Z (2 << 0) - /* 16 bit format or some aditional bit ? */ -# define R300_DEPTH_FORMAT_UNK32 (32 << 0) +#define R300_ZB_FORMAT 0x4f10 +# define R300_DEPTHFORMAT_16BIT_INT_Z (0 << 0) +# define R300_DEPTHFORMAT_16BIT_13E3 (1 << 0) +# define R300_DEPTHFORMAT_24BIT_INT_Z_8BIT_STENCIL (2 << 0) +/* reserved up to (15 << 0) */ +# define R300_INVERT_13E3_LEADING_ONES (0 << 4) +# define R300_INVERT_13E3_LEADING_ZEROS (1 << 4) -#define R300_RB3D_EARLY_Z 0x4F14 -# define R300_EARLY_Z_DISABLE (0 << 0) -# define R300_EARLY_Z_ENABLE (1 << 0) +#define R300_ZB_ZTOP 0x4F14 +# define R300_ZTOP_DISABLE (0 << 0) +# define R300_ZTOP_ENABLE (1 << 0) /* gap */ -#define R300_RB3D_ZCACHE_CTLSTAT 0x4F18 /* GUESS */ -# define R300_RB3D_ZCACHE_UNKNOWN_01 0x1 -# define R300_RB3D_ZCACHE_UNKNOWN_03 0x3 +#define R300_ZB_ZCACHE_CTLSTAT 0x4f18 +# define R300_ZB_ZCACHE_CTLSTAT_ZC_FLUSH_NO_EFFECT (0 << 0) +# define R300_ZB_ZCACHE_CTLSTAT_ZC_FLUSH_FLUSH_AND_FREE (1 << 0) +# define R300_ZB_ZCACHE_CTLSTAT_ZC_FREE_NO_EFFECT (0 << 1) +# define R300_ZB_ZCACHE_CTLSTAT_ZC_FREE_FREE (1 << 1) +# define R300_ZB_ZCACHE_CTLSTAT_ZC_BUSY_IDLE (0 << 31) +# define R300_ZB_ZCACHE_CTLSTAT_ZC_BUSY_BUSY (1 << 31) + +#define R300_ZB_BW_CNTL 0x4f1c +# define R300_HIZ_DISABLE (0 << 0) +# define R300_HIZ_ENABLE (1 << 0) +# define R300_HIZ_MIN (0 << 1) +# define R300_HIZ_MAX (1 << 1) +# define R300_FAST_FILL_DISABLE (0 << 2) +# define R300_FAST_FILL_ENABLE (1 << 2) +# define R300_RD_COMP_DISABLE (0 << 3) +# define R300_RD_COMP_ENABLE (1 << 3) +# define R300_WR_COMP_DISABLE (0 << 4) +# define R300_WR_COMP_ENABLE (1 << 4) +# define R300_ZB_CB_CLEAR_RMW (0 << 5) +# define R300_ZB_CB_CLEAR_CACHE_LINEAR (1 << 5) +# define R300_FORCE_COMPRESSED_STENCIL_VALUE_DISABLE (0 << 6) +# define R300_FORCE_COMPRESSED_STENCIL_VALUE_ENABLE (1 << 6) + +# define R500_ZEQUAL_OPTIMIZE_ENABLE (0 << 7) +# define R500_ZEQUAL_OPTIMIZE_DISABLE (1 << 7) +# define R500_SEQUAL_OPTIMIZE_ENABLE (0 << 8) +# define R500_SEQUAL_OPTIMIZE_DISABLE (1 << 8) + +# define R500_BMASK_ENABLE (0 << 10) +# define R500_BMASK_DISABLE (1 << 10) +# define R500_HIZ_EQUAL_REJECT_DISABLE (0 << 11) +# define R500_HIZ_EQUAL_REJECT_ENABLE (1 << 11) +# define R500_HIZ_FP_EXP_BITS_DISABLE (0 << 12) +# define R500_HIZ_FP_EXP_BITS_1 (1 << 12) +# define R500_HIZ_FP_EXP_BITS_2 (2 << 12) +# define R500_HIZ_FP_EXP_BITS_3 (3 << 12) +# define R500_HIZ_FP_EXP_BITS_4 (4 << 12) +# define R500_HIZ_FP_EXP_BITS_5 (5 << 12) +# define R500_HIZ_FP_INVERT_LEADING_ONES (0 << 15) +# define R500_HIZ_FP_INVERT_LEADING_ZEROS (1 << 15) +# define R500_TILE_OVERWRITE_RECOMPRESSION_ENABLE (0 << 16) +# define R500_TILE_OVERWRITE_RECOMPRESSION_DISABLE (1 << 16) +# define R500_CONTIGUOUS_6XAA_SAMPLES_ENABLE (0 << 17) +# define R500_CONTIGUOUS_6XAA_SAMPLES_DISABLE (1 << 17) +# define R500_PEQ_PACKING_DISABLE (0 << 18) +# define R500_PEQ_PACKING_ENABLE (1 << 18) +# define R500_COVERED_PTR_MASKING_DISABLE (0 << 18) +# define R500_COVERED_PTR_MASKING_ENABLE (1 << 18) + /* gap */ -#define R300_RB3D_DEPTHOFFSET 0x4F20 -#define R300_RB3D_DEPTHPITCH 0x4F24 -# define R300_DEPTHPITCH_MASK 0x00001FF8 /* GUESS */ -# define R300_DEPTH_TILE_ENABLE (1 << 16) /* GUESS */ -# define R300_DEPTH_MICROTILE_ENABLE (1 << 17) /* GUESS */ -# define R300_DEPTH_ENDIAN_NO_SWAP (0 << 18) /* GUESS */ -# define R300_DEPTH_ENDIAN_WORD_SWAP (1 << 18) /* GUESS */ -# define R300_DEPTH_ENDIAN_DWORD_SWAP (2 << 18) /* GUESS */ +/* Z Buffer Address Offset. + * Bits 31 to 5 are used for aligned Z buffer address offset for macro tiles. + */ +#define R300_ZB_DEPTHOFFSET 0x4f20 + +/* Z Buffer Pitch and Endian Control */ +#define R300_ZB_DEPTHPITCH 0x4f24 +# define R300_DEPTHPITCH_MASK 0x00003FFC +# define R300_DEPTHMACROTILE_DISABLE (0 << 16) +# define R300_DEPTHMACROTILE_ENABLE (1 << 16) +# define R300_DEPTHMICROTILE_LINEAR (0 << 17) +# define R300_DEPTHMICROTILE_TILED (1 << 17) +# define R300_DEPTHMICROTILE_TILED_SQUARE (2 << 17) +# define R300_DEPTHENDIAN_NO_SWAP (0 << 18) +# define R300_DEPTHENDIAN_WORD_SWAP (1 << 18) +# define R300_DEPTHENDIAN_DWORD_SWAP (2 << 18) +# define R300_DEPTHENDIAN_HALF_DWORD_SWAP (3 << 18) + +/* Z Buffer Clear Value */ +#define R300_ZB_DEPTHCLEARVALUE 0x4f28 + +#define R300_ZB_ZMASK_OFFSET 0x4f30 +#define R300_ZB_ZMASK_PITCH 0x4f34 +#define R300_ZB_ZMASK_WRINDEX 0x4f38 +#define R300_ZB_ZMASK_DWORD 0x4f3c +#define R300_ZB_ZMASK_RDINDEX 0x4f40 + +/* Hierarchical Z Memory Offset */ +#define R300_ZB_HIZ_OFFSET 0x4f44 + +/* Hierarchical Z Write Index */ +#define R300_ZB_HIZ_WRINDEX 0x4f48 + +/* Hierarchical Z Data */ +#define R300_ZB_HIZ_DWORD 0x4f4c + +/* Hierarchical Z Read Index */ +#define R300_ZB_HIZ_RDINDEX 0x4f50 + +/* Hierarchical Z Pitch */ +#define R300_ZB_HIZ_PITCH 0x4f54 + +/* Z Buffer Z Pass Counter Data */ +#define R300_ZB_ZPASS_DATA 0x4f58 + +/* Z Buffer Z Pass Counter Address */ +#define R300_ZB_ZPASS_ADDR 0x4f5c + +/* Depth buffer X and Y coordinate offset */ +#define R300_ZB_DEPTHXY_OFFSET 0x4f60 +# define R300_DEPTHX_OFFSET_SHIFT 1 +# define R300_DEPTHX_OFFSET_MASK 0x000007FE +# define R300_DEPTHY_OFFSET_SHIFT 17 +# define R300_DEPTHY_OFFSET_MASK 0x07FE0000 + +/* Sets the fifo sizes */ +#define R500_ZB_FIFO_SIZE 0x4fd0 +# define R500_OP_FIFO_SIZE_FULL (0 << 0) +# define R500_OP_FIFO_SIZE_HALF (1 << 0) +# define R500_OP_FIFO_SIZE_QUATER (2 << 0) +# define R500_OP_FIFO_SIZE_EIGTHS (4 << 0) + +/* Stencil Reference Value and Mask for backfacing quads */ +/* R300_ZB_STENCILREFMASK handles front face */ +#define R500_ZB_STENCILREFMASK_BF 0x4fd4 +# define R500_STENCILREF_SHIFT 0 +# define R500_STENCILREF_MASK 0x000000ff +# define R500_STENCILMASK_SHIFT 8 +# define R500_STENCILMASK_MASK 0x0000ff00 +# define R500_STENCILWRITEMASK_SHIFT 16 +# define R500_STENCILWRITEMASK_MASK 0x00ff0000 /* BEGIN: Vertex program instruction set */ @@ -1623,4 +1753,20 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define R300_CP_CMD_BITBLT_MULTI 0xC0009B00 +#define R500_VAP_INDEX_OFFSET 0x208c + +#define R500_GA_US_VECTOR_INDEX 0x4250 +#define R500_GA_US_VECTOR_DATA 0x4254 + +#define R500_RS_IP_0 0x4074 +#define R500_RS_INST_0 0x4320 + +#define R500_US_CONFIG 0x4600 + +#define R500_US_FC_CTRL 0x4624 +#define R500_US_CODE_ADDR 0x4630 + +#define R500_RB3D_COLOR_CLEAR_VALUE_AR 0x46c0 +#define R500_RB3D_CONSTANT_COLOR_AR 0x4ef8 + #endif /* _R300_REG_H */ diff --git a/drivers/char/drm/radeon_cp.c b/drivers/char/drm/radeon_cp.c index f6f6c92bf77..e53158f0ecb 100644 --- a/drivers/char/drm/radeon_cp.c +++ b/drivers/char/drm/radeon_cp.c @@ -2,6 +2,7 @@ /* * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * Copyright 2007 Advanced Micro Devices, Inc. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -34,789 +35,13 @@ #include "radeon_drv.h" #include "r300_reg.h" +#include "radeon_microcode.h" + #define RADEON_FIFO_DEBUG 0 static int radeon_do_cleanup_cp(struct drm_device * dev); -/* CP microcode (from ATI) */ -static const u32 R200_cp_microcode[][2] = { - {0x21007000, 0000000000}, - {0x20007000, 0000000000}, - {0x000000ab, 0x00000004}, - {0x000000af, 0x00000004}, - {0x66544a49, 0000000000}, - {0x49494174, 0000000000}, - {0x54517d83, 0000000000}, - {0x498d8b64, 0000000000}, - {0x49494949, 0000000000}, - {0x49da493c, 0000000000}, - {0x49989898, 0000000000}, - {0xd34949d5, 0000000000}, - {0x9dc90e11, 0000000000}, - {0xce9b9b9b, 0000000000}, - {0x000f0000, 0x00000016}, - {0x352e232c, 0000000000}, - {0x00000013, 0x00000004}, - {0x000f0000, 0x00000016}, - {0x352e272c, 0000000000}, - {0x000f0001, 0x00000016}, - {0x3239362f, 0000000000}, - {0x000077ef, 0x00000002}, - {0x00061000, 0x00000002}, - {0x00000020, 0x0000001a}, - {0x00004000, 0x0000001e}, - {0x00061000, 0x00000002}, - {0x00000020, 0x0000001a}, - {0x00004000, 0x0000001e}, - {0x00061000, 0x00000002}, - {0x00000020, 0x0000001a}, - {0x00004000, 0x0000001e}, - {0x00000016, 0x00000004}, - {0x0003802a, 0x00000002}, - {0x040067e0, 0x00000002}, - {0x00000016, 0x00000004}, - {0x000077e0, 0x00000002}, - {0x00065000, 0x00000002}, - {0x000037e1, 0x00000002}, - {0x040067e1, 0x00000006}, - {0x000077e0, 0x00000002}, - {0x000077e1, 0x00000002}, - {0x000077e1, 0x00000006}, - {0xffffffff, 0000000000}, - {0x10000000, 0000000000}, - {0x0003802a, 0x00000002}, - {0x040067e0, 0x00000006}, - {0x00007675, 0x00000002}, - {0x00007676, 0x00000002}, - {0x00007677, 0x00000002}, - {0x00007678, 0x00000006}, - {0x0003802b, 0x00000002}, - {0x04002676, 0x00000002}, - {0x00007677, 0x00000002}, - {0x00007678, 0x00000006}, - {0x0000002e, 0x00000018}, - {0x0000002e, 0x00000018}, - {0000000000, 0x00000006}, - {0x0000002f, 0x00000018}, - {0x0000002f, 0x00000018}, - {0000000000, 0x00000006}, - {0x01605000, 0x00000002}, - {0x00065000, 0x00000002}, - {0x00098000, 0x00000002}, - {0x00061000, 0x00000002}, - {0x64c0603d, 0x00000004}, - {0x00080000, 0x00000016}, - {0000000000, 0000000000}, - {0x0400251d, 0x00000002}, - {0x00007580, 0x00000002}, - {0x00067581, 0x00000002}, - {0x04002580, 0x00000002}, - {0x00067581, 0x00000002}, - {0x00000046, 0x00000004}, - {0x00005000, 0000000000}, - {0x00061000, 0x00000002}, - {0x0000750e, 0x00000002}, - {0x00019000, 0x00000002}, - {0x00011055, 0x00000014}, - {0x00000055, 0x00000012}, - {0x0400250f, 0x00000002}, - {0x0000504a, 0x00000004}, - {0x00007565, 0x00000002}, - {0x00007566, 0x00000002}, - {0x00000051, 0x00000004}, - {0x01e655b4, 0x00000002}, - {0x4401b0dc, 0x00000002}, - {0x01c110dc, 0x00000002}, - {0x2666705d, 0x00000018}, - {0x040c2565, 0x00000002}, - {0x0000005d, 0x00000018}, - {0x04002564, 0x00000002}, - {0x00007566, 0x00000002}, - {0x00000054, 0x00000004}, - {0x00401060, 0x00000008}, - {0x00101000, 0x00000002}, - {0x000d80ff, 0x00000002}, - {0x00800063, 0x00000008}, - {0x000f9000, 0x00000002}, - {0x000e00ff, 0x00000002}, - {0000000000, 0x00000006}, - {0x00000080, 0x00000018}, - {0x00000054, 0x00000004}, - {0x00007576, 0x00000002}, - {0x00065000, 0x00000002}, - {0x00009000, 0x00000002}, - {0x00041000, 0x00000002}, - {0x0c00350e, 0x00000002}, - {0x00049000, 0x00000002}, - {0x00051000, 0x00000002}, - {0x01e785f8, 0x00000002}, - {0x00200000, 0x00000002}, - {0x00600073, 0x0000000c}, - {0x00007563, 0x00000002}, - {0x006075f0, 0x00000021}, - {0x20007068, 0x00000004}, - {0x00005068, 0x00000004}, - {0x00007576, 0x00000002}, - {0x00007577, 0x00000002}, - {0x0000750e, 0x00000002}, - {0x0000750f, 0x00000002}, - {0x00a05000, 0x00000002}, - {0x00600076, 0x0000000c}, - {0x006075f0, 0x00000021}, - {0x000075f8, 0x00000002}, - {0x00000076, 0x00000004}, - {0x000a750e, 0x00000002}, - {0x0020750f, 0x00000002}, - {0x00600079, 0x00000004}, - {0x00007570, 0x00000002}, - {0x00007571, 0x00000002}, - {0x00007572, 0x00000006}, - {0x00005000, 0x00000002}, - {0x00a05000, 0x00000002}, - {0x00007568, 0x00000002}, - {0x00061000, 0x00000002}, - {0x00000084, 0x0000000c}, - {0x00058000, 0x00000002}, - {0x0c607562, 0x00000002}, - {0x00000086, 0x00000004}, - {0x00600085, 0x00000004}, - {0x400070dd, 0000000000}, - {0x000380dd, 0x00000002}, - {0x00000093, 0x0000001c}, - {0x00065095, 0x00000018}, - {0x040025bb, 0x00000002}, - {0x00061096, 0x00000018}, - {0x040075bc, 0000000000}, - {0x000075bb, 0x00000002}, - {0x000075bc, 0000000000}, - {0x00090000, 0x00000006}, - {0x00090000, 0x00000002}, - {0x000d8002, 0x00000006}, - {0x00005000, 0x00000002}, - {0x00007821, 0x00000002}, - {0x00007800, 0000000000}, - {0x00007821, 0x00000002}, - {0x00007800, 0000000000}, - {0x01665000, 0x00000002}, - {0x000a0000, 0x00000002}, - {0x000671cc, 0x00000002}, - {0x0286f1cd, 0x00000002}, - {0x000000a3, 0x00000010}, - {0x21007000, 0000000000}, - {0x000000aa, 0x0000001c}, - {0x00065000, 0x00000002}, - {0x000a0000, 0x00000002}, - {0x00061000, 0x00000002}, - {0x000b0000, 0x00000002}, - {0x38067000, 0x00000002}, - {0x000a00a6, 0x00000004}, - {0x20007000, 0000000000}, - {0x01200000, 0x00000002}, - {0x20077000, 0x00000002}, - {0x01200000, 0x00000002}, - {0x20007000, 0000000000}, - {0x00061000, 0x00000002}, - {0x0120751b, 0x00000002}, - {0x8040750a, 0x00000002}, - {0x8040750b, 0x00000002}, - {0x00110000, 0x00000002}, - {0x000380dd, 0x00000002}, - {0x000000bd, 0x0000001c}, - {0x00061096, 0x00000018}, - {0x844075bd, 0x00000002}, - {0x00061095, 0x00000018}, - {0x840075bb, 0x00000002}, - {0x00061096, 0x00000018}, - {0x844075bc, 0x00000002}, - {0x000000c0, 0x00000004}, - {0x804075bd, 0x00000002}, - {0x800075bb, 0x00000002}, - {0x804075bc, 0x00000002}, - {0x00108000, 0x00000002}, - {0x01400000, 0x00000002}, - {0x006000c4, 0x0000000c}, - {0x20c07000, 0x00000020}, - {0x000000c6, 0x00000012}, - {0x00800000, 0x00000006}, - {0x0080751d, 0x00000006}, - {0x000025bb, 0x00000002}, - {0x000040c0, 0x00000004}, - {0x0000775c, 0x00000002}, - {0x00a05000, 0x00000002}, - {0x00661000, 0x00000002}, - {0x0460275d, 0x00000020}, - {0x00004000, 0000000000}, - {0x00007999, 0x00000002}, - {0x00a05000, 0x00000002}, - {0x00661000, 0x00000002}, - {0x0460299b, 0x00000020}, - {0x00004000, 0000000000}, - {0x01e00830, 0x00000002}, - {0x21007000, 0000000000}, - {0x00005000, 0x00000002}, - {0x00038042, 0x00000002}, - {0x040025e0, 0x00000002}, - {0x000075e1, 0000000000}, - {0x00000001, 0000000000}, - {0x000380d9, 0x00000002}, - {0x04007394, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, -}; - -static const u32 radeon_cp_microcode[][2] = { - {0x21007000, 0000000000}, - {0x20007000, 0000000000}, - {0x000000b4, 0x00000004}, - {0x000000b8, 0x00000004}, - {0x6f5b4d4c, 0000000000}, - {0x4c4c427f, 0000000000}, - {0x5b568a92, 0000000000}, - {0x4ca09c6d, 0000000000}, - {0xad4c4c4c, 0000000000}, - {0x4ce1af3d, 0000000000}, - {0xd8afafaf, 0000000000}, - {0xd64c4cdc, 0000000000}, - {0x4cd10d10, 0000000000}, - {0x000f0000, 0x00000016}, - {0x362f242d, 0000000000}, - {0x00000012, 0x00000004}, - {0x000f0000, 0x00000016}, - {0x362f282d, 0000000000}, - {0x000380e7, 0x00000002}, - {0x04002c97, 0x00000002}, - {0x000f0001, 0x00000016}, - {0x333a3730, 0000000000}, - {0x000077ef, 0x00000002}, - {0x00061000, 0x00000002}, - {0x00000021, 0x0000001a}, - {0x00004000, 0x0000001e}, - {0x00061000, 0x00000002}, - {0x00000021, 0x0000001a}, - {0x00004000, 0x0000001e}, - {0x00061000, 0x00000002}, - {0x00000021, 0x0000001a}, - {0x00004000, 0x0000001e}, - {0x00000017, 0x00000004}, - {0x0003802b, 0x00000002}, - {0x040067e0, 0x00000002}, - {0x00000017, 0x00000004}, - {0x000077e0, 0x00000002}, - {0x00065000, 0x00000002}, - {0x000037e1, 0x00000002}, - {0x040067e1, 0x00000006}, - {0x000077e0, 0x00000002}, - {0x000077e1, 0x00000002}, - {0x000077e1, 0x00000006}, - {0xffffffff, 0000000000}, - {0x10000000, 0000000000}, - {0x0003802b, 0x00000002}, - {0x040067e0, 0x00000006}, - {0x00007675, 0x00000002}, - {0x00007676, 0x00000002}, - {0x00007677, 0x00000002}, - {0x00007678, 0x00000006}, - {0x0003802c, 0x00000002}, - {0x04002676, 0x00000002}, - {0x00007677, 0x00000002}, - {0x00007678, 0x00000006}, - {0x0000002f, 0x00000018}, - {0x0000002f, 0x00000018}, - {0000000000, 0x00000006}, - {0x00000030, 0x00000018}, - {0x00000030, 0x00000018}, - {0000000000, 0x00000006}, - {0x01605000, 0x00000002}, - {0x00065000, 0x00000002}, - {0x00098000, 0x00000002}, - {0x00061000, 0x00000002}, - {0x64c0603e, 0x00000004}, - {0x000380e6, 0x00000002}, - {0x040025c5, 0x00000002}, - {0x00080000, 0x00000016}, - {0000000000, 0000000000}, - {0x0400251d, 0x00000002}, - {0x00007580, 0x00000002}, - {0x00067581, 0x00000002}, - {0x04002580, 0x00000002}, - {0x00067581, 0x00000002}, - {0x00000049, 0x00000004}, - {0x00005000, 0000000000}, - {0x000380e6, 0x00000002}, - {0x040025c5, 0x00000002}, - {0x00061000, 0x00000002}, - {0x0000750e, 0x00000002}, - {0x00019000, 0x00000002}, - {0x00011055, 0x00000014}, - {0x00000055, 0x00000012}, - {0x0400250f, 0x00000002}, - {0x0000504f, 0x00000004}, - {0x000380e6, 0x00000002}, - {0x040025c5, 0x00000002}, - {0x00007565, 0x00000002}, - {0x00007566, 0x00000002}, - {0x00000058, 0x00000004}, - {0x000380e6, 0x00000002}, - {0x040025c5, 0x00000002}, - {0x01e655b4, 0x00000002}, - {0x4401b0e4, 0x00000002}, - {0x01c110e4, 0x00000002}, - {0x26667066, 0x00000018}, - {0x040c2565, 0x00000002}, - {0x00000066, 0x00000018}, - {0x04002564, 0x00000002}, - {0x00007566, 0x00000002}, - {0x0000005d, 0x00000004}, - {0x00401069, 0x00000008}, - {0x00101000, 0x00000002}, - {0x000d80ff, 0x00000002}, - {0x0080006c, 0x00000008}, - {0x000f9000, 0x00000002}, - {0x000e00ff, 0x00000002}, - {0000000000, 0x00000006}, - {0x0000008f, 0x00000018}, - {0x0000005b, 0x00000004}, - {0x000380e6, 0x00000002}, - {0x040025c5, 0x00000002}, - {0x00007576, 0x00000002}, - {0x00065000, 0x00000002}, - {0x00009000, 0x00000002}, - {0x00041000, 0x00000002}, - {0x0c00350e, 0x00000002}, - {0x00049000, 0x00000002}, - {0x00051000, 0x00000002}, - {0x01e785f8, 0x00000002}, - {0x00200000, 0x00000002}, - {0x0060007e, 0x0000000c}, - {0x00007563, 0x00000002}, - {0x006075f0, 0x00000021}, - {0x20007073, 0x00000004}, - {0x00005073, 0x00000004}, - {0x000380e6, 0x00000002}, - {0x040025c5, 0x00000002}, - {0x00007576, 0x00000002}, - {0x00007577, 0x00000002}, - {0x0000750e, 0x00000002}, - {0x0000750f, 0x00000002}, - {0x00a05000, 0x00000002}, - {0x00600083, 0x0000000c}, - {0x006075f0, 0x00000021}, - {0x000075f8, 0x00000002}, - {0x00000083, 0x00000004}, - {0x000a750e, 0x00000002}, - {0x000380e6, 0x00000002}, - {0x040025c5, 0x00000002}, - {0x0020750f, 0x00000002}, - {0x00600086, 0x00000004}, - {0x00007570, 0x00000002}, - {0x00007571, 0x00000002}, - {0x00007572, 0x00000006}, - {0x000380e6, 0x00000002}, - {0x040025c5, 0x00000002}, - {0x00005000, 0x00000002}, - {0x00a05000, 0x00000002}, - {0x00007568, 0x00000002}, - {0x00061000, 0x00000002}, - {0x00000095, 0x0000000c}, - {0x00058000, 0x00000002}, - {0x0c607562, 0x00000002}, - {0x00000097, 0x00000004}, - {0x000380e6, 0x00000002}, - {0x040025c5, 0x00000002}, - {0x00600096, 0x00000004}, - {0x400070e5, 0000000000}, - {0x000380e6, 0x00000002}, - {0x040025c5, 0x00000002}, - {0x000380e5, 0x00000002}, - {0x000000a8, 0x0000001c}, - {0x000650aa, 0x00000018}, - {0x040025bb, 0x00000002}, - {0x000610ab, 0x00000018}, - {0x040075bc, 0000000000}, - {0x000075bb, 0x00000002}, - {0x000075bc, 0000000000}, - {0x00090000, 0x00000006}, - {0x00090000, 0x00000002}, - {0x000d8002, 0x00000006}, - {0x00007832, 0x00000002}, - {0x00005000, 0x00000002}, - {0x000380e7, 0x00000002}, - {0x04002c97, 0x00000002}, - {0x00007820, 0x00000002}, - {0x00007821, 0x00000002}, - {0x00007800, 0000000000}, - {0x01200000, 0x00000002}, - {0x20077000, 0x00000002}, - {0x01200000, 0x00000002}, - {0x20007000, 0x00000002}, - {0x00061000, 0x00000002}, - {0x0120751b, 0x00000002}, - {0x8040750a, 0x00000002}, - {0x8040750b, 0x00000002}, - {0x00110000, 0x00000002}, - {0x000380e5, 0x00000002}, - {0x000000c6, 0x0000001c}, - {0x000610ab, 0x00000018}, - {0x844075bd, 0x00000002}, - {0x000610aa, 0x00000018}, - {0x840075bb, 0x00000002}, - {0x000610ab, 0x00000018}, - {0x844075bc, 0x00000002}, - {0x000000c9, 0x00000004}, - {0x804075bd, 0x00000002}, - {0x800075bb, 0x00000002}, - {0x804075bc, 0x00000002}, - {0x00108000, 0x00000002}, - {0x01400000, 0x00000002}, - {0x006000cd, 0x0000000c}, - {0x20c07000, 0x00000020}, - {0x000000cf, 0x00000012}, - {0x00800000, 0x00000006}, - {0x0080751d, 0x00000006}, - {0000000000, 0000000000}, - {0x0000775c, 0x00000002}, - {0x00a05000, 0x00000002}, - {0x00661000, 0x00000002}, - {0x0460275d, 0x00000020}, - {0x00004000, 0000000000}, - {0x01e00830, 0x00000002}, - {0x21007000, 0000000000}, - {0x6464614d, 0000000000}, - {0x69687420, 0000000000}, - {0x00000073, 0000000000}, - {0000000000, 0000000000}, - {0x00005000, 0x00000002}, - {0x000380d0, 0x00000002}, - {0x040025e0, 0x00000002}, - {0x000075e1, 0000000000}, - {0x00000001, 0000000000}, - {0x000380e0, 0x00000002}, - {0x04002394, 0x00000002}, - {0x00005000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0x00000008, 0000000000}, - {0x00000004, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, -}; - -static const u32 R300_cp_microcode[][2] = { - {0x4200e000, 0000000000}, - {0x4000e000, 0000000000}, - {0x000000af, 0x00000008}, - {0x000000b3, 0x00000008}, - {0x6c5a504f, 0000000000}, - {0x4f4f497a, 0000000000}, - {0x5a578288, 0000000000}, - {0x4f91906a, 0000000000}, - {0x4f4f4f4f, 0000000000}, - {0x4fe24f44, 0000000000}, - {0x4f9c9c9c, 0000000000}, - {0xdc4f4fde, 0000000000}, - {0xa1cd4f4f, 0000000000}, - {0xd29d9d9d, 0000000000}, - {0x4f0f9fd7, 0000000000}, - {0x000ca000, 0x00000004}, - {0x000d0012, 0x00000038}, - {0x0000e8b4, 0x00000004}, - {0x000d0014, 0x00000038}, - {0x0000e8b6, 0x00000004}, - {0x000d0016, 0x00000038}, - {0x0000e854, 0x00000004}, - {0x000d0018, 0x00000038}, - {0x0000e855, 0x00000004}, - {0x000d001a, 0x00000038}, - {0x0000e856, 0x00000004}, - {0x000d001c, 0x00000038}, - {0x0000e857, 0x00000004}, - {0x000d001e, 0x00000038}, - {0x0000e824, 0x00000004}, - {0x000d0020, 0x00000038}, - {0x0000e825, 0x00000004}, - {0x000d0022, 0x00000038}, - {0x0000e830, 0x00000004}, - {0x000d0024, 0x00000038}, - {0x0000f0c0, 0x00000004}, - {0x000d0026, 0x00000038}, - {0x0000f0c1, 0x00000004}, - {0x000d0028, 0x00000038}, - {0x0000f041, 0x00000004}, - {0x000d002a, 0x00000038}, - {0x0000f184, 0x00000004}, - {0x000d002c, 0x00000038}, - {0x0000f185, 0x00000004}, - {0x000d002e, 0x00000038}, - {0x0000f186, 0x00000004}, - {0x000d0030, 0x00000038}, - {0x0000f187, 0x00000004}, - {0x000d0032, 0x00000038}, - {0x0000f180, 0x00000004}, - {0x000d0034, 0x00000038}, - {0x0000f393, 0x00000004}, - {0x000d0036, 0x00000038}, - {0x0000f38a, 0x00000004}, - {0x000d0038, 0x00000038}, - {0x0000f38e, 0x00000004}, - {0x0000e821, 0x00000004}, - {0x0140a000, 0x00000004}, - {0x00000043, 0x00000018}, - {0x00cce800, 0x00000004}, - {0x001b0001, 0x00000004}, - {0x08004800, 0x00000004}, - {0x001b0001, 0x00000004}, - {0x08004800, 0x00000004}, - {0x001b0001, 0x00000004}, - {0x08004800, 0x00000004}, - {0x0000003a, 0x00000008}, - {0x0000a000, 0000000000}, - {0x02c0a000, 0x00000004}, - {0x000ca000, 0x00000004}, - {0x00130000, 0x00000004}, - {0x000c2000, 0x00000004}, - {0xc980c045, 0x00000008}, - {0x2000451d, 0x00000004}, - {0x0000e580, 0x00000004}, - {0x000ce581, 0x00000004}, - {0x08004580, 0x00000004}, - {0x000ce581, 0x00000004}, - {0x0000004c, 0x00000008}, - {0x0000a000, 0000000000}, - {0x000c2000, 0x00000004}, - {0x0000e50e, 0x00000004}, - {0x00032000, 0x00000004}, - {0x00022056, 0x00000028}, - {0x00000056, 0x00000024}, - {0x0800450f, 0x00000004}, - {0x0000a050, 0x00000008}, - {0x0000e565, 0x00000004}, - {0x0000e566, 0x00000004}, - {0x00000057, 0x00000008}, - {0x03cca5b4, 0x00000004}, - {0x05432000, 0x00000004}, - {0x00022000, 0x00000004}, - {0x4ccce063, 0x00000030}, - {0x08274565, 0x00000004}, - {0x00000063, 0x00000030}, - {0x08004564, 0x00000004}, - {0x0000e566, 0x00000004}, - {0x0000005a, 0x00000008}, - {0x00802066, 0x00000010}, - {0x00202000, 0x00000004}, - {0x001b00ff, 0x00000004}, - {0x01000069, 0x00000010}, - {0x001f2000, 0x00000004}, - {0x001c00ff, 0x00000004}, - {0000000000, 0x0000000c}, - {0x00000085, 0x00000030}, - {0x0000005a, 0x00000008}, - {0x0000e576, 0x00000004}, - {0x000ca000, 0x00000004}, - {0x00012000, 0x00000004}, - {0x00082000, 0x00000004}, - {0x1800650e, 0x00000004}, - {0x00092000, 0x00000004}, - {0x000a2000, 0x00000004}, - {0x000f0000, 0x00000004}, - {0x00400000, 0x00000004}, - {0x00000079, 0x00000018}, - {0x0000e563, 0x00000004}, - {0x00c0e5f9, 0x000000c2}, - {0x0000006e, 0x00000008}, - {0x0000a06e, 0x00000008}, - {0x0000e576, 0x00000004}, - {0x0000e577, 0x00000004}, - {0x0000e50e, 0x00000004}, - {0x0000e50f, 0x00000004}, - {0x0140a000, 0x00000004}, - {0x0000007c, 0x00000018}, - {0x00c0e5f9, 0x000000c2}, - {0x0000007c, 0x00000008}, - {0x0014e50e, 0x00000004}, - {0x0040e50f, 0x00000004}, - {0x00c0007f, 0x00000008}, - {0x0000e570, 0x00000004}, - {0x0000e571, 0x00000004}, - {0x0000e572, 0x0000000c}, - {0x0000a000, 0x00000004}, - {0x0140a000, 0x00000004}, - {0x0000e568, 0x00000004}, - {0x000c2000, 0x00000004}, - {0x00000089, 0x00000018}, - {0x000b0000, 0x00000004}, - {0x18c0e562, 0x00000004}, - {0x0000008b, 0x00000008}, - {0x00c0008a, 0x00000008}, - {0x000700e4, 0x00000004}, - {0x00000097, 0x00000038}, - {0x000ca099, 0x00000030}, - {0x080045bb, 0x00000004}, - {0x000c209a, 0x00000030}, - {0x0800e5bc, 0000000000}, - {0x0000e5bb, 0x00000004}, - {0x0000e5bc, 0000000000}, - {0x00120000, 0x0000000c}, - {0x00120000, 0x00000004}, - {0x001b0002, 0x0000000c}, - {0x0000a000, 0x00000004}, - {0x0000e821, 0x00000004}, - {0x0000e800, 0000000000}, - {0x0000e821, 0x00000004}, - {0x0000e82e, 0000000000}, - {0x02cca000, 0x00000004}, - {0x00140000, 0x00000004}, - {0x000ce1cc, 0x00000004}, - {0x050de1cd, 0x00000004}, - {0x000000a7, 0x00000020}, - {0x4200e000, 0000000000}, - {0x000000ae, 0x00000038}, - {0x000ca000, 0x00000004}, - {0x00140000, 0x00000004}, - {0x000c2000, 0x00000004}, - {0x00160000, 0x00000004}, - {0x700ce000, 0x00000004}, - {0x001400aa, 0x00000008}, - {0x4000e000, 0000000000}, - {0x02400000, 0x00000004}, - {0x400ee000, 0x00000004}, - {0x02400000, 0x00000004}, - {0x4000e000, 0000000000}, - {0x000c2000, 0x00000004}, - {0x0240e51b, 0x00000004}, - {0x0080e50a, 0x00000005}, - {0x0080e50b, 0x00000005}, - {0x00220000, 0x00000004}, - {0x000700e4, 0x00000004}, - {0x000000c1, 0x00000038}, - {0x000c209a, 0x00000030}, - {0x0880e5bd, 0x00000005}, - {0x000c2099, 0x00000030}, - {0x0800e5bb, 0x00000005}, - {0x000c209a, 0x00000030}, - {0x0880e5bc, 0x00000005}, - {0x000000c4, 0x00000008}, - {0x0080e5bd, 0x00000005}, - {0x0000e5bb, 0x00000005}, - {0x0080e5bc, 0x00000005}, - {0x00210000, 0x00000004}, - {0x02800000, 0x00000004}, - {0x00c000c8, 0x00000018}, - {0x4180e000, 0x00000040}, - {0x000000ca, 0x00000024}, - {0x01000000, 0x0000000c}, - {0x0100e51d, 0x0000000c}, - {0x000045bb, 0x00000004}, - {0x000080c4, 0x00000008}, - {0x0000f3ce, 0x00000004}, - {0x0140a000, 0x00000004}, - {0x00cc2000, 0x00000004}, - {0x08c053cf, 0x00000040}, - {0x00008000, 0000000000}, - {0x0000f3d2, 0x00000004}, - {0x0140a000, 0x00000004}, - {0x00cc2000, 0x00000004}, - {0x08c053d3, 0x00000040}, - {0x00008000, 0000000000}, - {0x0000f39d, 0x00000004}, - {0x0140a000, 0x00000004}, - {0x00cc2000, 0x00000004}, - {0x08c0539e, 0x00000040}, - {0x00008000, 0000000000}, - {0x03c00830, 0x00000004}, - {0x4200e000, 0000000000}, - {0x0000a000, 0x00000004}, - {0x200045e0, 0x00000004}, - {0x0000e5e1, 0000000000}, - {0x00000001, 0000000000}, - {0x000700e1, 0x00000004}, - {0x0800e394, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, - {0000000000, 0000000000}, -}; - -static u32 RADEON_READ_MCIND(drm_radeon_private_t *dev_priv, int addr) +static u32 R500_READ_MCIND(drm_radeon_private_t *dev_priv, int addr) { u32 ret; RADEON_WRITE(R520_MC_IND_INDEX, 0x7f0000 | (addr & 0xff)); @@ -825,21 +50,41 @@ static u32 RADEON_READ_MCIND(drm_radeon_private_t *dev_priv, int addr) return ret; } +static u32 RS480_READ_MCIND(drm_radeon_private_t *dev_priv, int addr) +{ + u32 ret; + RADEON_WRITE(RS480_NB_MC_INDEX, addr & 0xff); + ret = RADEON_READ(RS480_NB_MC_DATA); + RADEON_WRITE(RS480_NB_MC_INDEX, 0xff); + return ret; +} + static u32 RS690_READ_MCIND(drm_radeon_private_t *dev_priv, int addr) { + u32 ret; RADEON_WRITE(RS690_MC_INDEX, (addr & RS690_MC_INDEX_MASK)); - return RADEON_READ(RS690_MC_DATA); + ret = RADEON_READ(RS690_MC_DATA); + RADEON_WRITE(RS690_MC_INDEX, RS690_MC_INDEX_MASK); + return ret; +} + +static u32 IGP_READ_MCIND(drm_radeon_private_t *dev_priv, int addr) +{ + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) + return RS690_READ_MCIND(dev_priv, addr); + else + return RS480_READ_MCIND(dev_priv, addr); } u32 radeon_read_fb_location(drm_radeon_private_t *dev_priv) { if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV515) - return RADEON_READ_MCIND(dev_priv, RV515_MC_FB_LOCATION); + return R500_READ_MCIND(dev_priv, RV515_MC_FB_LOCATION); else if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) return RS690_READ_MCIND(dev_priv, RS690_MC_FB_LOCATION); else if ((dev_priv->flags & RADEON_FAMILY_MASK) > CHIP_RV515) - return RADEON_READ_MCIND(dev_priv, R520_MC_FB_LOCATION); + return R500_READ_MCIND(dev_priv, R520_MC_FB_LOCATION); else return RADEON_READ(RADEON_MC_FB_LOCATION); } @@ -847,11 +92,11 @@ u32 radeon_read_fb_location(drm_radeon_private_t *dev_priv) static void radeon_write_fb_location(drm_radeon_private_t *dev_priv, u32 fb_loc) { if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV515) - RADEON_WRITE_MCIND(RV515_MC_FB_LOCATION, fb_loc); + R500_WRITE_MCIND(RV515_MC_FB_LOCATION, fb_loc); else if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) RS690_WRITE_MCIND(RS690_MC_FB_LOCATION, fb_loc); else if ((dev_priv->flags & RADEON_FAMILY_MASK) > CHIP_RV515) - RADEON_WRITE_MCIND(R520_MC_FB_LOCATION, fb_loc); + R500_WRITE_MCIND(R520_MC_FB_LOCATION, fb_loc); else RADEON_WRITE(RADEON_MC_FB_LOCATION, fb_loc); } @@ -859,15 +104,39 @@ static void radeon_write_fb_location(drm_radeon_private_t *dev_priv, u32 fb_loc) static void radeon_write_agp_location(drm_radeon_private_t *dev_priv, u32 agp_loc) { if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV515) - RADEON_WRITE_MCIND(RV515_MC_AGP_LOCATION, agp_loc); + R500_WRITE_MCIND(RV515_MC_AGP_LOCATION, agp_loc); else if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) RS690_WRITE_MCIND(RS690_MC_AGP_LOCATION, agp_loc); else if ((dev_priv->flags & RADEON_FAMILY_MASK) > CHIP_RV515) - RADEON_WRITE_MCIND(R520_MC_AGP_LOCATION, agp_loc); + R500_WRITE_MCIND(R520_MC_AGP_LOCATION, agp_loc); else RADEON_WRITE(RADEON_MC_AGP_LOCATION, agp_loc); } +static void radeon_write_agp_base(drm_radeon_private_t *dev_priv, u64 agp_base) +{ + u32 agp_base_hi = upper_32_bits(agp_base); + u32 agp_base_lo = agp_base & 0xffffffff; + + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV515) { + R500_WRITE_MCIND(RV515_MC_AGP_BASE, agp_base_lo); + R500_WRITE_MCIND(RV515_MC_AGP_BASE_2, agp_base_hi); + } else if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) { + RS690_WRITE_MCIND(RS690_MC_AGP_BASE, agp_base_lo); + RS690_WRITE_MCIND(RS690_MC_AGP_BASE_2, agp_base_hi); + } else if ((dev_priv->flags & RADEON_FAMILY_MASK) > CHIP_RV515) { + R500_WRITE_MCIND(R520_MC_AGP_BASE, agp_base_lo); + R500_WRITE_MCIND(R520_MC_AGP_BASE_2, agp_base_hi); + } else if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS480) { + RADEON_WRITE(RADEON_AGP_BASE, agp_base_lo); + RADEON_WRITE(RS480_AGP_BASE_2, 0); + } else { + RADEON_WRITE(RADEON_AGP_BASE, agp_base_lo); + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R200) + RADEON_WRITE(RADEON_AGP_BASE_2, agp_base_hi); + } +} + static int RADEON_READ_PLL(struct drm_device * dev, int addr) { drm_radeon_private_t *dev_priv = dev->dev_private; @@ -882,15 +151,6 @@ static u32 RADEON_READ_PCIE(drm_radeon_private_t *dev_priv, int addr) return RADEON_READ(RADEON_PCIE_DATA); } -static u32 RADEON_READ_IGPGART(drm_radeon_private_t *dev_priv, int addr) -{ - u32 ret; - RADEON_WRITE(RADEON_IGPGART_INDEX, addr & 0x7f); - ret = RADEON_READ(RADEON_IGPGART_DATA); - RADEON_WRITE(RADEON_IGPGART_INDEX, 0x7f); - return ret; -} - #if RADEON_FIFO_DEBUG static void radeon_status(drm_radeon_private_t * dev_priv) { @@ -925,16 +185,36 @@ static int radeon_do_pixcache_flush(drm_radeon_private_t * dev_priv) dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; - tmp = RADEON_READ(RADEON_RB3D_DSTCACHE_CTLSTAT); - tmp |= RADEON_RB3D_DC_FLUSH_ALL; - RADEON_WRITE(RADEON_RB3D_DSTCACHE_CTLSTAT, tmp); + if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV280) { + tmp = RADEON_READ(RADEON_RB3D_DSTCACHE_CTLSTAT); + tmp |= RADEON_RB3D_DC_FLUSH_ALL; + RADEON_WRITE(RADEON_RB3D_DSTCACHE_CTLSTAT, tmp); - for (i = 0; i < dev_priv->usec_timeout; i++) { - if (!(RADEON_READ(RADEON_RB3D_DSTCACHE_CTLSTAT) - & RADEON_RB3D_DC_BUSY)) { - return 0; + for (i = 0; i < dev_priv->usec_timeout; i++) { + if (!(RADEON_READ(RADEON_RB3D_DSTCACHE_CTLSTAT) + & RADEON_RB3D_DC_BUSY)) { + return 0; + } + DRM_UDELAY(1); + } + } else { + /* 3D */ + tmp = RADEON_READ(R300_RB3D_DSTCACHE_CTLSTAT); + tmp |= RADEON_RB3D_DC_FLUSH_ALL; + RADEON_WRITE(R300_RB3D_DSTCACHE_CTLSTAT, tmp); + + /* 2D */ + tmp = RADEON_READ(R300_DSTCACHE_CTLSTAT); + tmp |= RADEON_RB3D_DC_FLUSH_ALL; + RADEON_WRITE(R300_DSTCACHE_CTLSTAT, tmp); + + for (i = 0; i < dev_priv->usec_timeout; i++) { + if (!(RADEON_READ(R300_DSTCACHE_CTLSTAT) + & RADEON_RB3D_DC_BUSY)) { + return 0; + } + DRM_UDELAY(1); } - DRM_UDELAY(1); } #if RADEON_FIFO_DEBUG @@ -991,6 +271,50 @@ static int radeon_do_wait_for_idle(drm_radeon_private_t * dev_priv) return -EBUSY; } +static void radeon_init_pipes(drm_radeon_private_t *dev_priv) +{ + uint32_t gb_tile_config, gb_pipe_sel = 0; + + /* RS4xx/RS6xx/R4xx/R5xx */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R420) { + gb_pipe_sel = RADEON_READ(R400_GB_PIPE_SELECT); + dev_priv->num_gb_pipes = ((gb_pipe_sel >> 12) & 0x3) + 1; + } else { + /* R3xx */ + if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R300) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R350)) { + dev_priv->num_gb_pipes = 2; + } else { + /* R3Vxx */ + dev_priv->num_gb_pipes = 1; + } + } + DRM_INFO("Num pipes: %d\n", dev_priv->num_gb_pipes); + + gb_tile_config = (R300_ENABLE_TILING | R300_TILE_SIZE_16 /*| R300_SUBPIXEL_1_16*/); + + switch (dev_priv->num_gb_pipes) { + case 2: gb_tile_config |= R300_PIPE_COUNT_R300; break; + case 3: gb_tile_config |= R300_PIPE_COUNT_R420_3P; break; + case 4: gb_tile_config |= R300_PIPE_COUNT_R420; break; + default: + case 1: gb_tile_config |= R300_PIPE_COUNT_RV350; break; + } + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV515) { + RADEON_WRITE_PLL(R500_DYN_SCLK_PWMEM_PIPE, (1 | ((gb_pipe_sel >> 8) & 0xf) << 4)); + RADEON_WRITE(R500_SU_REG_DEST, ((1 << dev_priv->num_gb_pipes) - 1)); + } + RADEON_WRITE(R300_GB_TILE_CONFIG, gb_tile_config); + radeon_do_wait_for_idle(dev_priv); + RADEON_WRITE(R300_DST_PIPE_CONFIG, RADEON_READ(R300_DST_PIPE_CONFIG) | R300_PIPE_AUTO_CONFIG); + RADEON_WRITE(R300_RB2D_DSTCACHE_MODE, (RADEON_READ(R300_RB2D_DSTCACHE_MODE) | + R300_DC_AUTOFLUSH_ENABLE | + R300_DC_DC_DISABLE_IGNORE_PE)); + + +} + /* ================================================================ * CP control, initialization */ @@ -1004,8 +328,22 @@ static void radeon_cp_load_microcode(drm_radeon_private_t * dev_priv) radeon_do_wait_for_idle(dev_priv); RADEON_WRITE(RADEON_CP_ME_RAM_ADDR, 0); - - if (dev_priv->microcode_version == UCODE_R200) { + if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R100) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV100) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV200) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS100) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS200)) { + DRM_INFO("Loading R100 Microcode\n"); + for (i = 0; i < 256; i++) { + RADEON_WRITE(RADEON_CP_ME_RAM_DATAH, + R100_cp_microcode[i][1]); + RADEON_WRITE(RADEON_CP_ME_RAM_DATAL, + R100_cp_microcode[i][0]); + } + } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R200) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV250) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV280) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS300)) { DRM_INFO("Loading R200 Microcode\n"); for (i = 0; i < 256; i++) { RADEON_WRITE(RADEON_CP_ME_RAM_DATAH, @@ -1013,7 +351,11 @@ static void radeon_cp_load_microcode(drm_radeon_private_t * dev_priv) RADEON_WRITE(RADEON_CP_ME_RAM_DATAL, R200_cp_microcode[i][0]); } - } else if (dev_priv->microcode_version == UCODE_R300) { + } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R300) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R350) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV350) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV380) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS480)) { DRM_INFO("Loading R300 Microcode\n"); for (i = 0; i < 256; i++) { RADEON_WRITE(RADEON_CP_ME_RAM_DATAH, @@ -1021,12 +363,35 @@ static void radeon_cp_load_microcode(drm_radeon_private_t * dev_priv) RADEON_WRITE(RADEON_CP_ME_RAM_DATAL, R300_cp_microcode[i][0]); } - } else { + } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R420) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV410)) { + DRM_INFO("Loading R400 Microcode\n"); for (i = 0; i < 256; i++) { RADEON_WRITE(RADEON_CP_ME_RAM_DATAH, - radeon_cp_microcode[i][1]); + R420_cp_microcode[i][1]); RADEON_WRITE(RADEON_CP_ME_RAM_DATAL, - radeon_cp_microcode[i][0]); + R420_cp_microcode[i][0]); + } + } else if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) { + DRM_INFO("Loading RS690 Microcode\n"); + for (i = 0; i < 256; i++) { + RADEON_WRITE(RADEON_CP_ME_RAM_DATAH, + RS690_cp_microcode[i][1]); + RADEON_WRITE(RADEON_CP_ME_RAM_DATAL, + RS690_cp_microcode[i][0]); + } + } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV515) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R520) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV530) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R580) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV560) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV570)) { + DRM_INFO("Loading R500 Microcode\n"); + for (i = 0; i < 256; i++) { + RADEON_WRITE(RADEON_CP_ME_RAM_DATAH, + R520_cp_microcode[i][1]); + RADEON_WRITE(RADEON_CP_ME_RAM_DATAL, + R520_cp_microcode[i][0]); } } } @@ -1121,12 +486,13 @@ static void radeon_do_cp_stop(drm_radeon_private_t * dev_priv) static int radeon_do_engine_reset(struct drm_device * dev) { drm_radeon_private_t *dev_priv = dev->dev_private; - u32 clock_cntl_index, mclk_cntl, rbbm_soft_reset; + u32 clock_cntl_index = 0, mclk_cntl = 0, rbbm_soft_reset; DRM_DEBUG("\n"); radeon_do_pixcache_flush(dev_priv); - if ((dev_priv->flags & RADEON_FAMILY_MASK) < CHIP_RV515) { + if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV410) { + /* may need something similar for newer chips */ clock_cntl_index = RADEON_READ(RADEON_CLOCK_CNTL_INDEX); mclk_cntl = RADEON_READ_PLL(dev, RADEON_MCLK_CNTL); @@ -1137,33 +503,39 @@ static int radeon_do_engine_reset(struct drm_device * dev) RADEON_FORCEON_YCLKB | RADEON_FORCEON_MC | RADEON_FORCEON_AIC)); + } - rbbm_soft_reset = RADEON_READ(RADEON_RBBM_SOFT_RESET); - - RADEON_WRITE(RADEON_RBBM_SOFT_RESET, (rbbm_soft_reset | - RADEON_SOFT_RESET_CP | - RADEON_SOFT_RESET_HI | - RADEON_SOFT_RESET_SE | - RADEON_SOFT_RESET_RE | - RADEON_SOFT_RESET_PP | - RADEON_SOFT_RESET_E2 | - RADEON_SOFT_RESET_RB)); - RADEON_READ(RADEON_RBBM_SOFT_RESET); - RADEON_WRITE(RADEON_RBBM_SOFT_RESET, (rbbm_soft_reset & - ~(RADEON_SOFT_RESET_CP | - RADEON_SOFT_RESET_HI | - RADEON_SOFT_RESET_SE | - RADEON_SOFT_RESET_RE | - RADEON_SOFT_RESET_PP | - RADEON_SOFT_RESET_E2 | - RADEON_SOFT_RESET_RB))); - RADEON_READ(RADEON_RBBM_SOFT_RESET); - + rbbm_soft_reset = RADEON_READ(RADEON_RBBM_SOFT_RESET); + + RADEON_WRITE(RADEON_RBBM_SOFT_RESET, (rbbm_soft_reset | + RADEON_SOFT_RESET_CP | + RADEON_SOFT_RESET_HI | + RADEON_SOFT_RESET_SE | + RADEON_SOFT_RESET_RE | + RADEON_SOFT_RESET_PP | + RADEON_SOFT_RESET_E2 | + RADEON_SOFT_RESET_RB)); + RADEON_READ(RADEON_RBBM_SOFT_RESET); + RADEON_WRITE(RADEON_RBBM_SOFT_RESET, (rbbm_soft_reset & + ~(RADEON_SOFT_RESET_CP | + RADEON_SOFT_RESET_HI | + RADEON_SOFT_RESET_SE | + RADEON_SOFT_RESET_RE | + RADEON_SOFT_RESET_PP | + RADEON_SOFT_RESET_E2 | + RADEON_SOFT_RESET_RB))); + RADEON_READ(RADEON_RBBM_SOFT_RESET); + + if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV410) { RADEON_WRITE_PLL(RADEON_MCLK_CNTL, mclk_cntl); RADEON_WRITE(RADEON_CLOCK_CNTL_INDEX, clock_cntl_index); RADEON_WRITE(RADEON_RBBM_SOFT_RESET, rbbm_soft_reset); } + /* setup the raster pipes */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R300) + radeon_init_pipes(dev_priv); + /* Reset the CP ring */ radeon_do_cp_reset(dev_priv); @@ -1194,7 +566,8 @@ static void radeon_cp_init_ring_buffer(struct drm_device * dev, #if __OS_HAS_AGP if (dev_priv->flags & RADEON_IS_AGP) { - RADEON_WRITE(RADEON_AGP_BASE, (unsigned int)dev->agp->base); + radeon_write_agp_base(dev_priv, dev->agp->base); + radeon_write_agp_location(dev_priv, (((dev_priv->gart_vm_start - 1 + dev_priv->gart_size) & 0xffff0000) | @@ -1339,102 +712,70 @@ static void radeon_test_writeback(drm_radeon_private_t * dev_priv) /* Enable or disable IGP GART on the chip */ static void radeon_set_igpgart(drm_radeon_private_t * dev_priv, int on) { - u32 temp, tmp; - - tmp = RADEON_READ(RADEON_AIC_CNTL); - if (on) { - DRM_DEBUG("programming igpgart %08X %08lX %08X\n", - dev_priv->gart_vm_start, - (long)dev_priv->gart_info.bus_addr, - dev_priv->gart_size); - - RADEON_WRITE_IGPGART(RADEON_IGPGART_UNK_18, 0x1000); - RADEON_WRITE_IGPGART(RADEON_IGPGART_ENABLE, 0x1); - RADEON_WRITE_IGPGART(RADEON_IGPGART_CTRL, 0x42040800); - RADEON_WRITE_IGPGART(RADEON_IGPGART_BASE_ADDR, - dev_priv->gart_info.bus_addr); - - temp = RADEON_READ_IGPGART(dev_priv, RADEON_IGPGART_UNK_39); - RADEON_WRITE_IGPGART(RADEON_IGPGART_UNK_39, temp); - - RADEON_WRITE(RADEON_AGP_BASE, (unsigned int)dev_priv->gart_vm_start); - dev_priv->gart_size = 32*1024*1024; - radeon_write_agp_location(dev_priv, - (((dev_priv->gart_vm_start - 1 + - dev_priv->gart_size) & 0xffff0000) | - (dev_priv->gart_vm_start >> 16))); - - temp = RADEON_READ_IGPGART(dev_priv, RADEON_IGPGART_ENABLE); - RADEON_WRITE_IGPGART(RADEON_IGPGART_ENABLE, temp); - - RADEON_READ_IGPGART(dev_priv, RADEON_IGPGART_FLUSH); - RADEON_WRITE_IGPGART(RADEON_IGPGART_FLUSH, 0x1); - RADEON_READ_IGPGART(dev_priv, RADEON_IGPGART_FLUSH); - RADEON_WRITE_IGPGART(RADEON_IGPGART_FLUSH, 0x0); - } -} - -/* Enable or disable RS690 GART on the chip */ -static void radeon_set_rs690gart(drm_radeon_private_t *dev_priv, int on) -{ u32 temp; if (on) { - DRM_DEBUG("programming rs690 gart %08X %08lX %08X\n", + DRM_DEBUG("programming igp gart %08X %08lX %08X\n", dev_priv->gart_vm_start, (long)dev_priv->gart_info.bus_addr, dev_priv->gart_size); - temp = RS690_READ_MCIND(dev_priv, RS690_MC_MISC_CNTL); - RS690_WRITE_MCIND(RS690_MC_MISC_CNTL, 0x5000); + temp = IGP_READ_MCIND(dev_priv, RS480_MC_MISC_CNTL); + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) + IGP_WRITE_MCIND(RS480_MC_MISC_CNTL, (RS480_GART_INDEX_REG_EN | + RS690_BLOCK_GFX_D3_EN)); + else + IGP_WRITE_MCIND(RS480_MC_MISC_CNTL, RS480_GART_INDEX_REG_EN); - RS690_WRITE_MCIND(RS690_MC_AGP_SIZE, - RS690_MC_GART_EN | RS690_MC_AGP_SIZE_32MB); + IGP_WRITE_MCIND(RS480_AGP_ADDRESS_SPACE_SIZE, (RS480_GART_EN | + RS480_VA_SIZE_32MB)); - temp = RS690_READ_MCIND(dev_priv, RS690_MC_GART_FEATURE_ID); - RS690_WRITE_MCIND(RS690_MC_GART_FEATURE_ID, 0x42040800); + temp = IGP_READ_MCIND(dev_priv, RS480_GART_FEATURE_ID); + IGP_WRITE_MCIND(RS480_GART_FEATURE_ID, (RS480_HANG_EN | + RS480_TLB_ENABLE | + RS480_GTW_LAC_EN | + RS480_1LEVEL_GART)); - RS690_WRITE_MCIND(RS690_MC_GART_BASE, - dev_priv->gart_info.bus_addr); + temp = dev_priv->gart_info.bus_addr & 0xfffff000; + temp |= (upper_32_bits(dev_priv->gart_info.bus_addr) & 0xff) << 4; + IGP_WRITE_MCIND(RS480_GART_BASE, temp); - temp = RS690_READ_MCIND(dev_priv, RS690_MC_AGP_MODE_CONTROL); - RS690_WRITE_MCIND(RS690_MC_AGP_MODE_CONTROL, 0x01400000); + temp = IGP_READ_MCIND(dev_priv, RS480_AGP_MODE_CNTL); + IGP_WRITE_MCIND(RS480_AGP_MODE_CNTL, ((1 << RS480_REQ_TYPE_SNOOP_SHIFT) | + RS480_REQ_TYPE_SNOOP_DIS)); - RS690_WRITE_MCIND(RS690_MC_AGP_BASE, - (unsigned int)dev_priv->gart_vm_start); + radeon_write_agp_base(dev_priv, dev_priv->gart_vm_start); dev_priv->gart_size = 32*1024*1024; temp = (((dev_priv->gart_vm_start - 1 + dev_priv->gart_size) & 0xffff0000) | (dev_priv->gart_vm_start >> 16)); - RS690_WRITE_MCIND(RS690_MC_AGP_LOCATION, temp); + radeon_write_agp_location(dev_priv, temp); - temp = RS690_READ_MCIND(dev_priv, RS690_MC_AGP_SIZE); - RS690_WRITE_MCIND(RS690_MC_AGP_SIZE, - RS690_MC_GART_EN | RS690_MC_AGP_SIZE_32MB); + temp = IGP_READ_MCIND(dev_priv, RS480_AGP_ADDRESS_SPACE_SIZE); + IGP_WRITE_MCIND(RS480_AGP_ADDRESS_SPACE_SIZE, (RS480_GART_EN | + RS480_VA_SIZE_32MB)); do { - temp = RS690_READ_MCIND(dev_priv, RS690_MC_GART_CACHE_CNTL); - if ((temp & RS690_MC_GART_CLEAR_STATUS) == - RS690_MC_GART_CLEAR_DONE) + temp = IGP_READ_MCIND(dev_priv, RS480_GART_CACHE_CNTRL); + if ((temp & RS480_GART_CACHE_INVALIDATE) == 0) break; DRM_UDELAY(1); } while (1); - RS690_WRITE_MCIND(RS690_MC_GART_CACHE_CNTL, - RS690_MC_GART_CC_CLEAR); + IGP_WRITE_MCIND(RS480_GART_CACHE_CNTRL, + RS480_GART_CACHE_INVALIDATE); + do { - temp = RS690_READ_MCIND(dev_priv, RS690_MC_GART_CACHE_CNTL); - if ((temp & RS690_MC_GART_CLEAR_STATUS) == - RS690_MC_GART_CLEAR_DONE) + temp = IGP_READ_MCIND(dev_priv, RS480_GART_CACHE_CNTRL); + if ((temp & RS480_GART_CACHE_INVALIDATE) == 0) break; DRM_UDELAY(1); } while (1); - RS690_WRITE_MCIND(RS690_MC_GART_CACHE_CNTL, - RS690_MC_GART_CC_NO_CHANGE); + IGP_WRITE_MCIND(RS480_GART_CACHE_CNTRL, 0); } else { - RS690_WRITE_MCIND(RS690_MC_AGP_SIZE, RS690_MC_GART_DIS); + IGP_WRITE_MCIND(RS480_AGP_ADDRESS_SPACE_SIZE, 0); } } @@ -1472,12 +813,8 @@ static void radeon_set_pcigart(drm_radeon_private_t * dev_priv, int on) { u32 tmp; - if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) { - radeon_set_rs690gart(dev_priv, on); - return; - } - - if (dev_priv->flags & RADEON_IS_IGPGART) { + if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) || + (dev_priv->flags & RADEON_IS_IGPGART)) { radeon_set_igpgart(dev_priv, on); return; } @@ -1951,6 +1288,7 @@ static int radeon_do_resume_cp(struct drm_device * dev) radeon_cp_init_ring_buffer(dev, dev_priv); radeon_do_engine_reset(dev); + radeon_enable_interrupt(dev); DRM_DEBUG("radeon_do_resume_cp() complete\n"); diff --git a/drivers/char/drm/radeon_drm.h b/drivers/char/drm/radeon_drm.h index aab82e121e0..73ff51f1231 100644 --- a/drivers/char/drm/radeon_drm.h +++ b/drivers/char/drm/radeon_drm.h @@ -240,6 +240,7 @@ typedef union { # define R300_NEW_WAIT_2D_2D_CLEAN_3D_3D_CLEAN 0x8 #define R300_CMD_SCRATCH 8 +#define R300_CMD_R500FP 9 typedef union { unsigned int u; @@ -268,6 +269,9 @@ typedef union { struct { unsigned char cmd_type, reg, n_bufs, flags; } scratch; + struct { + unsigned char cmd_type, count, adrlo, adrhi_flags; + } r500fp; } drm_r300_cmd_header_t; #define RADEON_FRONT 0x1 @@ -278,6 +282,9 @@ typedef union { #define RADEON_USE_HIERZ 0x40000000 #define RADEON_USE_COMP_ZBUF 0x20000000 +#define R500FP_CONSTANT_TYPE (1 << 1) +#define R500FP_CONSTANT_CLAMP (1 << 2) + /* Primitive types */ #define RADEON_POINTS 0x1 @@ -669,6 +676,7 @@ typedef struct drm_radeon_indirect { #define RADEON_PARAM_CARD_TYPE 12 #define RADEON_PARAM_VBLANK_CRTC 13 /* VBLANK CRTC */ #define RADEON_PARAM_FB_LOCATION 14 /* FB location */ +#define RADEON_PARAM_NUM_GB_PIPES 15 /* num GB pipes */ typedef struct drm_radeon_getparam { int param; diff --git a/drivers/char/drm/radeon_drv.h b/drivers/char/drm/radeon_drv.h index 173ae620223..3f0eca957aa 100644 --- a/drivers/char/drm/radeon_drv.h +++ b/drivers/char/drm/radeon_drv.h @@ -38,7 +38,7 @@ #define DRIVER_NAME "radeon" #define DRIVER_DESC "ATI Radeon" -#define DRIVER_DATE "20060524" +#define DRIVER_DATE "20080528" /* Interface history: * @@ -98,9 +98,10 @@ * 1.26- Add support for variable size PCI(E) gart aperture * 1.27- Add support for IGP GART * 1.28- Add support for VBL on CRTC2 + * 1.29- R500 3D cmd buffer support */ #define DRIVER_MAJOR 1 -#define DRIVER_MINOR 28 +#define DRIVER_MINOR 29 #define DRIVER_PATCHLEVEL 0 /* @@ -122,7 +123,7 @@ enum radeon_family { CHIP_RV380, CHIP_R420, CHIP_RV410, - CHIP_RS400, + CHIP_RS480, CHIP_RS690, CHIP_RV515, CHIP_R520, @@ -294,6 +295,7 @@ typedef struct drm_radeon_private { int vblank_crtc; uint32_t irq_enable_reg; int irq_enabled; + uint32_t r500_disp_irq_reg; struct radeon_surface surfaces[RADEON_MAX_SURFACES]; struct radeon_virt_surface virt_surfaces[2 * RADEON_MAX_SURFACES]; @@ -307,6 +309,8 @@ typedef struct drm_radeon_private { /* starting from here on, data is preserved accross an open */ uint32_t flags; /* see radeon_chip_flags */ unsigned long fb_aper_offset; + + int num_gb_pipes; } drm_radeon_private_t; typedef struct drm_radeon_buf_priv { @@ -382,6 +386,7 @@ extern irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS); extern void radeon_driver_irq_preinstall(struct drm_device * dev); extern void radeon_driver_irq_postinstall(struct drm_device * dev); extern void radeon_driver_irq_uninstall(struct drm_device * dev); +extern void radeon_enable_interrupt(struct drm_device *dev); extern int radeon_vblank_crtc_get(struct drm_device *dev); extern int radeon_vblank_crtc_set(struct drm_device *dev, int64_t value); @@ -444,13 +449,13 @@ extern int r300_do_cp_cmdbuf(struct drm_device * dev, #define RADEON_PCIE_DATA 0x0034 #define RADEON_PCIE_TX_GART_CNTL 0x10 # define RADEON_PCIE_TX_GART_EN (1 << 0) -# define RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_PASS_THRU (0<<1) -# define RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_CLAMP_LO (1<<1) -# define RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_DISCARD (3<<1) -# define RADEON_PCIE_TX_GART_MODE_32_128_CACHE (0<<3) -# define RADEON_PCIE_TX_GART_MODE_8_4_128_CACHE (1<<3) -# define RADEON_PCIE_TX_GART_CHK_RW_VALID_EN (1<<5) -# define RADEON_PCIE_TX_GART_INVALIDATE_TLB (1<<8) +# define RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_PASS_THRU (0 << 1) +# define RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_CLAMP_LO (1 << 1) +# define RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_DISCARD (3 << 1) +# define RADEON_PCIE_TX_GART_MODE_32_128_CACHE (0 << 3) +# define RADEON_PCIE_TX_GART_MODE_8_4_128_CACHE (1 << 3) +# define RADEON_PCIE_TX_GART_CHK_RW_VALID_EN (1 << 5) +# define RADEON_PCIE_TX_GART_INVALIDATE_TLB (1 << 8) #define RADEON_PCIE_TX_DISCARD_RD_ADDR_LO 0x11 #define RADEON_PCIE_TX_DISCARD_RD_ADDR_HI 0x12 #define RADEON_PCIE_TX_GART_BASE 0x13 @@ -459,14 +464,9 @@ extern int r300_do_cp_cmdbuf(struct drm_device * dev, #define RADEON_PCIE_TX_GART_END_LO 0x16 #define RADEON_PCIE_TX_GART_END_HI 0x17 -#define RADEON_IGPGART_INDEX 0x168 -#define RADEON_IGPGART_DATA 0x16c -#define RADEON_IGPGART_UNK_18 0x18 -#define RADEON_IGPGART_CTRL 0x2b -#define RADEON_IGPGART_BASE_ADDR 0x2c -#define RADEON_IGPGART_FLUSH 0x2e -#define RADEON_IGPGART_ENABLE 0x38 -#define RADEON_IGPGART_UNK_39 0x39 +#define RS480_NB_MC_INDEX 0x168 +# define RS480_NB_MC_IND_WR_EN (1 << 8) +#define RS480_NB_MC_DATA 0x16c #define RS690_MC_INDEX 0x78 # define RS690_MC_INDEX_MASK 0x1ff @@ -474,45 +474,91 @@ extern int r300_do_cp_cmdbuf(struct drm_device * dev, # define RS690_MC_INDEX_WR_ACK 0x7f #define RS690_MC_DATA 0x7c -#define RS690_MC_MISC_CNTL 0x18 -#define RS690_MC_GART_FEATURE_ID 0x2b -#define RS690_MC_GART_BASE 0x2c -#define RS690_MC_GART_CACHE_CNTL 0x2e -# define RS690_MC_GART_CC_NO_CHANGE 0x0 -# define RS690_MC_GART_CC_CLEAR 0x1 -# define RS690_MC_GART_CLEAR_STATUS (1 << 1) -# define RS690_MC_GART_CLEAR_DONE (0 << 1) -# define RS690_MC_GART_CLEAR_PENDING (1 << 1) -#define RS690_MC_AGP_SIZE 0x38 -# define RS690_MC_GART_DIS 0x0 -# define RS690_MC_GART_EN 0x1 -# define RS690_MC_AGP_SIZE_32MB (0 << 1) -# define RS690_MC_AGP_SIZE_64MB (1 << 1) -# define RS690_MC_AGP_SIZE_128MB (2 << 1) -# define RS690_MC_AGP_SIZE_256MB (3 << 1) -# define RS690_MC_AGP_SIZE_512MB (4 << 1) -# define RS690_MC_AGP_SIZE_1GB (5 << 1) -# define RS690_MC_AGP_SIZE_2GB (6 << 1) -#define RS690_MC_AGP_MODE_CONTROL 0x39 +/* MC indirect registers */ +#define RS480_MC_MISC_CNTL 0x18 +# define RS480_DISABLE_GTW (1 << 1) +/* switch between MCIND GART and MM GART registers. 0 = mmgart, 1 = mcind gart */ +# define RS480_GART_INDEX_REG_EN (1 << 12) +# define RS690_BLOCK_GFX_D3_EN (1 << 14) +#define RS480_K8_FB_LOCATION 0x1e +#define RS480_GART_FEATURE_ID 0x2b +# define RS480_HANG_EN (1 << 11) +# define RS480_TLB_ENABLE (1 << 18) +# define RS480_P2P_ENABLE (1 << 19) +# define RS480_GTW_LAC_EN (1 << 25) +# define RS480_2LEVEL_GART (0 << 30) +# define RS480_1LEVEL_GART (1 << 30) +# define RS480_PDC_EN (1 << 31) +#define RS480_GART_BASE 0x2c +#define RS480_GART_CACHE_CNTRL 0x2e +# define RS480_GART_CACHE_INVALIDATE (1 << 0) /* wait for it to clear */ +#define RS480_AGP_ADDRESS_SPACE_SIZE 0x38 +# define RS480_GART_EN (1 << 0) +# define RS480_VA_SIZE_32MB (0 << 1) +# define RS480_VA_SIZE_64MB (1 << 1) +# define RS480_VA_SIZE_128MB (2 << 1) +# define RS480_VA_SIZE_256MB (3 << 1) +# define RS480_VA_SIZE_512MB (4 << 1) +# define RS480_VA_SIZE_1GB (5 << 1) +# define RS480_VA_SIZE_2GB (6 << 1) +#define RS480_AGP_MODE_CNTL 0x39 +# define RS480_POST_GART_Q_SIZE (1 << 18) +# define RS480_NONGART_SNOOP (1 << 19) +# define RS480_AGP_RD_BUF_SIZE (1 << 20) +# define RS480_REQ_TYPE_SNOOP_SHIFT 22 +# define RS480_REQ_TYPE_SNOOP_MASK 0x3 +# define RS480_REQ_TYPE_SNOOP_DIS (1 << 24) +#define RS480_MC_MISC_UMA_CNTL 0x5f +#define RS480_MC_MCLK_CNTL 0x7a +#define RS480_MC_UMA_DUALCH_CNTL 0x86 + #define RS690_MC_FB_LOCATION 0x100 #define RS690_MC_AGP_LOCATION 0x101 #define RS690_MC_AGP_BASE 0x102 +#define RS690_MC_AGP_BASE_2 0x103 #define R520_MC_IND_INDEX 0x70 -#define R520_MC_IND_WR_EN (1<<24) +#define R520_MC_IND_WR_EN (1 << 24) #define R520_MC_IND_DATA 0x74 #define RV515_MC_FB_LOCATION 0x01 #define RV515_MC_AGP_LOCATION 0x02 +#define RV515_MC_AGP_BASE 0x03 +#define RV515_MC_AGP_BASE_2 0x04 #define R520_MC_FB_LOCATION 0x04 #define R520_MC_AGP_LOCATION 0x05 +#define R520_MC_AGP_BASE 0x06 +#define R520_MC_AGP_BASE_2 0x07 #define RADEON_MPP_TB_CONFIG 0x01c0 #define RADEON_MEM_CNTL 0x0140 #define RADEON_MEM_SDRAM_MODE_REG 0x0158 +#define RADEON_AGP_BASE_2 0x015c /* r200+ only */ +#define RS480_AGP_BASE_2 0x0164 #define RADEON_AGP_BASE 0x0170 +/* pipe config regs */ +#define R400_GB_PIPE_SELECT 0x402c +#define R500_DYN_SCLK_PWMEM_PIPE 0x000d /* PLL */ +#define R500_SU_REG_DEST 0x42c8 +#define R300_GB_TILE_CONFIG 0x4018 +# define R300_ENABLE_TILING (1 << 0) +# define R300_PIPE_COUNT_RV350 (0 << 1) +# define R300_PIPE_COUNT_R300 (3 << 1) +# define R300_PIPE_COUNT_R420_3P (6 << 1) +# define R300_PIPE_COUNT_R420 (7 << 1) +# define R300_TILE_SIZE_8 (0 << 4) +# define R300_TILE_SIZE_16 (1 << 4) +# define R300_TILE_SIZE_32 (2 << 4) +# define R300_SUBPIXEL_1_12 (0 << 16) +# define R300_SUBPIXEL_1_16 (1 << 16) +#define R300_DST_PIPE_CONFIG 0x170c +# define R300_PIPE_AUTO_CONFIG (1 << 31) +#define R300_RB2D_DSTCACHE_MODE 0x3428 +# define R300_DC_AUTOFLUSH_ENABLE (1 << 8) +# define R300_DC_DC_DISABLE_IGNORE_PE (1 << 17) + #define RADEON_RB3D_COLOROFFSET 0x1c40 #define RADEON_RB3D_COLORPITCH 0x1c48 @@ -616,11 +662,12 @@ extern int r300_do_cp_cmdbuf(struct drm_device * dev, #define RADEON_PP_TXFILTER_1 0x1c6c #define RADEON_PP_TXFILTER_2 0x1c84 -#define RADEON_RB2D_DSTCACHE_CTLSTAT 0x342c -# define RADEON_RB2D_DC_FLUSH (3 << 0) -# define RADEON_RB2D_DC_FREE (3 << 2) -# define RADEON_RB2D_DC_FLUSH_ALL 0xf -# define RADEON_RB2D_DC_BUSY (1 << 31) +#define R300_RB2D_DSTCACHE_CTLSTAT 0x342c /* use R300_DSTCACHE_CTLSTAT */ +#define R300_DSTCACHE_CTLSTAT 0x1714 +# define R300_RB2D_DC_FLUSH (3 << 0) +# define R300_RB2D_DC_FREE (3 << 2) +# define R300_RB2D_DC_FLUSH_ALL 0xf +# define R300_RB2D_DC_BUSY (1 << 31) #define RADEON_RB3D_CNTL 0x1c3c # define RADEON_ALPHA_BLEND_ENABLE (1 << 0) # define RADEON_PLANE_MASK_ENABLE (1 << 1) @@ -643,11 +690,18 @@ extern int r300_do_cp_cmdbuf(struct drm_device * dev, # define RADEON_RB3D_ZC_FREE (1 << 2) # define RADEON_RB3D_ZC_FLUSH_ALL 0x5 # define RADEON_RB3D_ZC_BUSY (1 << 31) +#define R300_ZB_ZCACHE_CTLSTAT 0x4f18 +# define R300_ZC_FLUSH (1 << 0) +# define R300_ZC_FREE (1 << 1) +# define R300_ZC_FLUSH_ALL 0x3 +# define R300_ZC_BUSY (1 << 31) #define RADEON_RB3D_DSTCACHE_CTLSTAT 0x325c # define RADEON_RB3D_DC_FLUSH (3 << 0) # define RADEON_RB3D_DC_FREE (3 << 2) # define RADEON_RB3D_DC_FLUSH_ALL 0xf # define RADEON_RB3D_DC_BUSY (1 << 31) +#define R300_RB3D_DSTCACHE_CTLSTAT 0x4e4c +# define R300_RB3D_DC_FINISH (1 << 4) #define RADEON_RB3D_ZSTENCILCNTL 0x1c2c # define RADEON_Z_TEST_MASK (7 << 4) # define RADEON_Z_TEST_ALWAYS (7 << 4) @@ -1057,6 +1111,31 @@ extern int r300_do_cp_cmdbuf(struct drm_device * dev, #define R200_VAP_PVS_CNTL_1 0x22D0 +#define R500_D1CRTC_STATUS 0x609c +#define R500_D2CRTC_STATUS 0x689c +#define R500_CRTC_V_BLANK (1<<0) + +#define R500_D1CRTC_FRAME_COUNT 0x60a4 +#define R500_D2CRTC_FRAME_COUNT 0x68a4 + +#define R500_D1MODE_V_COUNTER 0x6530 +#define R500_D2MODE_V_COUNTER 0x6d30 + +#define R500_D1MODE_VBLANK_STATUS 0x6534 +#define R500_D2MODE_VBLANK_STATUS 0x6d34 +#define R500_VBLANK_OCCURED (1<<0) +#define R500_VBLANK_ACK (1<<4) +#define R500_VBLANK_STAT (1<<12) +#define R500_VBLANK_INT (1<<16) + +#define R500_DxMODE_INT_MASK 0x6540 +#define R500_D1MODE_INT_MASK (1<<0) +#define R500_D2MODE_INT_MASK (1<<8) + +#define R500_DISP_INTERRUPT_STATUS 0x7edc +#define R500_D1_VBLANK_INTERRUPT (1 << 4) +#define R500_D2_VBLANK_INTERRUPT (1 << 5) + /* Constants */ #define RADEON_MAX_USEC_TIMEOUT 100000 /* 100 ms */ @@ -1078,42 +1157,50 @@ extern int r300_do_cp_cmdbuf(struct drm_device * dev, #define RADEON_READ8(reg) DRM_READ8( dev_priv->mmio, (reg) ) #define RADEON_WRITE8(reg,val) DRM_WRITE8( dev_priv->mmio, (reg), (val) ) -#define RADEON_WRITE_PLL( addr, val ) \ +#define RADEON_WRITE_PLL(addr, val) \ do { \ - RADEON_WRITE8( RADEON_CLOCK_CNTL_INDEX, \ + RADEON_WRITE8(RADEON_CLOCK_CNTL_INDEX, \ ((addr) & 0x1f) | RADEON_PLL_WR_EN ); \ - RADEON_WRITE( RADEON_CLOCK_CNTL_DATA, (val) ); \ + RADEON_WRITE(RADEON_CLOCK_CNTL_DATA, (val)); \ } while (0) -#define RADEON_WRITE_IGPGART( addr, val ) \ +#define RADEON_WRITE_PCIE(addr, val) \ do { \ - RADEON_WRITE( RADEON_IGPGART_INDEX, \ - ((addr) & 0x7f) | (1 << 8)); \ - RADEON_WRITE( RADEON_IGPGART_DATA, (val) ); \ - RADEON_WRITE( RADEON_IGPGART_INDEX, 0x7f ); \ + RADEON_WRITE8(RADEON_PCIE_INDEX, \ + ((addr) & 0xff)); \ + RADEON_WRITE(RADEON_PCIE_DATA, (val)); \ } while (0) -#define RADEON_WRITE_PCIE( addr, val ) \ -do { \ - RADEON_WRITE8( RADEON_PCIE_INDEX, \ - ((addr) & 0xff)); \ - RADEON_WRITE( RADEON_PCIE_DATA, (val) ); \ +#define R500_WRITE_MCIND(addr, val) \ +do { \ + RADEON_WRITE(R520_MC_IND_INDEX, 0xff0000 | ((addr) & 0xff)); \ + RADEON_WRITE(R520_MC_IND_DATA, (val)); \ + RADEON_WRITE(R520_MC_IND_INDEX, 0); \ } while (0) -#define RADEON_WRITE_MCIND( addr, val ) \ - do { \ - RADEON_WRITE(R520_MC_IND_INDEX, 0xff0000 | ((addr) & 0xff)); \ - RADEON_WRITE(R520_MC_IND_DATA, (val)); \ - RADEON_WRITE(R520_MC_IND_INDEX, 0); \ - } while (0) +#define RS480_WRITE_MCIND(addr, val) \ +do { \ + RADEON_WRITE(RS480_NB_MC_INDEX, \ + ((addr) & 0xff) | RS480_NB_MC_IND_WR_EN); \ + RADEON_WRITE(RS480_NB_MC_DATA, (val)); \ + RADEON_WRITE(RS480_NB_MC_INDEX, 0xff); \ +} while (0) -#define RS690_WRITE_MCIND( addr, val ) \ +#define RS690_WRITE_MCIND(addr, val) \ do { \ RADEON_WRITE(RS690_MC_INDEX, RS690_MC_INDEX_WR_EN | ((addr) & RS690_MC_INDEX_MASK)); \ RADEON_WRITE(RS690_MC_DATA, val); \ RADEON_WRITE(RS690_MC_INDEX, RS690_MC_INDEX_WR_ACK); \ } while (0) +#define IGP_WRITE_MCIND(addr, val) \ +do { \ + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) \ + RS690_WRITE_MCIND(addr, val); \ + else \ + RS480_WRITE_MCIND(addr, val); \ +} while (0) + #define CP_PACKET0( reg, n ) \ (RADEON_CP_PACKET0 | ((n) << 16) | ((reg) >> 2)) #define CP_PACKET0_TABLE( reg, n ) \ @@ -1154,23 +1241,43 @@ do { \ } while (0) #define RADEON_FLUSH_CACHE() do { \ - OUT_RING( CP_PACKET0( RADEON_RB3D_DSTCACHE_CTLSTAT, 0 ) ); \ - OUT_RING( RADEON_RB3D_DC_FLUSH ); \ + if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV280) { \ + OUT_RING(CP_PACKET0(RADEON_RB3D_DSTCACHE_CTLSTAT, 0)); \ + OUT_RING(RADEON_RB3D_DC_FLUSH); \ + } else { \ + OUT_RING(CP_PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); \ + OUT_RING(RADEON_RB3D_DC_FLUSH); \ + } \ } while (0) #define RADEON_PURGE_CACHE() do { \ - OUT_RING( CP_PACKET0( RADEON_RB3D_DSTCACHE_CTLSTAT, 0 ) ); \ - OUT_RING( RADEON_RB3D_DC_FLUSH_ALL ); \ + if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV280) { \ + OUT_RING(CP_PACKET0(RADEON_RB3D_DSTCACHE_CTLSTAT, 0)); \ + OUT_RING(RADEON_RB3D_DC_FLUSH_ALL); \ + } else { \ + OUT_RING(CP_PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); \ + OUT_RING(RADEON_RB3D_DC_FLUSH_ALL); \ + } \ } while (0) #define RADEON_FLUSH_ZCACHE() do { \ - OUT_RING( CP_PACKET0( RADEON_RB3D_ZCACHE_CTLSTAT, 0 ) ); \ - OUT_RING( RADEON_RB3D_ZC_FLUSH ); \ + if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV280) { \ + OUT_RING(CP_PACKET0(RADEON_RB3D_ZCACHE_CTLSTAT, 0)); \ + OUT_RING(RADEON_RB3D_ZC_FLUSH); \ + } else { \ + OUT_RING(CP_PACKET0(R300_ZB_ZCACHE_CTLSTAT, 0)); \ + OUT_RING(R300_ZC_FLUSH); \ + } \ } while (0) #define RADEON_PURGE_ZCACHE() do { \ - OUT_RING( CP_PACKET0( RADEON_RB3D_ZCACHE_CTLSTAT, 0 ) ); \ - OUT_RING( RADEON_RB3D_ZC_FLUSH_ALL ); \ + if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV280) { \ + OUT_RING(CP_PACKET0(RADEON_RB3D_ZCACHE_CTLSTAT, 0)); \ + OUT_RING(RADEON_RB3D_ZC_FLUSH_ALL); \ + } else { \ + OUT_RING(CP_PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); \ + OUT_RING(R300_ZC_FLUSH_ALL); \ + } \ } while (0) /* ================================================================ diff --git a/drivers/char/drm/radeon_irq.c b/drivers/char/drm/radeon_irq.c index 009af3814b6..ee40d197deb 100644 --- a/drivers/char/drm/radeon_irq.c +++ b/drivers/char/drm/radeon_irq.c @@ -234,7 +234,7 @@ int radeon_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_pr return radeon_wait_irq(dev, irqwait->irq_seq); } -static void radeon_enable_interrupt(struct drm_device *dev) +void radeon_enable_interrupt(struct drm_device *dev) { drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private; diff --git a/drivers/char/drm/radeon_microcode.h b/drivers/char/drm/radeon_microcode.h new file mode 100644 index 00000000000..a348c9e7db1 --- /dev/null +++ b/drivers/char/drm/radeon_microcode.h @@ -0,0 +1,1844 @@ +/* + * Copyright 2007 Advanced Micro Devices, Inc. + * 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, sublicense, + * 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 NONINFRINGEMENT. IN NO EVENT SHALL + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) 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. + * + */ + +#ifndef RADEON_MICROCODE_H +#define RADEON_MICROCODE_H + +/* production radeon ucode r1xx-r6xx */ +static const u32 R100_cp_microcode[][2] = { + { 0x21007000, 0000000000 }, + { 0x20007000, 0000000000 }, + { 0x000000b4, 0x00000004 }, + { 0x000000b8, 0x00000004 }, + { 0x6f5b4d4c, 0000000000 }, + { 0x4c4c427f, 0000000000 }, + { 0x5b568a92, 0000000000 }, + { 0x4ca09c6d, 0000000000 }, + { 0xad4c4c4c, 0000000000 }, + { 0x4ce1af3d, 0000000000 }, + { 0xd8afafaf, 0000000000 }, + { 0xd64c4cdc, 0000000000 }, + { 0x4cd10d10, 0000000000 }, + { 0x000f0000, 0x00000016 }, + { 0x362f242d, 0000000000 }, + { 0x00000012, 0x00000004 }, + { 0x000f0000, 0x00000016 }, + { 0x362f282d, 0000000000 }, + { 0x000380e7, 0x00000002 }, + { 0x04002c97, 0x00000002 }, + { 0x000f0001, 0x00000016 }, + { 0x333a3730, 0000000000 }, + { 0x000077ef, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x00000021, 0x0000001a }, + { 0x00004000, 0x0000001e }, + { 0x00061000, 0x00000002 }, + { 0x00000021, 0x0000001a }, + { 0x00004000, 0x0000001e }, + { 0x00061000, 0x00000002 }, + { 0x00000021, 0x0000001a }, + { 0x00004000, 0x0000001e }, + { 0x00000017, 0x00000004 }, + { 0x0003802b, 0x00000002 }, + { 0x040067e0, 0x00000002 }, + { 0x00000017, 0x00000004 }, + { 0x000077e0, 0x00000002 }, + { 0x00065000, 0x00000002 }, + { 0x000037e1, 0x00000002 }, + { 0x040067e1, 0x00000006 }, + { 0x000077e0, 0x00000002 }, + { 0x000077e1, 0x00000002 }, + { 0x000077e1, 0x00000006 }, + { 0xffffffff, 0000000000 }, + { 0x10000000, 0000000000 }, + { 0x0003802b, 0x00000002 }, + { 0x040067e0, 0x00000006 }, + { 0x00007675, 0x00000002 }, + { 0x00007676, 0x00000002 }, + { 0x00007677, 0x00000002 }, + { 0x00007678, 0x00000006 }, + { 0x0003802c, 0x00000002 }, + { 0x04002676, 0x00000002 }, + { 0x00007677, 0x00000002 }, + { 0x00007678, 0x00000006 }, + { 0x0000002f, 0x00000018 }, + { 0x0000002f, 0x00000018 }, + { 0000000000, 0x00000006 }, + { 0x00000030, 0x00000018 }, + { 0x00000030, 0x00000018 }, + { 0000000000, 0x00000006 }, + { 0x01605000, 0x00000002 }, + { 0x00065000, 0x00000002 }, + { 0x00098000, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x64c0603e, 0x00000004 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00080000, 0x00000016 }, + { 0000000000, 0000000000 }, + { 0x0400251d, 0x00000002 }, + { 0x00007580, 0x00000002 }, + { 0x00067581, 0x00000002 }, + { 0x04002580, 0x00000002 }, + { 0x00067581, 0x00000002 }, + { 0x00000049, 0x00000004 }, + { 0x00005000, 0000000000 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x0000750e, 0x00000002 }, + { 0x00019000, 0x00000002 }, + { 0x00011055, 0x00000014 }, + { 0x00000055, 0x00000012 }, + { 0x0400250f, 0x00000002 }, + { 0x0000504f, 0x00000004 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00007565, 0x00000002 }, + { 0x00007566, 0x00000002 }, + { 0x00000058, 0x00000004 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x01e655b4, 0x00000002 }, + { 0x4401b0e4, 0x00000002 }, + { 0x01c110e4, 0x00000002 }, + { 0x26667066, 0x00000018 }, + { 0x040c2565, 0x00000002 }, + { 0x00000066, 0x00000018 }, + { 0x04002564, 0x00000002 }, + { 0x00007566, 0x00000002 }, + { 0x0000005d, 0x00000004 }, + { 0x00401069, 0x00000008 }, + { 0x00101000, 0x00000002 }, + { 0x000d80ff, 0x00000002 }, + { 0x0080006c, 0x00000008 }, + { 0x000f9000, 0x00000002 }, + { 0x000e00ff, 0x00000002 }, + { 0000000000, 0x00000006 }, + { 0x0000008f, 0x00000018 }, + { 0x0000005b, 0x00000004 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00007576, 0x00000002 }, + { 0x00065000, 0x00000002 }, + { 0x00009000, 0x00000002 }, + { 0x00041000, 0x00000002 }, + { 0x0c00350e, 0x00000002 }, + { 0x00049000, 0x00000002 }, + { 0x00051000, 0x00000002 }, + { 0x01e785f8, 0x00000002 }, + { 0x00200000, 0x00000002 }, + { 0x0060007e, 0x0000000c }, + { 0x00007563, 0x00000002 }, + { 0x006075f0, 0x00000021 }, + { 0x20007073, 0x00000004 }, + { 0x00005073, 0x00000004 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00007576, 0x00000002 }, + { 0x00007577, 0x00000002 }, + { 0x0000750e, 0x00000002 }, + { 0x0000750f, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00600083, 0x0000000c }, + { 0x006075f0, 0x00000021 }, + { 0x000075f8, 0x00000002 }, + { 0x00000083, 0x00000004 }, + { 0x000a750e, 0x00000002 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x0020750f, 0x00000002 }, + { 0x00600086, 0x00000004 }, + { 0x00007570, 0x00000002 }, + { 0x00007571, 0x00000002 }, + { 0x00007572, 0x00000006 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00005000, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00007568, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x00000095, 0x0000000c }, + { 0x00058000, 0x00000002 }, + { 0x0c607562, 0x00000002 }, + { 0x00000097, 0x00000004 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00600096, 0x00000004 }, + { 0x400070e5, 0000000000 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x000380e5, 0x00000002 }, + { 0x000000a8, 0x0000001c }, + { 0x000650aa, 0x00000018 }, + { 0x040025bb, 0x00000002 }, + { 0x000610ab, 0x00000018 }, + { 0x040075bc, 0000000000 }, + { 0x000075bb, 0x00000002 }, + { 0x000075bc, 0000000000 }, + { 0x00090000, 0x00000006 }, + { 0x00090000, 0x00000002 }, + { 0x000d8002, 0x00000006 }, + { 0x00007832, 0x00000002 }, + { 0x00005000, 0x00000002 }, + { 0x000380e7, 0x00000002 }, + { 0x04002c97, 0x00000002 }, + { 0x00007820, 0x00000002 }, + { 0x00007821, 0x00000002 }, + { 0x00007800, 0000000000 }, + { 0x01200000, 0x00000002 }, + { 0x20077000, 0x00000002 }, + { 0x01200000, 0x00000002 }, + { 0x20007000, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x0120751b, 0x00000002 }, + { 0x8040750a, 0x00000002 }, + { 0x8040750b, 0x00000002 }, + { 0x00110000, 0x00000002 }, + { 0x000380e5, 0x00000002 }, + { 0x000000c6, 0x0000001c }, + { 0x000610ab, 0x00000018 }, + { 0x844075bd, 0x00000002 }, + { 0x000610aa, 0x00000018 }, + { 0x840075bb, 0x00000002 }, + { 0x000610ab, 0x00000018 }, + { 0x844075bc, 0x00000002 }, + { 0x000000c9, 0x00000004 }, + { 0x804075bd, 0x00000002 }, + { 0x800075bb, 0x00000002 }, + { 0x804075bc, 0x00000002 }, + { 0x00108000, 0x00000002 }, + { 0x01400000, 0x00000002 }, + { 0x006000cd, 0x0000000c }, + { 0x20c07000, 0x00000020 }, + { 0x000000cf, 0x00000012 }, + { 0x00800000, 0x00000006 }, + { 0x0080751d, 0x00000006 }, + { 0000000000, 0000000000 }, + { 0x0000775c, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00661000, 0x00000002 }, + { 0x0460275d, 0x00000020 }, + { 0x00004000, 0000000000 }, + { 0x01e00830, 0x00000002 }, + { 0x21007000, 0000000000 }, + { 0x6464614d, 0000000000 }, + { 0x69687420, 0000000000 }, + { 0x00000073, 0000000000 }, + { 0000000000, 0000000000 }, + { 0x00005000, 0x00000002 }, + { 0x000380d0, 0x00000002 }, + { 0x040025e0, 0x00000002 }, + { 0x000075e1, 0000000000 }, + { 0x00000001, 0000000000 }, + { 0x000380e0, 0x00000002 }, + { 0x04002394, 0x00000002 }, + { 0x00005000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0x00000008, 0000000000 }, + { 0x00000004, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, +}; + +static const u32 R200_cp_microcode[][2] = { + { 0x21007000, 0000000000 }, + { 0x20007000, 0000000000 }, + { 0x000000bf, 0x00000004 }, + { 0x000000c3, 0x00000004 }, + { 0x7a685e5d, 0000000000 }, + { 0x5d5d5588, 0000000000 }, + { 0x68659197, 0000000000 }, + { 0x5da19f78, 0000000000 }, + { 0x5d5d5d5d, 0000000000 }, + { 0x5dee5d50, 0000000000 }, + { 0xf2acacac, 0000000000 }, + { 0xe75df9e9, 0000000000 }, + { 0xb1dd0e11, 0000000000 }, + { 0xe2afafaf, 0000000000 }, + { 0x000f0000, 0x00000016 }, + { 0x452f232d, 0000000000 }, + { 0x00000013, 0x00000004 }, + { 0x000f0000, 0x00000016 }, + { 0x452f272d, 0000000000 }, + { 0x000f0001, 0x00000016 }, + { 0x3e4d4a37, 0000000000 }, + { 0x000077ef, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x00000020, 0x0000001a }, + { 0x00004000, 0x0000001e }, + { 0x00061000, 0x00000002 }, + { 0x00000020, 0x0000001a }, + { 0x00004000, 0x0000001e }, + { 0x00061000, 0x00000002 }, + { 0x00000020, 0x0000001a }, + { 0x00004000, 0x0000001e }, + { 0x00000016, 0x00000004 }, + { 0x0003802a, 0x00000002 }, + { 0x040067e0, 0x00000002 }, + { 0x00000016, 0x00000004 }, + { 0x000077e0, 0x00000002 }, + { 0x00065000, 0x00000002 }, + { 0x000037e1, 0x00000002 }, + { 0x040067e1, 0x00000006 }, + { 0x000077e0, 0x00000002 }, + { 0x000077e1, 0x00000002 }, + { 0x000077e1, 0x00000006 }, + { 0xffffffff, 0000000000 }, + { 0x10000000, 0000000000 }, + { 0x07f007f0, 0000000000 }, + { 0x0003802a, 0x00000002 }, + { 0x040067e0, 0x00000006 }, + { 0x0003802c, 0x00000002 }, + { 0x04002741, 0x00000002 }, + { 0x04002741, 0x00000002 }, + { 0x04002743, 0x00000002 }, + { 0x00007675, 0x00000002 }, + { 0x00007676, 0x00000002 }, + { 0x00007677, 0x00000002 }, + { 0x00007678, 0x00000006 }, + { 0x0003802c, 0x00000002 }, + { 0x04002741, 0x00000002 }, + { 0x04002741, 0x00000002 }, + { 0x04002743, 0x00000002 }, + { 0x00007676, 0x00000002 }, + { 0x00007677, 0x00000002 }, + { 0x00007678, 0x00000006 }, + { 0x0003802b, 0x00000002 }, + { 0x04002676, 0x00000002 }, + { 0x00007677, 0x00000002 }, + { 0x0003802c, 0x00000002 }, + { 0x04002741, 0x00000002 }, + { 0x04002743, 0x00000002 }, + { 0x00007678, 0x00000006 }, + { 0x0003802c, 0x00000002 }, + { 0x04002741, 0x00000002 }, + { 0x04002741, 0x00000002 }, + { 0x04002743, 0x00000002 }, + { 0x00007678, 0x00000006 }, + { 0x0000002f, 0x00000018 }, + { 0x0000002f, 0x00000018 }, + { 0000000000, 0x00000006 }, + { 0x00000037, 0x00000018 }, + { 0x00000037, 0x00000018 }, + { 0000000000, 0x00000006 }, + { 0x01605000, 0x00000002 }, + { 0x00065000, 0x00000002 }, + { 0x00098000, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x64c06051, 0x00000004 }, + { 0x00080000, 0x00000016 }, + { 0000000000, 0000000000 }, + { 0x0400251d, 0x00000002 }, + { 0x00007580, 0x00000002 }, + { 0x00067581, 0x00000002 }, + { 0x04002580, 0x00000002 }, + { 0x00067581, 0x00000002 }, + { 0x0000005a, 0x00000004 }, + { 0x00005000, 0000000000 }, + { 0x00061000, 0x00000002 }, + { 0x0000750e, 0x00000002 }, + { 0x00019000, 0x00000002 }, + { 0x00011064, 0x00000014 }, + { 0x00000064, 0x00000012 }, + { 0x0400250f, 0x00000002 }, + { 0x0000505e, 0x00000004 }, + { 0x00007565, 0x00000002 }, + { 0x00007566, 0x00000002 }, + { 0x00000065, 0x00000004 }, + { 0x01e655b4, 0x00000002 }, + { 0x4401b0f0, 0x00000002 }, + { 0x01c110f0, 0x00000002 }, + { 0x26667071, 0x00000018 }, + { 0x040c2565, 0x00000002 }, + { 0x00000071, 0x00000018 }, + { 0x04002564, 0x00000002 }, + { 0x00007566, 0x00000002 }, + { 0x00000068, 0x00000004 }, + { 0x00401074, 0x00000008 }, + { 0x00101000, 0x00000002 }, + { 0x000d80ff, 0x00000002 }, + { 0x00800077, 0x00000008 }, + { 0x000f9000, 0x00000002 }, + { 0x000e00ff, 0x00000002 }, + { 0000000000, 0x00000006 }, + { 0x00000094, 0x00000018 }, + { 0x00000068, 0x00000004 }, + { 0x00007576, 0x00000002 }, + { 0x00065000, 0x00000002 }, + { 0x00009000, 0x00000002 }, + { 0x00041000, 0x00000002 }, + { 0x0c00350e, 0x00000002 }, + { 0x00049000, 0x00000002 }, + { 0x00051000, 0x00000002 }, + { 0x01e785f8, 0x00000002 }, + { 0x00200000, 0x00000002 }, + { 0x00600087, 0x0000000c }, + { 0x00007563, 0x00000002 }, + { 0x006075f0, 0x00000021 }, + { 0x2000707c, 0x00000004 }, + { 0x0000507c, 0x00000004 }, + { 0x00007576, 0x00000002 }, + { 0x00007577, 0x00000002 }, + { 0x0000750e, 0x00000002 }, + { 0x0000750f, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x0060008a, 0x0000000c }, + { 0x006075f0, 0x00000021 }, + { 0x000075f8, 0x00000002 }, + { 0x0000008a, 0x00000004 }, + { 0x000a750e, 0x00000002 }, + { 0x0020750f, 0x00000002 }, + { 0x0060008d, 0x00000004 }, + { 0x00007570, 0x00000002 }, + { 0x00007571, 0x00000002 }, + { 0x00007572, 0x00000006 }, + { 0x00005000, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00007568, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x00000098, 0x0000000c }, + { 0x00058000, 0x00000002 }, + { 0x0c607562, 0x00000002 }, + { 0x0000009a, 0x00000004 }, + { 0x00600099, 0x00000004 }, + { 0x400070f1, 0000000000 }, + { 0x000380f1, 0x00000002 }, + { 0x000000a7, 0x0000001c }, + { 0x000650a9, 0x00000018 }, + { 0x040025bb, 0x00000002 }, + { 0x000610aa, 0x00000018 }, + { 0x040075bc, 0000000000 }, + { 0x000075bb, 0x00000002 }, + { 0x000075bc, 0000000000 }, + { 0x00090000, 0x00000006 }, + { 0x00090000, 0x00000002 }, + { 0x000d8002, 0x00000006 }, + { 0x00005000, 0x00000002 }, + { 0x00007821, 0x00000002 }, + { 0x00007800, 0000000000 }, + { 0x00007821, 0x00000002 }, + { 0x00007800, 0000000000 }, + { 0x01665000, 0x00000002 }, + { 0x000a0000, 0x00000002 }, + { 0x000671cc, 0x00000002 }, + { 0x0286f1cd, 0x00000002 }, + { 0x000000b7, 0x00000010 }, + { 0x21007000, 0000000000 }, + { 0x000000be, 0x0000001c }, + { 0x00065000, 0x00000002 }, + { 0x000a0000, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x000b0000, 0x00000002 }, + { 0x38067000, 0x00000002 }, + { 0x000a00ba, 0x00000004 }, + { 0x20007000, 0000000000 }, + { 0x01200000, 0x00000002 }, + { 0x20077000, 0x00000002 }, + { 0x01200000, 0x00000002 }, + { 0x20007000, 0000000000 }, + { 0x00061000, 0x00000002 }, + { 0x0120751b, 0x00000002 }, + { 0x8040750a, 0x00000002 }, + { 0x8040750b, 0x00000002 }, + { 0x00110000, 0x00000002 }, + { 0x000380f1, 0x00000002 }, + { 0x000000d1, 0x0000001c }, + { 0x000610aa, 0x00000018 }, + { 0x844075bd, 0x00000002 }, + { 0x000610a9, 0x00000018 }, + { 0x840075bb, 0x00000002 }, + { 0x000610aa, 0x00000018 }, + { 0x844075bc, 0x00000002 }, + { 0x000000d4, 0x00000004 }, + { 0x804075bd, 0x00000002 }, + { 0x800075bb, 0x00000002 }, + { 0x804075bc, 0x00000002 }, + { 0x00108000, 0x00000002 }, + { 0x01400000, 0x00000002 }, + { 0x006000d8, 0x0000000c }, + { 0x20c07000, 0x00000020 }, + { 0x000000da, 0x00000012 }, + { 0x00800000, 0x00000006 }, + { 0x0080751d, 0x00000006 }, + { 0x000025bb, 0x00000002 }, + { 0x000040d4, 0x00000004 }, + { 0x0000775c, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00661000, 0x00000002 }, + { 0x0460275d, 0x00000020 }, + { 0x00004000, 0000000000 }, + { 0x00007999, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00661000, 0x00000002 }, + { 0x0460299b, 0x00000020 }, + { 0x00004000, 0000000000 }, + { 0x01e00830, 0x00000002 }, + { 0x21007000, 0000000000 }, + { 0x00005000, 0x00000002 }, + { 0x00038056, 0x00000002 }, + { 0x040025e0, 0x00000002 }, + { 0x000075e1, 0000000000 }, + { 0x00000001, 0000000000 }, + { 0x000380ed, 0x00000002 }, + { 0x04007394, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0x000078c4, 0x00000002 }, + { 0x000078c5, 0x00000002 }, + { 0x000078c6, 0x00000002 }, + { 0x00007924, 0x00000002 }, + { 0x00007925, 0x00000002 }, + { 0x00007926, 0x00000002 }, + { 0x000000f2, 0x00000004 }, + { 0x00007924, 0x00000002 }, + { 0x00007925, 0x00000002 }, + { 0x00007926, 0x00000002 }, + { 0x000000f9, 0x00000004 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, +}; + +static const u32 R300_cp_microcode[][2] = { + { 0x4200e000, 0000000000 }, + { 0x4000e000, 0000000000 }, + { 0x000000ae, 0x00000008 }, + { 0x000000b2, 0x00000008 }, + { 0x67554b4a, 0000000000 }, + { 0x4a4a4475, 0000000000 }, + { 0x55527d83, 0000000000 }, + { 0x4a8c8b65, 0000000000 }, + { 0x4aef4af6, 0000000000 }, + { 0x4ae14a4a, 0000000000 }, + { 0xe4979797, 0000000000 }, + { 0xdb4aebdd, 0000000000 }, + { 0x9ccc4a4a, 0000000000 }, + { 0xd1989898, 0000000000 }, + { 0x4a0f9ad6, 0000000000 }, + { 0x000ca000, 0x00000004 }, + { 0x000d0012, 0x00000038 }, + { 0x0000e8b4, 0x00000004 }, + { 0x000d0014, 0x00000038 }, + { 0x0000e8b6, 0x00000004 }, + { 0x000d0016, 0x00000038 }, + { 0x0000e854, 0x00000004 }, + { 0x000d0018, 0x00000038 }, + { 0x0000e855, 0x00000004 }, + { 0x000d001a, 0x00000038 }, + { 0x0000e856, 0x00000004 }, + { 0x000d001c, 0x00000038 }, + { 0x0000e857, 0x00000004 }, + { 0x000d001e, 0x00000038 }, + { 0x0000e824, 0x00000004 }, + { 0x000d0020, 0x00000038 }, + { 0x0000e825, 0x00000004 }, + { 0x000d0022, 0x00000038 }, + { 0x0000e830, 0x00000004 }, + { 0x000d0024, 0x00000038 }, + { 0x0000f0c0, 0x00000004 }, + { 0x000d0026, 0x00000038 }, + { 0x0000f0c1, 0x00000004 }, + { 0x000d0028, 0x00000038 }, + { 0x0000f041, 0x00000004 }, + { 0x000d002a, 0x00000038 }, + { 0x0000f184, 0x00000004 }, + { 0x000d002c, 0x00000038 }, + { 0x0000f185, 0x00000004 }, + { 0x000d002e, 0x00000038 }, + { 0x0000f186, 0x00000004 }, + { 0x000d0030, 0x00000038 }, + { 0x0000f187, 0x00000004 }, + { 0x000d0032, 0x00000038 }, + { 0x0000f180, 0x00000004 }, + { 0x000d0034, 0x00000038 }, + { 0x0000f393, 0x00000004 }, + { 0x000d0036, 0x00000038 }, + { 0x0000f38a, 0x00000004 }, + { 0x000d0038, 0x00000038 }, + { 0x0000f38e, 0x00000004 }, + { 0x0000e821, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00000043, 0x00000018 }, + { 0x00cce800, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x08004800, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x08004800, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x08004800, 0x00000004 }, + { 0x0000003a, 0x00000008 }, + { 0x0000a000, 0000000000 }, + { 0x2000451d, 0x00000004 }, + { 0x0000e580, 0x00000004 }, + { 0x000ce581, 0x00000004 }, + { 0x08004580, 0x00000004 }, + { 0x000ce581, 0x00000004 }, + { 0x00000047, 0x00000008 }, + { 0x0000a000, 0000000000 }, + { 0x000c2000, 0x00000004 }, + { 0x0000e50e, 0x00000004 }, + { 0x00032000, 0x00000004 }, + { 0x00022051, 0x00000028 }, + { 0x00000051, 0x00000024 }, + { 0x0800450f, 0x00000004 }, + { 0x0000a04b, 0x00000008 }, + { 0x0000e565, 0x00000004 }, + { 0x0000e566, 0x00000004 }, + { 0x00000052, 0x00000008 }, + { 0x03cca5b4, 0x00000004 }, + { 0x05432000, 0x00000004 }, + { 0x00022000, 0x00000004 }, + { 0x4ccce05e, 0x00000030 }, + { 0x08274565, 0x00000004 }, + { 0x0000005e, 0x00000030 }, + { 0x08004564, 0x00000004 }, + { 0x0000e566, 0x00000004 }, + { 0x00000055, 0x00000008 }, + { 0x00802061, 0x00000010 }, + { 0x00202000, 0x00000004 }, + { 0x001b00ff, 0x00000004 }, + { 0x01000064, 0x00000010 }, + { 0x001f2000, 0x00000004 }, + { 0x001c00ff, 0x00000004 }, + { 0000000000, 0x0000000c }, + { 0x00000080, 0x00000030 }, + { 0x00000055, 0x00000008 }, + { 0x0000e576, 0x00000004 }, + { 0x000ca000, 0x00000004 }, + { 0x00012000, 0x00000004 }, + { 0x00082000, 0x00000004 }, + { 0x1800650e, 0x00000004 }, + { 0x00092000, 0x00000004 }, + { 0x000a2000, 0x00000004 }, + { 0x000f0000, 0x00000004 }, + { 0x00400000, 0x00000004 }, + { 0x00000074, 0x00000018 }, + { 0x0000e563, 0x00000004 }, + { 0x00c0e5f9, 0x000000c2 }, + { 0x00000069, 0x00000008 }, + { 0x0000a069, 0x00000008 }, + { 0x0000e576, 0x00000004 }, + { 0x0000e577, 0x00000004 }, + { 0x0000e50e, 0x00000004 }, + { 0x0000e50f, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00000077, 0x00000018 }, + { 0x00c0e5f9, 0x000000c2 }, + { 0x00000077, 0x00000008 }, + { 0x0014e50e, 0x00000004 }, + { 0x0040e50f, 0x00000004 }, + { 0x00c0007a, 0x00000008 }, + { 0x0000e570, 0x00000004 }, + { 0x0000e571, 0x00000004 }, + { 0x0000e572, 0x0000000c }, + { 0x0000a000, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x0000e568, 0x00000004 }, + { 0x000c2000, 0x00000004 }, + { 0x00000084, 0x00000018 }, + { 0x000b0000, 0x00000004 }, + { 0x18c0e562, 0x00000004 }, + { 0x00000086, 0x00000008 }, + { 0x00c00085, 0x00000008 }, + { 0x000700e3, 0x00000004 }, + { 0x00000092, 0x00000038 }, + { 0x000ca094, 0x00000030 }, + { 0x080045bb, 0x00000004 }, + { 0x000c2095, 0x00000030 }, + { 0x0800e5bc, 0000000000 }, + { 0x0000e5bb, 0x00000004 }, + { 0x0000e5bc, 0000000000 }, + { 0x00120000, 0x0000000c }, + { 0x00120000, 0x00000004 }, + { 0x001b0002, 0x0000000c }, + { 0x0000a000, 0x00000004 }, + { 0x0000e821, 0x00000004 }, + { 0x0000e800, 0000000000 }, + { 0x0000e821, 0x00000004 }, + { 0x0000e82e, 0000000000 }, + { 0x02cca000, 0x00000004 }, + { 0x00140000, 0x00000004 }, + { 0x000ce1cc, 0x00000004 }, + { 0x050de1cd, 0x00000004 }, + { 0x00400000, 0x00000004 }, + { 0x000000a4, 0x00000018 }, + { 0x00c0a000, 0x00000004 }, + { 0x000000a1, 0x00000008 }, + { 0x000000a6, 0x00000020 }, + { 0x4200e000, 0000000000 }, + { 0x000000ad, 0x00000038 }, + { 0x000ca000, 0x00000004 }, + { 0x00140000, 0x00000004 }, + { 0x000c2000, 0x00000004 }, + { 0x00160000, 0x00000004 }, + { 0x700ce000, 0x00000004 }, + { 0x001400a9, 0x00000008 }, + { 0x4000e000, 0000000000 }, + { 0x02400000, 0x00000004 }, + { 0x400ee000, 0x00000004 }, + { 0x02400000, 0x00000004 }, + { 0x4000e000, 0000000000 }, + { 0x000c2000, 0x00000004 }, + { 0x0240e51b, 0x00000004 }, + { 0x0080e50a, 0x00000005 }, + { 0x0080e50b, 0x00000005 }, + { 0x00220000, 0x00000004 }, + { 0x000700e3, 0x00000004 }, + { 0x000000c0, 0x00000038 }, + { 0x000c2095, 0x00000030 }, + { 0x0880e5bd, 0x00000005 }, + { 0x000c2094, 0x00000030 }, + { 0x0800e5bb, 0x00000005 }, + { 0x000c2095, 0x00000030 }, + { 0x0880e5bc, 0x00000005 }, + { 0x000000c3, 0x00000008 }, + { 0x0080e5bd, 0x00000005 }, + { 0x0000e5bb, 0x00000005 }, + { 0x0080e5bc, 0x00000005 }, + { 0x00210000, 0x00000004 }, + { 0x02800000, 0x00000004 }, + { 0x00c000c7, 0x00000018 }, + { 0x4180e000, 0x00000040 }, + { 0x000000c9, 0x00000024 }, + { 0x01000000, 0x0000000c }, + { 0x0100e51d, 0x0000000c }, + { 0x000045bb, 0x00000004 }, + { 0x000080c3, 0x00000008 }, + { 0x0000f3ce, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00cc2000, 0x00000004 }, + { 0x08c053cf, 0x00000040 }, + { 0x00008000, 0000000000 }, + { 0x0000f3d2, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00cc2000, 0x00000004 }, + { 0x08c053d3, 0x00000040 }, + { 0x00008000, 0000000000 }, + { 0x0000f39d, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00cc2000, 0x00000004 }, + { 0x08c0539e, 0x00000040 }, + { 0x00008000, 0000000000 }, + { 0x03c00830, 0x00000004 }, + { 0x4200e000, 0000000000 }, + { 0x0000a000, 0x00000004 }, + { 0x200045e0, 0x00000004 }, + { 0x0000e5e1, 0000000000 }, + { 0x00000001, 0000000000 }, + { 0x000700e0, 0x00000004 }, + { 0x0800e394, 0000000000 }, + { 0000000000, 0000000000 }, + { 0x0000e8c4, 0x00000004 }, + { 0x0000e8c5, 0x00000004 }, + { 0x0000e8c6, 0x00000004 }, + { 0x0000e928, 0x00000004 }, + { 0x0000e929, 0x00000004 }, + { 0x0000e92a, 0x00000004 }, + { 0x000000e4, 0x00000008 }, + { 0x0000e928, 0x00000004 }, + { 0x0000e929, 0x00000004 }, + { 0x0000e92a, 0x00000004 }, + { 0x000000eb, 0x00000008 }, + { 0x02c02000, 0x00000004 }, + { 0x00060000, 0x00000004 }, + { 0x000000f3, 0x00000034 }, + { 0x000000f0, 0x00000008 }, + { 0x00008000, 0x00000004 }, + { 0xc000e000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0x000c2000, 0x00000004 }, + { 0x001d0018, 0x00000004 }, + { 0x001a0001, 0x00000004 }, + { 0x000000fb, 0x00000034 }, + { 0x0000004a, 0x00000008 }, + { 0x0500a04a, 0x00000008 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, +}; + +static const u32 R420_cp_microcode[][2] = { + { 0x4200e000, 0000000000 }, + { 0x4000e000, 0000000000 }, + { 0x00000099, 0x00000008 }, + { 0x0000009d, 0x00000008 }, + { 0x4a554b4a, 0000000000 }, + { 0x4a4a4467, 0000000000 }, + { 0x55526f75, 0000000000 }, + { 0x4a7e7d65, 0000000000 }, + { 0xd9d3dff6, 0000000000 }, + { 0x4ac54a4a, 0000000000 }, + { 0xc8828282, 0000000000 }, + { 0xbf4acfc1, 0000000000 }, + { 0x87b04a4a, 0000000000 }, + { 0xb5838383, 0000000000 }, + { 0x4a0f85ba, 0000000000 }, + { 0x000ca000, 0x00000004 }, + { 0x000d0012, 0x00000038 }, + { 0x0000e8b4, 0x00000004 }, + { 0x000d0014, 0x00000038 }, + { 0x0000e8b6, 0x00000004 }, + { 0x000d0016, 0x00000038 }, + { 0x0000e854, 0x00000004 }, + { 0x000d0018, 0x00000038 }, + { 0x0000e855, 0x00000004 }, + { 0x000d001a, 0x00000038 }, + { 0x0000e856, 0x00000004 }, + { 0x000d001c, 0x00000038 }, + { 0x0000e857, 0x00000004 }, + { 0x000d001e, 0x00000038 }, + { 0x0000e824, 0x00000004 }, + { 0x000d0020, 0x00000038 }, + { 0x0000e825, 0x00000004 }, + { 0x000d0022, 0x00000038 }, + { 0x0000e830, 0x00000004 }, + { 0x000d0024, 0x00000038 }, + { 0x0000f0c0, 0x00000004 }, + { 0x000d0026, 0x00000038 }, + { 0x0000f0c1, 0x00000004 }, + { 0x000d0028, 0x00000038 }, + { 0x0000f041, 0x00000004 }, + { 0x000d002a, 0x00000038 }, + { 0x0000f184, 0x00000004 }, + { 0x000d002c, 0x00000038 }, + { 0x0000f185, 0x00000004 }, + { 0x000d002e, 0x00000038 }, + { 0x0000f186, 0x00000004 }, + { 0x000d0030, 0x00000038 }, + { 0x0000f187, 0x00000004 }, + { 0x000d0032, 0x00000038 }, + { 0x0000f180, 0x00000004 }, + { 0x000d0034, 0x00000038 }, + { 0x0000f393, 0x00000004 }, + { 0x000d0036, 0x00000038 }, + { 0x0000f38a, 0x00000004 }, + { 0x000d0038, 0x00000038 }, + { 0x0000f38e, 0x00000004 }, + { 0x0000e821, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00000043, 0x00000018 }, + { 0x00cce800, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x08004800, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x08004800, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x08004800, 0x00000004 }, + { 0x0000003a, 0x00000008 }, + { 0x0000a000, 0000000000 }, + { 0x2000451d, 0x00000004 }, + { 0x0000e580, 0x00000004 }, + { 0x000ce581, 0x00000004 }, + { 0x08004580, 0x00000004 }, + { 0x000ce581, 0x00000004 }, + { 0x00000047, 0x00000008 }, + { 0x0000a000, 0000000000 }, + { 0x000c2000, 0x00000004 }, + { 0x0000e50e, 0x00000004 }, + { 0x00032000, 0x00000004 }, + { 0x00022051, 0x00000028 }, + { 0x00000051, 0x00000024 }, + { 0x0800450f, 0x00000004 }, + { 0x0000a04b, 0x00000008 }, + { 0x0000e565, 0x00000004 }, + { 0x0000e566, 0x00000004 }, + { 0x00000052, 0x00000008 }, + { 0x03cca5b4, 0x00000004 }, + { 0x05432000, 0x00000004 }, + { 0x00022000, 0x00000004 }, + { 0x4ccce05e, 0x00000030 }, + { 0x08274565, 0x00000004 }, + { 0x0000005e, 0x00000030 }, + { 0x08004564, 0x00000004 }, + { 0x0000e566, 0x00000004 }, + { 0x00000055, 0x00000008 }, + { 0x00802061, 0x00000010 }, + { 0x00202000, 0x00000004 }, + { 0x001b00ff, 0x00000004 }, + { 0x01000064, 0x00000010 }, + { 0x001f2000, 0x00000004 }, + { 0x001c00ff, 0x00000004 }, + { 0000000000, 0x0000000c }, + { 0x00000072, 0x00000030 }, + { 0x00000055, 0x00000008 }, + { 0x0000e576, 0x00000004 }, + { 0x0000e577, 0x00000004 }, + { 0x0000e50e, 0x00000004 }, + { 0x0000e50f, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00000069, 0x00000018 }, + { 0x00c0e5f9, 0x000000c2 }, + { 0x00000069, 0x00000008 }, + { 0x0014e50e, 0x00000004 }, + { 0x0040e50f, 0x00000004 }, + { 0x00c0006c, 0x00000008 }, + { 0x0000e570, 0x00000004 }, + { 0x0000e571, 0x00000004 }, + { 0x0000e572, 0x0000000c }, + { 0x0000a000, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x0000e568, 0x00000004 }, + { 0x000c2000, 0x00000004 }, + { 0x00000076, 0x00000018 }, + { 0x000b0000, 0x00000004 }, + { 0x18c0e562, 0x00000004 }, + { 0x00000078, 0x00000008 }, + { 0x00c00077, 0x00000008 }, + { 0x000700c7, 0x00000004 }, + { 0x00000080, 0x00000038 }, + { 0x0000e5bb, 0x00000004 }, + { 0x0000e5bc, 0000000000 }, + { 0x0000a000, 0x00000004 }, + { 0x0000e821, 0x00000004 }, + { 0x0000e800, 0000000000 }, + { 0x0000e821, 0x00000004 }, + { 0x0000e82e, 0000000000 }, + { 0x02cca000, 0x00000004 }, + { 0x00140000, 0x00000004 }, + { 0x000ce1cc, 0x00000004 }, + { 0x050de1cd, 0x00000004 }, + { 0x00400000, 0x00000004 }, + { 0x0000008f, 0x00000018 }, + { 0x00c0a000, 0x00000004 }, + { 0x0000008c, 0x00000008 }, + { 0x00000091, 0x00000020 }, + { 0x4200e000, 0000000000 }, + { 0x00000098, 0x00000038 }, + { 0x000ca000, 0x00000004 }, + { 0x00140000, 0x00000004 }, + { 0x000c2000, 0x00000004 }, + { 0x00160000, 0x00000004 }, + { 0x700ce000, 0x00000004 }, + { 0x00140094, 0x00000008 }, + { 0x4000e000, 0000000000 }, + { 0x02400000, 0x00000004 }, + { 0x400ee000, 0x00000004 }, + { 0x02400000, 0x00000004 }, + { 0x4000e000, 0000000000 }, + { 0x000c2000, 0x00000004 }, + { 0x0240e51b, 0x00000004 }, + { 0x0080e50a, 0x00000005 }, + { 0x0080e50b, 0x00000005 }, + { 0x00220000, 0x00000004 }, + { 0x000700c7, 0x00000004 }, + { 0x000000a4, 0x00000038 }, + { 0x0080e5bd, 0x00000005 }, + { 0x0000e5bb, 0x00000005 }, + { 0x0080e5bc, 0x00000005 }, + { 0x00210000, 0x00000004 }, + { 0x02800000, 0x00000004 }, + { 0x00c000ab, 0x00000018 }, + { 0x4180e000, 0x00000040 }, + { 0x000000ad, 0x00000024 }, + { 0x01000000, 0x0000000c }, + { 0x0100e51d, 0x0000000c }, + { 0x000045bb, 0x00000004 }, + { 0x000080a7, 0x00000008 }, + { 0x0000f3ce, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00cc2000, 0x00000004 }, + { 0x08c053cf, 0x00000040 }, + { 0x00008000, 0000000000 }, + { 0x0000f3d2, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00cc2000, 0x00000004 }, + { 0x08c053d3, 0x00000040 }, + { 0x00008000, 0000000000 }, + { 0x0000f39d, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00cc2000, 0x00000004 }, + { 0x08c0539e, 0x00000040 }, + { 0x00008000, 0000000000 }, + { 0x03c00830, 0x00000004 }, + { 0x4200e000, 0000000000 }, + { 0x0000a000, 0x00000004 }, + { 0x200045e0, 0x00000004 }, + { 0x0000e5e1, 0000000000 }, + { 0x00000001, 0000000000 }, + { 0x000700c4, 0x00000004 }, + { 0x0800e394, 0000000000 }, + { 0000000000, 0000000000 }, + { 0x0000e8c4, 0x00000004 }, + { 0x0000e8c5, 0x00000004 }, + { 0x0000e8c6, 0x00000004 }, + { 0x0000e928, 0x00000004 }, + { 0x0000e929, 0x00000004 }, + { 0x0000e92a, 0x00000004 }, + { 0x000000c8, 0x00000008 }, + { 0x0000e928, 0x00000004 }, + { 0x0000e929, 0x00000004 }, + { 0x0000e92a, 0x00000004 }, + { 0x000000cf, 0x00000008 }, + { 0x02c02000, 0x00000004 }, + { 0x00060000, 0x00000004 }, + { 0x000000d7, 0x00000034 }, + { 0x000000d4, 0x00000008 }, + { 0x00008000, 0x00000004 }, + { 0xc000e000, 0000000000 }, + { 0x0000e1cc, 0x00000004 }, + { 0x0500e1cd, 0x00000004 }, + { 0x000ca000, 0x00000004 }, + { 0x000000de, 0x00000034 }, + { 0x000000da, 0x00000008 }, + { 0x0000a000, 0000000000 }, + { 0x0019e1cc, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x0500a000, 0x00000004 }, + { 0x080041cd, 0x00000004 }, + { 0x000ca000, 0x00000004 }, + { 0x000000fb, 0x00000034 }, + { 0x0000004a, 0x00000008 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0x000c2000, 0x00000004 }, + { 0x001d0018, 0x00000004 }, + { 0x001a0001, 0x00000004 }, + { 0x000000fb, 0x00000034 }, + { 0x0000004a, 0x00000008 }, + { 0x0500a04a, 0x00000008 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, +}; + +static const u32 RS600_cp_microcode[][2] = { + { 0x4200e000, 0000000000 }, + { 0x4000e000, 0000000000 }, + { 0x000000a0, 0x00000008 }, + { 0x000000a4, 0x00000008 }, + { 0x4a554b4a, 0000000000 }, + { 0x4a4a4467, 0000000000 }, + { 0x55526f75, 0000000000 }, + { 0x4a7e7d65, 0000000000 }, + { 0x4ae74af6, 0000000000 }, + { 0x4ad34a4a, 0000000000 }, + { 0xd6898989, 0000000000 }, + { 0xcd4addcf, 0000000000 }, + { 0x8ebe4ae2, 0000000000 }, + { 0xc38a8a8a, 0000000000 }, + { 0x4a0f8cc8, 0000000000 }, + { 0x000ca000, 0x00000004 }, + { 0x000d0012, 0x00000038 }, + { 0x0000e8b4, 0x00000004 }, + { 0x000d0014, 0x00000038 }, + { 0x0000e8b6, 0x00000004 }, + { 0x000d0016, 0x00000038 }, + { 0x0000e854, 0x00000004 }, + { 0x000d0018, 0x00000038 }, + { 0x0000e855, 0x00000004 }, + { 0x000d001a, 0x00000038 }, + { 0x0000e856, 0x00000004 }, + { 0x000d001c, 0x00000038 }, + { 0x0000e857, 0x00000004 }, + { 0x000d001e, 0x00000038 }, + { 0x0000e824, 0x00000004 }, + { 0x000d0020, 0x00000038 }, + { 0x0000e825, 0x00000004 }, + { 0x000d0022, 0x00000038 }, + { 0x0000e830, 0x00000004 }, + { 0x000d0024, 0x00000038 }, + { 0x0000f0c0, 0x00000004 }, + { 0x000d0026, 0x00000038 }, + { 0x0000f0c1, 0x00000004 }, + { 0x000d0028, 0x00000038 }, + { 0x0000f041, 0x00000004 }, + { 0x000d002a, 0x00000038 }, + { 0x0000f184, 0x00000004 }, + { 0x000d002c, 0x00000038 }, + { 0x0000f185, 0x00000004 }, + { 0x000d002e, 0x00000038 }, + { 0x0000f186, 0x00000004 }, + { 0x000d0030, 0x00000038 }, + { 0x0000f187, 0x00000004 }, + { 0x000d0032, 0x00000038 }, + { 0x0000f180, 0x00000004 }, + { 0x000d0034, 0x00000038 }, + { 0x0000f393, 0x00000004 }, + { 0x000d0036, 0x00000038 }, + { 0x0000f38a, 0x00000004 }, + { 0x000d0038, 0x00000038 }, + { 0x0000f38e, 0x00000004 }, + { 0x0000e821, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00000043, 0x00000018 }, + { 0x00cce800, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x08004800, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x08004800, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x08004800, 0x00000004 }, + { 0x0000003a, 0x00000008 }, + { 0x0000a000, 0000000000 }, + { 0x2000451d, 0x00000004 }, + { 0x0000e580, 0x00000004 }, + { 0x000ce581, 0x00000004 }, + { 0x08004580, 0x00000004 }, + { 0x000ce581, 0x00000004 }, + { 0x00000047, 0x00000008 }, + { 0x0000a000, 0000000000 }, + { 0x000c2000, 0x00000004 }, + { 0x0000e50e, 0x00000004 }, + { 0x00032000, 0x00000004 }, + { 0x00022051, 0x00000028 }, + { 0x00000051, 0x00000024 }, + { 0x0800450f, 0x00000004 }, + { 0x0000a04b, 0x00000008 }, + { 0x0000e565, 0x00000004 }, + { 0x0000e566, 0x00000004 }, + { 0x00000052, 0x00000008 }, + { 0x03cca5b4, 0x00000004 }, + { 0x05432000, 0x00000004 }, + { 0x00022000, 0x00000004 }, + { 0x4ccce05e, 0x00000030 }, + { 0x08274565, 0x00000004 }, + { 0x0000005e, 0x00000030 }, + { 0x08004564, 0x00000004 }, + { 0x0000e566, 0x00000004 }, + { 0x00000055, 0x00000008 }, + { 0x00802061, 0x00000010 }, + { 0x00202000, 0x00000004 }, + { 0x001b00ff, 0x00000004 }, + { 0x01000064, 0x00000010 }, + { 0x001f2000, 0x00000004 }, + { 0x001c00ff, 0x00000004 }, + { 0000000000, 0x0000000c }, + { 0x00000072, 0x00000030 }, + { 0x00000055, 0x00000008 }, + { 0x0000e576, 0x00000004 }, + { 0x0000e577, 0x00000004 }, + { 0x0000e50e, 0x00000004 }, + { 0x0000e50f, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00000069, 0x00000018 }, + { 0x00c0e5f9, 0x000000c2 }, + { 0x00000069, 0x00000008 }, + { 0x0014e50e, 0x00000004 }, + { 0x0040e50f, 0x00000004 }, + { 0x00c0006c, 0x00000008 }, + { 0x0000e570, 0x00000004 }, + { 0x0000e571, 0x00000004 }, + { 0x0000e572, 0x0000000c }, + { 0x0000a000, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x0000e568, 0x00000004 }, + { 0x000c2000, 0x00000004 }, + { 0x00000076, 0x00000018 }, + { 0x000b0000, 0x00000004 }, + { 0x18c0e562, 0x00000004 }, + { 0x00000078, 0x00000008 }, + { 0x00c00077, 0x00000008 }, + { 0x000700d5, 0x00000004 }, + { 0x00000084, 0x00000038 }, + { 0x000ca086, 0x00000030 }, + { 0x080045bb, 0x00000004 }, + { 0x000c2087, 0x00000030 }, + { 0x0800e5bc, 0000000000 }, + { 0x0000e5bb, 0x00000004 }, + { 0x0000e5bc, 0000000000 }, + { 0x00120000, 0x0000000c }, + { 0x00120000, 0x00000004 }, + { 0x001b0002, 0x0000000c }, + { 0x0000a000, 0x00000004 }, + { 0x0000e821, 0x00000004 }, + { 0x0000e800, 0000000000 }, + { 0x0000e821, 0x00000004 }, + { 0x0000e82e, 0000000000 }, + { 0x02cca000, 0x00000004 }, + { 0x00140000, 0x00000004 }, + { 0x000ce1cc, 0x00000004 }, + { 0x050de1cd, 0x00000004 }, + { 0x00400000, 0x00000004 }, + { 0x00000096, 0x00000018 }, + { 0x00c0a000, 0x00000004 }, + { 0x00000093, 0x00000008 }, + { 0x00000098, 0x00000020 }, + { 0x4200e000, 0000000000 }, + { 0x0000009f, 0x00000038 }, + { 0x000ca000, 0x00000004 }, + { 0x00140000, 0x00000004 }, + { 0x000c2000, 0x00000004 }, + { 0x00160000, 0x00000004 }, + { 0x700ce000, 0x00000004 }, + { 0x0014009b, 0x00000008 }, + { 0x4000e000, 0000000000 }, + { 0x02400000, 0x00000004 }, + { 0x400ee000, 0x00000004 }, + { 0x02400000, 0x00000004 }, + { 0x4000e000, 0000000000 }, + { 0x000c2000, 0x00000004 }, + { 0x0240e51b, 0x00000004 }, + { 0x0080e50a, 0x00000005 }, + { 0x0080e50b, 0x00000005 }, + { 0x00220000, 0x00000004 }, + { 0x000700d5, 0x00000004 }, + { 0x000000b2, 0x00000038 }, + { 0x000c2087, 0x00000030 }, + { 0x0880e5bd, 0x00000005 }, + { 0x000c2086, 0x00000030 }, + { 0x0800e5bb, 0x00000005 }, + { 0x000c2087, 0x00000030 }, + { 0x0880e5bc, 0x00000005 }, + { 0x000000b5, 0x00000008 }, + { 0x0080e5bd, 0x00000005 }, + { 0x0000e5bb, 0x00000005 }, + { 0x0080e5bc, 0x00000005 }, + { 0x00210000, 0x00000004 }, + { 0x02800000, 0x00000004 }, + { 0x00c000b9, 0x00000018 }, + { 0x4180e000, 0x00000040 }, + { 0x000000bb, 0x00000024 }, + { 0x01000000, 0x0000000c }, + { 0x0100e51d, 0x0000000c }, + { 0x000045bb, 0x00000004 }, + { 0x000080b5, 0x00000008 }, + { 0x0000f3ce, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00cc2000, 0x00000004 }, + { 0x08c053cf, 0x00000040 }, + { 0x00008000, 0000000000 }, + { 0x0000f3d2, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00cc2000, 0x00000004 }, + { 0x08c053d3, 0x00000040 }, + { 0x00008000, 0000000000 }, + { 0x0000f39d, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00cc2000, 0x00000004 }, + { 0x08c0539e, 0x00000040 }, + { 0x00008000, 0000000000 }, + { 0x03c00830, 0x00000004 }, + { 0x4200e000, 0000000000 }, + { 0x0000a000, 0x00000004 }, + { 0x200045e0, 0x00000004 }, + { 0x0000e5e1, 0000000000 }, + { 0x00000001, 0000000000 }, + { 0x000700d2, 0x00000004 }, + { 0x0800e394, 0000000000 }, + { 0000000000, 0000000000 }, + { 0x0000e8c4, 0x00000004 }, + { 0x0000e8c5, 0x00000004 }, + { 0x0000e8c6, 0x00000004 }, + { 0x0000e928, 0x00000004 }, + { 0x0000e929, 0x00000004 }, + { 0x0000e92a, 0x00000004 }, + { 0x000000d6, 0x00000008 }, + { 0x0000e928, 0x00000004 }, + { 0x0000e929, 0x00000004 }, + { 0x0000e92a, 0x00000004 }, + { 0x000000dd, 0x00000008 }, + { 0x00e00116, 0000000000 }, + { 0x000700e1, 0x00000004 }, + { 0x0800401c, 0x00000004 }, + { 0x200050e7, 0x00000004 }, + { 0x0000e01d, 0x00000004 }, + { 0x000000e4, 0x00000008 }, + { 0x02c02000, 0x00000004 }, + { 0x00060000, 0x00000004 }, + { 0x000000eb, 0x00000034 }, + { 0x000000e8, 0x00000008 }, + { 0x00008000, 0x00000004 }, + { 0xc000e000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0x000c2000, 0x00000004 }, + { 0x001d0018, 0x00000004 }, + { 0x001a0001, 0x00000004 }, + { 0x000000fb, 0x00000034 }, + { 0x0000004a, 0x00000008 }, + { 0x0500a04a, 0x00000008 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, +}; + +static const u32 RS690_cp_microcode[][2] = { + { 0x000000dd, 0x00000008 }, + { 0x000000df, 0x00000008 }, + { 0x000000a0, 0x00000008 }, + { 0x000000a4, 0x00000008 }, + { 0x4a554b4a, 0000000000 }, + { 0x4a4a4467, 0000000000 }, + { 0x55526f75, 0000000000 }, + { 0x4a7e7d65, 0000000000 }, + { 0x4ad74af6, 0000000000 }, + { 0x4ac94a4a, 0000000000 }, + { 0xcc898989, 0000000000 }, + { 0xc34ad3c5, 0000000000 }, + { 0x8e4a4a4a, 0000000000 }, + { 0x4a8a8a8a, 0000000000 }, + { 0x4a0f8c4a, 0000000000 }, + { 0x000ca000, 0x00000004 }, + { 0x000d0012, 0x00000038 }, + { 0x0000e8b4, 0x00000004 }, + { 0x000d0014, 0x00000038 }, + { 0x0000e8b6, 0x00000004 }, + { 0x000d0016, 0x00000038 }, + { 0x0000e854, 0x00000004 }, + { 0x000d0018, 0x00000038 }, + { 0x0000e855, 0x00000004 }, + { 0x000d001a, 0x00000038 }, + { 0x0000e856, 0x00000004 }, + { 0x000d001c, 0x00000038 }, + { 0x0000e857, 0x00000004 }, + { 0x000d001e, 0x00000038 }, + { 0x0000e824, 0x00000004 }, + { 0x000d0020, 0x00000038 }, + { 0x0000e825, 0x00000004 }, + { 0x000d0022, 0x00000038 }, + { 0x0000e830, 0x00000004 }, + { 0x000d0024, 0x00000038 }, + { 0x0000f0c0, 0x00000004 }, + { 0x000d0026, 0x00000038 }, + { 0x0000f0c1, 0x00000004 }, + { 0x000d0028, 0x00000038 }, + { 0x0000f041, 0x00000004 }, + { 0x000d002a, 0x00000038 }, + { 0x0000f184, 0x00000004 }, + { 0x000d002c, 0x00000038 }, + { 0x0000f185, 0x00000004 }, + { 0x000d002e, 0x00000038 }, + { 0x0000f186, 0x00000004 }, + { 0x000d0030, 0x00000038 }, + { 0x0000f187, 0x00000004 }, + { 0x000d0032, 0x00000038 }, + { 0x0000f180, 0x00000004 }, + { 0x000d0034, 0x00000038 }, + { 0x0000f393, 0x00000004 }, + { 0x000d0036, 0x00000038 }, + { 0x0000f38a, 0x00000004 }, + { 0x000d0038, 0x00000038 }, + { 0x0000f38e, 0x00000004 }, + { 0x0000e821, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00000043, 0x00000018 }, + { 0x00cce800, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x08004800, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x08004800, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x08004800, 0x00000004 }, + { 0x0000003a, 0x00000008 }, + { 0x0000a000, 0000000000 }, + { 0x2000451d, 0x00000004 }, + { 0x0000e580, 0x00000004 }, + { 0x000ce581, 0x00000004 }, + { 0x08004580, 0x00000004 }, + { 0x000ce581, 0x00000004 }, + { 0x00000047, 0x00000008 }, + { 0x0000a000, 0000000000 }, + { 0x000c2000, 0x00000004 }, + { 0x0000e50e, 0x00000004 }, + { 0x00032000, 0x00000004 }, + { 0x00022051, 0x00000028 }, + { 0x00000051, 0x00000024 }, + { 0x0800450f, 0x00000004 }, + { 0x0000a04b, 0x00000008 }, + { 0x0000e565, 0x00000004 }, + { 0x0000e566, 0x00000004 }, + { 0x00000052, 0x00000008 }, + { 0x03cca5b4, 0x00000004 }, + { 0x05432000, 0x00000004 }, + { 0x00022000, 0x00000004 }, + { 0x4ccce05e, 0x00000030 }, + { 0x08274565, 0x00000004 }, + { 0x0000005e, 0x00000030 }, + { 0x08004564, 0x00000004 }, + { 0x0000e566, 0x00000004 }, + { 0x00000055, 0x00000008 }, + { 0x00802061, 0x00000010 }, + { 0x00202000, 0x00000004 }, + { 0x001b00ff, 0x00000004 }, + { 0x01000064, 0x00000010 }, + { 0x001f2000, 0x00000004 }, + { 0x001c00ff, 0x00000004 }, + { 0000000000, 0x0000000c }, + { 0x00000072, 0x00000030 }, + { 0x00000055, 0x00000008 }, + { 0x0000e576, 0x00000004 }, + { 0x0000e577, 0x00000004 }, + { 0x0000e50e, 0x00000004 }, + { 0x0000e50f, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00000069, 0x00000018 }, + { 0x00c0e5f9, 0x000000c2 }, + { 0x00000069, 0x00000008 }, + { 0x0014e50e, 0x00000004 }, + { 0x0040e50f, 0x00000004 }, + { 0x00c0006c, 0x00000008 }, + { 0x0000e570, 0x00000004 }, + { 0x0000e571, 0x00000004 }, + { 0x0000e572, 0x0000000c }, + { 0x0000a000, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x0000e568, 0x00000004 }, + { 0x000c2000, 0x00000004 }, + { 0x00000076, 0x00000018 }, + { 0x000b0000, 0x00000004 }, + { 0x18c0e562, 0x00000004 }, + { 0x00000078, 0x00000008 }, + { 0x00c00077, 0x00000008 }, + { 0x000700cb, 0x00000004 }, + { 0x00000084, 0x00000038 }, + { 0x000ca086, 0x00000030 }, + { 0x080045bb, 0x00000004 }, + { 0x000c2087, 0x00000030 }, + { 0x0800e5bc, 0000000000 }, + { 0x0000e5bb, 0x00000004 }, + { 0x0000e5bc, 0000000000 }, + { 0x00120000, 0x0000000c }, + { 0x00120000, 0x00000004 }, + { 0x001b0002, 0x0000000c }, + { 0x0000a000, 0x00000004 }, + { 0x0000e821, 0x00000004 }, + { 0x0000e800, 0000000000 }, + { 0x0000e821, 0x00000004 }, + { 0x0000e82e, 0000000000 }, + { 0x02cca000, 0x00000004 }, + { 0x00140000, 0x00000004 }, + { 0x000ce1cc, 0x00000004 }, + { 0x050de1cd, 0x00000004 }, + { 0x00400000, 0x00000004 }, + { 0x00000096, 0x00000018 }, + { 0x00c0a000, 0x00000004 }, + { 0x00000093, 0x00000008 }, + { 0x00000098, 0x00000020 }, + { 0x4200e000, 0000000000 }, + { 0x0000009f, 0x00000038 }, + { 0x000ca000, 0x00000004 }, + { 0x00140000, 0x00000004 }, + { 0x000c2000, 0x00000004 }, + { 0x00160000, 0x00000004 }, + { 0x700ce000, 0x00000004 }, + { 0x0014009b, 0x00000008 }, + { 0x4000e000, 0000000000 }, + { 0x02400000, 0x00000004 }, + { 0x400ee000, 0x00000004 }, + { 0x02400000, 0x00000004 }, + { 0x4000e000, 0000000000 }, + { 0x00100000, 0x0000002c }, + { 0x00004000, 0000000000 }, + { 0x080045c8, 0x00000004 }, + { 0x00240005, 0x00000004 }, + { 0x08004d0b, 0x00000004 }, + { 0x000c2000, 0x00000004 }, + { 0x0240e51b, 0x00000004 }, + { 0x0080e50a, 0x00000005 }, + { 0x0080e50b, 0x00000005 }, + { 0x00220000, 0x00000004 }, + { 0x000700cb, 0x00000004 }, + { 0x000000b7, 0x00000038 }, + { 0x000c2087, 0x00000030 }, + { 0x0880e5bd, 0x00000005 }, + { 0x000c2086, 0x00000030 }, + { 0x0800e5bb, 0x00000005 }, + { 0x000c2087, 0x00000030 }, + { 0x0880e5bc, 0x00000005 }, + { 0x000000ba, 0x00000008 }, + { 0x0080e5bd, 0x00000005 }, + { 0x0000e5bb, 0x00000005 }, + { 0x0080e5bc, 0x00000005 }, + { 0x00210000, 0x00000004 }, + { 0x02800000, 0x00000004 }, + { 0x00c000be, 0x00000018 }, + { 0x4180e000, 0x00000040 }, + { 0x000000c0, 0x00000024 }, + { 0x01000000, 0x0000000c }, + { 0x0100e51d, 0x0000000c }, + { 0x000045bb, 0x00000004 }, + { 0x000080ba, 0x00000008 }, + { 0x03c00830, 0x00000004 }, + { 0x4200e000, 0000000000 }, + { 0x0000a000, 0x00000004 }, + { 0x200045e0, 0x00000004 }, + { 0x0000e5e1, 0000000000 }, + { 0x00000001, 0000000000 }, + { 0x000700c8, 0x00000004 }, + { 0x0800e394, 0000000000 }, + { 0000000000, 0000000000 }, + { 0x0000e8c4, 0x00000004 }, + { 0x0000e8c5, 0x00000004 }, + { 0x0000e8c6, 0x00000004 }, + { 0x0000e928, 0x00000004 }, + { 0x0000e929, 0x00000004 }, + { 0x0000e92a, 0x00000004 }, + { 0x000000cc, 0x00000008 }, + { 0x0000e928, 0x00000004 }, + { 0x0000e929, 0x00000004 }, + { 0x0000e92a, 0x00000004 }, + { 0x000000d3, 0x00000008 }, + { 0x02c02000, 0x00000004 }, + { 0x00060000, 0x00000004 }, + { 0x000000db, 0x00000034 }, + { 0x000000d8, 0x00000008 }, + { 0x00008000, 0x00000004 }, + { 0xc000e000, 0000000000 }, + { 0x000000e1, 0x00000030 }, + { 0x4200e000, 0000000000 }, + { 0x000000e1, 0x00000030 }, + { 0x4000e000, 0000000000 }, + { 0x0025001b, 0x00000004 }, + { 0x00230000, 0x00000004 }, + { 0x00250005, 0x00000004 }, + { 0x000000e6, 0x00000034 }, + { 0000000000, 0x0000000c }, + { 0x00244000, 0x00000004 }, + { 0x080045c8, 0x00000004 }, + { 0x00240005, 0x00000004 }, + { 0x08004d0b, 0x0000000c }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0x000c2000, 0x00000004 }, + { 0x001d0018, 0x00000004 }, + { 0x001a0001, 0x00000004 }, + { 0x000000fb, 0x00000034 }, + { 0x0000004a, 0x00000008 }, + { 0x0500a04a, 0x00000008 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, +}; + +static const u32 R520_cp_microcode[][2] = { + { 0x4200e000, 0000000000 }, + { 0x4000e000, 0000000000 }, + { 0x00000099, 0x00000008 }, + { 0x0000009d, 0x00000008 }, + { 0x4a554b4a, 0000000000 }, + { 0x4a4a4467, 0000000000 }, + { 0x55526f75, 0000000000 }, + { 0x4a7e7d65, 0000000000 }, + { 0xe0dae6f6, 0000000000 }, + { 0x4ac54a4a, 0000000000 }, + { 0xc8828282, 0000000000 }, + { 0xbf4acfc1, 0000000000 }, + { 0x87b04ad5, 0000000000 }, + { 0xb5838383, 0000000000 }, + { 0x4a0f85ba, 0000000000 }, + { 0x000ca000, 0x00000004 }, + { 0x000d0012, 0x00000038 }, + { 0x0000e8b4, 0x00000004 }, + { 0x000d0014, 0x00000038 }, + { 0x0000e8b6, 0x00000004 }, + { 0x000d0016, 0x00000038 }, + { 0x0000e854, 0x00000004 }, + { 0x000d0018, 0x00000038 }, + { 0x0000e855, 0x00000004 }, + { 0x000d001a, 0x00000038 }, + { 0x0000e856, 0x00000004 }, + { 0x000d001c, 0x00000038 }, + { 0x0000e857, 0x00000004 }, + { 0x000d001e, 0x00000038 }, + { 0x0000e824, 0x00000004 }, + { 0x000d0020, 0x00000038 }, + { 0x0000e825, 0x00000004 }, + { 0x000d0022, 0x00000038 }, + { 0x0000e830, 0x00000004 }, + { 0x000d0024, 0x00000038 }, + { 0x0000f0c0, 0x00000004 }, + { 0x000d0026, 0x00000038 }, + { 0x0000f0c1, 0x00000004 }, + { 0x000d0028, 0x00000038 }, + { 0x0000e000, 0x00000004 }, + { 0x000d002a, 0x00000038 }, + { 0x0000e000, 0x00000004 }, + { 0x000d002c, 0x00000038 }, + { 0x0000e000, 0x00000004 }, + { 0x000d002e, 0x00000038 }, + { 0x0000e000, 0x00000004 }, + { 0x000d0030, 0x00000038 }, + { 0x0000e000, 0x00000004 }, + { 0x000d0032, 0x00000038 }, + { 0x0000f180, 0x00000004 }, + { 0x000d0034, 0x00000038 }, + { 0x0000f393, 0x00000004 }, + { 0x000d0036, 0x00000038 }, + { 0x0000f38a, 0x00000004 }, + { 0x000d0038, 0x00000038 }, + { 0x0000f38e, 0x00000004 }, + { 0x0000e821, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00000043, 0x00000018 }, + { 0x00cce800, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x08004800, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x08004800, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x08004800, 0x00000004 }, + { 0x0000003a, 0x00000008 }, + { 0x0000a000, 0000000000 }, + { 0x2000451d, 0x00000004 }, + { 0x0000e580, 0x00000004 }, + { 0x000ce581, 0x00000004 }, + { 0x08004580, 0x00000004 }, + { 0x000ce581, 0x00000004 }, + { 0x00000047, 0x00000008 }, + { 0x0000a000, 0000000000 }, + { 0x000c2000, 0x00000004 }, + { 0x0000e50e, 0x00000004 }, + { 0x00032000, 0x00000004 }, + { 0x00022051, 0x00000028 }, + { 0x00000051, 0x00000024 }, + { 0x0800450f, 0x00000004 }, + { 0x0000a04b, 0x00000008 }, + { 0x0000e565, 0x00000004 }, + { 0x0000e566, 0x00000004 }, + { 0x00000052, 0x00000008 }, + { 0x03cca5b4, 0x00000004 }, + { 0x05432000, 0x00000004 }, + { 0x00022000, 0x00000004 }, + { 0x4ccce05e, 0x00000030 }, + { 0x08274565, 0x00000004 }, + { 0x0000005e, 0x00000030 }, + { 0x08004564, 0x00000004 }, + { 0x0000e566, 0x00000004 }, + { 0x00000055, 0x00000008 }, + { 0x00802061, 0x00000010 }, + { 0x00202000, 0x00000004 }, + { 0x001b00ff, 0x00000004 }, + { 0x01000064, 0x00000010 }, + { 0x001f2000, 0x00000004 }, + { 0x001c00ff, 0x00000004 }, + { 0000000000, 0x0000000c }, + { 0x00000072, 0x00000030 }, + { 0x00000055, 0x00000008 }, + { 0x0000e576, 0x00000004 }, + { 0x0000e577, 0x00000004 }, + { 0x0000e50e, 0x00000004 }, + { 0x0000e50f, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00000069, 0x00000018 }, + { 0x00c0e5f9, 0x000000c2 }, + { 0x00000069, 0x00000008 }, + { 0x0014e50e, 0x00000004 }, + { 0x0040e50f, 0x00000004 }, + { 0x00c0006c, 0x00000008 }, + { 0x0000e570, 0x00000004 }, + { 0x0000e571, 0x00000004 }, + { 0x0000e572, 0x0000000c }, + { 0x0000a000, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x0000e568, 0x00000004 }, + { 0x000c2000, 0x00000004 }, + { 0x00000076, 0x00000018 }, + { 0x000b0000, 0x00000004 }, + { 0x18c0e562, 0x00000004 }, + { 0x00000078, 0x00000008 }, + { 0x00c00077, 0x00000008 }, + { 0x000700c7, 0x00000004 }, + { 0x00000080, 0x00000038 }, + { 0x0000e5bb, 0x00000004 }, + { 0x0000e5bc, 0000000000 }, + { 0x0000a000, 0x00000004 }, + { 0x0000e821, 0x00000004 }, + { 0x0000e800, 0000000000 }, + { 0x0000e821, 0x00000004 }, + { 0x0000e82e, 0000000000 }, + { 0x02cca000, 0x00000004 }, + { 0x00140000, 0x00000004 }, + { 0x000ce1cc, 0x00000004 }, + { 0x050de1cd, 0x00000004 }, + { 0x00400000, 0x00000004 }, + { 0x0000008f, 0x00000018 }, + { 0x00c0a000, 0x00000004 }, + { 0x0000008c, 0x00000008 }, + { 0x00000091, 0x00000020 }, + { 0x4200e000, 0000000000 }, + { 0x00000098, 0x00000038 }, + { 0x000ca000, 0x00000004 }, + { 0x00140000, 0x00000004 }, + { 0x000c2000, 0x00000004 }, + { 0x00160000, 0x00000004 }, + { 0x700ce000, 0x00000004 }, + { 0x00140094, 0x00000008 }, + { 0x4000e000, 0000000000 }, + { 0x02400000, 0x00000004 }, + { 0x400ee000, 0x00000004 }, + { 0x02400000, 0x00000004 }, + { 0x4000e000, 0000000000 }, + { 0x000c2000, 0x00000004 }, + { 0x0240e51b, 0x00000004 }, + { 0x0080e50a, 0x00000005 }, + { 0x0080e50b, 0x00000005 }, + { 0x00220000, 0x00000004 }, + { 0x000700c7, 0x00000004 }, + { 0x000000a4, 0x00000038 }, + { 0x0080e5bd, 0x00000005 }, + { 0x0000e5bb, 0x00000005 }, + { 0x0080e5bc, 0x00000005 }, + { 0x00210000, 0x00000004 }, + { 0x02800000, 0x00000004 }, + { 0x00c000ab, 0x00000018 }, + { 0x4180e000, 0x00000040 }, + { 0x000000ad, 0x00000024 }, + { 0x01000000, 0x0000000c }, + { 0x0100e51d, 0x0000000c }, + { 0x000045bb, 0x00000004 }, + { 0x000080a7, 0x00000008 }, + { 0x0000f3ce, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00cc2000, 0x00000004 }, + { 0x08c053cf, 0x00000040 }, + { 0x00008000, 0000000000 }, + { 0x0000f3d2, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00cc2000, 0x00000004 }, + { 0x08c053d3, 0x00000040 }, + { 0x00008000, 0000000000 }, + { 0x0000f39d, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00cc2000, 0x00000004 }, + { 0x08c0539e, 0x00000040 }, + { 0x00008000, 0000000000 }, + { 0x03c00830, 0x00000004 }, + { 0x4200e000, 0000000000 }, + { 0x0000a000, 0x00000004 }, + { 0x200045e0, 0x00000004 }, + { 0x0000e5e1, 0000000000 }, + { 0x00000001, 0000000000 }, + { 0x000700c4, 0x00000004 }, + { 0x0800e394, 0000000000 }, + { 0000000000, 0000000000 }, + { 0x0000e8c4, 0x00000004 }, + { 0x0000e8c5, 0x00000004 }, + { 0x0000e8c6, 0x00000004 }, + { 0x0000e928, 0x00000004 }, + { 0x0000e929, 0x00000004 }, + { 0x0000e92a, 0x00000004 }, + { 0x000000c8, 0x00000008 }, + { 0x0000e928, 0x00000004 }, + { 0x0000e929, 0x00000004 }, + { 0x0000e92a, 0x00000004 }, + { 0x000000cf, 0x00000008 }, + { 0xdeadbeef, 0000000000 }, + { 0x00000116, 0000000000 }, + { 0x000700d3, 0x00000004 }, + { 0x080050e7, 0x00000004 }, + { 0x000700d4, 0x00000004 }, + { 0x0800401c, 0x00000004 }, + { 0x0000e01d, 0000000000 }, + { 0x02c02000, 0x00000004 }, + { 0x00060000, 0x00000004 }, + { 0x000000de, 0x00000034 }, + { 0x000000db, 0x00000008 }, + { 0x00008000, 0x00000004 }, + { 0xc000e000, 0000000000 }, + { 0x0000e1cc, 0x00000004 }, + { 0x0500e1cd, 0x00000004 }, + { 0x000ca000, 0x00000004 }, + { 0x000000e5, 0x00000034 }, + { 0x000000e1, 0x00000008 }, + { 0x0000a000, 0000000000 }, + { 0x0019e1cc, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x0500a000, 0x00000004 }, + { 0x080041cd, 0x00000004 }, + { 0x000ca000, 0x00000004 }, + { 0x000000fb, 0x00000034 }, + { 0x0000004a, 0x00000008 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0x000c2000, 0x00000004 }, + { 0x001d0018, 0x00000004 }, + { 0x001a0001, 0x00000004 }, + { 0x000000fb, 0x00000034 }, + { 0x0000004a, 0x00000008 }, + { 0x0500a04a, 0x00000008 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, +}; + + +#endif diff --git a/drivers/char/drm/radeon_state.c b/drivers/char/drm/radeon_state.c index 6f75512f591..11c146b4921 100644 --- a/drivers/char/drm/radeon_state.c +++ b/drivers/char/drm/radeon_state.c @@ -1662,7 +1662,7 @@ static int radeon_cp_dispatch_texture(struct drm_device * dev, u32 height; int i; u32 texpitch, microtile; - u32 offset; + u32 offset, byte_offset; RING_LOCALS; if (radeon_check_and_fixup_offset(dev_priv, file_priv, &tex->offset)) { @@ -1727,6 +1727,13 @@ static int radeon_cp_dispatch_texture(struct drm_device * dev, } else microtile = 0; + /* this might fail for zero-sized uploads - are those illegal? */ + if (!radeon_check_offset(dev_priv, tex->offset + image->height * + blit_width - 1)) { + DRM_ERROR("Invalid final destination offset\n"); + return -EINVAL; + } + DRM_DEBUG("tex=%dx%d blit=%d\n", tex_width, tex->height, blit_width); do { @@ -1840,6 +1847,7 @@ static int radeon_cp_dispatch_texture(struct drm_device * dev, } #undef RADEON_COPY_MT + byte_offset = (image->y & ~2047) * blit_width; buf->file_priv = file_priv; buf->used = size; offset = dev_priv->gart_buffers_offset + buf->offset; @@ -1854,9 +1862,9 @@ static int radeon_cp_dispatch_texture(struct drm_device * dev, RADEON_DP_SRC_SOURCE_MEMORY | RADEON_GMC_CLR_CMP_CNTL_DIS | RADEON_GMC_WR_MSK_DIS); OUT_RING((spitch << 22) | (offset >> 10)); - OUT_RING((texpitch << 22) | (tex->offset >> 10)); + OUT_RING((texpitch << 22) | ((tex->offset >> 10) + (byte_offset >> 10))); OUT_RING(0); - OUT_RING((image->x << 16) | image->y); + OUT_RING((image->x << 16) | (image->y % 2048)); OUT_RING((image->width << 16) | height); RADEON_WAIT_UNTIL_2D_IDLE(); ADVANCE_RING(); @@ -3037,6 +3045,9 @@ static int radeon_cp_getparam(struct drm_device *dev, void *data, struct drm_fil case RADEON_PARAM_FB_LOCATION: value = radeon_read_fb_location(dev_priv); break; + case RADEON_PARAM_NUM_GB_PIPES: + value = dev_priv->num_gb_pipes; + break; default: DRM_DEBUG("Invalid parameter %d\n", param->param); return -EINVAL; diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c index dd68f8541c2..db2ae421627 100644 --- a/drivers/char/hvc_xen.c +++ b/drivers/char/hvc_xen.c @@ -39,9 +39,14 @@ static int xencons_irq; /* ------------------------------------------------------------------ */ +static unsigned long console_pfn = ~0ul; + static inline struct xencons_interface *xencons_interface(void) { - return mfn_to_virt(xen_start_info->console.domU.mfn); + if (console_pfn == ~0ul) + return mfn_to_virt(xen_start_info->console.domU.mfn); + else + return __va(console_pfn << PAGE_SHIFT); } static inline void notify_daemon(void) @@ -101,20 +106,32 @@ static int __init xen_init(void) { struct hvc_struct *hp; - if (!is_running_on_xen()) - return 0; + if (!is_running_on_xen() || + is_initial_xendomain() || + !xen_start_info->console.domU.evtchn) + return -ENODEV; xencons_irq = bind_evtchn_to_irq(xen_start_info->console.domU.evtchn); if (xencons_irq < 0) - xencons_irq = 0 /* NO_IRQ */; + xencons_irq = 0; /* NO_IRQ */ + hp = hvc_alloc(HVC_COOKIE, xencons_irq, &hvc_ops, 256); if (IS_ERR(hp)) return PTR_ERR(hp); hvc = hp; + + console_pfn = mfn_to_pfn(xen_start_info->console.domU.mfn); + return 0; } +void xen_console_resume(void) +{ + if (xencons_irq) + rebind_evtchn_irq(xen_start_info->console.domU.evtchn, xencons_irq); +} + static void __exit xen_fini(void) { if (hvc) @@ -134,12 +151,28 @@ module_init(xen_init); module_exit(xen_fini); console_initcall(xen_cons_init); +static void raw_console_write(const char *str, int len) +{ + while(len > 0) { + int rc = HYPERVISOR_console_io(CONSOLEIO_write, len, (char *)str); + if (rc <= 0) + break; + + str += rc; + len -= rc; + } +} + +#ifdef CONFIG_EARLY_PRINTK static void xenboot_write_console(struct console *console, const char *string, unsigned len) { unsigned int linelen, off = 0; const char *pos; + raw_console_write(string, len); + + write_console(0, "(early) ", 8); while (off < len && NULL != (pos = strchr(string+off, '\n'))) { linelen = pos-string+off; if (off + linelen > len) @@ -155,5 +188,23 @@ static void xenboot_write_console(struct console *console, const char *string, struct console xenboot_console = { .name = "xenboot", .write = xenboot_write_console, - .flags = CON_PRINTBUFFER | CON_BOOT, + .flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME, }; +#endif /* CONFIG_EARLY_PRINTK */ + +void xen_raw_console_write(const char *str) +{ + raw_console_write(str, strlen(str)); +} + +void xen_raw_printk(const char *fmt, ...) +{ + static char buf[512]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + xen_raw_console_write(buf); +} diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index e94bee03231..750131010af 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -3322,7 +3322,7 @@ static int send_break(struct tty_struct *tty, unsigned int duration) msleep_interruptible(duration); tty->ops->break_ctl(tty, 0); tty_write_unlock(tty); - if (!signal_pending(current)) + if (signal_pending(current)) return -EINTR; return 0; } diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index b1a757a5ee2..8f81139d619 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -981,16 +981,9 @@ EXPORT_SYMBOL_GPL(tty_perform_flush); int n_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { - struct tty_struct *real_tty; unsigned long flags; int retval; - if (tty->driver->type == TTY_DRIVER_TYPE_PTY && - tty->driver->subtype == PTY_TYPE_MASTER) - real_tty = tty->link; - else - real_tty = tty; - switch (cmd) { case TCXONC: retval = tty_check_change(tty); diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index 85e2ba7fcfb..bf4830082a1 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -27,6 +27,8 @@ #include <linux/moduleparam.h> #include <linux/connector.h> #include <linux/mutex.h> +#include <linux/proc_fs.h> +#include <linux/spinlock.h> #include <net/sock.h> @@ -403,6 +405,40 @@ static void cn_callback(void *data) mutex_unlock(¬ify_lock); } +static int cn_proc_show(struct seq_file *m, void *v) +{ + struct cn_queue_dev *dev = cdev.cbdev; + struct cn_callback_entry *cbq; + + seq_printf(m, "Name ID\n"); + + spin_lock_bh(&dev->queue_lock); + + list_for_each_entry(cbq, &dev->queue_list, callback_entry) { + seq_printf(m, "%-15s %u:%u\n", + cbq->id.name, + cbq->id.id.idx, + cbq->id.id.val); + } + + spin_unlock_bh(&dev->queue_lock); + + return 0; +} + +static int cn_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cn_proc_show, NULL); +} + +static const struct file_operations cn_file_ops = { + .owner = THIS_MODULE, + .open = cn_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release +}; + static int __devinit cn_init(void) { struct cn_dev *dev = &cdev; @@ -434,6 +470,8 @@ static int __devinit cn_init(void) return -EINVAL; } + proc_net_fops_create(&init_net, "connector", S_IRUGO, &cn_file_ops); + return 0; } @@ -443,6 +481,8 @@ static void __devexit cn_fini(void) cn_already_initialized = 0; + proc_net_remove(&init_net, "connector"); + cn_del_callback(&dev->id); cn_queue_free_dev(dev->cbdev); netlink_kernel_release(dev->nls); diff --git a/drivers/firewire/Kconfig b/drivers/firewire/Kconfig index fb4d391810b..76f26710fc1 100644 --- a/drivers/firewire/Kconfig +++ b/drivers/firewire/Kconfig @@ -1,28 +1,26 @@ -comment "An alternative FireWire stack is available with EXPERIMENTAL=y" +comment "A new alternative FireWire stack is available with EXPERIMENTAL=y" depends on EXPERIMENTAL=n +comment "Enable only one of the two stacks, unless you know what you are doing" + depends on EXPERIMENTAL + config FIREWIRE - tristate "IEEE 1394 (FireWire) support - alternative stack, EXPERIMENTAL" + tristate "New FireWire stack, EXPERIMENTAL" depends on EXPERIMENTAL select CRC_ITU_T help This is the "Juju" FireWire stack, a new alternative implementation designed for robustness and simplicity. You can build either this - stack, or the classic stack (the ieee1394 driver, ohci1394 etc.) - or both. Please read http://wiki.linux1394.org/JujuMigration before - you enable the new stack. + stack, or the old stack (the ieee1394 driver, ohci1394 etc.) or both. + Please read http://wiki.linux1394.org/JujuMigration before you + enable the new stack. To compile this driver as a module, say M here: the module will be called firewire-core. It functionally replaces ieee1394, raw1394, and video1394. - NOTE: - - You should only build ONE of the stacks, unless you REALLY know what - you are doing. - config FIREWIRE_OHCI - tristate "Support for OHCI FireWire host controllers" + tristate "OHCI-1394 controllers" depends on PCI && FIREWIRE help Enable this driver if you have a FireWire controller based @@ -33,12 +31,12 @@ config FIREWIRE_OHCI called firewire-ohci. It replaces ohci1394 of the classic IEEE 1394 stack. - NOTE: + NOTE: - You should only build ohci1394 or firewire-ohci, but not both. - If you nevertheless want to install both, you should configure them - only as modules and blacklist the driver(s) which you don't want to - have auto-loaded. Add either + You should only build either firewire-ohci or the old ohci1394 driver, + but not both. If you nevertheless want to install both, you should + configure them only as modules and blacklist the driver(s) which you + don't want to have auto-loaded. Add either blacklist firewire-ohci or @@ -60,7 +58,7 @@ config FIREWIRE_OHCI_DEBUG default y config FIREWIRE_SBP2 - tristate "Support for storage devices (SBP-2 protocol driver)" + tristate "Storage devices (SBP-2 protocol)" depends on FIREWIRE && SCSI help This option enables you to use SBP-2 devices connected to a diff --git a/drivers/firewire/fw-cdev.c b/drivers/firewire/fw-cdev.c index dda14015e87..c639915fc3c 100644 --- a/drivers/firewire/fw-cdev.c +++ b/drivers/firewire/fw-cdev.c @@ -205,6 +205,7 @@ fw_device_op_read(struct file *file, return dequeue_event(client, buffer, count); } +/* caller must hold card->lock so that node pointers can be dereferenced here */ static void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, struct client *client) @@ -214,7 +215,6 @@ fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, event->closure = client->bus_reset_closure; event->type = FW_CDEV_EVENT_BUS_RESET; event->generation = client->device->generation; - smp_rmb(); /* node_id must not be older than generation */ event->node_id = client->device->node_id; event->local_node_id = card->local_node->node_id; event->bm_node_id = 0; /* FIXME: We don't track the BM. */ @@ -274,6 +274,7 @@ static int ioctl_get_info(struct client *client, void *buffer) { struct fw_cdev_get_info *get_info = buffer; struct fw_cdev_event_bus_reset bus_reset; + struct fw_card *card = client->device->card; unsigned long ret = 0; client->version = get_info->version; @@ -299,13 +300,17 @@ static int ioctl_get_info(struct client *client, void *buffer) client->bus_reset_closure = get_info->bus_reset_closure; if (get_info->bus_reset != 0) { void __user *uptr = u64_to_uptr(get_info->bus_reset); + unsigned long flags; + spin_lock_irqsave(&card->lock, flags); fill_bus_reset_event(&bus_reset, client); + spin_unlock_irqrestore(&card->lock, flags); + if (copy_to_user(uptr, &bus_reset, sizeof(bus_reset))) return -EFAULT; } - get_info->card = client->device->card->index; + get_info->card = card->index; return 0; } diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c index 4f02c55f13e..0b66306af47 100644 --- a/drivers/firewire/fw-ohci.c +++ b/drivers/firewire/fw-ohci.c @@ -265,27 +265,25 @@ static void log_irqs(u32 evt) !(evt & OHCI1394_busReset)) return; - printk(KERN_DEBUG KBUILD_MODNAME ": IRQ " - "%08x%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" : "", - evt & OHCI1394_reqTxComplete ? " AT_req" : "", - evt & OHCI1394_respTxComplete ? " AT_resp" : "", - evt & OHCI1394_isochRx ? " IR" : "", - evt & OHCI1394_isochTx ? " IT" : "", - evt & OHCI1394_postedWriteErr ? " postedWriteErr" : "", - evt & OHCI1394_cycleTooLong ? " cycleTooLong" : "", - evt & OHCI1394_cycle64Seconds ? " cycle64Seconds" : "", - evt & OHCI1394_regAccessFail ? " regAccessFail" : "", - evt & OHCI1394_busReset ? " busReset" : "", - evt & ~(OHCI1394_selfIDComplete | OHCI1394_RQPkt | - OHCI1394_RSPkt | OHCI1394_reqTxComplete | - OHCI1394_respTxComplete | OHCI1394_isochRx | - OHCI1394_isochTx | OHCI1394_postedWriteErr | - OHCI1394_cycleTooLong | OHCI1394_cycle64Seconds | - OHCI1394_regAccessFail | OHCI1394_busReset) + fw_notify("IRQ %08x%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" : "", + evt & OHCI1394_reqTxComplete ? " AT_req" : "", + evt & OHCI1394_respTxComplete ? " AT_resp" : "", + evt & OHCI1394_isochRx ? " IR" : "", + evt & OHCI1394_isochTx ? " IT" : "", + evt & OHCI1394_postedWriteErr ? " postedWriteErr" : "", + evt & OHCI1394_cycleTooLong ? " cycleTooLong" : "", + evt & OHCI1394_cycle64Seconds ? " cycle64Seconds" : "", + evt & OHCI1394_regAccessFail ? " regAccessFail" : "", + evt & OHCI1394_busReset ? " busReset" : "", + evt & ~(OHCI1394_selfIDComplete | OHCI1394_RQPkt | + OHCI1394_RSPkt | OHCI1394_reqTxComplete | + OHCI1394_respTxComplete | OHCI1394_isochRx | + OHCI1394_isochTx | OHCI1394_postedWriteErr | + OHCI1394_cycleTooLong | OHCI1394_cycle64Seconds | + OHCI1394_regAccessFail | OHCI1394_busReset) ? " ?" : ""); } @@ -308,23 +306,22 @@ static void log_selfids(int node_id, int generation, int self_id_count, u32 *s) if (likely(!(param_debug & OHCI_PARAM_DEBUG_SELFIDS))) return; - printk(KERN_DEBUG KBUILD_MODNAME ": %d selfIDs, generation %d, " - "local node ID %04x\n", self_id_count, generation, node_id); + fw_notify("%d selfIDs, generation %d, local node ID %04x\n", + self_id_count, generation, node_id); for (; self_id_count--; ++s) if ((*s & 1 << 23) == 0) - printk(KERN_DEBUG "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" : ""); + fw_notify("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 - printk(KERN_DEBUG "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)); + fw_notify("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)); } static const char *evts[] = { @@ -373,15 +370,14 @@ static void log_ar_at_event(char dir, int speed, u32 *header, int evt) evt = 0x1f; if (evt == OHCI1394_evt_bus_reset) { - printk(KERN_DEBUG "A%c evt_bus_reset, generation %d\n", - dir, (header[2] >> 16) & 0xff); + fw_notify("A%c evt_bus_reset, generation %d\n", + dir, (header[2] >> 16) & 0xff); return; } if (header[0] == ~header[1]) { - printk(KERN_DEBUG "A%c %s, %s, %08x\n", - dir, evts[evt], phys[header[0] >> 30 & 0x3], - header[0]); + fw_notify("A%c %s, %s, %08x\n", + dir, evts[evt], phys[header[0] >> 30 & 0x3], header[0]); return; } @@ -400,24 +396,23 @@ static void log_ar_at_event(char dir, int speed, u32 *header, int evt) switch (tcode) { case 0xe: case 0xa: - printk(KERN_DEBUG "A%c %s, %s\n", - dir, evts[evt], tcodes[tcode]); + fw_notify("A%c %s, %s\n", dir, evts[evt], tcodes[tcode]); break; case 0x0: case 0x1: case 0x4: case 0x5: case 0x9: - printk(KERN_DEBUG "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); + 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); break; default: - printk(KERN_DEBUG "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); + 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); } } @@ -548,6 +543,11 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer) p.header_length = 12; p.payload_length = 0; break; + + default: + /* FIXME: Stop context, discard everything, and restart? */ + p.header_length = 0; + p.payload_length = 0; } p.payload = (void *) buffer + p.header_length; @@ -1468,6 +1468,9 @@ static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length) reg_write(ohci, OHCI1394_HCControlClear, OHCI1394_HCControl_noByteSwapData); + reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->self_id_bus); + reg_write(ohci, OHCI1394_LinkControlClear, + OHCI1394_LinkControl_rcvPhyPkt); reg_write(ohci, OHCI1394_LinkControlSet, OHCI1394_LinkControl_rcvSelfID | OHCI1394_LinkControl_cycleTimerEnable | @@ -1481,7 +1484,6 @@ static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length) ar_context_run(&ohci->ar_request_ctx); ar_context_run(&ohci->ar_response_ctx); - reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->self_id_bus); reg_write(ohci, OHCI1394_PhyUpperBound, 0x00010000); reg_write(ohci, OHCI1394_IntEventClear, ~0); reg_write(ohci, OHCI1394_IntMaskClear, ~0); diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index b2458bb8e9c..227d2e036cd 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c @@ -1051,7 +1051,8 @@ static int sbp2_scan_unit_dir(struct sbp2_target *tgt, u32 *directory, break; case SBP2_CSR_LOGICAL_UNIT_DIRECTORY: - if (sbp2_scan_logical_unit_dir(tgt, ci.p + value) < 0) + /* Adjust for the increment in the iterator */ + if (sbp2_scan_logical_unit_dir(tgt, ci.p - 1 + value) < 0) return -ENOMEM; break; } diff --git a/drivers/firewire/fw-transaction.c b/drivers/firewire/fw-transaction.c index ccf0e4cf108..03ae8a77c47 100644 --- a/drivers/firewire/fw-transaction.c +++ b/drivers/firewire/fw-transaction.c @@ -20,6 +20,7 @@ #include <linux/completion.h> #include <linux/kernel.h> +#include <linux/kref.h> #include <linux/module.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -297,37 +298,55 @@ EXPORT_SYMBOL(fw_send_request); struct fw_phy_packet { struct fw_packet packet; struct completion done; + struct kref kref; }; -static void -transmit_phy_packet_callback(struct fw_packet *packet, - struct fw_card *card, int status) +static void phy_packet_release(struct kref *kref) +{ + struct fw_phy_packet *p = + container_of(kref, struct fw_phy_packet, kref); + kfree(p); +} + +static void transmit_phy_packet_callback(struct fw_packet *packet, + struct fw_card *card, int status) { struct fw_phy_packet *p = container_of(packet, struct fw_phy_packet, packet); complete(&p->done); + kref_put(&p->kref, phy_packet_release); } void fw_send_phy_config(struct fw_card *card, int node_id, int generation, int gap_count) { - struct fw_phy_packet p; + struct fw_phy_packet *p; + long timeout = DIV_ROUND_UP(HZ, 10); u32 data = PHY_IDENTIFIER(PHY_PACKET_CONFIG) | PHY_CONFIG_ROOT_ID(node_id) | PHY_CONFIG_GAP_COUNT(gap_count); - p.packet.header[0] = data; - p.packet.header[1] = ~data; - p.packet.header_length = 8; - p.packet.payload_length = 0; - p.packet.speed = SCODE_100; - p.packet.generation = generation; - p.packet.callback = transmit_phy_packet_callback; - init_completion(&p.done); - - card->driver->send_request(card, &p.packet); - wait_for_completion(&p.done); + p = kmalloc(sizeof(*p), GFP_KERNEL); + if (p == NULL) + return; + + p->packet.header[0] = data; + p->packet.header[1] = ~data; + p->packet.header_length = 8; + p->packet.payload_length = 0; + p->packet.speed = SCODE_100; + p->packet.generation = generation; + p->packet.callback = transmit_phy_packet_callback; + init_completion(&p->done); + kref_set(&p->kref, 2); + + card->driver->send_request(card, &p->packet); + timeout = wait_for_completion_timeout(&p->done, timeout); + kref_put(&p->kref, phy_packet_release); + + /* will leak p if the callback is never executed */ + WARN_ON(timeout == 0); } void fw_flush_transactions(struct fw_card *card) @@ -572,7 +591,8 @@ allocate_request(struct fw_packet *p) break; default: - BUG(); + fw_error("ERROR - corrupt request received - %08x %08x %08x\n", + p->header[0], p->header[1], p->header[2]); return NULL; } diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index bbd28342e77..008c38ba774 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -28,12 +28,18 @@ config DEBUG_GPIO comment "I2C GPIO expanders:" config GPIO_PCA953X - tristate "PCA953x I/O ports" + tristate "PCA953x, PCA955x, and MAX7310 I/O ports" depends on I2C help - Say yes here to support the PCA9534 (8-bit), PCA9535 (16-bit), - PCA9536 (4-bit), PCA9537 (4-bit), PCA9538 (8-bit), and PCA9539 - (16-bit) I/O ports. These parts are made by NXP and TI. + Say yes here to provide access to several register-oriented + SMBus I/O expanders, made mostly by NXP or TI. Compatible + models include: + + 4 bits: pca9536, pca9537 + + 8 bits: max7310, pca9534, pca9538, pca9554, pca9557 + + 16 bits: pca9535, pca9539, pca9555 This driver can also be built as a module. If so, the module will be called pca953x. diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c index 7e40e8a55ed..a380730b61a 100644 --- a/drivers/gpio/pca953x.c +++ b/drivers/gpio/pca953x.c @@ -33,7 +33,7 @@ static const struct i2c_device_id pca953x_id[] = { { "pca9554", 8, }, { "pca9555", 16, }, { "pca9557", 8, }, - /* REVISIT several pca955x parts should work here too */ + { "max7310", 8, }, { } }; MODULE_DEVICE_TABLE(i2c, pca953x_id); diff --git a/drivers/hwmon/abituguru3.c b/drivers/hwmon/abituguru3.c index ed33fddc4de..f00f497b9ca 100644 --- a/drivers/hwmon/abituguru3.c +++ b/drivers/hwmon/abituguru3.c @@ -30,6 +30,7 @@ #include <linux/platform_device.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> +#include <linux/dmi.h> #include <asm/io.h> /* uGuru3 bank addresses */ @@ -323,7 +324,7 @@ static const struct abituguru3_motherboard_info abituguru3_motherboards[] = { { "AUX1 Fan", 36, 2, 60, 1, 0 }, { NULL, 0, 0, 0, 0, 0 } } }, - { 0x0013, "unknown", { + { 0x0013, "Abit AW8D", { { "CPU Core", 0, 0, 10, 1, 0 }, { "DDR", 1, 0, 10, 1, 0 }, { "DDR VTT", 2, 0, 10, 1, 0 }, @@ -349,6 +350,7 @@ static const struct abituguru3_motherboard_info abituguru3_motherboards[] = { { "AUX2 Fan", 36, 2, 60, 1, 0 }, { "AUX3 Fan", 37, 2, 60, 1, 0 }, { "AUX4 Fan", 38, 2, 60, 1, 0 }, + { "AUX5 Fan", 39, 2, 60, 1, 0 }, { NULL, 0, 0, 0, 0, 0 } } }, { 0x0014, "Abit AB9 Pro", { @@ -1111,11 +1113,12 @@ static int __init abituguru3_detect(void) { /* See if there is an uguru3 there. An idle uGuru3 will hold 0x00 or 0x08 at DATA and 0xAC at CMD. Sometimes the uGuru3 will hold 0x05 - at CMD instead, why is unknown. So we test for 0x05 too. */ + or 0x55 at CMD instead, why is unknown. */ u8 data_val = inb_p(ABIT_UGURU3_BASE + ABIT_UGURU3_DATA); u8 cmd_val = inb_p(ABIT_UGURU3_BASE + ABIT_UGURU3_CMD); if (((data_val == 0x00) || (data_val == 0x08)) && - ((cmd_val == 0xAC) || (cmd_val == 0x05))) + ((cmd_val == 0xAC) || (cmd_val == 0x05) || + (cmd_val == 0x55))) return ABIT_UGURU3_BASE; ABIT_UGURU3_DEBUG("no Abit uGuru3 found, data = 0x%02X, cmd = " @@ -1138,6 +1141,15 @@ static int __init abituguru3_init(void) int address, err; struct resource res = { .flags = IORESOURCE_IO }; +#ifdef CONFIG_DMI + const char *board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); + + /* safety check, refuse to load on non Abit motherboards */ + if (!force && (!board_vendor || + strcmp(board_vendor, "http://www.abit.com.tw/"))) + return -ENODEV; +#endif + address = abituguru3_detect(); if (address < 0) return address; diff --git a/drivers/hwmon/adt7473.c b/drivers/hwmon/adt7473.c index c1009d6f979..93dbf5e7ff8 100644 --- a/drivers/hwmon/adt7473.c +++ b/drivers/hwmon/adt7473.c @@ -309,6 +309,9 @@ no_sensor_update: ADT7473_REG_PWM_BHVR(i)); } + i = i2c_smbus_read_byte_data(client, ADT7473_REG_CFG4); + data->max_duty_at_overheat = !!(i & ADT7473_CFG4_MAX_DUTY_AT_OVT); + data->limits_last_updated = local_jiffies; data->limits_valid = 1; diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c index 26df06f840e..50f22690d61 100644 --- a/drivers/hwmon/hdaps.c +++ b/drivers/hwmon/hdaps.c @@ -516,17 +516,23 @@ static struct dmi_system_id __initdata hdaps_whitelist[] = { HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R52"), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61i"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61"), HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T41"), HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T42"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T43"), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61p"), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X41"), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61s"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad Z60m"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61m"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61p"), { .ident = NULL } }; diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index fa769690515..de698dc7302 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -251,10 +251,13 @@ static int lm75_detach_client(struct i2c_client *client) the SMBus standard. */ static int lm75_read_value(struct i2c_client *client, u8 reg) { + int value; + if (reg == LM75_REG_CONF) return i2c_smbus_read_byte_data(client, reg); - else - return swab16(i2c_smbus_read_word_data(client, reg)); + + value = i2c_smbus_read_word_data(client, reg); + return (value < 0) ? value : swab16(value); } static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value) @@ -287,9 +290,16 @@ static struct lm75_data *lm75_update_device(struct device *dev) int i; dev_dbg(&client->dev, "Starting lm75 update\n"); - for (i = 0; i < ARRAY_SIZE(data->temp); i++) - data->temp[i] = lm75_read_value(client, - LM75_REG_TEMP[i]); + for (i = 0; i < ARRAY_SIZE(data->temp); i++) { + int status; + + status = lm75_read_value(client, LM75_REG_TEMP[i]); + if (status < 0) + dev_dbg(&client->dev, "reg %d, err %d\n", + LM75_REG_TEMP[i], status); + else + data->temp[i] = status; + } data->last_updated = jiffies; data->valid = 1; } diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 182fe6a5605..ee5eca1c192 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -192,23 +192,20 @@ static int RANGE_TO_REG( int range ) { int i; - if ( range < lm85_range_map[0] ) { - return 0 ; - } else if ( range > lm85_range_map[15] ) { + if (range >= lm85_range_map[15]) return 15 ; - } else { /* find closest match */ - for ( i = 14 ; i >= 0 ; --i ) { - if ( range > lm85_range_map[i] ) { /* range bracketed */ - if ((lm85_range_map[i+1] - range) < - (range - lm85_range_map[i])) { - i++; - break; - } - break; - } + + /* Find the closest match */ + for (i = 14; i >= 0; --i) { + if (range >= lm85_range_map[i]) { + if ((lm85_range_map[i + 1] - range) < + (range - lm85_range_map[i])) + return i + 1; + return i; } } - return( i & 0x0f ); + + return 0; } #define RANGE_FROM_REG(val) (lm85_range_map[(val)&0x0f]) diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 1305ef190fc..9e8c875437b 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -290,12 +290,12 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) * bus, or started a new i2c message */ - if (iicstat & S3C2410_IICSTAT_LASTBIT && + if (iicstat & S3C2410_IICSTAT_LASTBIT && !(i2c->msg->flags & I2C_M_IGNORE_NAK)) { /* ack was not received... */ dev_dbg(i2c->dev, "ack was not received\n"); - s3c24xx_i2c_stop(i2c, -EREMOTEIO); + s3c24xx_i2c_stop(i2c, -ENXIO); goto out_ack; } @@ -305,7 +305,7 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) i2c->state = STATE_WRITE; /* terminate the transfer if there is nothing to do - * (used by the i2c probe to find devices */ + * as this is used by the i2c probe to find devices. */ if (is_lastmsg(i2c) && i2c->msg->len == 0) { s3c24xx_i2c_stop(i2c, 0); @@ -323,7 +323,17 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) * end of the message, and if so, work out what to do */ + if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) { + if (iicstat & S3C2410_IICSTAT_LASTBIT) { + dev_dbg(i2c->dev, "WRITE: No Ack\n"); + + s3c24xx_i2c_stop(i2c, -ECONNREFUSED); + goto out_ack; + } + } + retry_write: + if (!is_msgend(i2c)) { byte = i2c->msg->buf[i2c->msg_ptr++]; writeb(byte, i2c->regs + S3C2410_IICDS); @@ -377,17 +387,6 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) * going to do any more read/write */ - if (!(i2c->msg->flags & I2C_M_IGNORE_NAK) && - !(is_msglast(i2c) && is_lastmsg(i2c))) { - - if (iicstat & S3C2410_IICSTAT_LASTBIT) { - dev_dbg(i2c->dev, "READ: No Ack\n"); - - s3c24xx_i2c_stop(i2c, -ECONNREFUSED); - goto out_ack; - } - } - byte = readb(i2c->regs + S3C2410_IICDS); i2c->msg->buf[i2c->msg_ptr++] = byte; @@ -949,3 +948,4 @@ MODULE_DESCRIPTION("S3C24XX I2C Bus driver"); MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:s3c2410-i2c"); +MODULE_ALIAS("platform:s3c2440-i2c"); diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c index d024ac8fad1..cc24803fadf 100644 --- a/drivers/ide/arm/palm_bk3710.c +++ b/drivers/ide/arm/palm_bk3710.c @@ -353,8 +353,8 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev) struct clk *clkp; struct resource *mem, *irq; ide_hwif_t *hwif; - void __iomem *base; - int pribase, i; + unsigned long base; + int i; hw_regs_t hw; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; @@ -374,22 +374,27 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev) printk(KERN_ERR "failed to get memory region resource\n"); return -ENODEV; } + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (irq == NULL) { printk(KERN_ERR "failed to get IRQ resource\n"); return -ENODEV; } - base = (void *)mem->start; + if (request_mem_region(mem->start, mem->end - mem->start + 1, + "palm_bk3710") == NULL) { + printk(KERN_ERR "failed to request memory region\n"); + return -EBUSY; + } + + base = IO_ADDRESS(mem->start); /* Configure the Palm Chip controller */ - palm_bk3710_chipinit(base); + palm_bk3710_chipinit((void __iomem *)base); - pribase = mem->start + IDE_PALM_ATA_PRI_REG_OFFSET; for (i = 0; i < IDE_NR_PORTS - 2; i++) - hw.io_ports_array[i] = pribase + i; - hw.io_ports.ctl_addr = mem->start + - IDE_PALM_ATA_PRI_CTL_OFFSET; + hw.io_ports_array[i] = base + IDE_PALM_ATA_PRI_REG_OFFSET + i; + hw.io_ports.ctl_addr = base + IDE_PALM_ATA_PRI_CTL_OFFSET; hw.irq = irq->start; hw.chipset = ide_palm3710; @@ -434,4 +439,3 @@ static int __init palm_bk3710_init(void) module_init(palm_bk3710_init); MODULE_LICENSE("GPL"); - diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 380fa0c8cc8..d27061b3932 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -646,8 +646,6 @@ static int ide_register_port(ide_hwif_t *hwif) goto out; } - get_device(&hwif->gendev); - hwif->portdev = device_create_drvdata(ide_port_class, &hwif->gendev, MKDEV(0, 0), hwif, hwif->name); if (IS_ERR(hwif->portdev)) { diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index 55ec7f79877..8af88bf0969 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c @@ -76,7 +76,7 @@ static int proc_ide_read_mate ide_hwif_t *hwif = (ide_hwif_t *) data; int len; - if (hwif && hwif->mate && hwif->mate->present) + if (hwif && hwif->mate) len = sprintf(page, "%s\n", hwif->mate->name); else len = sprintf(page, "(none)\n"); diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 0c908ca3ff7..ab545ffa154 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -225,10 +225,10 @@ static u8 wait_drive_not_busy(ide_drive_t *drive) u8 stat; /* - * Last sector was transfered, wait until drive is ready. - * This can take up to 10 usec, but we will wait max 1 ms. + * Last sector was transfered, wait until device is ready. This can + * take up to 6 ms on some ATAPI devices, so we will wait max 10 ms. */ - for (retries = 0; retries < 100; retries++) { + for (retries = 0; retries < 1000; retries++) { stat = ide_read_status(drive); if (stat & BUSY_STAT) diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index c758dcb13b1..300431d080a 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -315,13 +315,14 @@ void ide_unregister(ide_hwif_t *hwif) BUG_ON(in_interrupt()); BUG_ON(irqs_disabled()); + mutex_lock(&ide_cfg_mtx); - spin_lock_irq(&ide_lock); - if (!hwif->present) - goto abort; - __ide_port_unregister_devices(hwif); - hwif->present = 0; + spin_lock_irq(&ide_lock); + if (hwif->present) { + __ide_port_unregister_devices(hwif); + hwif->present = 0; + } spin_unlock_irq(&ide_lock); ide_proc_unregister_port(hwif); @@ -351,16 +352,15 @@ void ide_unregister(ide_hwif_t *hwif) blk_unregister_region(MKDEV(hwif->major, 0), MAX_DRIVES<<PARTN_BITS); kfree(hwif->sg_table); unregister_blkdev(hwif->major, hwif->name); - spin_lock_irq(&ide_lock); if (hwif->dma_base) ide_release_dma_engine(hwif); + spin_lock_irq(&ide_lock); /* restore hwif data to pristine status */ ide_init_port_data(hwif, hwif->index); - -abort: spin_unlock_irq(&ide_lock); + mutex_unlock(&ide_cfg_mtx); } @@ -1094,13 +1094,6 @@ struct bus_type ide_bus_type = { EXPORT_SYMBOL_GPL(ide_bus_type); -static void ide_port_class_release(struct device *portdev) -{ - ide_hwif_t *hwif = dev_get_drvdata(portdev); - - put_device(&hwif->gendev); -} - int ide_vlb_clk; EXPORT_SYMBOL_GPL(ide_vlb_clk); @@ -1305,7 +1298,6 @@ static int __init ide_init(void) ret = PTR_ERR(ide_port_class); goto out_port_class; } - ide_port_class->dev_release = ide_port_class_release; init_ide_data(); diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c index f633b6b3c7f..3381424d70a 100644 --- a/drivers/ide/legacy/ide-cs.c +++ b/drivers/ide/legacy/ide-cs.c @@ -410,6 +410,7 @@ static struct pcmcia_device_id ide_ids[] = { PCMCIA_DEVICE_MANF_CARD(0x001c, 0x0001), /* Mitsubishi CFA */ PCMCIA_DEVICE_MANF_CARD(0x0032, 0x0704), PCMCIA_DEVICE_MANF_CARD(0x0045, 0x0401), /* SanDisk CFA */ + PCMCIA_DEVICE_MANF_CARD(0x004f, 0x0000), /* Kingston */ PCMCIA_DEVICE_MANF_CARD(0x0098, 0x0000), /* Toshiba */ PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x002d), PCMCIA_DEVICE_MANF_CARD(0x00ce, 0x0000), /* Samsung */ @@ -439,6 +440,7 @@ static struct pcmcia_device_id ide_ids[] = { PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCIDE", 0x547e66dc, 0x5c5ab149), PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCIDEII", 0x547e66dc, 0xb3662674), PCMCIA_DEVICE_PROD_ID12("LOOKMEET", "CBIDE2 ", 0xe37be2b5, 0x8671043b), + PCMCIA_DEVICE_PROD_ID12("M-Systems", "CF300", 0x7ed2ad87, 0x7e9e78ee), PCMCIA_DEVICE_PROD_ID12("M-Systems", "CF500", 0x7ed2ad87, 0x7a13045c), PCMCIA_DEVICE_PROD_ID2("NinjaATA-", 0xebe0bd79), PCMCIA_DEVICE_PROD_ID12("PCMCIA", "CD-ROM", 0x281f1c5d, 0x66536591), @@ -449,6 +451,7 @@ static struct pcmcia_device_id ide_ids[] = { PCMCIA_DEVICE_PROD_ID12("SMI VENDOR", "SMI PRODUCT", 0x30896c92, 0x703cc5f6), PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "MK2001MPL", 0xb4585a1a, 0x3489e003), PCMCIA_DEVICE_PROD_ID1("TRANSCEND 512M ", 0xd0909443), + PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS1GCF45", 0x709b1bf1, 0xf68b6f32), PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS1GCF80", 0x709b1bf1, 0x2a54d4b1), PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS2GCF120", 0x709b1bf1, 0x969aa4f2), PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS4GCF120", 0x709b1bf1, 0xf54a91c8), diff --git a/drivers/ieee1394/Kconfig b/drivers/ieee1394/Kconfig index 545663ef820..95f45f9b8e5 100644 --- a/drivers/ieee1394/Kconfig +++ b/drivers/ieee1394/Kconfig @@ -4,7 +4,7 @@ menu "IEEE 1394 (FireWire) support" source "drivers/firewire/Kconfig" config IEEE1394 - tristate "IEEE 1394 (FireWire) support" + tristate "Stable FireWire stack" depends on PCI || BROKEN help IEEE 1394 describes a high performance serial bus, which is also @@ -19,30 +19,45 @@ config IEEE1394 To compile this driver as a module, say M here: the module will be called ieee1394. -comment "Subsystem Options" - depends on IEEE1394 - -config IEEE1394_VERBOSEDEBUG - bool "Excessive debugging output" - depends on IEEE1394 +config IEEE1394_OHCI1394 + tristate "OHCI-1394 controllers" + depends on PCI && IEEE1394 help - If you say Y here, you will get very verbose debugging logs from - the subsystem which includes a dump of the header of every sent - and received packet. This can amount to a high amount of data - collected in a very short time which is usually also saved to - disk by the system logging daemons. + Enable this driver if you have an IEEE 1394 controller based on the + OHCI-1394 specification. The current driver is only tested with OHCI + chipsets made by Texas Instruments and NEC. Most third-party vendors + use one of these chipsets. It should work with any OHCI-1394 + compliant card, however. - Say Y if you really want or need the debugging output, everyone - else says N. + To compile this driver as a module, say M here: the + module will be called ohci1394. -comment "Controllers" - depends on IEEE1394 + NOTE: -comment "Texas Instruments PCILynx requires I2C" + You should only build either ohci1394 or the new firewire-ohci driver, + but not both. If you nevertheless want to install both, you should + configure them only as modules and blacklist the driver(s) which you + don't want to have auto-loaded. Add either + + blacklist firewire-ohci + or + blacklist ohci1394 + blacklist video1394 + blacklist dv1394 + + to /etc/modprobe.conf or /etc/modprobe.d/* and update modprobe.conf + depending on your distribution. The latter two modules should be + blacklisted together with ohci1394 because they depend on ohci1394. + + If you have an old modprobe which doesn't implement the blacklist + directive, use "install modulename /bin/true" for the modules to be + blacklisted. + +comment "PCILynx controller requires I2C" depends on IEEE1394 && I2C=n config IEEE1394_PCILYNX - tristate "Texas Instruments PCILynx support" + tristate "PCILynx controller" depends on PCI && IEEE1394 && I2C select I2C_ALGOBIT help @@ -57,35 +72,11 @@ config IEEE1394_PCILYNX PowerMacs G3 B&W contain the PCILynx controller. Therefore almost everybody can say N here. -config IEEE1394_OHCI1394 - tristate "OHCI-1394 support" - depends on PCI && IEEE1394 - help - Enable this driver if you have an IEEE 1394 controller based on the - OHCI-1394 specification. The current driver is only tested with OHCI - chipsets made by Texas Instruments and NEC. Most third-party vendors - use one of these chipsets. It should work with any OHCI-1394 - compliant card, however. - - To compile this driver as a module, say M here: the - module will be called ohci1394. - -comment "Protocols" - depends on IEEE1394 - -config IEEE1394_VIDEO1394 - tristate "OHCI-1394 Video support" - depends on IEEE1394 && IEEE1394_OHCI1394 - help - This option enables video device usage for OHCI-1394 cards. Enable - this option only if you have an IEEE 1394 video device connected to - an OHCI-1394 card. - comment "SBP-2 support (for storage devices) requires SCSI" depends on IEEE1394 && SCSI=n config IEEE1394_SBP2 - tristate "SBP-2 support (Harddisks etc.)" + tristate "Storage devices (SBP-2 protocol)" depends on IEEE1394 && SCSI help This option enables you to use SBP-2 devices connected to an IEEE @@ -127,24 +118,47 @@ config IEEE1394_ETH1394 The module is called eth1394 although it does not emulate Ethernet. +config IEEE1394_RAWIO + tristate "raw1394 userspace interface" + depends on IEEE1394 + help + This option adds support for the raw1394 device file which enables + direct communication of user programs with IEEE 1394 devices + (isochronous and asynchronous). Almost all application programs + which access FireWire require this option. + + To compile this driver as a module, say M here: the module will be + called raw1394. + +config IEEE1394_VIDEO1394 + tristate "video1394 userspace interface" + depends on IEEE1394 && IEEE1394_OHCI1394 + help + This option adds support for the video1394 device files which enable + isochronous communication of user programs with IEEE 1394 devices, + especially video capture or export. This interface is used by all + libdc1394 based programs and by several other programs, in addition to + the raw1394 interface. It is generally not required for DV capture. + + To compile this driver as a module, say M here: the module will be + called video1394. + config IEEE1394_DV1394 - tristate "OHCI-DV I/O support (deprecated)" + tristate "dv1394 userspace interface (deprecated)" depends on IEEE1394 && IEEE1394_OHCI1394 help The dv1394 driver is unsupported and may be removed from Linux in a future release. Its functionality is now provided by raw1394 together with libraries such as libiec61883. -config IEEE1394_RAWIO - tristate "Raw IEEE1394 I/O support" +config IEEE1394_VERBOSEDEBUG + bool "Excessive debugging output" depends on IEEE1394 help - This option adds support for the raw1394 device file which enables - direct communication of user programs with the IEEE 1394 bus and thus - with the attached peripherals. Almost all application programs which - access FireWire require this option. + If you say Y here, you will get very verbose debugging logs from the + ieee1394 drivers, including sent and received packet headers. This + will quickly result in large amounts of data sent to the system log. - To compile this driver as a module, say M here: the module will be - called raw1394. + Say Y if you really need the debugging output. Everyone else says N. endmenu diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index f806da184b5..caed42bf7ef 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -423,7 +423,7 @@ static void ib_uverbs_async_handler(struct ib_uverbs_file *file, unsigned long flags; spin_lock_irqsave(&file->async_file->lock, flags); - if (!file->async_file->is_closed) { + if (file->async_file->is_closed) { spin_unlock_irqrestore(&file->async_file->lock, flags); return; } diff --git a/drivers/infiniband/hw/mthca/mthca_memfree.c b/drivers/infiniband/hw/mthca/mthca_memfree.c index b224079d4e1..d5862e5d99a 100644 --- a/drivers/infiniband/hw/mthca/mthca_memfree.c +++ b/drivers/infiniband/hw/mthca/mthca_memfree.c @@ -109,7 +109,11 @@ static int mthca_alloc_icm_pages(struct scatterlist *mem, int order, gfp_t gfp_m { struct page *page; - page = alloc_pages(gfp_mask, order); + /* + * Use __GFP_ZERO because buggy firmware assumes ICM pages are + * cleared, and subtle failures are seen if they aren't. + */ + page = alloc_pages(gfp_mask | __GFP_ZERO, order); if (!page) return -ENOMEM; diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index 99b3c4ae86e..d617da9bd35 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -2456,10 +2456,8 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, if ((page_count!=0)&&(page_count<<12)-(region->offset&(4096-1))>=region->length) goto enough_pages; if ((page_count&0x01FF) == 0) { - if (page_count>(1024*512)) { + if (page_count >= 1024 * 512) { ib_umem_release(region); - pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase, - vpbl.pbl_pbase); nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index); kfree(nesmr); diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c index eebc72465fc..72c63e5dd63 100644 --- a/drivers/input/ff-core.c +++ b/drivers/input/ff-core.c @@ -28,6 +28,7 @@ #include <linux/input.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/sched.h> /* * Check that the effect_id is a valid effect and whether the user @@ -166,8 +167,10 @@ int input_ff_upload(struct input_dev *dev, struct ff_effect *effect, if (ret) goto out; + spin_lock_irq(&dev->event_lock); ff->effects[id] = *effect; ff->effect_owners[id] = file; + spin_unlock_irq(&dev->event_lock); out: mutex_unlock(&ff->mutex); @@ -189,16 +192,22 @@ static int erase_effect(struct input_dev *dev, int effect_id, if (error) return error; + spin_lock_irq(&dev->event_lock); ff->playback(dev, effect_id, 0); + ff->effect_owners[effect_id] = NULL; + spin_unlock_irq(&dev->event_lock); if (ff->erase) { error = ff->erase(dev, effect_id); - if (error) + if (error) { + spin_lock_irq(&dev->event_lock); + ff->effect_owners[effect_id] = file; + spin_unlock_irq(&dev->event_lock); + return error; + } } - ff->effect_owners[effect_id] = NULL; - return 0; } @@ -263,8 +272,6 @@ int input_ff_event(struct input_dev *dev, unsigned int type, if (type != EV_FF) return 0; - mutex_lock(&ff->mutex); - switch (code) { case FF_GAIN: if (!test_bit(FF_GAIN, dev->ffbit) || value > 0xffff) @@ -286,7 +293,6 @@ int input_ff_event(struct input_dev *dev, unsigned int type, break; } - mutex_unlock(&ff->mutex); return 0; } EXPORT_SYMBOL_GPL(input_ff_event); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 3ad8bd9f754..432699d61c5 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -15,7 +15,6 @@ if INPUT_MISC config INPUT_PCSPKR tristate "PC Speaker support" depends on PCSPKR_PLATFORM - depends on SND_PCSP=n help Say Y here if you want the standard PC Speaker to be used for bells and whistles. diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c index 8dd3942f302..ce6fdec19e1 100644 --- a/drivers/input/mouse/appletouch.c +++ b/drivers/input/mouse/appletouch.c @@ -589,6 +589,21 @@ static void atp_close(struct input_dev *input) dev->open = 0; } +static int atp_handle_geyser(struct atp *dev) +{ + struct usb_device *udev = dev->udev; + + if (!atp_is_fountain(dev)) { + /* switch to raw sensor mode */ + if (atp_geyser_init(udev)) + return -EIO; + + printk(KERN_INFO "appletouch: Geyser mode initialized.\n"); + } + + return 0; +} + static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id) { struct atp *dev; @@ -633,14 +648,6 @@ static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id else dev->datalen = 81; - if (!atp_is_fountain(dev)) { - /* switch to raw sensor mode */ - if (atp_geyser_init(udev)) - goto err_free_devs; - - printk(KERN_INFO "appletouch: Geyser mode initialized.\n"); - } - dev->urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->urb) goto err_free_devs; @@ -654,6 +661,10 @@ static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id usb_rcvintpipe(udev, int_in_endpointAddr), dev->data, dev->datalen, atp_complete, dev, 1); + error = atp_handle_geyser(dev); + if (error) + goto err_free_buffer; + usb_make_path(udev, dev->phys, sizeof(dev->phys)); strlcat(dev->phys, "/input0", sizeof(dev->phys)); @@ -744,6 +755,20 @@ static void atp_disconnect(struct usb_interface *iface) printk(KERN_INFO "input: appletouch disconnected\n"); } +static int atp_recover(struct atp *dev) +{ + int error; + + error = atp_handle_geyser(dev); + if (error) + return error; + + if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC)) + return -EIO; + + return 0; +} + static int atp_suspend(struct usb_interface *iface, pm_message_t message) { struct atp *dev = usb_get_intfdata(iface); @@ -764,12 +789,20 @@ static int atp_resume(struct usb_interface *iface) return 0; } +static int atp_reset_resume(struct usb_interface *iface) +{ + struct atp *dev = usb_get_intfdata(iface); + + return atp_recover(dev); +} + static struct usb_driver atp_driver = { .name = "appletouch", .probe = atp_probe, .disconnect = atp_disconnect, .suspend = atp_suspend, .resume = atp_resume, + .reset_resume = atp_reset_resume, .id_table = atp_table, }; diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 9aafa96cb74..78eb7841174 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -193,6 +193,13 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { }, }, { + .ident = "Fujitsu-Siemens Amilo Pro 2030", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"), + }, + }, + { /* * No data is coming from the touchscreen unless KBC * is in legacy mode. diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 592ff55b62d..170f71ee577 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -952,8 +952,12 @@ static int i8042_resume(struct platform_device *dev) i8042_ctr |= I8042_CTR_AUXDIS | I8042_CTR_KBDDIS; i8042_ctr &= ~(I8042_CTR_AUXINT | I8042_CTR_KBDINT); if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { - printk(KERN_ERR "i8042: Can't write CTR to resume\n"); - return -EIO; + printk(KERN_WARNING "i8042: Can't write CTR to resume, retrying...\n"); + msleep(50); + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { + printk(KERN_ERR "i8042: CTR write retry failed\n"); + return -EIO; + } } diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/xen-kbdfront.c index 0f47f4697cd..9ce3b3baf3a 100644 --- a/drivers/input/xen-kbdfront.c +++ b/drivers/input/xen-kbdfront.c @@ -66,6 +66,9 @@ static irqreturn_t input_handler(int rq, void *dev_id) case XENKBD_TYPE_MOTION: input_report_rel(dev, REL_X, event->motion.rel_x); input_report_rel(dev, REL_Y, event->motion.rel_y); + if (event->motion.rel_z) + input_report_rel(dev, REL_WHEEL, + -event->motion.rel_z); break; case XENKBD_TYPE_KEY: dev = NULL; @@ -84,6 +87,9 @@ static irqreturn_t input_handler(int rq, void *dev_id) case XENKBD_TYPE_POS: input_report_abs(dev, ABS_X, event->pos.abs_x); input_report_abs(dev, ABS_Y, event->pos.abs_y); + if (event->pos.rel_z) + input_report_rel(dev, REL_WHEEL, + -event->pos.rel_z); break; } if (dev) @@ -152,7 +158,7 @@ static int __devinit xenkbd_probe(struct xenbus_device *dev, ptr->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS); for (i = BTN_LEFT; i <= BTN_TASK; i++) set_bit(i, ptr->keybit); - ptr->relbit[0] = BIT(REL_X) | BIT(REL_Y); + ptr->relbit[0] = BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL); input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0); input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0); @@ -294,6 +300,16 @@ InitWait: */ if (dev->state != XenbusStateConnected) goto InitWait; /* no InitWait seen yet, fudge it */ + + /* Set input abs params to match backend screen res */ + if (xenbus_scanf(XBT_NIL, info->xbdev->otherend, + "width", "%d", &val) > 0) + input_set_abs_params(info->ptr, ABS_X, 0, val, 0, 0); + + if (xenbus_scanf(XBT_NIL, info->xbdev->otherend, + "height", "%d", &val) > 0) + input_set_abs_params(info->ptr, ABS_Y, 0, val, 0, 0); + break; case XenbusStateClosing: @@ -337,4 +353,6 @@ static void __exit xenkbd_cleanup(void) module_init(xenkbd_init); module_exit(xenkbd_cleanup); +MODULE_DESCRIPTION("Xen virtual keyboard/pointer device frontend"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("xen:vkbd"); diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h index 005bd045d2e..5faefeaf679 100644 --- a/drivers/lguest/lg.h +++ b/drivers/lguest/lg.h @@ -136,7 +136,6 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user); * first step in the migration to the kernel types. pte_pfn is already defined * in the kernel. */ #define pgd_flags(x) (pgd_val(x) & ~PAGE_MASK) -#define pte_flags(x) (pte_val(x) & ~PAGE_MASK) #define pgd_pfn(x) (pgd_val(x) >> PAGE_SHIFT) /* interrupts_and_traps.c: */ diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index 5126d5d9ea0..2e554a4ab33 100644 --- a/drivers/lguest/x86/core.c +++ b/drivers/lguest/x86/core.c @@ -176,7 +176,7 @@ void lguest_arch_run_guest(struct lg_cpu *cpu) * we set it now, so we can trap and pass that trap to the Guest if it * uses the FPU. */ if (cpu->ts) - lguest_set_ts(); + unlazy_fpu(current); /* SYSENTER is an optimized way of doing system calls. We can't allow * it because it always jumps to privilege level 0. A normal Guest @@ -196,6 +196,10 @@ void lguest_arch_run_guest(struct lg_cpu *cpu) * trap made the switcher code come back, and an error code which some * traps set. */ + /* Restore SYSENTER if it's supposed to be on. */ + if (boot_cpu_has(X86_FEATURE_SEP)) + wrmsr(MSR_IA32_SYSENTER_CS, __KERNEL_CS, 0); + /* If the Guest page faulted, then the cr2 register will tell us the * bad virtual address. We have to grab this now, because once we * re-enable interrupts an interrupt could fault and thus overwrite @@ -203,13 +207,12 @@ void lguest_arch_run_guest(struct lg_cpu *cpu) if (cpu->regs->trapnum == 14) cpu->arch.last_pagefault = read_cr2(); /* Similarly, if we took a trap because the Guest used the FPU, - * we have to restore the FPU it expects to see. */ + * we have to restore the FPU it expects to see. + * math_state_restore() may sleep and we may even move off to + * a different CPU. So all the critical stuff should be done + * before this. */ else if (cpu->regs->trapnum == 7) math_state_restore(); - - /* Restore SYSENTER if it's supposed to be on. */ - if (boot_cpu_has(X86_FEATURE_SEP)) - wrmsr(MSR_IA32_SYSENTER_CS, __KERNEL_CS, 0); } /*H:130 Now we've examined the hypercall code; our Guest can make requests. diff --git a/drivers/macintosh/mediabay.c b/drivers/macintosh/mediabay.c index c34bdf852e3..818aba36854 100644 --- a/drivers/macintosh/mediabay.c +++ b/drivers/macintosh/mediabay.c @@ -84,7 +84,7 @@ struct media_bay_info { int cd_irq; int cd_retry; #endif -#if defined(CONFIG_BLK_DEV_IDE_PMAC) || defined(CONFIG_MAC_FLOPPY) +#if defined(CONFIG_BLK_DEV_IDE_PMAC) int cd_index; #endif }; @@ -417,6 +417,7 @@ static void poll_media_bay(struct media_bay_info* bay) } } +#ifdef CONFIG_BLK_DEV_IDE_PMAC int check_media_bay(struct device_node *which_bay, int what) { int i; @@ -432,7 +433,6 @@ int check_media_bay(struct device_node *which_bay, int what) } EXPORT_SYMBOL(check_media_bay); -#ifdef CONFIG_BLK_DEV_IDE_PMAC int check_media_bay_by_base(unsigned long base, int what) { int i; diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c index 77ad192962c..d86d57af282 100644 --- a/drivers/macintosh/smu.c +++ b/drivers/macintosh/smu.c @@ -483,12 +483,15 @@ int __init smu_init (void) if (smu_cmdbuf_abs == 0) { printk(KERN_ERR "SMU: Command buffer not allocated !\n"); + of_node_put(np); return -EINVAL; } smu = alloc_bootmem(sizeof(struct smu_device)); - if (smu == NULL) + if (smu == NULL) { + of_node_put(np); return -ENOMEM; + } memset(smu, 0, sizeof(*smu)); spin_lock_init(&smu->lock); diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c index 54f4942a296..5366dc93fb3 100644 --- a/drivers/macintosh/therm_adt746x.c +++ b/drivers/macintosh/therm_adt746x.c @@ -562,18 +562,24 @@ thermostat_init(void) therm_type = ADT7460; else if (of_device_is_compatible(np, "adt7467")) therm_type = ADT7467; - else + else { + of_node_put(np); return -ENODEV; + } prop = of_get_property(np, "hwsensor-params-version", NULL); printk(KERN_INFO "adt746x: version %d (%ssupported)\n", *prop, (*prop == 1)?"":"un"); - if (*prop != 1) + if (*prop != 1) { + of_node_put(np); return -ENODEV; + } prop = of_get_property(np, "reg", NULL); - if (!prop) + if (!prop) { + of_node_put(np); return -ENODEV; + } /* look for bus either by path or using "reg" */ if (strstr(np->full_name, "/i2c-bus@") != NULL) { @@ -610,6 +616,7 @@ thermostat_init(void) if (of_dev == NULL) { printk(KERN_ERR "Can't register temperatures device !\n"); + of_node_put(np); return -ENODEV; } diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 835def11419..ab6a61db63c 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -432,6 +432,7 @@ static int crypt_convert(struct crypt_config *cc, case 0: atomic_dec(&ctx->pending); ctx->sector++; + cond_resched(); continue; /* error */ diff --git a/drivers/md/md.c b/drivers/md/md.c index 7cf512a34cc..2580ac1b9b0 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -3897,8 +3897,10 @@ static void autorun_devices(int part) md_probe(dev, NULL, NULL); mddev = mddev_find(dev); - if (!mddev) { - printk(KERN_ERR + if (!mddev || !mddev->gendisk) { + if (mddev) + mddev_put(mddev); + printk(KERN_ERR "md: cannot allocate memory for md drive.\n"); break; } diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 1de17da34a9..a71277b640a 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -2137,6 +2137,8 @@ static int run(mddev_t *mddev) !test_bit(In_sync, &disk->rdev->flags)) { disk->head_position = 0; mddev->degraded++; + if (disk->rdev) + conf->fullsync = 1; } } diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index c37e256b117..54c8ee28fcc 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -2898,6 +2898,8 @@ static void handle_stripe5(struct stripe_head *sh) for (i = conf->raid_disks; i--; ) { set_bit(R5_Wantwrite, &sh->dev[i].flags); + set_bit(R5_LOCKED, &dev->flags); + s.locked++; if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) sh->ops.count++; } @@ -2911,6 +2913,7 @@ static void handle_stripe5(struct stripe_head *sh) conf->raid_disks); s.locked += handle_write_operations5(sh, 1, 1); } else if (s.expanded && + s.locked == 0 && !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending)) { clear_bit(STRIPE_EXPAND_READY, &sh->state); atomic_dec(&conf->reshape_stripes); @@ -4305,7 +4308,9 @@ static int run(mddev_t *mddev) " disk %d\n", bdevname(rdev->bdev,b), raid_disk); working_disks++; - } + } else + /* Cannot rely on bitmap to complete recovery */ + conf->fullsync = 1; } /* diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index a3485817e46..8fa91f846d5 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -2201,3 +2201,41 @@ IR_KEYTAB_TYPE ir_codes_powercolor_real_angel[IR_KEYTAB_SIZE] = { [0x25] = KEY_POWER, /* power */ }; EXPORT_SYMBOL_GPL(ir_codes_powercolor_real_angel); + +IR_KEYTAB_TYPE ir_codes_avermedia_a16d[IR_KEYTAB_SIZE] = { + [0x20] = KEY_LIST, + [0x00] = KEY_POWER, + [0x28] = KEY_1, + [0x18] = KEY_2, + [0x38] = KEY_3, + [0x24] = KEY_4, + [0x14] = KEY_5, + [0x34] = KEY_6, + [0x2c] = KEY_7, + [0x1c] = KEY_8, + [0x3c] = KEY_9, + [0x12] = KEY_SUBTITLE, + [0x22] = KEY_0, + [0x32] = KEY_REWIND, + [0x3a] = KEY_SHUFFLE, + [0x02] = KEY_PRINT, + [0x11] = KEY_CHANNELDOWN, + [0x31] = KEY_CHANNELUP, + [0x0c] = KEY_ZOOM, + [0x1e] = KEY_VOLUMEDOWN, + [0x3e] = KEY_VOLUMEUP, + [0x0a] = KEY_MUTE, + [0x04] = KEY_AUDIO, + [0x26] = KEY_RECORD, + [0x06] = KEY_PLAY, + [0x36] = KEY_STOP, + [0x16] = KEY_PAUSE, + [0x2e] = KEY_REWIND, + [0x0e] = KEY_FASTFORWARD, + [0x30] = KEY_TEXT, + [0x21] = KEY_GREEN, + [0x01] = KEY_BLUE, + [0x08] = KEY_EPG, + [0x2a] = KEY_MENU, +}; +EXPORT_SYMBOL_GPL(ir_codes_avermedia_a16d); diff --git a/drivers/media/common/tuners/tda18271-common.c b/drivers/media/common/tuners/tda18271-common.c index f1894fec32b..6fb5b458656 100644 --- a/drivers/media/common/tuners/tda18271-common.c +++ b/drivers/media/common/tuners/tda18271-common.c @@ -649,9 +649,17 @@ int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq) u8 val; int ret = tda18271_lookup_map(fe, RF_CAL, freq, &val); + /* The TDA18271HD/C1 rf_cal map lookup is expected to go out of range + * for frequencies above 61.1 MHz. In these cases, the internal RF + * tracking filters calibration mechanism is used. + * + * There is no need to warn the user about this. + */ + if (ret < 0) + goto fail; regs[R_EB14] = val; - +fail: return ret; } diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index 89c01fb1f85..93063c6fbbf 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -45,6 +45,21 @@ static inline int charge_pump_source(struct dvb_frontend *fe, int force) TDA18271_MAIN_PLL, force); } +static inline void tda18271_set_if_notch(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + + switch (priv->mode) { + case TDA18271_ANALOG: + regs[R_MPD] &= ~0x80; /* IF notch = 0 */ + break; + case TDA18271_DIGITAL: + regs[R_MPD] |= 0x80; /* IF notch = 1 */ + break; + } +} + static int tda18271_channel_configuration(struct dvb_frontend *fe, struct tda18271_std_map_item *map, u32 freq, u32 bw) @@ -60,25 +75,18 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, regs[R_EP3] &= ~0x1f; /* clear std bits */ regs[R_EP3] |= (map->agc_mode << 3) | map->std; - /* set rfagc to high speed mode */ - regs[R_EP3] &= ~0x04; + if (priv->id == TDA18271HDC2) { + /* set rfagc to high speed mode */ + regs[R_EP3] &= ~0x04; + } /* set cal mode to normal */ regs[R_EP4] &= ~0x03; - /* update IF output level & IF notch frequency */ + /* update IF output level */ regs[R_EP4] &= ~0x1c; /* clear if level bits */ regs[R_EP4] |= (map->if_lvl << 2); - switch (priv->mode) { - case TDA18271_ANALOG: - regs[R_MPD] &= ~0x80; /* IF notch = 0 */ - break; - case TDA18271_DIGITAL: - regs[R_MPD] |= 0x80; /* IF notch = 1 */ - break; - } - /* update FM_RFn */ regs[R_EP4] &= ~0x80; regs[R_EP4] |= map->fm_rfn << 7; @@ -95,6 +103,9 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, /* disable Power Level Indicator */ regs[R_EP1] |= 0x40; + /* make sure thermometer is off */ + regs[R_TM] &= ~0x10; + /* frequency dependent parameters */ tda18271_calc_ir_measure(fe, &freq); @@ -135,6 +146,7 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, switch (priv->role) { case TDA18271_MASTER: tda18271_calc_main_pll(fe, N); + tda18271_set_if_notch(fe); tda18271_write_regs(fe, R_MPD, 4); break; case TDA18271_SLAVE: @@ -142,6 +154,7 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, tda18271_write_regs(fe, R_CPD, 4); regs[R_MPD] = regs[R_CPD] & 0x7f; + tda18271_set_if_notch(fe); tda18271_write_regs(fe, R_MPD, 1); break; } @@ -160,12 +173,14 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, msleep(20); - /* set rfagc to normal speed mode */ - if (map->fm_rfn) - regs[R_EP3] &= ~0x04; - else - regs[R_EP3] |= 0x04; - ret = tda18271_write_regs(fe, R_EP3, 1); + if (priv->id == TDA18271HDC2) { + /* set rfagc to normal speed mode */ + if (map->fm_rfn) + regs[R_EP3] &= ~0x04; + else + regs[R_EP3] |= 0x04; + ret = tda18271_write_regs(fe, R_EP3, 1); + } fail: return ret; } @@ -507,7 +522,7 @@ static int tda18271_powerscan_init(struct dvb_frontend *fe) /* set cal mode to normal */ regs[R_EP4] &= ~0x03; - /* update IF output level & IF notch frequency */ + /* update IF output level */ regs[R_EP4] &= ~0x1c; /* clear if level bits */ ret = tda18271_write_regs(fe, R_EP3, 2); diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index ceae6db901e..7cf4f5bdb2e 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -177,6 +177,7 @@ static XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = { {"FM Radio-INPUT1", 0x0208, 0x9002} }; +static int xc5000_is_firmware_loaded(struct dvb_frontend *fe); static int xc5000_writeregs(struct xc5000_priv *priv, u8 *buf, u8 len); static int xc5000_readregs(struct xc5000_priv *priv, u8 *buf, u8 len); static void xc5000_TunerReset(struct dvb_frontend *fe); @@ -352,7 +353,7 @@ static int xc_SetTVStandard(struct xc5000_priv *priv, static int xc_shutdown(struct xc5000_priv *priv) { - return 0; + return XC_RESULT_SUCCESS; /* Fixme: cannot bring tuner back alive once shutdown * without reloading the driver modules. * return xc_write_reg(priv, XREG_POWER_DOWN, 0); @@ -685,6 +686,25 @@ static int xc5000_set_params(struct dvb_frontend *fe, return 0; } +static int xc5000_is_firmware_loaded(struct dvb_frontend *fe) +{ + struct xc5000_priv *priv = fe->tuner_priv; + int ret; + u16 id; + + ret = xc5000_readreg(priv, XREG_PRODUCT_ID, &id); + if (ret == XC_RESULT_SUCCESS) { + if (id == XC_PRODUCT_ID_FW_NOT_LOADED) + ret = XC_RESULT_RESET_FAILURE; + else + ret = XC_RESULT_SUCCESS; + } + + dprintk(1, "%s() returns %s id = 0x%x\n", __func__, + ret == XC_RESULT_SUCCESS ? "True" : "False", id); + return ret; +} + static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe); static int xc5000_set_analog_params(struct dvb_frontend *fe, @@ -693,7 +713,7 @@ static int xc5000_set_analog_params(struct dvb_frontend *fe, struct xc5000_priv *priv = fe->tuner_priv; int ret; - if(priv->fwloaded == 0) + if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) xc_load_fw_and_init_tuner(fe); dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n", @@ -808,11 +828,10 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe) struct xc5000_priv *priv = fe->tuner_priv; int ret = 0; - if (priv->fwloaded == 0) { + if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) { ret = xc5000_fwupload(fe); if (ret != XC_RESULT_SUCCESS) return ret; - priv->fwloaded = 1; } /* Start the tuner self-calibration process */ @@ -852,7 +871,6 @@ static int xc5000_sleep(struct dvb_frontend *fe) return -EREMOTEIO; } else { - /* priv->fwloaded = 0; */ return XC_RESULT_SUCCESS; } } @@ -933,7 +951,6 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, cfg->i2c_address); printk(KERN_INFO "xc5000: Firmware has been loaded previously\n"); - priv->fwloaded = 1; break; case XC_PRODUCT_ID_FW_NOT_LOADED: printk(KERN_INFO @@ -941,7 +958,6 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, cfg->i2c_address); printk(KERN_INFO "xc5000: Firmware has not been loaded previously\n"); - priv->fwloaded = 0; break; default: printk(KERN_ERR diff --git a/drivers/media/common/tuners/xc5000_priv.h b/drivers/media/common/tuners/xc5000_priv.h index ecebfe4745a..a72a9887fe7 100644 --- a/drivers/media/common/tuners/xc5000_priv.h +++ b/drivers/media/common/tuners/xc5000_priv.h @@ -30,7 +30,6 @@ struct xc5000_priv { u32 bandwidth; u8 video_standard; u8 rf_mode; - u8 fwloaded; void *devptr; }; diff --git a/drivers/media/dvb/dvb-usb/gl861.c b/drivers/media/dvb/dvb-usb/gl861.c index 0a8ac64a4e3..037f7ffb47b 100644 --- a/drivers/media/dvb/dvb-usb/gl861.c +++ b/drivers/media/dvb/dvb-usb/gl861.c @@ -47,6 +47,8 @@ static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, return -EINVAL; } + msleep(1); /* avoid I2C errors */ + return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type, value, index, rbuf, rlen, 2000); } @@ -92,16 +94,6 @@ static struct i2c_algorithm gl861_i2c_algo = { }; /* Callbacks for DVB USB */ -static int gl861_identify_state(struct usb_device *udev, - struct dvb_usb_device_properties *props, - struct dvb_usb_device_description **desc, - int *cold) -{ - *cold = 0; - - return 0; -} - static struct zl10353_config gl861_zl10353_config = { .demod_address = 0x0f, .no_tuner = 1, @@ -172,7 +164,6 @@ static struct dvb_usb_device_properties gl861_properties = { .size_of_priv = 0, - .identify_state = gl861_identify_state, .num_adapters = 1, .adapter = {{ @@ -194,13 +185,15 @@ static struct dvb_usb_device_properties gl861_properties = { .num_device_descs = 2, .devices = { - { "MSI Mega Sky 55801 DVB-T USB2.0", - { &gl861_table[0], NULL }, - { NULL }, + { + .name = "MSI Mega Sky 55801 DVB-T USB2.0", + .cold_ids = { NULL }, + .warm_ids = { &gl861_table[0], NULL }, }, - { "A-LINK DTU DVB-T USB2.0", - { &gl861_table[1], NULL }, - { NULL }, + { + .name = "A-LINK DTU DVB-T USB2.0", + .cold_ids = { NULL }, + .warm_ids = { &gl861_table[1], NULL }, }, } }; diff --git a/drivers/media/dvb/dvb-usb/umt-010.c b/drivers/media/dvb/dvb-usb/umt-010.c index 9e7653bb3b6..118aab1a3e5 100644 --- a/drivers/media/dvb/dvb-usb/umt-010.c +++ b/drivers/media/dvb/dvb-usb/umt-010.c @@ -107,7 +107,7 @@ static struct dvb_usb_device_properties umt_properties = { /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 20, + .count = MAX_NO_URBS_FOR_DATA_STREAM, .endpoint = 0x06, .u = { .bulk = { diff --git a/drivers/media/dvb/frontends/au8522.c b/drivers/media/dvb/frontends/au8522.c index 084a280c2d7..03900d241a7 100644 --- a/drivers/media/dvb/frontends/au8522.c +++ b/drivers/media/dvb/frontends/au8522.c @@ -463,10 +463,13 @@ static int au8522_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) { struct au8522_state *state = fe->demodulator_priv; + int ret = -EINVAL; dprintk("%s(frequency=%d)\n", __func__, p->frequency); - state->current_frequency = p->frequency; + if ((state->current_frequency == p->frequency) && + (state->current_modulation == p->u.vsb.modulation)) + return 0; au8522_enable_modulation(fe, p->u.vsb.modulation); @@ -476,11 +479,16 @@ static int au8522_set_frontend(struct dvb_frontend *fe, if (fe->ops.tuner_ops.set_params) { if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - fe->ops.tuner_ops.set_params(fe, p); + ret = fe->ops.tuner_ops.set_params(fe, p); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); } + if (ret < 0) + return ret; + + state->current_frequency = p->frequency; + return 0; } @@ -498,6 +506,16 @@ static int au8522_init(struct dvb_frontend *fe) return 0; } +static int au8522_sleep(struct dvb_frontend *fe) +{ + struct au8522_state *state = fe->demodulator_priv; + dprintk("%s()\n", __func__); + + state->current_frequency = 0; + + return 0; +} + static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct au8522_state *state = fe->demodulator_priv; @@ -509,10 +527,8 @@ static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status) if (state->current_modulation == VSB_8) { dprintk("%s() Checking VSB_8\n", __func__); reg = au8522_readreg(state, 0x4088); - if (reg & 0x01) - *status |= FE_HAS_VITERBI; - if (reg & 0x02) - *status |= FE_HAS_LOCK | FE_HAS_SYNC; + if ((reg & 0x03) == 0x03) + *status |= FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI; } else { dprintk("%s() Checking QAM\n", __func__); reg = au8522_readreg(state, 0x4541); @@ -672,6 +688,7 @@ static struct dvb_frontend_ops au8522_ops = { }, .init = au8522_init, + .sleep = au8522_sleep, .i2c_gate_ctrl = au8522_i2c_gate_ctrl, .set_frontend = au8522_set_frontend, .get_frontend = au8522_get_frontend, diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb/frontends/stv0299.c index 17556183e87..35435bef8e7 100644 --- a/drivers/media/dvb/frontends/stv0299.c +++ b/drivers/media/dvb/frontends/stv0299.c @@ -63,6 +63,7 @@ struct stv0299_state { u32 symbol_rate; fe_code_rate_t fec_inner; int errmode; + u32 ucblocks; }; #define STATUS_BER 0 @@ -501,8 +502,10 @@ static int stv0299_read_ber(struct dvb_frontend* fe, u32* ber) { struct stv0299_state* state = fe->demodulator_priv; - if (state->errmode != STATUS_BER) return 0; - *ber = (stv0299_readreg (state, 0x1d) << 8) | stv0299_readreg (state, 0x1e); + if (state->errmode != STATUS_BER) + return -ENOSYS; + + *ber = stv0299_readreg(state, 0x1e) | (stv0299_readreg(state, 0x1d) << 8); return 0; } @@ -540,8 +543,12 @@ static int stv0299_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) { struct stv0299_state* state = fe->demodulator_priv; - if (state->errmode != STATUS_UCBLOCKS) *ucblocks = 0; - else *ucblocks = (stv0299_readreg (state, 0x1d) << 8) | stv0299_readreg (state, 0x1e); + if (state->errmode != STATUS_UCBLOCKS) + return -ENOSYS; + + state->ucblocks += stv0299_readreg(state, 0x1e); + state->ucblocks += (stv0299_readreg(state, 0x1d) << 8); + *ucblocks = state->ucblocks; return 0; } diff --git a/drivers/media/dvb/frontends/tda10023.c b/drivers/media/dvb/frontends/tda10023.c index 0727b80bc4d..c6ff5b82ff8 100644 --- a/drivers/media/dvb/frontends/tda10023.c +++ b/drivers/media/dvb/frontends/tda10023.c @@ -116,9 +116,12 @@ static u8 tda10023_readreg (struct tda10023_state* state, u8 reg) int ret; ret = i2c_transfer (state->i2c, msg, 2); - if (ret != 2) - printk("DVB: TDA10023: %s: readreg error (ret == %i)\n", - __func__, ret); + if (ret != 2) { + int num = state->frontend.dvb ? state->frontend.dvb->num : -1; + printk(KERN_ERR "DVB: TDA10023(%d): %s: readreg error " + "(reg == 0x%02x, ret == %i)\n", + num, __func__, reg, ret); + } return b1[0]; } @@ -129,11 +132,12 @@ static int tda10023_writereg (struct tda10023_state* state, u8 reg, u8 data) int ret; ret = i2c_transfer (state->i2c, &msg, 1); - if (ret != 1) - printk("DVB: TDA10023(%d): %s, writereg error " + if (ret != 1) { + int num = state->frontend.dvb ? state->frontend.dvb->num : -1; + printk(KERN_ERR "DVB: TDA10023(%d): %s, writereg error " "(reg == 0x%02x, val == 0x%02x, ret == %i)\n", - state->frontend.dvb->num, __func__, reg, data, ret); - + num, __func__, reg, data, ret); + } return (ret != 1) ? -EREMOTEIO : 0; } @@ -464,7 +468,7 @@ struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config, int i; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda10023_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda10023_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c index 49973846373..a0d63865356 100644 --- a/drivers/media/dvb/frontends/tda1004x.c +++ b/drivers/media/dvb/frontends/tda1004x.c @@ -1248,11 +1248,14 @@ struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, struct i2c_adapter* i2c) { struct tda1004x_state *state; + int id; /* allocate memory for the internal state */ state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL); - if (!state) + if (!state) { + printk(KERN_ERR "Can't alocate memory for tda10045 state\n"); return NULL; + } /* setup the state */ state->config = config; @@ -1260,7 +1263,15 @@ struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, state->demod_type = TDA1004X_DEMOD_TDA10045; /* check if the demod is there */ - if (tda1004x_read_byte(state, TDA1004X_CHIPID) != 0x25) { + id = tda1004x_read_byte(state, TDA1004X_CHIPID); + if (id < 0) { + printk(KERN_ERR "tda10045: chip is not answering. Giving up.\n"); + kfree(state); + return NULL; + } + + if (id != 0x25) { + printk(KERN_ERR "Invalid tda1004x ID = 0x%02x. Can't proceed\n", id); kfree(state); return NULL; } @@ -1307,11 +1318,14 @@ struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, struct i2c_adapter* i2c) { struct tda1004x_state *state; + int id; /* allocate memory for the internal state */ state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL); - if (!state) + if (!state) { + printk(KERN_ERR "Can't alocate memory for tda10046 state\n"); return NULL; + } /* setup the state */ state->config = config; @@ -1319,7 +1333,14 @@ struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, state->demod_type = TDA1004X_DEMOD_TDA10046; /* check if the demod is there */ - if (tda1004x_read_byte(state, TDA1004X_CHIPID) != 0x46) { + id = tda1004x_read_byte(state, TDA1004X_CHIPID); + if (id < 0) { + printk(KERN_ERR "tda10046: chip is not answering. Giving up.\n"); + kfree(state); + return NULL; + } + if (id != 0x46) { + printk(KERN_ERR "Invalid tda1004x ID = 0x%02x. Can't proceed\n", id); kfree(state); return NULL; } diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig index d4339b1b3b6..07643e01009 100644 --- a/drivers/media/dvb/ttpci/Kconfig +++ b/drivers/media/dvb/ttpci/Kconfig @@ -101,6 +101,7 @@ config DVB_BUDGET config DVB_BUDGET_CI tristate "Budget cards with onboard CI connector" depends on DVB_BUDGET_CORE && I2C + depends on INPUT # due to IR select DVB_STV0297 if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_TDA1004X if !DVB_FE_CUSTOMISE diff --git a/drivers/media/dvb/ttpci/av7110_hw.c b/drivers/media/dvb/ttpci/av7110_hw.c index 9d81074b31d..3a3f5279e92 100644 --- a/drivers/media/dvb/ttpci/av7110_hw.c +++ b/drivers/media/dvb/ttpci/av7110_hw.c @@ -427,6 +427,7 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) if (err) { printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n", __func__, type); + av7110->arm_errors++; return -ETIMEDOUT; } msleep(1); @@ -853,10 +854,8 @@ static osd_raw_window_t bpp2bit[8] = { static inline int WaitUntilBmpLoaded(struct av7110 *av7110) { - int ret = wait_event_interruptible_timeout(av7110->bmpq, + int ret = wait_event_timeout(av7110->bmpq, av7110->bmp_state != BMP_LOADING, 10*HZ); - if (ret == -ERESTARTSYS) - return ret; if (ret == 0) { printk("dvb-ttpci: warning: timeout waiting in LoadBitmap: %d, %d\n", ret, av7110->bmp_state); diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 3b26fbd3e55..5ccb0aeca8c 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -793,6 +793,14 @@ menuconfig V4L_USB_DRIVERS if V4L_USB_DRIVERS && USB +config USB_VIDEO_CLASS + tristate "USB Video Class (UVC)" + ---help--- + Support for the USB Video Class (UVC). Currently only video + input devices, such as webcams, are supported. + + For more information see: <http://linux-uvc.berlios.de/> + source "drivers/media/video/pvrusb2/Kconfig" source "drivers/media/video/em28xx/Kconfig" diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index dff0d6abe91..ecbbfaab24d 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -136,6 +136,8 @@ obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o obj-$(CONFIG_VIDEO_AU0828) += au0828/ +obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c index a2a6983444f..898e12395e7 100644 --- a/drivers/media/video/au0828/au0828-cards.c +++ b/drivers/media/video/au0828/au0828-cards.c @@ -77,8 +77,14 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) /* Make sure we support the board model */ switch (tv.model) { + case 72000: /* WinTV-HVR950q (Retail, IR, ATSC/QAM */ case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and basic analog video */ + case 72211: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */ + case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */ + case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */ + case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and basic analog video */ case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and basic analog video */ + case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ break; default: printk(KERN_WARNING "%s: warning: " @@ -175,6 +181,18 @@ struct usb_device_id au0828_usb_id_table [] = { .driver_info = AU0828_BOARD_HAUPPAUGE_HVR850 }, { USB_DEVICE(0x0fe9, 0xd620), .driver_info = AU0828_BOARD_DVICO_FUSIONHDTV7 }, + { USB_DEVICE(0x2040, 0x7210), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x7217), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x721b), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x721f), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x7280), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x0fd9, 0x0008), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, { }, }; diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig index 5f942690570..9aefdc5ea79 100644 --- a/drivers/media/video/cx18/Kconfig +++ b/drivers/media/video/cx18/Kconfig @@ -10,8 +10,8 @@ config VIDEO_CX18 select VIDEO_TVEEPROM select VIDEO_CX2341X select VIDEO_CS5345 - select DVB_S5H1409 - select MEDIA_TUNER_MXL5005S + select DVB_S5H1409 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE ---help--- This is a video4linux driver for Conexant cx23418 based PCI combo video recorder devices. diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index 9a26751615c..faca43eb940 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/drivers/media/video/cx18/cx18-av-core.c @@ -69,6 +69,58 @@ int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask, or_value); } +int cx18_av_write_no_acfg(struct cx18 *cx, u16 addr, u8 value, int no_acfg_mask) +{ + int retval; + u32 saved_reg[8] = {0}; + + if (no_acfg_mask & CXADEC_NO_ACFG_AFE) { + saved_reg[0] = cx18_av_read4(cx, CXADEC_CHIP_CTRL); + saved_reg[1] = cx18_av_read4(cx, CXADEC_AFE_CTRL); + } + + if (no_acfg_mask & CXADEC_NO_ACFG_PLL) { + saved_reg[2] = cx18_av_read4(cx, CXADEC_PLL_CTRL1); + saved_reg[3] = cx18_av_read4(cx, CXADEC_VID_PLL_FRAC); + } + + if (no_acfg_mask & CXADEC_NO_ACFG_VID) { + saved_reg[4] = cx18_av_read4(cx, CXADEC_HORIZ_TIM_CTRL); + saved_reg[5] = cx18_av_read4(cx, CXADEC_VERT_TIM_CTRL); + saved_reg[6] = cx18_av_read4(cx, CXADEC_SRC_COMB_CFG); + saved_reg[7] = cx18_av_read4(cx, CXADEC_CHROMA_VBIOFF_CFG); + } + + retval = cx18_av_write(cx, addr, value); + + if (no_acfg_mask & CXADEC_NO_ACFG_AFE) { + cx18_av_write4(cx, CXADEC_CHIP_CTRL, saved_reg[0]); + cx18_av_write4(cx, CXADEC_AFE_CTRL, saved_reg[1]); + } + + if (no_acfg_mask & CXADEC_NO_ACFG_PLL) { + cx18_av_write4(cx, CXADEC_PLL_CTRL1, saved_reg[2]); + cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, saved_reg[3]); + } + + if (no_acfg_mask & CXADEC_NO_ACFG_VID) { + cx18_av_write4(cx, CXADEC_HORIZ_TIM_CTRL, saved_reg[4]); + cx18_av_write4(cx, CXADEC_VERT_TIM_CTRL, saved_reg[5]); + cx18_av_write4(cx, CXADEC_SRC_COMB_CFG, saved_reg[6]); + cx18_av_write4(cx, CXADEC_CHROMA_VBIOFF_CFG, saved_reg[7]); + } + + return retval; +} + +int cx18_av_and_or_no_acfg(struct cx18 *cx, u16 addr, unsigned and_mask, + u8 or_value, int no_acfg_mask) +{ + return cx18_av_write_no_acfg(cx, addr, + (cx18_av_read(cx, addr) & and_mask) | + or_value, no_acfg_mask); +} + /* ----------------------------------------------------------------------- */ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, @@ -170,13 +222,15 @@ static void input_change(struct cx18 *cx) /* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */ if (std & V4L2_STD_SECAM) - cx18_av_write(cx, 0x402, 0); + cx18_av_write_no_acfg(cx, 0x402, 0, CXADEC_NO_ACFG_ALL); else { - cx18_av_write(cx, 0x402, 0x04); + cx18_av_write_no_acfg(cx, 0x402, 0x04, CXADEC_NO_ACFG_ALL); cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11); } - cx18_av_and_or(cx, 0x401, ~0x60, 0); - cx18_av_and_or(cx, 0x401, ~0x60, 0x60); + cx18_av_and_or_no_acfg(cx, 0x401, ~0x60, 0, + CXADEC_NO_ACFG_PLL | CXADEC_NO_ACFG_VID); + cx18_av_and_or_no_acfg(cx, 0x401, ~0x60, 0x60, + CXADEC_NO_ACFG_PLL | CXADEC_NO_ACFG_VID); if (std & V4L2_STD_525_60) { if (std == V4L2_STD_NTSC_M_JP) { @@ -228,7 +282,7 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, if ((vid_input & ~0xff0) || luma < CX18_AV_SVIDEO_LUMA1 || - luma > CX18_AV_SVIDEO_LUMA4 || + luma > CX18_AV_SVIDEO_LUMA8 || chroma < CX18_AV_SVIDEO_CHROMA4 || chroma > CX18_AV_SVIDEO_CHROMA8) { CX18_ERR("0x%04x is not a valid video input!\n", @@ -262,7 +316,8 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, cx18_av_write(cx, 0x103, reg); /* Set INPUT_MODE to Composite (0) or S-Video (1) */ - cx18_av_and_or(cx, 0x401, ~0x6, is_composite ? 0 : 0x02); + cx18_av_and_or_no_acfg(cx, 0x401, ~0x6, is_composite ? 0 : 0x02, + CXADEC_NO_ACFG_PLL | CXADEC_NO_ACFG_VID); /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ cx18_av_and_or(cx, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */ @@ -318,12 +373,12 @@ static int set_v4lstd(struct cx18 *cx) This happens for example with the Yuan MPC622. */ if (fmt >= 4 && fmt < 8) { /* Set format to NTSC-M */ - cx18_av_and_or(cx, 0x400, ~0xf, 1); + cx18_av_and_or_no_acfg(cx, 0x400, ~0xf, 1, CXADEC_NO_ACFG_AFE); /* Turn off LCOMB */ cx18_av_and_or(cx, 0x47b, ~6, 0); } - cx18_av_and_or(cx, 0x400, ~0xf, fmt); - cx18_av_and_or(cx, 0x403, ~0x3, pal_m); + cx18_av_and_or_no_acfg(cx, 0x400, ~0xf, fmt, CXADEC_NO_ACFG_AFE); + cx18_av_and_or_no_acfg(cx, 0x403, ~0x3, pal_m, CXADEC_NO_ACFG_ALL); cx18_av_vbi_setup(cx); input_change(cx); return 0; diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h index 786901d72e9..c172823ce1d 100644 --- a/drivers/media/video/cx18/cx18-av-core.h +++ b/drivers/media/video/cx18/cx18-av-core.h @@ -37,12 +37,16 @@ enum cx18_av_video_input { CX18_AV_COMPOSITE7, CX18_AV_COMPOSITE8, - /* S-Video inputs consist of one luma input (In1-In4) ORed with one + /* S-Video inputs consist of one luma input (In1-In8) ORed with one chroma input (In5-In8) */ CX18_AV_SVIDEO_LUMA1 = 0x10, CX18_AV_SVIDEO_LUMA2 = 0x20, CX18_AV_SVIDEO_LUMA3 = 0x30, CX18_AV_SVIDEO_LUMA4 = 0x40, + CX18_AV_SVIDEO_LUMA5 = 0x50, + CX18_AV_SVIDEO_LUMA6 = 0x60, + CX18_AV_SVIDEO_LUMA7 = 0x70, + CX18_AV_SVIDEO_LUMA8 = 0x80, CX18_AV_SVIDEO_CHROMA4 = 0x400, CX18_AV_SVIDEO_CHROMA5 = 0x500, CX18_AV_SVIDEO_CHROMA6 = 0x600, @@ -291,14 +295,24 @@ struct cx18_av_state { #define CXADEC_SELECT_AUDIO_STANDARD_FM 0xF9 /* FM radio */ #define CXADEC_SELECT_AUDIO_STANDARD_AUTO 0xFF /* Auto detect */ +/* Flags on what to preserve on write to 0x400-0x403 with cx18_av_.*_no_acfg()*/ +#define CXADEC_NO_ACFG_AFE 0x01 /* Preserve 0x100-0x107 */ +#define CXADEC_NO_ACFG_PLL 0x02 /* Preserve 0x108-0x10f */ +#define CXADEC_NO_ACFG_VID 0x04 /* Preserve 0x470-0x47f */ +#define CXADEC_NO_ACFG_ALL 0x07 + /* ----------------------------------------------------------------------- */ /* cx18_av-core.c */ int cx18_av_write(struct cx18 *cx, u16 addr, u8 value); int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value); +int cx18_av_write_no_acfg(struct cx18 *cx, u16 addr, u8 value, + int no_acfg_mask); u8 cx18_av_read(struct cx18 *cx, u16 addr); u32 cx18_av_read4(struct cx18 *cx, u16 addr); int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value); int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value); +int cx18_av_and_or_no_acfg(struct cx18 *cx, u16 addr, unsigned mask, u8 value, + int no_acfg_mask); int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg); /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index baccd079243..c26e0ef5b07 100644 --- a/drivers/media/video/cx18/cx18-cards.c +++ b/drivers/media/video/cx18/cx18-cards.c @@ -23,6 +23,7 @@ #include "cx18-driver.h" #include "cx18-cards.h" +#include "cx18-av-core.h" #include "cx18-i2c.h" #include <media/cs5345.h> @@ -54,22 +55,22 @@ static const struct cx18_card cx18_card_hvr1600_esmt = { .hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER | CX18_HW_CS5345 | CX18_HW_DVB, .video_inputs = { - { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 }, - { CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 }, - { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 }, - { CX18_CARD_INPUT_SVIDEO2, 2, CX23418_SVIDEO2 }, - { CX18_CARD_INPUT_COMPOSITE2, 2, CX23418_COMPOSITE4 }, + { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, + { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, + { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 }, + { CX18_CARD_INPUT_SVIDEO2, 2, CX18_AV_SVIDEO2 }, + { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 }, }, .audio_inputs = { { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, + CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, { CX18_CARD_INPUT_LINE_IN1, - CX23418_AUDIO_SERIAL, CS5345_IN_2 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_2 }, { CX18_CARD_INPUT_LINE_IN2, - CX23418_AUDIO_SERIAL, CS5345_IN_2 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_3 }, }, .radio_input = { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO_SERIAL, 0 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_4 }, .ddr = { /* ESMT M13S128324A-5B memory */ .chip_config = 0x003, @@ -81,6 +82,11 @@ static const struct cx18_card cx18_card_hvr1600_esmt = { }, .gpio_init.initial_value = 0x3001, .gpio_init.direction = 0x3001, + .gpio_i2c_slave_reset = { + .active_lo_mask = 0x3001, + .msecs_asserted = 10, + .msecs_recovery = 40, + }, .i2c = &cx18_i2c_std, }; @@ -94,22 +100,22 @@ static const struct cx18_card cx18_card_hvr1600_samsung = { .hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER | CX18_HW_CS5345 | CX18_HW_DVB, .video_inputs = { - { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 }, - { CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 }, - { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 }, - { CX18_CARD_INPUT_SVIDEO2, 2, CX23418_SVIDEO2 }, - { CX18_CARD_INPUT_COMPOSITE2, 2, CX23418_COMPOSITE4 }, + { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, + { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, + { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 }, + { CX18_CARD_INPUT_SVIDEO2, 2, CX18_AV_SVIDEO2 }, + { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 }, }, .audio_inputs = { { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, + CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, { CX18_CARD_INPUT_LINE_IN1, - CX23418_AUDIO_SERIAL, CS5345_IN_2 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_2 }, { CX18_CARD_INPUT_LINE_IN2, - CX23418_AUDIO_SERIAL, CS5345_IN_2 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_3 }, }, .radio_input = { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO_SERIAL, 0 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_4 }, .ddr = { /* Samsung K4D263238G-VC33 memory */ .chip_config = 0x003, @@ -121,6 +127,11 @@ static const struct cx18_card cx18_card_hvr1600_samsung = { }, .gpio_init.initial_value = 0x3001, .gpio_init.direction = 0x3001, + .gpio_i2c_slave_reset = { + .active_lo_mask = 0x3001, + .msecs_asserted = 10, + .msecs_recovery = 40, + }, .i2c = &cx18_i2c_std, }; @@ -141,19 +152,19 @@ static const struct cx18_card cx18_card_h900 = { .hw_audio_ctrl = CX18_HW_CX23418, .hw_all = CX18_HW_TUNER, .video_inputs = { - { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE2 }, + { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, { CX18_CARD_INPUT_SVIDEO1, 1, - CX23418_SVIDEO_LUMA3 | CX23418_SVIDEO_CHROMA4 }, - { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE1 }, + CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, + { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 }, }, .audio_inputs = { { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO8, 0 }, + CX18_AV_AUDIO8, 0 }, { CX18_CARD_INPUT_LINE_IN1, - CX23418_AUDIO_SERIAL, 0 }, + CX18_AV_AUDIO_SERIAL, 0 }, }, .radio_input = { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO_SERIAL, 0 }, + CX18_AV_AUDIO_SERIAL, 0 }, .tuners = { { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, }, @@ -183,23 +194,26 @@ static const struct cx18_card_pci_info cx18_pci_mpc718[] = { static const struct cx18_card cx18_card_mpc718 = { .type = CX18_CARD_YUAN_MPC718, .name = "Yuan MPC718", - .comment = "Not yet supported!\n", - .v4l2_capabilities = 0, + .comment = "Some Composite and S-Video inputs are currently working.\n", + .v4l2_capabilities = CX18_CAP_ENCODER, .hw_audio_ctrl = CX18_HW_CX23418, .hw_all = CX18_HW_TUNER, .video_inputs = { - { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 }, - { CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 }, - { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 }, + { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, + { CX18_CARD_INPUT_SVIDEO1, 1, + CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, + { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 }, + { CX18_CARD_INPUT_SVIDEO2, 2, + CX18_AV_SVIDEO_LUMA7 | CX18_AV_SVIDEO_CHROMA8 }, + { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE6 }, + { CX18_CARD_INPUT_COMPOSITE3, 2, CX18_AV_COMPOSITE3 }, }, .audio_inputs = { - { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO8, 0 }, - { CX18_CARD_INPUT_LINE_IN1, - CX23418_AUDIO_SERIAL, 0 }, + { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, + { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL, 0 }, + { CX18_CARD_INPUT_LINE_IN2, CX18_AV_AUDIO_SERIAL, 0 }, }, - .radio_input = { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO_SERIAL, 0 }, + .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO_SERIAL, 0 }, .tuners = { /* XC3028 tuner */ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h index bccb67f0db1..dc2dd945d4c 100644 --- a/drivers/media/video/cx18/cx18-cards.h +++ b/drivers/media/video/cx18/cx18-cards.h @@ -36,36 +36,6 @@ #define CX18_CARD_INPUT_COMPOSITE2 5 #define CX18_CARD_INPUT_COMPOSITE3 6 -enum cx34180_video_input { - /* Composite video inputs In1-In8 */ - CX23418_COMPOSITE1 = 1, - CX23418_COMPOSITE2, - CX23418_COMPOSITE3, - CX23418_COMPOSITE4, - CX23418_COMPOSITE5, - CX23418_COMPOSITE6, - CX23418_COMPOSITE7, - CX23418_COMPOSITE8, - - /* S-Video inputs consist of one luma input (In1-In4) ORed with one - chroma input (In5-In8) */ - CX23418_SVIDEO_LUMA1 = 0x10, - CX23418_SVIDEO_LUMA2 = 0x20, - CX23418_SVIDEO_LUMA3 = 0x30, - CX23418_SVIDEO_LUMA4 = 0x40, - CX23418_SVIDEO_CHROMA4 = 0x400, - CX23418_SVIDEO_CHROMA5 = 0x500, - CX23418_SVIDEO_CHROMA6 = 0x600, - CX23418_SVIDEO_CHROMA7 = 0x700, - CX23418_SVIDEO_CHROMA8 = 0x800, - - /* S-Video aliases for common luma/chroma combinations */ - CX23418_SVIDEO1 = 0x510, - CX23418_SVIDEO2 = 0x620, - CX23418_SVIDEO3 = 0x730, - CX23418_SVIDEO4 = 0x840, -}; - /* audio inputs */ #define CX18_CARD_INPUT_AUD_TUNER 1 #define CX18_CARD_INPUT_LINE_IN1 2 @@ -75,16 +45,6 @@ enum cx34180_video_input { #define CX18_CARD_MAX_AUDIO_INPUTS 3 #define CX18_CARD_MAX_TUNERS 2 -enum cx23418_audio_input { - /* Audio inputs: serial or In4-In8 */ - CX23418_AUDIO_SERIAL, - CX23418_AUDIO4 = 4, - CX23418_AUDIO5, - CX23418_AUDIO6, - CX23418_AUDIO7, - CX23418_AUDIO8, -}; - /* V4L2 capability aliases */ #define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE) @@ -118,6 +78,13 @@ struct cx18_gpio_init { /* set initial GPIO DIR and OUT values */ u32 initial_value; }; +struct cx18_gpio_i2c_slave_reset { + u32 active_lo_mask; /* GPIO outputs that reset i2c chips when low */ + u32 active_hi_mask; /* GPIO outputs that reset i2c chips when high */ + int msecs_asserted; /* time period reset must remain asserted */ + int msecs_recovery; /* time after deassert for chips to be ready */ +}; + struct cx18_card_tuner { v4l2_std_id std; /* standard for which the tuner is suitable */ int tuner; /* tuner ID (from tuner.h) */ @@ -154,7 +121,8 @@ struct cx18_card { /* GPIO card-specific settings */ u8 xceive_pin; /* XCeive tuner GPIO reset pin */ - struct cx18_gpio_init gpio_init; + struct cx18_gpio_init gpio_init; + struct cx18_gpio_i2c_slave_reset gpio_i2c_slave_reset; struct cx18_card_tuner tuners[CX18_CARD_MAX_TUNERS]; struct cx18_card_tuner_i2c *i2c; diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c index c9744173f96..cae38985b13 100644 --- a/drivers/media/video/cx18/cx18-dvb.c +++ b/drivers/media/video/cx18/cx18-dvb.c @@ -69,11 +69,21 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) struct dvb_demux *demux = feed->demux; struct cx18_stream *stream = (struct cx18_stream *) demux->priv; struct cx18 *cx = stream->cx; - int ret = -EINVAL; + int ret; u32 v; CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n", feed->pid, feed->index); + + mutex_lock(&cx->serialize_lock); + ret = cx18_init_on_first_open(cx); + mutex_unlock(&cx->serialize_lock); + if (ret) { + CX18_ERR("Failed to initialize firmware starting DVB feed\n"); + return ret; + } + ret = -EINVAL; + switch (cx->card->type) { case CX18_CARD_HVR_1600_ESMT: case CX18_CARD_HVR_1600_SAMSUNG: @@ -101,6 +111,11 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) if (stream->dvb.feeding++ == 0) { CX18_DEBUG_INFO("Starting Transport DMA\n"); ret = cx18_start_v4l2_encode_stream(stream); + if (ret < 0) { + CX18_DEBUG_INFO( + "Failed to start Transport DMA\n"); + stream->dvb.feeding--; + } } else ret = 0; mutex_unlock(&stream->dvb.feedlock); diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/video/cx18/cx18-gpio.c index ceb63653c92..b302833f6f9 100644 --- a/drivers/media/video/cx18/cx18-gpio.c +++ b/drivers/media/video/cx18/cx18-gpio.c @@ -53,10 +53,34 @@ static void gpio_write(struct cx18 *cx) write_reg(((dir & 0xffff) << 16) | (val & 0xffff), CX18_REG_GPIO_OUT1); write_reg(dir & 0xffff0000, CX18_REG_GPIO_DIR2); - write_reg((dir & 0xffff0000) | ((val & 0xffff0000) >> 16), + write_reg_sync((dir & 0xffff0000) | ((val & 0xffff0000) >> 16), CX18_REG_GPIO_OUT2); } +void cx18_reset_i2c_slaves_gpio(struct cx18 *cx) +{ + const struct cx18_gpio_i2c_slave_reset *p; + + p = &cx->card->gpio_i2c_slave_reset; + + if ((p->active_lo_mask | p->active_hi_mask) == 0) + return; + + /* Assuming that the masks are a subset of the bits in gpio_dir */ + + /* Assert */ + cx->gpio_val = + (cx->gpio_val | p->active_hi_mask) & ~(p->active_lo_mask); + gpio_write(cx); + schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_asserted)); + + /* Deassert */ + cx->gpio_val = + (cx->gpio_val | p->active_lo_mask) & ~(p->active_hi_mask); + gpio_write(cx); + schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery)); +} + void cx18_gpio_init(struct cx18 *cx) { cx->gpio_dir = cx->card->gpio_init.direction; diff --git a/drivers/media/video/cx18/cx18-gpio.h b/drivers/media/video/cx18/cx18-gpio.h index 41bac8856b5..525c328f748 100644 --- a/drivers/media/video/cx18/cx18-gpio.h +++ b/drivers/media/video/cx18/cx18-gpio.h @@ -21,4 +21,5 @@ */ void cx18_gpio_init(struct cx18 *cx); +void cx18_reset_i2c_slaves_gpio(struct cx18 *cx); int cx18_reset_tuner_gpio(void *dev, int cmd, int value); diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index 1d6c51a7531..680bc4e35b7 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c @@ -405,6 +405,8 @@ int init_cx18_i2c(struct cx18 *cx) cx18_setscl(&cx->i2c_algo_cb_data[1], 1); cx18_setsda(&cx->i2c_algo_cb_data[1], 1); + cx18_reset_i2c_slaves_gpio(cx); + return i2c_bit_add_bus(&cx->i2c_adap[0]) || i2c_bit_add_bus(&cx->i2c_adap[1]); } diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 607efdcd22f..1da6f134888 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -433,7 +433,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp int chroma = vid_input & 0xf00; if ((vid_input & ~0xff0) || - luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA4 || + luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA8 || chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) { v4l_err(client, "0x%04x is not a valid video input!\n", vid_input); diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c index e976fc6bef7..80c8883e54b 100644 --- a/drivers/media/video/cx88/cx88-alsa.c +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -332,6 +332,12 @@ static int snd_cx88_pcm_open(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; int err; + if (!chip) { + printk(KERN_ERR "BUG: cx88 can't find device struct." + " Can't proceed with open\n"); + return -ENODEV; + } + err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS); if (err < 0) goto _error; diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c index 92b2a6db4fd..3c006103c1e 100644 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ b/drivers/media/video/em28xx/em28xx-audio.c @@ -268,6 +268,12 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) dprintk("opening device and trying to acquire exclusive lock\n"); + if (!dev) { + printk(KERN_ERR "BUG: em28xx can't find device struct." + " Can't proceed with open\n"); + return -ENODEV; + } + /* Sets volume, mute, etc */ dev->mute = 0; @@ -415,6 +421,12 @@ static int em28xx_audio_init(struct em28xx *dev) static int devnr; int ret, err; + if (dev->has_audio_class) { + /* This device does not support the extension (in this case + the device is expecting the snd-usb-audio module */ + return 0; + } + printk(KERN_INFO "em28xx-audio.c: probing for em28x1 " "non standard usbaudio\n"); printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus " @@ -458,6 +470,12 @@ static int em28xx_audio_fini(struct em28xx *dev) if (dev == NULL) return 0; + if (dev->has_audio_class) { + /* This device does not support the extension (in this case + the device is expecting the snd-usb-audio module */ + return 0; + } + if (dev->adev) { snd_card_free(dev->adev->sndcard); kfree(dev->adev); diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 3e4f3c7e92e..8cbda43727c 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -157,6 +157,7 @@ struct em28xx_board em28xx_boards[] = { .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, .mts_firmware = 1, + .has_dvb = 1, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -524,6 +525,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID); if (rc > 0) { switch (rc) { + case CHIP_ID_EM2860: + em28xx_info("chip ID is em2860\n"); + break; case CHIP_ID_EM2883: em28xx_info("chip ID is em2882/em2883\n"); dev->wait_after_write = 0; diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index 8cf4983f003..0b2333ee07f 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -382,6 +382,11 @@ static int dvb_init(struct em28xx *dev) int result = 0; struct em28xx_dvb *dvb; + if (!dev->has_dvb) { + /* This device does not support the extension */ + return 0; + } + dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL); if (dvb == NULL) { @@ -444,6 +449,11 @@ out_free: static int dvb_fini(struct em28xx *dev) { + if (!dev->has_dvb) { + /* This device does not support the extension */ + return 0; + } + if (dev->dvb) { unregister_dvb(dev->dvb); dev->dvb = NULL; diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h index 9058bed0795..fac1ab23f62 100644 --- a/drivers/media/video/em28xx/em28xx-reg.h +++ b/drivers/media/video/em28xx/em28xx-reg.h @@ -84,5 +84,6 @@ /* FIXME: Need to be populated with the other chip ID's */ enum em28xx_chip_id { + CHIP_ID_EM2860 = 34, CHIP_ID_EM2883 = 36, }; diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index fb163ecd921..285bc62bbe4 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1848,32 +1848,28 @@ static DEFINE_MUTEX(em28xx_extension_devlist_lock); int em28xx_register_extension(struct em28xx_ops *ops) { - struct em28xx *h, *dev = NULL; - - list_for_each_entry(h, &em28xx_devlist, devlist) - dev = h; + struct em28xx *dev = NULL; mutex_lock(&em28xx_extension_devlist_lock); list_add_tail(&ops->next, &em28xx_extension_devlist); - if (dev) - ops->init(dev); - + list_for_each_entry(dev, &em28xx_devlist, devlist) { + if (dev) + ops->init(dev); + } printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name); mutex_unlock(&em28xx_extension_devlist_lock); - return 0; } EXPORT_SYMBOL(em28xx_register_extension); void em28xx_unregister_extension(struct em28xx_ops *ops) { - struct em28xx *h, *dev = NULL; - - list_for_each_entry(h, &em28xx_devlist, devlist) - dev = h; + struct em28xx *dev = NULL; - if (dev) - ops->fini(dev); + list_for_each_entry(dev, &em28xx_devlist, devlist) { + if (dev) + ops->fini(dev); + } mutex_lock(&em28xx_extension_devlist_lock); printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name); diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 7cc8e9b19fb..5ec5bb9a94d 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -1019,12 +1019,12 @@ static int pxa_camera_probe(struct platform_device *pdev) struct pxa_camera_dev *pcdev; struct resource *res; void __iomem *base; - unsigned int irq; + int irq; int err = 0; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); - if (!res || !irq) { + if (!res || irq < 0) { err = -ENODEV; goto exit; } diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c index ba3082422a0..f118de6e367 100644 --- a/drivers/media/video/saa7134/saa7134-alsa.c +++ b/drivers/media/video/saa7134/saa7134-alsa.c @@ -613,9 +613,15 @@ static int snd_card_saa7134_capture_open(struct snd_pcm_substream * substream) struct snd_pcm_runtime *runtime = substream->runtime; snd_card_saa7134_pcm_t *pcm; snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); - struct saa7134_dev *dev = saa7134->dev; + struct saa7134_dev *dev; int amux, err; + if (!saa7134) { + printk(KERN_ERR "BUG: saa7134 can't find device struct." + " Can't proceed with open\n"); + return -ENODEV; + } + dev = saa7134->dev; mutex_lock(&dev->dmasound.lock); dev->dmasound.read_count = 0; diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index b111903aa32..2618cfa592e 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -4114,11 +4114,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - /* - TODO: .mpeg = SAA7134_MPEG_DVB, - */ - .inputs = {{ .name = name_tv, .vmux = 1, @@ -4157,7 +4153,7 @@ struct saa7134_board saa7134_boards[] = { } }, .radio = { .name = name_radio, - .amux = LINE1, + .amux = TV, }, }, [SAA7134_BOARD_AVERMEDIA_M115] = { @@ -4167,6 +4163,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, .inputs = {{ .name = name_tv, .vmux = 1, @@ -5351,22 +5348,21 @@ static int saa7134_xc2028_callback(struct saa7134_dev *dev, { switch (command) { case XC2028_TUNER_RESET: - saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x06e20000, 0x06e20000); - saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x06a20000, 0x06a20000); - mdelay(250); - saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x06e20000, 0); - saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x06a20000, 0); - mdelay(250); - saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x06e20000, 0x06e20000); - saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x06a20000, 0x06a20000); - mdelay(250); - saa_andorl(SAA7133_ANALOG_IO_SELECT >> 2, 0x02, 0x02); - saa_andorl(SAA7134_ANALOG_IN_CTRL1 >> 2, 0x81, 0x81); - saa_andorl(SAA7134_AUDIO_CLOCK0 >> 2, 0x03187de7, 0x03187de7); - saa_andorl(SAA7134_AUDIO_PLL_CTRL >> 2, 0x03, 0x03); - saa_andorl(SAA7134_AUDIO_CLOCKS_PER_FIELD0 >> 2, - 0x0001e000, 0x0001e000); - return 0; + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00000000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000); + switch (dev->board) { + case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + saa7134_set_gpio(dev, 23, 0); + msleep(10); + saa7134_set_gpio(dev, 23, 1); + break; + case SAA7134_BOARD_AVERMEDIA_A16D: + saa7134_set_gpio(dev, 21, 0); + msleep(10); + saa7134_set_gpio(dev, 21, 1); + break; + } + return 0; } return -EINVAL; } @@ -5553,9 +5549,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08000000, 0x00000000); break; case SAA7134_BOARD_AVERMEDIA_CARDBUS: - case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: case SAA7134_BOARD_AVERMEDIA_M115: - case SAA7134_BOARD_AVERMEDIA_A16D: /* power-down tuner chip */ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0xffffffff, 0); saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0); @@ -5565,6 +5559,18 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0xffffffff); msleep(10); break; + case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + saa7134_set_gpio(dev, 23, 0); + msleep(10); + saa7134_set_gpio(dev, 23, 1); + break; + case SAA7134_BOARD_AVERMEDIA_A16D: + saa7134_set_gpio(dev, 21, 0); + msleep(10); + saa7134_set_gpio(dev, 21, 1); + msleep(1); + dev->has_remote = SAA7134_REMOTE_GPIO; + break; case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: /* power-down tuner chip */ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x000A8004, 0x000A8004); @@ -5615,7 +5621,8 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x80040100, 0x80040100); saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x80040100, 0x00040100); printk("%s: %s: hybrid analog/dvb card\n" - "%s: Sorry, only the analog inputs are supported for now.\n", + "%s: Sorry, only analog s-video and composite input " + "are supported for now.\n", dev->name, card(dev).name, dev->name); break; } @@ -5675,6 +5682,7 @@ static void saa7134_tuner_setup(struct saa7134_dev *dev) switch (dev->board) { case SAA7134_BOARD_AVERMEDIA_A16D: + case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: ctl.demod = XC3028_FE_ZARLINK456; break; default: diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 469f93aac00..341b101b035 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -153,12 +153,12 @@ static int mt352_aver777_init(struct dvb_frontend* fe) return 0; } -static int mt352_aver_a16d_init(struct dvb_frontend *fe) +static int mt352_avermedia_xc3028_init(struct dvb_frontend *fe) { - static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x2d }; - static u8 reset [] = { RESET, 0x80 }; - static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; - static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0xa0 }; + static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x2d }; + static u8 reset [] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg [] = { AGC_TARGET, 0xe }; static u8 capt_range_cfg[] = { CAPT_RANGE, 0x33 }; mt352_write(fe, clock_config, sizeof(clock_config)); @@ -167,12 +167,9 @@ static int mt352_aver_a16d_init(struct dvb_frontend *fe) mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); mt352_write(fe, agc_cfg, sizeof(agc_cfg)); mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); - return 0; } - - static int mt352_pinnacle_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { @@ -215,14 +212,10 @@ static struct mt352_config avermedia_777 = { .demod_init = mt352_aver777_init, }; -static struct mt352_config avermedia_16d = { - .demod_address = 0xf, - .demod_init = mt352_aver_a16d_init, -}; - -static struct mt352_config avermedia_e506r_mt352_dev = { +static struct mt352_config avermedia_xc3028_mt352_dev = { .demod_address = (0x1e >> 1), .no_tuner = 1, + .demod_init = mt352_avermedia_xc3028_init, }; /* ================================================================== @@ -975,9 +968,10 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_AVERMEDIA_A16D: - dprintk("avertv A16D dvb setup\n"); - dev->dvb.frontend = dvb_attach(mt352_attach, &avermedia_16d, - &dev->i2c_adap); + dprintk("AverMedia A16D dvb setup\n"); + dev->dvb.frontend = dvb_attach(mt352_attach, + &avermedia_xc3028_mt352_dev, + &dev->i2c_adap); attach_xc3028 = 1; break; case SAA7134_BOARD_MD7134: @@ -1091,7 +1085,8 @@ static int dvb_init(struct saa7134_dev *dev) ads_tech_duo_config.tuner_address); goto dettach_frontend; } - } + } else + wprintk("failed to attach tda10046\n"); break; case SAA7134_BOARD_TEVION_DVBT_220RF: if (configure_tda827x_fe(dev, &tevion_dvbt220rf_config, @@ -1260,11 +1255,14 @@ static int dvb_init(struct saa7134_dev *dev) goto dettach_frontend; break; case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + dprintk("AverMedia E506R dvb setup\n"); + saa7134_set_gpio(dev, 25, 0); + msleep(10); + saa7134_set_gpio(dev, 25, 1); dev->dvb.frontend = dvb_attach(mt352_attach, - &avermedia_e506r_mt352_dev, - &dev->i2c_adap); + &avermedia_xc3028_mt352_dev, + &dev->i2c_adap); attach_xc3028 = 1; - break; case SAA7134_BOARD_MD7134_BRIDGE_2: dev->dvb.frontend = dvb_attach(tda10086_attach, &sd1878_4m, &dev->i2c_adap); @@ -1338,7 +1336,8 @@ static int dvb_init(struct saa7134_dev *dev) return ret; dettach_frontend: - dvb_frontend_detach(dev->dvb.frontend); + if (dev->dvb.frontend) + dvb_frontend_detach(dev->dvb.frontend); dev->dvb.frontend = NULL; return -1; diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c index 81431ee4184..3ae71a34082 100644 --- a/drivers/media/video/saa7134/saa7134-empress.c +++ b/drivers/media/video/saa7134/saa7134-empress.c @@ -110,9 +110,10 @@ static int ts_release(struct inode *inode, struct file *file) { struct saa7134_dev *dev = file->private_data; + mutex_lock(&dev->empress_tsq.vb_lock); + videobuf_stop(&dev->empress_tsq); videobuf_mmap_free(&dev->empress_tsq); - dev->empress_users--; /* stop the encoder */ ts_reset_encoder(dev); @@ -121,6 +122,10 @@ static int ts_release(struct inode *inode, struct file *file) saa_writeb(SAA7134_AUDIO_MUTE_CTRL, saa_readb(SAA7134_AUDIO_MUTE_CTRL) | (1 << 6)); + dev->empress_users--; + + mutex_unlock(&dev->empress_tsq.vb_lock); + return 0; } @@ -218,8 +223,7 @@ static int empress_enum_fmt_cap(struct file *file, void *priv, static int empress_g_fmt_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; saa7134_i2c_call_clients(dev, VIDIOC_G_FMT, f); @@ -232,8 +236,7 @@ static int empress_g_fmt_cap(struct file *file, void *priv, static int empress_s_fmt_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; saa7134_i2c_call_clients(dev, VIDIOC_S_FMT, f); @@ -247,8 +250,7 @@ static int empress_s_fmt_cap(struct file *file, void *priv, static int empress_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; return videobuf_reqbufs(&dev->empress_tsq, p); } @@ -256,24 +258,21 @@ static int empress_reqbufs(struct file *file, void *priv, static int empress_querybuf(struct file *file, void *priv, struct v4l2_buffer *b) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; return videobuf_querybuf(&dev->empress_tsq, b); } static int empress_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; return videobuf_qbuf(&dev->empress_tsq, b); } static int empress_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; return videobuf_dqbuf(&dev->empress_tsq, b, file->f_flags & O_NONBLOCK); @@ -282,8 +281,7 @@ static int empress_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) static int empress_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; return videobuf_streamon(&dev->empress_tsq); } @@ -291,8 +289,7 @@ static int empress_streamon(struct file *file, void *priv, static int empress_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; return videobuf_streamoff(&dev->empress_tsq); } @@ -300,8 +297,7 @@ static int empress_streamoff(struct file *file, void *priv, static int empress_s_ext_ctrls(struct file *file, void *priv, struct v4l2_ext_controls *ctrls) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; /* count == 0 is abused in saa6752hs.c, so that special case is handled here explicitly. */ @@ -320,8 +316,7 @@ static int empress_s_ext_ctrls(struct file *file, void *priv, static int empress_g_ext_ctrls(struct file *file, void *priv, struct v4l2_ext_controls *ctrls) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) return -EINVAL; diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 919632b10aa..76e6501d238 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -323,6 +323,15 @@ int saa7134_input_init1(struct saa7134_dev *dev) saa_setb(SAA7134_GPIO_GPMODE1, 0x1); saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); break; + case SAA7134_BOARD_AVERMEDIA_A16D: + ir_codes = ir_codes_avermedia_a16d; + mask_keycode = 0x02F200; + mask_keydown = 0x000400; + polling = 50; /* ms */ + /* Without this we won't receive key up events */ + saa_setb(SAA7134_GPIO_GPMODE1, 0x1); + saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); + break; case SAA7134_BOARD_KWORLD_TERMINATOR: ir_codes = ir_codes_pixelview; mask_keycode = 0x00001f; diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index a1b92446c8b..d015bfe0095 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -763,15 +763,6 @@ static struct device_driver ic_drv = { .owner = THIS_MODULE, }; -/* - * Image capture host - this is a host device, not a bus device, so, - * no bus reference, no probing. - */ -static struct class soc_camera_host_class = { - .owner = THIS_MODULE, - .name = "camera_host", -}; - static void dummy_release(struct device *dev) { } @@ -801,7 +792,6 @@ int soc_camera_host_register(struct soc_camera_host *ici) /* Number might be equal to the platform device ID */ sprintf(ici->dev.bus_id, "camera_host%d", ici->nr); - ici->dev.class = &soc_camera_host_class; mutex_lock(&list_lock); list_for_each_entry(ix, &hosts, list) { @@ -1003,14 +993,9 @@ static int __init soc_camera_init(void) ret = driver_register(&ic_drv); if (ret) goto edrvr; - ret = class_register(&soc_camera_host_class); - if (ret) - goto eclr; return 0; -eclr: - driver_unregister(&ic_drv); edrvr: bus_unregister(&soc_camera_bus_type); return ret; @@ -1018,7 +1003,6 @@ edrvr: static void __exit soc_camera_exit(void) { - class_unregister(&soc_camera_host_class); driver_unregister(&ic_drv); bus_unregister(&soc_camera_bus_type); } diff --git a/drivers/media/video/uvc/Makefile b/drivers/media/video/uvc/Makefile new file mode 100644 index 00000000000..968c1994eda --- /dev/null +++ b/drivers/media/video/uvc/Makefile @@ -0,0 +1,3 @@ +uvcvideo-objs := uvc_driver.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_ctrl.o \ + uvc_status.o uvc_isight.o +obj-$(CONFIG_USB_VIDEO_CLASS) += uvcvideo.o diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c new file mode 100644 index 00000000000..f0ee46d1554 --- /dev/null +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -0,0 +1,1256 @@ +/* + * uvc_ctrl.c -- USB Video Class driver - Controls + * + * Copyright (C) 2005-2008 + * Laurent Pinchart (laurent.pinchart@skynet.be) + * + * 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/version.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/uaccess.h> +#include <linux/usb.h> +#include <linux/videodev2.h> +#include <linux/vmalloc.h> +#include <linux/wait.h> +#include <asm/atomic.h> + +#include "uvcvideo.h" + +#define UVC_CTRL_NDATA 2 +#define UVC_CTRL_DATA_CURRENT 0 +#define UVC_CTRL_DATA_BACKUP 1 + +/* ------------------------------------------------------------------------ + * Control, formats, ... + */ + +static struct uvc_control_info uvc_ctrls[] = { + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_BRIGHTNESS_CONTROL, + .index = 0, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_CONTRAST_CONTROL, + .index = 1, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_HUE_CONTROL, + .index = 2, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_SATURATION_CONTROL, + .index = 3, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_SHARPNESS_CONTROL, + .index = 4, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_GAMMA_CONTROL, + .index = 5, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_BACKLIGHT_COMPENSATION_CONTROL, + .index = 8, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_GAIN_CONTROL, + .index = 9, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_POWER_LINE_FREQUENCY_CONTROL, + .index = 10, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_HUE_AUTO_CONTROL, + .index = 11, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR + | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_AE_MODE_CONTROL, + .index = 1, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR + | UVC_CONTROL_GET_DEF | UVC_CONTROL_GET_RES + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_AE_PRIORITY_CONTROL, + .index = 2, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, + .index = 3, + .size = 4, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_FOCUS_ABSOLUTE_CONTROL, + .index = 5, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_FOCUS_AUTO_CONTROL, + .index = 17, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR + | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, + .index = 12, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR + | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_TEMPERATURE_CONTROL, + .index = 6, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, + .index = 13, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR + | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL, + .index = 7, + .size = 4, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + }, +}; + +static struct uvc_menu_info power_line_frequency_controls[] = { + { 0, "Disabled" }, + { 1, "50 Hz" }, + { 2, "60 Hz" }, +}; + +static struct uvc_menu_info exposure_auto_controls[] = { + { 1, "Manual Mode" }, + { 2, "Auto Mode" }, + { 4, "Shutter Priority Mode" }, + { 8, "Aperture Priority Mode" }, +}; + +static struct uvc_control_mapping uvc_ctrl_mappings[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .name = "Brightness", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_BRIGHTNESS_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_CONTRAST, + .name = "Contrast", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_CONTRAST_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_HUE, + .name = "Hue", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_HUE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_SATURATION, + .name = "Saturation", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_SATURATION_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_SHARPNESS, + .name = "Sharpness", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_SHARPNESS_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_GAMMA, + .name = "Gamma", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_GAMMA_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_BACKLIGHT_COMPENSATION, + .name = "Backlight Compensation", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_BACKLIGHT_COMPENSATION_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_GAIN, + .name = "Gain", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_GAIN_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .name = "Power Line Frequency", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_POWER_LINE_FREQUENCY_CONTROL, + .size = 2, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_MENU, + .data_type = UVC_CTRL_DATA_TYPE_ENUM, + .menu_info = power_line_frequency_controls, + .menu_count = ARRAY_SIZE(power_line_frequency_controls), + }, + { + .id = V4L2_CID_HUE_AUTO, + .name = "Hue, Auto", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_HUE_AUTO_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, + { + .id = V4L2_CID_EXPOSURE_AUTO, + .name = "Exposure, Auto", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_AE_MODE_CONTROL, + .size = 4, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_MENU, + .data_type = UVC_CTRL_DATA_TYPE_BITMASK, + .menu_info = exposure_auto_controls, + .menu_count = ARRAY_SIZE(exposure_auto_controls), + }, + { + .id = V4L2_CID_EXPOSURE_AUTO_PRIORITY, + .name = "Exposure, Auto Priority", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_AE_PRIORITY_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, + { + .id = V4L2_CID_EXPOSURE_ABSOLUTE, + .name = "Exposure (Absolute)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, + .size = 32, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .name = "White Balance Temperature, Auto", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, + { + .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, + .name = "White Balance Temperature", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_TEMPERATURE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .name = "White Balance Component, Auto", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .name = "White Balance Blue Component", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_RED_BALANCE, + .name = "White Balance Red Component", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL, + .size = 16, + .offset = 16, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_FOCUS_ABSOLUTE, + .name = "Focus (absolute)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_FOCUS_ABSOLUTE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_FOCUS_AUTO, + .name = "Focus, Auto", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_FOCUS_AUTO_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, +}; + +/* ------------------------------------------------------------------------ + * Utility functions + */ + +static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id) +{ + return ctrl->data + id * ctrl->info->size; +} + +static inline int uvc_get_bit(const __u8 *data, int bit) +{ + return (data[bit >> 3] >> (bit & 7)) & 1; +} + +/* Extract the bit string specified by mapping->offset and mapping->size + * from the little-endian data stored at 'data' and return the result as + * a signed 32bit integer. Sign extension will be performed if the mapping + * references a signed data type. + */ +static __s32 uvc_get_le_value(const __u8 *data, + struct uvc_control_mapping *mapping) +{ + int bits = mapping->size; + int offset = mapping->offset; + __s32 value = 0; + __u8 mask; + + data += offset / 8; + offset &= 7; + mask = ((1LL << bits) - 1) << offset; + + for (; bits > 0; data++) { + __u8 byte = *data & mask; + value |= offset > 0 ? (byte >> offset) : (byte << (-offset)); + bits -= 8 - (offset > 0 ? offset : 0); + offset -= 8; + mask = (1 << bits) - 1; + } + + /* Sign-extend the value if needed */ + if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED) + value |= -(value & (1 << (mapping->size - 1))); + + return value; +} + +/* Set the bit string specified by mapping->offset and mapping->size + * in the little-endian data stored at 'data' to the value 'value'. + */ +static void uvc_set_le_value(__s32 value, __u8 *data, + struct uvc_control_mapping *mapping) +{ + int bits = mapping->size; + int offset = mapping->offset; + __u8 mask; + + data += offset / 8; + offset &= 7; + + for (; bits > 0; data++) { + mask = ((1LL << bits) - 1) << offset; + *data = (*data & ~mask) | ((value << offset) & mask); + value >>= offset ? offset : 8; + bits -= 8 - offset; + offset = 0; + } +} + +/* ------------------------------------------------------------------------ + * Terminal and unit management + */ + +static const __u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING; +static const __u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA; +static const __u8 uvc_media_transport_input_guid[16] = + UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT; + +static int uvc_entity_match_guid(struct uvc_entity *entity, __u8 guid[16]) +{ + switch (UVC_ENTITY_TYPE(entity)) { + case ITT_CAMERA: + return memcmp(uvc_camera_guid, guid, 16) == 0; + + case ITT_MEDIA_TRANSPORT_INPUT: + return memcmp(uvc_media_transport_input_guid, guid, 16) == 0; + + case VC_PROCESSING_UNIT: + return memcmp(uvc_processing_guid, guid, 16) == 0; + + case VC_EXTENSION_UNIT: + return memcmp(entity->extension.guidExtensionCode, + guid, 16) == 0; + + default: + return 0; + } +} + +/* ------------------------------------------------------------------------ + * UVC Controls + */ + +static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id, + struct uvc_control_mapping **mapping, struct uvc_control **control, + int next) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *map; + unsigned int i; + + if (entity == NULL) + return; + + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + if (ctrl->info == NULL) + continue; + + list_for_each_entry(map, &ctrl->info->mappings, list) { + if ((map->id == v4l2_id) && !next) { + *control = ctrl; + *mapping = map; + return; + } + + if ((*mapping == NULL || (*mapping)->id > map->id) && + (map->id > v4l2_id) && next) { + *control = ctrl; + *mapping = map; + } + } + } +} + +struct uvc_control *uvc_find_control(struct uvc_video_device *video, + __u32 v4l2_id, struct uvc_control_mapping **mapping) +{ + struct uvc_control *ctrl = NULL; + struct uvc_entity *entity; + int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL; + + *mapping = NULL; + + /* Mask the query flags. */ + v4l2_id &= V4L2_CTRL_ID_MASK; + + /* Find the control. */ + __uvc_find_control(video->processing, v4l2_id, mapping, &ctrl, next); + if (ctrl && !next) + return ctrl; + + list_for_each_entry(entity, &video->iterms, chain) { + __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); + if (ctrl && !next) + return ctrl; + } + + list_for_each_entry(entity, &video->extensions, chain) { + __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); + if (ctrl && !next) + return ctrl; + } + + if (ctrl == NULL && !next) + uvc_trace(UVC_TRACE_CONTROL, "Control 0x%08x not found.\n", + v4l2_id); + + return ctrl; +} + +int uvc_query_v4l2_ctrl(struct uvc_video_device *video, + struct v4l2_queryctrl *v4l2_ctrl) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *mapping; + struct uvc_menu_info *menu; + unsigned int i; + __u8 data[8]; + int ret; + + ctrl = uvc_find_control(video, v4l2_ctrl->id, &mapping); + if (ctrl == NULL) + return -EINVAL; + + v4l2_ctrl->id = mapping->id; + v4l2_ctrl->type = mapping->v4l2_type; + strncpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name); + v4l2_ctrl->flags = 0; + + if (!(ctrl->info->flags & UVC_CONTROL_SET_CUR)) + v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + if (ctrl->info->flags & UVC_CONTROL_GET_DEF) { + if ((ret = uvc_query_ctrl(video->dev, GET_DEF, ctrl->entity->id, + video->dev->intfnum, ctrl->info->selector, + &data, ctrl->info->size)) < 0) + return ret; + v4l2_ctrl->default_value = uvc_get_le_value(data, mapping); + } + + if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { + v4l2_ctrl->minimum = 0; + v4l2_ctrl->maximum = mapping->menu_count - 1; + v4l2_ctrl->step = 1; + + menu = mapping->menu_info; + for (i = 0; i < mapping->menu_count; ++i, ++menu) { + if (menu->value == v4l2_ctrl->default_value) { + v4l2_ctrl->default_value = i; + break; + } + } + + return 0; + } + + if (ctrl->info->flags & UVC_CONTROL_GET_MIN) { + if ((ret = uvc_query_ctrl(video->dev, GET_MIN, ctrl->entity->id, + video->dev->intfnum, ctrl->info->selector, + &data, ctrl->info->size)) < 0) + return ret; + v4l2_ctrl->minimum = uvc_get_le_value(data, mapping); + } + if (ctrl->info->flags & UVC_CONTROL_GET_MAX) { + if ((ret = uvc_query_ctrl(video->dev, GET_MAX, ctrl->entity->id, + video->dev->intfnum, ctrl->info->selector, + &data, ctrl->info->size)) < 0) + return ret; + v4l2_ctrl->maximum = uvc_get_le_value(data, mapping); + } + if (ctrl->info->flags & UVC_CONTROL_GET_RES) { + if ((ret = uvc_query_ctrl(video->dev, GET_RES, ctrl->entity->id, + video->dev->intfnum, ctrl->info->selector, + &data, ctrl->info->size)) < 0) + return ret; + v4l2_ctrl->step = uvc_get_le_value(data, mapping); + } + + return 0; +} + + +/* -------------------------------------------------------------------------- + * Control transactions + * + * To make extended set operations as atomic as the hardware allows, controls + * are handled using begin/commit/rollback operations. + * + * At the beginning of a set request, uvc_ctrl_begin should be called to + * initialize the request. This function acquires the control lock. + * + * When setting a control, the new value is stored in the control data field + * at position UVC_CTRL_DATA_CURRENT. The control is then marked as dirty for + * later processing. If the UVC and V4L2 control sizes differ, the current + * value is loaded from the hardware before storing the new value in the data + * field. + * + * After processing all controls in the transaction, uvc_ctrl_commit or + * uvc_ctrl_rollback must be called to apply the pending changes to the + * hardware or revert them. When applying changes, all controls marked as + * dirty will be modified in the UVC device, and the dirty flag will be + * cleared. When reverting controls, the control data field + * UVC_CTRL_DATA_CURRENT is reverted to its previous value + * (UVC_CTRL_DATA_BACKUP) for all dirty controls. Both functions release the + * control lock. + */ +int uvc_ctrl_begin(struct uvc_video_device *video) +{ + return mutex_lock_interruptible(&video->ctrl_mutex) ? -ERESTARTSYS : 0; +} + +static int uvc_ctrl_commit_entity(struct uvc_device *dev, + struct uvc_entity *entity, int rollback) +{ + struct uvc_control *ctrl; + unsigned int i; + int ret; + + if (entity == NULL) + return 0; + + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + if (ctrl->info == NULL || !ctrl->dirty) + continue; + + if (!rollback) + ret = uvc_query_ctrl(dev, SET_CUR, ctrl->entity->id, + dev->intfnum, ctrl->info->selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info->size); + else + ret = 0; + + if (rollback || ret < 0) + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), + ctrl->info->size); + + if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) + ctrl->loaded = 0; + + ctrl->dirty = 0; + + if (ret < 0) + return ret; + } + + return 0; +} + +int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback) +{ + struct uvc_entity *entity; + int ret = 0; + + /* Find the control. */ + ret = uvc_ctrl_commit_entity(video->dev, video->processing, rollback); + if (ret < 0) + goto done; + + list_for_each_entry(entity, &video->iterms, chain) { + ret = uvc_ctrl_commit_entity(video->dev, entity, rollback); + if (ret < 0) + goto done; + } + + list_for_each_entry(entity, &video->extensions, chain) { + ret = uvc_ctrl_commit_entity(video->dev, entity, rollback); + if (ret < 0) + goto done; + } + +done: + mutex_unlock(&video->ctrl_mutex); + return ret; +} + +int uvc_ctrl_get(struct uvc_video_device *video, + struct v4l2_ext_control *xctrl) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *mapping; + struct uvc_menu_info *menu; + unsigned int i; + int ret; + + ctrl = uvc_find_control(video, xctrl->id, &mapping); + if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) + return -EINVAL; + + if (!ctrl->loaded) { + ret = uvc_query_ctrl(video->dev, GET_CUR, ctrl->entity->id, + video->dev->intfnum, ctrl->info->selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info->size); + if (ret < 0) + return ret; + + if ((ctrl->info->flags & UVC_CONTROL_AUTO_UPDATE) == 0) + ctrl->loaded = 1; + } + + xctrl->value = uvc_get_le_value( + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping); + + if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { + menu = mapping->menu_info; + for (i = 0; i < mapping->menu_count; ++i, ++menu) { + if (menu->value == xctrl->value) { + xctrl->value = i; + break; + } + } + } + + return 0; +} + +int uvc_ctrl_set(struct uvc_video_device *video, + struct v4l2_ext_control *xctrl) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *mapping; + s32 value = xctrl->value; + int ret; + + ctrl = uvc_find_control(video, xctrl->id, &mapping); + if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0) + return -EINVAL; + + if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { + if (value < 0 || value >= mapping->menu_count) + return -EINVAL; + value = mapping->menu_info[value].value; + } + + if (!ctrl->loaded && (ctrl->info->size * 8) != mapping->size) { + if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) { + memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + 0, ctrl->info->size); + } else { + ret = uvc_query_ctrl(video->dev, GET_CUR, + ctrl->entity->id, video->dev->intfnum, + ctrl->info->selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info->size); + if (ret < 0) + return ret; + } + + if ((ctrl->info->flags & UVC_CONTROL_AUTO_UPDATE) == 0) + ctrl->loaded = 1; + } + + if (!ctrl->dirty) { + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info->size); + } + + uvc_set_le_value(value, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping); + + ctrl->dirty = 1; + ctrl->modified = 1; + return 0; +} + +/* -------------------------------------------------------------------------- + * Dynamic controls + */ + +int uvc_xu_ctrl_query(struct uvc_video_device *video, + struct uvc_xu_control *xctrl, int set) +{ + struct uvc_entity *entity; + struct uvc_control *ctrl = NULL; + unsigned int i, found = 0; + __u8 *data; + int ret; + + /* Find the extension unit. */ + list_for_each_entry(entity, &video->extensions, chain) { + if (entity->id == xctrl->unit) + break; + } + + if (entity->id != xctrl->unit) { + uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n", + xctrl->unit); + return -EINVAL; + } + + /* Find the control. */ + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + if (ctrl->info == NULL) + continue; + + if (ctrl->info->selector == xctrl->selector) { + found = 1; + break; + } + } + + if (!found) { + uvc_trace(UVC_TRACE_CONTROL, + "Control " UVC_GUID_FORMAT "/%u not found.\n", + UVC_GUID_ARGS(entity->extension.guidExtensionCode), + xctrl->selector); + return -EINVAL; + } + + /* Validate control data size. */ + if (ctrl->info->size != xctrl->size) + return -EINVAL; + + if ((set && !(ctrl->info->flags & UVC_CONTROL_SET_CUR)) || + (!set && !(ctrl->info->flags & UVC_CONTROL_GET_CUR))) + return -EINVAL; + + if (mutex_lock_interruptible(&video->ctrl_mutex)) + return -ERESTARTSYS; + + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + xctrl->size); + data = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT); + + if (set && copy_from_user(data, xctrl->data, xctrl->size)) { + ret = -EFAULT; + goto out; + } + + ret = uvc_query_ctrl(video->dev, set ? SET_CUR : GET_CUR, xctrl->unit, + video->dev->intfnum, xctrl->selector, data, + xctrl->size); + if (ret < 0) + goto out; + + if (!set && copy_to_user(xctrl->data, data, xctrl->size)) { + ret = -EFAULT; + goto out; + } + +out: + if (ret) + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), + xctrl->size); + + mutex_unlock(&video->ctrl_mutex); + return ret; +} + +/* -------------------------------------------------------------------------- + * Suspend/resume + */ + +/* + * Restore control values after resume, skipping controls that haven't been + * changed. + * + * TODO + * - Don't restore modified controls that are back to their default value. + * - Handle restore order (Auto-Exposure Mode should be restored before + * Exposure Time). + */ +int uvc_ctrl_resume_device(struct uvc_device *dev) +{ + struct uvc_control *ctrl; + struct uvc_entity *entity; + unsigned int i; + int ret; + + /* Walk the entities list and restore controls when possible. */ + list_for_each_entry(entity, &dev->entities, list) { + + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + + if (ctrl->info == NULL || !ctrl->modified || + (ctrl->info->flags & UVC_CONTROL_RESTORE) == 0) + continue; + + printk(KERN_INFO "restoring control " UVC_GUID_FORMAT + "/%u/%u\n", UVC_GUID_ARGS(ctrl->info->entity), + ctrl->info->index, ctrl->info->selector); + ctrl->dirty = 1; + } + + ret = uvc_ctrl_commit_entity(dev, entity, 0); + if (ret < 0) + return ret; + } + + return 0; +} + +/* -------------------------------------------------------------------------- + * Control and mapping handling + */ + +static void uvc_ctrl_add_ctrl(struct uvc_device *dev, + struct uvc_control_info *info) +{ + struct uvc_entity *entity; + struct uvc_control *ctrl = NULL; + int ret, found = 0; + unsigned int i; + + list_for_each_entry(entity, &dev->entities, list) { + if (!uvc_entity_match_guid(entity, info->entity)) + continue; + + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + if (ctrl->index == info->index) { + found = 1; + break; + } + } + + if (found) + break; + } + + if (!found) + return; + + if (UVC_ENTITY_TYPE(entity) == VC_EXTENSION_UNIT) { + /* Check if the device control information and length match + * the user supplied information. + */ + __u32 flags; + __le16 size; + __u8 inf; + + if ((ret = uvc_query_ctrl(dev, GET_LEN, ctrl->entity->id, + dev->intfnum, info->selector, (__u8 *)&size, 2)) < 0) { + uvc_trace(UVC_TRACE_CONTROL, "GET_LEN failed on " + "control " UVC_GUID_FORMAT "/%u (%d).\n", + UVC_GUID_ARGS(info->entity), info->selector, + ret); + return; + } + + if (info->size != le16_to_cpu(size)) { + uvc_trace(UVC_TRACE_CONTROL, "Control " UVC_GUID_FORMAT + "/%u size doesn't match user supplied " + "value.\n", UVC_GUID_ARGS(info->entity), + info->selector); + return; + } + + if ((ret = uvc_query_ctrl(dev, GET_INFO, ctrl->entity->id, + dev->intfnum, info->selector, &inf, 1)) < 0) { + uvc_trace(UVC_TRACE_CONTROL, "GET_INFO failed on " + "control " UVC_GUID_FORMAT "/%u (%d).\n", + UVC_GUID_ARGS(info->entity), info->selector, + ret); + return; + } + + flags = info->flags; + if (((flags & UVC_CONTROL_GET_CUR) && !(inf & (1 << 0))) || + ((flags & UVC_CONTROL_SET_CUR) && !(inf & (1 << 1)))) { + uvc_trace(UVC_TRACE_CONTROL, "Control " + UVC_GUID_FORMAT "/%u flags don't match " + "supported operations.\n", + UVC_GUID_ARGS(info->entity), info->selector); + return; + } + } + + ctrl->info = info; + ctrl->data = kmalloc(ctrl->info->size * UVC_CTRL_NDATA, GFP_KERNEL); + uvc_trace(UVC_TRACE_CONTROL, "Added control " UVC_GUID_FORMAT "/%u " + "to device %s entity %u\n", UVC_GUID_ARGS(ctrl->info->entity), + ctrl->info->selector, dev->udev->devpath, entity->id); +} + +/* + * Add an item to the UVC control information list, and instantiate a control + * structure for each device that supports the control. + */ +int uvc_ctrl_add_info(struct uvc_control_info *info) +{ + struct uvc_control_info *ctrl; + struct uvc_device *dev; + int ret = 0; + + /* Find matching controls by walking the devices, entities and + * controls list. + */ + mutex_lock(&uvc_driver.ctrl_mutex); + + /* First check if the list contains a control matching the new one. + * Bail out if it does. + */ + list_for_each_entry(ctrl, &uvc_driver.controls, list) { + if (memcmp(ctrl->entity, info->entity, 16)) + continue; + + if (ctrl->selector == info->selector) { + uvc_trace(UVC_TRACE_CONTROL, "Control " + UVC_GUID_FORMAT "/%u is already defined.\n", + UVC_GUID_ARGS(info->entity), info->selector); + ret = -EEXIST; + goto end; + } + if (ctrl->index == info->index) { + uvc_trace(UVC_TRACE_CONTROL, "Control " + UVC_GUID_FORMAT "/%u would overwrite index " + "%d.\n", UVC_GUID_ARGS(info->entity), + info->selector, info->index); + ret = -EEXIST; + goto end; + } + } + + list_for_each_entry(dev, &uvc_driver.devices, list) + uvc_ctrl_add_ctrl(dev, info); + + INIT_LIST_HEAD(&info->mappings); + list_add_tail(&info->list, &uvc_driver.controls); +end: + mutex_unlock(&uvc_driver.ctrl_mutex); + return ret; +} + +int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping) +{ + struct uvc_control_info *info; + struct uvc_control_mapping *map; + int ret = -EINVAL; + + if (mapping->id & ~V4L2_CTRL_ID_MASK) { + uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s' with " + "invalid control id 0x%08x\n", mapping->name, + mapping->id); + return -EINVAL; + } + + mutex_lock(&uvc_driver.ctrl_mutex); + list_for_each_entry(info, &uvc_driver.controls, list) { + if (memcmp(info->entity, mapping->entity, 16) || + info->selector != mapping->selector) + continue; + + if (info->size * 8 < mapping->size + mapping->offset) { + uvc_trace(UVC_TRACE_CONTROL, "Mapping '%s' would " + "overflow control " UVC_GUID_FORMAT "/%u\n", + mapping->name, UVC_GUID_ARGS(info->entity), + info->selector); + ret = -EOVERFLOW; + goto end; + } + + /* Check if the list contains a mapping matching the new one. + * Bail out if it does. + */ + list_for_each_entry(map, &info->mappings, list) { + if (map->id == mapping->id) { + uvc_trace(UVC_TRACE_CONTROL, "Mapping '%s' is " + "already defined.\n", mapping->name); + ret = -EEXIST; + goto end; + } + } + + mapping->ctrl = info; + list_add_tail(&mapping->list, &info->mappings); + uvc_trace(UVC_TRACE_CONTROL, "Adding mapping %s to control " + UVC_GUID_FORMAT "/%u.\n", mapping->name, + UVC_GUID_ARGS(info->entity), info->selector); + + ret = 0; + break; + } +end: + mutex_unlock(&uvc_driver.ctrl_mutex); + return ret; +} + +/* + * Initialize device controls. + */ +int uvc_ctrl_init_device(struct uvc_device *dev) +{ + struct uvc_control_info *info; + struct uvc_control *ctrl; + struct uvc_entity *entity; + unsigned int i; + + /* Walk the entities list and instantiate controls */ + list_for_each_entry(entity, &dev->entities, list) { + unsigned int bControlSize = 0, ncontrols = 0; + __u8 *bmControls = NULL; + + if (UVC_ENTITY_TYPE(entity) == VC_EXTENSION_UNIT) { + bmControls = entity->extension.bmControls; + bControlSize = entity->extension.bControlSize; + } else if (UVC_ENTITY_TYPE(entity) == VC_PROCESSING_UNIT) { + bmControls = entity->processing.bmControls; + bControlSize = entity->processing.bControlSize; + } else if (UVC_ENTITY_TYPE(entity) == ITT_CAMERA) { + bmControls = entity->camera.bmControls; + bControlSize = entity->camera.bControlSize; + } + + for (i = 0; i < bControlSize; ++i) + ncontrols += hweight8(bmControls[i]); + + if (ncontrols == 0) + continue; + + entity->controls = kzalloc(ncontrols*sizeof *ctrl, GFP_KERNEL); + if (entity->controls == NULL) + return -ENOMEM; + + entity->ncontrols = ncontrols; + + ctrl = entity->controls; + for (i = 0; i < bControlSize * 8; ++i) { + if (uvc_get_bit(bmControls, i) == 0) + continue; + + ctrl->entity = entity; + ctrl->index = i; + ctrl++; + } + } + + /* Walk the controls info list and associate them with the device + * controls, then add the device to the global device list. This has + * to be done while holding the controls lock, to make sure + * uvc_ctrl_add_info() will not get called in-between. + */ + mutex_lock(&uvc_driver.ctrl_mutex); + list_for_each_entry(info, &uvc_driver.controls, list) + uvc_ctrl_add_ctrl(dev, info); + + list_add_tail(&dev->list, &uvc_driver.devices); + mutex_unlock(&uvc_driver.ctrl_mutex); + + return 0; +} + +/* + * Cleanup device controls. + */ +void uvc_ctrl_cleanup_device(struct uvc_device *dev) +{ + struct uvc_entity *entity; + unsigned int i; + + /* Remove the device from the global devices list */ + mutex_lock(&uvc_driver.ctrl_mutex); + if (dev->list.next != NULL) + list_del(&dev->list); + mutex_unlock(&uvc_driver.ctrl_mutex); + + list_for_each_entry(entity, &dev->entities, list) { + for (i = 0; i < entity->ncontrols; ++i) + kfree(entity->controls[i].data); + + kfree(entity->controls); + } +} + +void uvc_ctrl_init(void) +{ + struct uvc_control_info *ctrl = uvc_ctrls; + struct uvc_control_info *cend = ctrl + ARRAY_SIZE(uvc_ctrls); + struct uvc_control_mapping *mapping = uvc_ctrl_mappings; + struct uvc_control_mapping *mend = + mapping + ARRAY_SIZE(uvc_ctrl_mappings); + + for (; ctrl < cend; ++ctrl) + uvc_ctrl_add_info(ctrl); + + for (; mapping < mend; ++mapping) + uvc_ctrl_add_mapping(mapping); +} diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c new file mode 100644 index 00000000000..60ced589f89 --- /dev/null +++ b/drivers/media/video/uvc/uvc_driver.c @@ -0,0 +1,1955 @@ +/* + * uvc_driver.c -- USB Video Class driver + * + * Copyright (C) 2005-2008 + * Laurent Pinchart (laurent.pinchart@skynet.be) + * + * 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 driver aims to support video input devices compliant with the 'USB + * Video Class' specification. + * + * The driver doesn't support the deprecated v4l1 interface. It implements the + * mmap capture method only, and doesn't do any image format conversion in + * software. If your user-space application doesn't support YUYV or MJPEG, fix + * it :-). Please note that the MJPEG data have been stripped from their + * Huffman tables (DHT marker), you will need to add it back if your JPEG + * codec can't handle MJPEG data. + */ + +#include <linux/kernel.h> +#include <linux/version.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/videodev2.h> +#include <linux/vmalloc.h> +#include <linux/wait.h> +#include <asm/atomic.h> + +#include <media/v4l2-common.h> + +#include "uvcvideo.h" + +#define DRIVER_AUTHOR "Laurent Pinchart <laurent.pinchart@skynet.be>" +#define DRIVER_DESC "USB Video Class driver" +#ifndef DRIVER_VERSION +#define DRIVER_VERSION "v0.1.0" +#endif + +static unsigned int uvc_quirks_param; +unsigned int uvc_trace_param; + +/* ------------------------------------------------------------------------ + * Control, formats, ... + */ + +static struct uvc_format_desc uvc_fmts[] = { + { + .name = "YUV 4:2:2 (YUYV)", + .guid = UVC_GUID_FORMAT_YUY2, + .fcc = V4L2_PIX_FMT_YUYV, + }, + { + .name = "YUV 4:2:0 (NV12)", + .guid = UVC_GUID_FORMAT_NV12, + .fcc = V4L2_PIX_FMT_NV12, + }, + { + .name = "MJPEG", + .guid = UVC_GUID_FORMAT_MJPEG, + .fcc = V4L2_PIX_FMT_MJPEG, + }, + { + .name = "YVU 4:2:0 (YV12)", + .guid = UVC_GUID_FORMAT_YV12, + .fcc = V4L2_PIX_FMT_YVU420, + }, + { + .name = "YUV 4:2:0 (I420)", + .guid = UVC_GUID_FORMAT_I420, + .fcc = V4L2_PIX_FMT_YUV420, + }, + { + .name = "YUV 4:2:2 (UYVY)", + .guid = UVC_GUID_FORMAT_UYVY, + .fcc = V4L2_PIX_FMT_UYVY, + }, + { + .name = "Greyscale", + .guid = UVC_GUID_FORMAT_Y800, + .fcc = V4L2_PIX_FMT_GREY, + }, + { + .name = "RGB Bayer", + .guid = UVC_GUID_FORMAT_BY8, + .fcc = V4L2_PIX_FMT_SBGGR8, + }, +}; + +/* ------------------------------------------------------------------------ + * Utility functions + */ + +struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts, + __u8 epaddr) +{ + struct usb_host_endpoint *ep; + unsigned int i; + + for (i = 0; i < alts->desc.bNumEndpoints; ++i) { + ep = &alts->endpoint[i]; + if (ep->desc.bEndpointAddress == epaddr) + return ep; + } + + return NULL; +} + +static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16]) +{ + unsigned int len = ARRAY_SIZE(uvc_fmts); + unsigned int i; + + for (i = 0; i < len; ++i) { + if (memcmp(guid, uvc_fmts[i].guid, 16) == 0) + return &uvc_fmts[i]; + } + + return NULL; +} + +static __u32 uvc_colorspace(const __u8 primaries) +{ + static const __u8 colorprimaries[] = { + 0, + V4L2_COLORSPACE_SRGB, + V4L2_COLORSPACE_470_SYSTEM_M, + V4L2_COLORSPACE_470_SYSTEM_BG, + V4L2_COLORSPACE_SMPTE170M, + V4L2_COLORSPACE_SMPTE240M, + }; + + if (primaries < ARRAY_SIZE(colorprimaries)) + return colorprimaries[primaries]; + + return 0; +} + +/* Simplify a fraction using a simple continued fraction decomposition. The + * idea here is to convert fractions such as 333333/10000000 to 1/30 using + * 32 bit arithmetic only. The algorithm is not perfect and relies upon two + * arbitrary parameters to remove non-significative terms from the simple + * continued fraction decomposition. Using 8 and 333 for n_terms and threshold + * respectively seems to give nice results. + */ +void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, + unsigned int n_terms, unsigned int threshold) +{ + uint32_t *an; + uint32_t x, y, r; + unsigned int i, n; + + an = kmalloc(n_terms * sizeof *an, GFP_KERNEL); + if (an == NULL) + return; + + /* Convert the fraction to a simple continued fraction. See + * http://mathforum.org/dr.math/faq/faq.fractions.html + * Stop if the current term is bigger than or equal to the given + * threshold. + */ + x = *numerator; + y = *denominator; + + for (n = 0; n < n_terms && y != 0; ++n) { + an[n] = x / y; + if (an[n] >= threshold) { + if (n < 2) + n++; + break; + } + + r = x - an[n] * y; + x = y; + y = r; + } + + /* Expand the simple continued fraction back to an integer fraction. */ + x = 0; + y = 1; + + for (i = n; i > 0; --i) { + r = y; + y = an[i-1] * y + x; + x = r; + } + + *numerator = y; + *denominator = x; + kfree(an); +} + +/* Convert a fraction to a frame interval in 100ns multiples. The idea here is + * to compute numerator / denominator * 10000000 using 32 bit fixed point + * arithmetic only. + */ +uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator) +{ + uint32_t multiplier; + + /* Saturate the result if the operation would overflow. */ + if (denominator == 0 || + numerator/denominator >= ((uint32_t)-1)/10000000) + return (uint32_t)-1; + + /* Divide both the denominator and the multiplier by two until + * numerator * multiplier doesn't overflow. If anyone knows a better + * algorithm please let me know. + */ + multiplier = 10000000; + while (numerator > ((uint32_t)-1)/multiplier) { + multiplier /= 2; + denominator /= 2; + } + + return denominator ? numerator * multiplier / denominator : 0; +} + +/* ------------------------------------------------------------------------ + * Terminal and unit management + */ + +static struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id) +{ + struct uvc_entity *entity; + + list_for_each_entry(entity, &dev->entities, list) { + if (entity->id == id) + return entity; + } + + return NULL; +} + +static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, + int id, struct uvc_entity *entity) +{ + unsigned int i; + + if (entity == NULL) + entity = list_entry(&dev->entities, struct uvc_entity, list); + + list_for_each_entry_continue(entity, &dev->entities, list) { + switch (UVC_ENTITY_TYPE(entity)) { + case TT_STREAMING: + if (entity->output.bSourceID == id) + return entity; + break; + + case VC_PROCESSING_UNIT: + if (entity->processing.bSourceID == id) + return entity; + break; + + case VC_SELECTOR_UNIT: + for (i = 0; i < entity->selector.bNrInPins; ++i) + if (entity->selector.baSourceID[i] == id) + return entity; + break; + + case VC_EXTENSION_UNIT: + for (i = 0; i < entity->extension.bNrInPins; ++i) + if (entity->extension.baSourceID[i] == id) + return entity; + break; + } + } + + return NULL; +} + +/* ------------------------------------------------------------------------ + * Descriptors handling + */ + +static int uvc_parse_format(struct uvc_device *dev, + struct uvc_streaming *streaming, struct uvc_format *format, + __u32 **intervals, unsigned char *buffer, int buflen) +{ + struct usb_interface *intf = streaming->intf; + struct usb_host_interface *alts = intf->cur_altsetting; + struct uvc_format_desc *fmtdesc; + struct uvc_frame *frame; + const unsigned char *start = buffer; + unsigned int interval; + unsigned int i, n; + __u8 ftype; + + format->type = buffer[2]; + format->index = buffer[3]; + + switch (buffer[2]) { + case VS_FORMAT_UNCOMPRESSED: + case VS_FORMAT_FRAME_BASED: + if (buflen < 27) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d FORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + /* Find the format descriptor from its GUID. */ + fmtdesc = uvc_format_by_guid(&buffer[5]); + + if (fmtdesc != NULL) { + strncpy(format->name, fmtdesc->name, + sizeof format->name); + format->fcc = fmtdesc->fcc; + } else { + uvc_printk(KERN_INFO, "Unknown video format " + UVC_GUID_FORMAT "\n", + UVC_GUID_ARGS(&buffer[5])); + snprintf(format->name, sizeof format->name, + UVC_GUID_FORMAT, UVC_GUID_ARGS(&buffer[5])); + format->fcc = 0; + } + + format->bpp = buffer[21]; + if (buffer[2] == VS_FORMAT_UNCOMPRESSED) { + ftype = VS_FRAME_UNCOMPRESSED; + } else { + ftype = VS_FRAME_FRAME_BASED; + if (buffer[27]) + format->flags = UVC_FMT_FLAG_COMPRESSED; + } + break; + + case VS_FORMAT_MJPEG: + if (buflen < 11) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d FORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + strncpy(format->name, "MJPEG", sizeof format->name); + format->fcc = V4L2_PIX_FMT_MJPEG; + format->flags = UVC_FMT_FLAG_COMPRESSED; + format->bpp = 0; + ftype = VS_FRAME_MJPEG; + break; + + case VS_FORMAT_DV: + if (buflen < 9) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d FORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + switch (buffer[8] & 0x7f) { + case 0: + strncpy(format->name, "SD-DV", sizeof format->name); + break; + case 1: + strncpy(format->name, "SDL-DV", sizeof format->name); + break; + case 2: + strncpy(format->name, "HD-DV", sizeof format->name); + break; + default: + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d: unknown DV format %u\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber, buffer[8]); + return -EINVAL; + } + + strncat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz", + sizeof format->name); + + format->fcc = V4L2_PIX_FMT_DV; + format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM; + format->bpp = 0; + ftype = 0; + + /* Create a dummy frame descriptor. */ + frame = &format->frame[0]; + memset(&format->frame[0], 0, sizeof format->frame[0]); + frame->bFrameIntervalType = 1; + frame->dwDefaultFrameInterval = 1; + frame->dwFrameInterval = *intervals; + *(*intervals)++ = 1; + format->nframes = 1; + break; + + case VS_FORMAT_MPEG2TS: + case VS_FORMAT_STREAM_BASED: + /* Not supported yet. */ + default: + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d unsupported format %u\n", + dev->udev->devnum, alts->desc.bInterfaceNumber, + buffer[2]); + return -EINVAL; + } + + uvc_trace(UVC_TRACE_DESCR, "Found format %s.\n", format->name); + + buflen -= buffer[0]; + buffer += buffer[0]; + + /* Parse the frame descriptors. Only uncompressed, MJPEG and frame + * based formats have frame descriptors. + */ + while (buflen > 2 && buffer[2] == ftype) { + frame = &format->frame[format->nframes]; + + if (ftype != VS_FRAME_FRAME_BASED) + n = buflen > 25 ? buffer[25] : 0; + else + n = buflen > 21 ? buffer[21] : 0; + + n = n ? n : 3; + + if (buflen < 26 + 4*n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d FRAME error\n", dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + frame->bFrameIndex = buffer[3]; + frame->bmCapabilities = buffer[4]; + frame->wWidth = le16_to_cpup((__le16 *)&buffer[5]); + frame->wHeight = le16_to_cpup((__le16 *)&buffer[7]); + frame->dwMinBitRate = le32_to_cpup((__le32 *)&buffer[9]); + frame->dwMaxBitRate = le32_to_cpup((__le32 *)&buffer[13]); + if (ftype != VS_FRAME_FRAME_BASED) { + frame->dwMaxVideoFrameBufferSize = + le32_to_cpup((__le32 *)&buffer[17]); + frame->dwDefaultFrameInterval = + le32_to_cpup((__le32 *)&buffer[21]); + frame->bFrameIntervalType = buffer[25]; + } else { + frame->dwMaxVideoFrameBufferSize = 0; + frame->dwDefaultFrameInterval = + le32_to_cpup((__le32 *)&buffer[17]); + frame->bFrameIntervalType = buffer[21]; + } + frame->dwFrameInterval = *intervals; + + /* Several UVC chipsets screw up dwMaxVideoFrameBufferSize + * completely. Observed behaviours range from setting the + * value to 1.1x the actual frame size of hardwiring the + * 16 low bits to 0. This results in a higher than necessary + * memory usage as well as a wrong image size information. For + * uncompressed formats this can be fixed by computing the + * value from the frame size. + */ + if (!(format->flags & UVC_FMT_FLAG_COMPRESSED)) + frame->dwMaxVideoFrameBufferSize = format->bpp + * frame->wWidth * frame->wHeight / 8; + + /* Some bogus devices report dwMinFrameInterval equal to + * dwMaxFrameInterval and have dwFrameIntervalStep set to + * zero. Setting all null intervals to 1 fixes the problem and + * some other divisions by zero which could happen. + */ + for (i = 0; i < n; ++i) { + interval = le32_to_cpup((__le32 *)&buffer[26+4*i]); + *(*intervals)++ = interval ? interval : 1; + } + + /* Make sure that the default frame interval stays between + * the boundaries. + */ + n -= frame->bFrameIntervalType ? 1 : 2; + frame->dwDefaultFrameInterval = + min(frame->dwFrameInterval[n], + max(frame->dwFrameInterval[0], + frame->dwDefaultFrameInterval)); + + uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n", + frame->wWidth, frame->wHeight, + 10000000/frame->dwDefaultFrameInterval, + (100000000/frame->dwDefaultFrameInterval)%10); + + format->nframes++; + buflen -= buffer[0]; + buffer += buffer[0]; + } + + if (buflen > 2 && buffer[2] == VS_STILL_IMAGE_FRAME) { + buflen -= buffer[0]; + buffer += buffer[0]; + } + + if (buflen > 2 && buffer[2] == VS_COLORFORMAT) { + if (buflen < 6) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d COLORFORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + format->colorspace = uvc_colorspace(buffer[3]); + + buflen -= buffer[0]; + buffer += buffer[0]; + } + + return buffer - start; +} + +static int uvc_parse_streaming(struct uvc_device *dev, + struct usb_interface *intf) +{ + struct uvc_streaming *streaming = NULL; + struct uvc_format *format; + struct uvc_frame *frame; + struct usb_host_interface *alts = &intf->altsetting[0]; + unsigned char *_buffer, *buffer = alts->extra; + int _buflen, buflen = alts->extralen; + unsigned int nformats = 0, nframes = 0, nintervals = 0; + unsigned int size, i, n, p; + __u32 *interval; + __u16 psize; + int ret = -EINVAL; + + if (intf->cur_altsetting->desc.bInterfaceSubClass + != SC_VIDEOSTREAMING) { + uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a " + "video streaming interface\n", dev->udev->devnum, + intf->altsetting[0].desc.bInterfaceNumber); + return -EINVAL; + } + + if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) { + uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already " + "claimed\n", dev->udev->devnum, + intf->altsetting[0].desc.bInterfaceNumber); + return -EINVAL; + } + + streaming = kzalloc(sizeof *streaming, GFP_KERNEL); + if (streaming == NULL) { + usb_driver_release_interface(&uvc_driver.driver, intf); + return -EINVAL; + } + + mutex_init(&streaming->mutex); + streaming->intf = usb_get_intf(intf); + streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; + + /* The Pico iMage webcam has its class-specific interface descriptors + * after the endpoint descriptors. + */ + if (buflen == 0) { + for (i = 0; i < alts->desc.bNumEndpoints; ++i) { + struct usb_host_endpoint *ep = &alts->endpoint[i]; + + if (ep->extralen == 0) + continue; + + if (ep->extralen > 2 && + ep->extra[1] == USB_DT_CS_INTERFACE) { + uvc_trace(UVC_TRACE_DESCR, "trying extra data " + "from endpoint %u.\n", i); + buffer = alts->endpoint[i].extra; + buflen = alts->endpoint[i].extralen; + break; + } + } + } + + /* Skip the standard interface descriptors. */ + while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) { + buflen -= buffer[0]; + buffer += buffer[0]; + } + + if (buflen <= 2) { + uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming " + "interface descriptors found.\n"); + goto error; + } + + /* Parse the header descriptor. */ + if (buffer[2] == VS_OUTPUT_HEADER) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " + "%d OUTPUT HEADER descriptor is not supported.\n", + dev->udev->devnum, alts->desc.bInterfaceNumber); + goto error; + } else if (buffer[2] == VS_INPUT_HEADER) { + p = buflen >= 5 ? buffer[3] : 0; + n = buflen >= 12 ? buffer[12] : 0; + + if (buflen < 13 + p*n || buffer[2] != VS_INPUT_HEADER) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " + "interface %d INPUT HEADER descriptor is " + "invalid.\n", dev->udev->devnum, + alts->desc.bInterfaceNumber); + goto error; + } + + streaming->header.bNumFormats = p; + streaming->header.bEndpointAddress = buffer[6]; + streaming->header.bmInfo = buffer[7]; + streaming->header.bTerminalLink = buffer[8]; + streaming->header.bStillCaptureMethod = buffer[9]; + streaming->header.bTriggerSupport = buffer[10]; + streaming->header.bTriggerUsage = buffer[11]; + streaming->header.bControlSize = n; + + streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL); + if (streaming->header.bmaControls == NULL) { + ret = -ENOMEM; + goto error; + } + + memcpy(streaming->header.bmaControls, &buffer[13], p*n); + } else { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " + "%d HEADER descriptor not found.\n", dev->udev->devnum, + alts->desc.bInterfaceNumber); + goto error; + } + + buflen -= buffer[0]; + buffer += buffer[0]; + + _buffer = buffer; + _buflen = buflen; + + /* Count the format and frame descriptors. */ + while (_buflen > 2) { + switch (_buffer[2]) { + case VS_FORMAT_UNCOMPRESSED: + case VS_FORMAT_MJPEG: + case VS_FORMAT_FRAME_BASED: + nformats++; + break; + + case VS_FORMAT_DV: + /* DV format has no frame descriptor. We will create a + * dummy frame descriptor with a dummy frame interval. + */ + nformats++; + nframes++; + nintervals++; + break; + + case VS_FORMAT_MPEG2TS: + case VS_FORMAT_STREAM_BASED: + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " + "interface %d FORMAT %u is not supported.\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber, _buffer[2]); + break; + + case VS_FRAME_UNCOMPRESSED: + case VS_FRAME_MJPEG: + nframes++; + if (_buflen > 25) + nintervals += _buffer[25] ? _buffer[25] : 3; + break; + + case VS_FRAME_FRAME_BASED: + nframes++; + if (_buflen > 21) + nintervals += _buffer[21] ? _buffer[21] : 3; + break; + } + + _buflen -= _buffer[0]; + _buffer += _buffer[0]; + } + + if (nformats == 0) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " + "%d has no supported formats defined.\n", + dev->udev->devnum, alts->desc.bInterfaceNumber); + goto error; + } + + size = nformats * sizeof *format + nframes * sizeof *frame + + nintervals * sizeof *interval; + format = kzalloc(size, GFP_KERNEL); + if (format == NULL) { + ret = -ENOMEM; + goto error; + } + + frame = (struct uvc_frame *)&format[nformats]; + interval = (__u32 *)&frame[nframes]; + + streaming->format = format; + streaming->nformats = nformats; + + /* Parse the format descriptors. */ + while (buflen > 2) { + switch (buffer[2]) { + case VS_FORMAT_UNCOMPRESSED: + case VS_FORMAT_MJPEG: + case VS_FORMAT_DV: + case VS_FORMAT_FRAME_BASED: + format->frame = frame; + ret = uvc_parse_format(dev, streaming, format, + &interval, buffer, buflen); + if (ret < 0) + goto error; + + frame += format->nframes; + format++; + + buflen -= ret; + buffer += ret; + continue; + + default: + break; + } + + buflen -= buffer[0]; + buffer += buffer[0]; + } + + /* Parse the alternate settings to find the maximum bandwidth. */ + for (i = 0; i < intf->num_altsetting; ++i) { + struct usb_host_endpoint *ep; + alts = &intf->altsetting[i]; + ep = uvc_find_endpoint(alts, + streaming->header.bEndpointAddress); + if (ep == NULL) + continue; + + psize = le16_to_cpu(ep->desc.wMaxPacketSize); + psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + if (psize > streaming->maxpsize) + streaming->maxpsize = psize; + } + + list_add_tail(&streaming->list, &dev->streaming); + return 0; + +error: + usb_driver_release_interface(&uvc_driver.driver, intf); + usb_put_intf(intf); + kfree(streaming->format); + kfree(streaming->header.bmaControls); + kfree(streaming); + return ret; +} + +/* Parse vendor-specific extensions. */ +static int uvc_parse_vendor_control(struct uvc_device *dev, + const unsigned char *buffer, int buflen) +{ + struct usb_device *udev = dev->udev; + struct usb_host_interface *alts = dev->intf->cur_altsetting; + struct uvc_entity *unit; + unsigned int n, p; + int handled = 0; + + switch (le16_to_cpu(dev->udev->descriptor.idVendor)) { + case 0x046d: /* Logitech */ + if (buffer[1] != 0x41 || buffer[2] != 0x01) + break; + + /* Logitech implements several vendor specific functions + * through vendor specific extension units (LXU). + * + * The LXU descriptors are similar to XU descriptors + * (see "USB Device Video Class for Video Devices", section + * 3.7.2.6 "Extension Unit Descriptor") with the following + * differences: + * + * ---------------------------------------------------------- + * 0 bLength 1 Number + * Size of this descriptor, in bytes: 24+p+n*2 + * ---------------------------------------------------------- + * 23+p+n bmControlsType N Bitmap + * Individual bits in the set are defined: + * 0: Absolute + * 1: Relative + * + * This bitset is mapped exactly the same as bmControls. + * ---------------------------------------------------------- + * 23+p+n*2 bReserved 1 Boolean + * ---------------------------------------------------------- + * 24+p+n*2 iExtension 1 Index + * Index of a string descriptor that describes this + * extension unit. + * ---------------------------------------------------------- + */ + p = buflen >= 22 ? buffer[21] : 0; + n = buflen >= 25 + p ? buffer[22+p] : 0; + + if (buflen < 25 + p + 2*n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d EXTENSION_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); + break; + } + + unit = kzalloc(sizeof *unit + p + 2*n, GFP_KERNEL); + if (unit == NULL) + return -ENOMEM; + + unit->id = buffer[3]; + unit->type = VC_EXTENSION_UNIT; + memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); + unit->extension.bNumControls = buffer[20]; + unit->extension.bNrInPins = + le16_to_cpup((__le16 *)&buffer[21]); + unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; + memcpy(unit->extension.baSourceID, &buffer[22], p); + unit->extension.bControlSize = buffer[22+p]; + unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p; + unit->extension.bmControlsType = (__u8 *)unit + sizeof *unit + + p + n; + memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); + + if (buffer[24+p+2*n] != 0) + usb_string(udev, buffer[24+p+2*n], unit->name, + sizeof unit->name); + else + sprintf(unit->name, "Extension %u", buffer[3]); + + list_add_tail(&unit->list, &dev->entities); + handled = 1; + break; + } + + return handled; +} + +static int uvc_parse_standard_control(struct uvc_device *dev, + const unsigned char *buffer, int buflen) +{ + struct usb_device *udev = dev->udev; + struct uvc_entity *unit, *term; + struct usb_interface *intf; + struct usb_host_interface *alts = dev->intf->cur_altsetting; + unsigned int i, n, p, len; + __u16 type; + + switch (buffer[2]) { + case VC_HEADER: + n = buflen >= 12 ? buffer[11] : 0; + + if (buflen < 12 || buflen < 12 + n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d HEADER error\n", udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + dev->uvc_version = le16_to_cpup((__le16 *)&buffer[3]); + dev->clock_frequency = le32_to_cpup((__le32 *)&buffer[7]); + + /* Parse all USB Video Streaming interfaces. */ + for (i = 0; i < n; ++i) { + intf = usb_ifnum_to_if(udev, buffer[12+i]); + if (intf == NULL) { + uvc_trace(UVC_TRACE_DESCR, "device %d " + "interface %d doesn't exists\n", + udev->devnum, i); + continue; + } + + uvc_parse_streaming(dev, intf); + } + break; + + case VC_INPUT_TERMINAL: + if (buflen < 8) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d INPUT_TERMINAL error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + /* Make sure the terminal type MSB is not null, otherwise it + * could be confused with a unit. + */ + type = le16_to_cpup((__le16 *)&buffer[4]); + if ((type & 0xff00) == 0) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d INPUT_TERMINAL %d has invalid " + "type 0x%04x, skipping\n", udev->devnum, + alts->desc.bInterfaceNumber, + buffer[3], type); + return 0; + } + + n = 0; + p = 0; + len = 8; + + if (type == ITT_CAMERA) { + n = buflen >= 15 ? buffer[14] : 0; + len = 15; + + } else if (type == ITT_MEDIA_TRANSPORT_INPUT) { + n = buflen >= 9 ? buffer[8] : 0; + p = buflen >= 10 + n ? buffer[9+n] : 0; + len = 10; + } + + if (buflen < len + n + p) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d INPUT_TERMINAL error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + term = kzalloc(sizeof *term + n + p, GFP_KERNEL); + if (term == NULL) + return -ENOMEM; + + term->id = buffer[3]; + term->type = type | UVC_TERM_INPUT; + + if (UVC_ENTITY_TYPE(term) == ITT_CAMERA) { + term->camera.bControlSize = n; + term->camera.bmControls = (__u8 *)term + sizeof *term; + term->camera.wObjectiveFocalLengthMin = + le16_to_cpup((__le16 *)&buffer[8]); + term->camera.wObjectiveFocalLengthMax = + le16_to_cpup((__le16 *)&buffer[10]); + term->camera.wOcularFocalLength = + le16_to_cpup((__le16 *)&buffer[12]); + memcpy(term->camera.bmControls, &buffer[15], n); + } else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT) { + term->media.bControlSize = n; + term->media.bmControls = (__u8 *)term + sizeof *term; + term->media.bTransportModeSize = p; + term->media.bmTransportModes = (__u8 *)term + + sizeof *term + n; + memcpy(term->media.bmControls, &buffer[9], n); + memcpy(term->media.bmTransportModes, &buffer[10+n], p); + } + + if (buffer[7] != 0) + usb_string(udev, buffer[7], term->name, + sizeof term->name); + else if (UVC_ENTITY_TYPE(term) == ITT_CAMERA) + sprintf(term->name, "Camera %u", buffer[3]); + else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT) + sprintf(term->name, "Media %u", buffer[3]); + else + sprintf(term->name, "Input %u", buffer[3]); + + list_add_tail(&term->list, &dev->entities); + break; + + case VC_OUTPUT_TERMINAL: + if (buflen < 9) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d OUTPUT_TERMINAL error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + /* Make sure the terminal type MSB is not null, otherwise it + * could be confused with a unit. + */ + type = le16_to_cpup((__le16 *)&buffer[4]); + if ((type & 0xff00) == 0) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d OUTPUT_TERMINAL %d has invalid " + "type 0x%04x, skipping\n", udev->devnum, + alts->desc.bInterfaceNumber, buffer[3], type); + return 0; + } + + term = kzalloc(sizeof *term, GFP_KERNEL); + if (term == NULL) + return -ENOMEM; + + term->id = buffer[3]; + term->type = type | UVC_TERM_OUTPUT; + term->output.bSourceID = buffer[7]; + + if (buffer[8] != 0) + usb_string(udev, buffer[8], term->name, + sizeof term->name); + else + sprintf(term->name, "Output %u", buffer[3]); + + list_add_tail(&term->list, &dev->entities); + break; + + case VC_SELECTOR_UNIT: + p = buflen >= 5 ? buffer[4] : 0; + + if (buflen < 5 || buflen < 6 + p) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d SELECTOR_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + unit = kzalloc(sizeof *unit + p, GFP_KERNEL); + if (unit == NULL) + return -ENOMEM; + + unit->id = buffer[3]; + unit->type = buffer[2]; + unit->selector.bNrInPins = buffer[4]; + unit->selector.baSourceID = (__u8 *)unit + sizeof *unit; + memcpy(unit->selector.baSourceID, &buffer[5], p); + + if (buffer[5+p] != 0) + usb_string(udev, buffer[5+p], unit->name, + sizeof unit->name); + else + sprintf(unit->name, "Selector %u", buffer[3]); + + list_add_tail(&unit->list, &dev->entities); + break; + + case VC_PROCESSING_UNIT: + n = buflen >= 8 ? buffer[7] : 0; + p = dev->uvc_version >= 0x0110 ? 10 : 9; + + if (buflen < p + n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d PROCESSING_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + unit = kzalloc(sizeof *unit + n, GFP_KERNEL); + if (unit == NULL) + return -ENOMEM; + + unit->id = buffer[3]; + unit->type = buffer[2]; + unit->processing.bSourceID = buffer[4]; + unit->processing.wMaxMultiplier = + le16_to_cpup((__le16 *)&buffer[5]); + unit->processing.bControlSize = buffer[7]; + unit->processing.bmControls = (__u8 *)unit + sizeof *unit; + memcpy(unit->processing.bmControls, &buffer[8], n); + if (dev->uvc_version >= 0x0110) + unit->processing.bmVideoStandards = buffer[9+n]; + + if (buffer[8+n] != 0) + usb_string(udev, buffer[8+n], unit->name, + sizeof unit->name); + else + sprintf(unit->name, "Processing %u", buffer[3]); + + list_add_tail(&unit->list, &dev->entities); + break; + + case VC_EXTENSION_UNIT: + p = buflen >= 22 ? buffer[21] : 0; + n = buflen >= 24 + p ? buffer[22+p] : 0; + + if (buflen < 24 + p + n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d EXTENSION_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + unit = kzalloc(sizeof *unit + p + n, GFP_KERNEL); + if (unit == NULL) + return -ENOMEM; + + unit->id = buffer[3]; + unit->type = buffer[2]; + memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); + unit->extension.bNumControls = buffer[20]; + unit->extension.bNrInPins = + le16_to_cpup((__le16 *)&buffer[21]); + unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; + memcpy(unit->extension.baSourceID, &buffer[22], p); + unit->extension.bControlSize = buffer[22+p]; + unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p; + memcpy(unit->extension.bmControls, &buffer[23+p], n); + + if (buffer[23+p+n] != 0) + usb_string(udev, buffer[23+p+n], unit->name, + sizeof unit->name); + else + sprintf(unit->name, "Extension %u", buffer[3]); + + list_add_tail(&unit->list, &dev->entities); + break; + + default: + uvc_trace(UVC_TRACE_DESCR, "Found an unknown CS_INTERFACE " + "descriptor (%u)\n", buffer[2]); + break; + } + + return 0; +} + +static int uvc_parse_control(struct uvc_device *dev) +{ + struct usb_host_interface *alts = dev->intf->cur_altsetting; + unsigned char *buffer = alts->extra; + int buflen = alts->extralen; + int ret; + + /* Parse the default alternate setting only, as the UVC specification + * defines a single alternate setting, the default alternate setting + * zero. + */ + + while (buflen > 2) { + if (uvc_parse_vendor_control(dev, buffer, buflen) || + buffer[1] != USB_DT_CS_INTERFACE) + goto next_descriptor; + + if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0) + return ret; + +next_descriptor: + buflen -= buffer[0]; + buffer += buffer[0]; + } + + /* Check if the optional status endpoint is present. */ + if (alts->desc.bNumEndpoints == 1) { + struct usb_host_endpoint *ep = &alts->endpoint[0]; + struct usb_endpoint_descriptor *desc = &ep->desc; + + if (usb_endpoint_is_int_in(desc) && + le16_to_cpu(desc->wMaxPacketSize) >= 8 && + desc->bInterval != 0) { + uvc_trace(UVC_TRACE_DESCR, "Found a Status endpoint " + "(addr %02x).\n", desc->bEndpointAddress); + dev->int_ep = ep; + } + } + + return 0; +} + +/* ------------------------------------------------------------------------ + * USB probe and disconnect + */ + +/* + * Unregister the video devices. + */ +static void uvc_unregister_video(struct uvc_device *dev) +{ + if (dev->video.vdev) { + if (dev->video.vdev->minor == -1) + video_device_release(dev->video.vdev); + else + video_unregister_device(dev->video.vdev); + dev->video.vdev = NULL; + } +} + +/* + * Scan the UVC descriptors to locate a chain starting at an Output Terminal + * and containing the following units: + * + * - a USB Streaming Output Terminal + * - zero or one Processing Unit + * - zero, one or mode single-input Selector Units + * - zero or one multiple-input Selector Units, provided all inputs are + * connected to input terminals + * - zero, one or mode single-input Extension Units + * - one Camera Input Terminal, or one or more External terminals. + * + * A side forward scan is made on each detected entity to check for additional + * extension units. + */ +static int uvc_scan_chain_entity(struct uvc_video_device *video, + struct uvc_entity *entity) +{ + switch (UVC_ENTITY_TYPE(entity)) { + case VC_EXTENSION_UNIT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- XU %d", entity->id); + + if (entity->extension.bNrInPins != 1) { + uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more " + "than 1 input pin.\n", entity->id); + return -1; + } + + list_add_tail(&entity->chain, &video->extensions); + break; + + case VC_PROCESSING_UNIT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- PU %d", entity->id); + + if (video->processing != NULL) { + uvc_trace(UVC_TRACE_DESCR, "Found multiple " + "Processing Units in chain.\n"); + return -1; + } + + video->processing = entity; + break; + + case VC_SELECTOR_UNIT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- SU %d", entity->id); + + /* Single-input selector units are ignored. */ + if (entity->selector.bNrInPins == 1) + break; + + if (video->selector != NULL) { + uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector " + "Units in chain.\n"); + return -1; + } + + video->selector = entity; + break; + + case ITT_VENDOR_SPECIFIC: + case ITT_CAMERA: + case ITT_MEDIA_TRANSPORT_INPUT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- IT %d\n", entity->id); + + list_add_tail(&entity->chain, &video->iterms); + break; + + default: + uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type " + "0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity)); + return -1; + } + + return 0; +} + +static int uvc_scan_chain_forward(struct uvc_video_device *video, + struct uvc_entity *entity, struct uvc_entity *prev) +{ + struct uvc_entity *forward; + int found; + + /* Forward scan */ + forward = NULL; + found = 0; + + while (1) { + forward = uvc_entity_by_reference(video->dev, entity->id, + forward); + if (forward == NULL) + break; + + if (UVC_ENTITY_TYPE(forward) != VC_EXTENSION_UNIT || + forward == prev) + continue; + + if (forward->extension.bNrInPins != 1) { + uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has" + "more than 1 input pin.\n", entity->id); + return -1; + } + + list_add_tail(&forward->chain, &video->extensions); + if (uvc_trace_param & UVC_TRACE_PROBE) { + if (!found) + printk(" (-> XU"); + + printk(" %d", forward->id); + found = 1; + } + } + if (found) + printk(")"); + + return 0; +} + +static int uvc_scan_chain_backward(struct uvc_video_device *video, + struct uvc_entity *entity) +{ + struct uvc_entity *term; + int id = -1, i; + + switch (UVC_ENTITY_TYPE(entity)) { + case VC_EXTENSION_UNIT: + id = entity->extension.baSourceID[0]; + break; + + case VC_PROCESSING_UNIT: + id = entity->processing.bSourceID; + break; + + case VC_SELECTOR_UNIT: + /* Single-input selector units are ignored. */ + if (entity->selector.bNrInPins == 1) { + id = entity->selector.baSourceID[0]; + break; + } + + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- IT"); + + video->selector = entity; + for (i = 0; i < entity->selector.bNrInPins; ++i) { + id = entity->selector.baSourceID[i]; + term = uvc_entity_by_id(video->dev, id); + if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { + uvc_trace(UVC_TRACE_DESCR, "Selector unit %d " + "input %d isn't connected to an " + "input terminal\n", entity->id, i); + return -1; + } + + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" %d", term->id); + + list_add_tail(&term->chain, &video->iterms); + uvc_scan_chain_forward(video, term, entity); + } + + if (uvc_trace_param & UVC_TRACE_PROBE) + printk("\n"); + + id = 0; + break; + } + + return id; +} + +static int uvc_scan_chain(struct uvc_video_device *video) +{ + struct uvc_entity *entity, *prev; + int id; + + entity = video->oterm; + uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id); + id = entity->output.bSourceID; + while (id != 0) { + prev = entity; + entity = uvc_entity_by_id(video->dev, id); + if (entity == NULL) { + uvc_trace(UVC_TRACE_DESCR, "Found reference to " + "unknown entity %d.\n", id); + return -1; + } + + /* Process entity */ + if (uvc_scan_chain_entity(video, entity) < 0) + return -1; + + /* Forward scan */ + if (uvc_scan_chain_forward(video, entity, prev) < 0) + return -1; + + /* Stop when a terminal is found. */ + if (!UVC_ENTITY_IS_UNIT(entity)) + break; + + /* Backward scan */ + id = uvc_scan_chain_backward(video, entity); + if (id < 0) + return id; + } + + /* Initialize the video buffers queue. */ + uvc_queue_init(&video->queue); + + return 0; +} + +/* + * Register the video devices. + * + * The driver currently supports a single video device per control interface + * only. The terminal and units must match the following structure: + * + * ITT_CAMERA -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING + * + * The Extension Units, if present, must have a single input pin. The + * Processing Unit and Extension Units can be in any order. Additional + * Extension Units connected to the main chain as single-unit branches are + * also supported. + */ +static int uvc_register_video(struct uvc_device *dev) +{ + struct video_device *vdev; + struct uvc_entity *term; + int found = 0, ret; + + /* Check if the control interface matches the structure we expect. */ + list_for_each_entry(term, &dev->entities, list) { + struct uvc_streaming *streaming; + + if (UVC_ENTITY_TYPE(term) != TT_STREAMING) + continue; + + memset(&dev->video, 0, sizeof dev->video); + mutex_init(&dev->video.ctrl_mutex); + INIT_LIST_HEAD(&dev->video.iterms); + INIT_LIST_HEAD(&dev->video.extensions); + dev->video.oterm = term; + dev->video.dev = dev; + if (uvc_scan_chain(&dev->video) < 0) + continue; + + list_for_each_entry(streaming, &dev->streaming, list) { + if (streaming->header.bTerminalLink == term->id) { + dev->video.streaming = streaming; + found = 1; + break; + } + } + + if (found) + break; + } + + if (!found) { + uvc_printk(KERN_INFO, "No valid video chain found.\n"); + return -1; + } + + if (uvc_trace_param & UVC_TRACE_PROBE) { + uvc_printk(KERN_INFO, "Found a valid video chain ("); + list_for_each_entry(term, &dev->video.iterms, chain) { + printk("%d", term->id); + if (term->chain.next != &dev->video.iterms) + printk(","); + } + printk(" -> %d).\n", dev->video.oterm->id); + } + + /* Initialize the streaming interface with default streaming + * parameters. + */ + if ((ret = uvc_video_init(&dev->video)) < 0) { + uvc_printk(KERN_ERR, "Failed to initialize the device " + "(%d).\n", ret); + return ret; + } + + /* Register the device with V4L. */ + vdev = video_device_alloc(); + if (vdev == NULL) + return -1; + + /* We already hold a reference to dev->udev. The video device will be + * unregistered before the reference is released, so we don't need to + * get another one. + */ + vdev->dev = &dev->intf->dev; + vdev->type = 0; + vdev->type2 = 0; + vdev->minor = -1; + vdev->fops = &uvc_fops; + vdev->release = video_device_release; + strncpy(vdev->name, dev->name, sizeof vdev->name); + + /* Set the driver data before calling video_register_device, otherwise + * uvc_v4l2_open might race us. + * + * FIXME: usb_set_intfdata hasn't been called so far. Is that a + * problem ? Does any function which could be called here get + * a pointer to the usb_interface ? + */ + dev->video.vdev = vdev; + video_set_drvdata(vdev, &dev->video); + + if (video_register_device(vdev, VFL_TYPE_GRABBER, -1) < 0) { + dev->video.vdev = NULL; + video_device_release(vdev); + return -1; + } + + return 0; +} + +/* + * Delete the UVC device. + * + * Called by the kernel when the last reference to the uvc_device structure + * is released. + * + * Unregistering the video devices is done here because every opened instance + * must be closed before the device can be unregistered. An alternative would + * have been to use another reference count for uvc_v4l2_open/uvc_release, and + * unregister the video devices on disconnect when that reference count drops + * to zero. + * + * As this function is called after or during disconnect(), all URBs have + * already been canceled by the USB core. There is no need to kill the + * interrupt URB manually. + */ +void uvc_delete(struct kref *kref) +{ + struct uvc_device *dev = container_of(kref, struct uvc_device, kref); + struct list_head *p, *n; + + /* Unregister the video device */ + uvc_unregister_video(dev); + usb_put_intf(dev->intf); + usb_put_dev(dev->udev); + + uvc_status_cleanup(dev); + uvc_ctrl_cleanup_device(dev); + + list_for_each_safe(p, n, &dev->entities) { + struct uvc_entity *entity; + entity = list_entry(p, struct uvc_entity, list); + kfree(entity); + } + + list_for_each_safe(p, n, &dev->streaming) { + struct uvc_streaming *streaming; + streaming = list_entry(p, struct uvc_streaming, list); + usb_driver_release_interface(&uvc_driver.driver, + streaming->intf); + usb_put_intf(streaming->intf); + kfree(streaming->format); + kfree(streaming->header.bmaControls); + kfree(streaming); + } + + kfree(dev); +} + +static int uvc_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct uvc_device *dev; + int ret; + + if (id->idVendor && id->idProduct) + uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s " + "(%04x:%04x)\n", udev->devpath, id->idVendor, + id->idProduct); + else + uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n", + udev->devpath); + + /* Allocate memory for the device and initialize it */ + if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL) + return -ENOMEM; + + INIT_LIST_HEAD(&dev->entities); + INIT_LIST_HEAD(&dev->streaming); + kref_init(&dev->kref); + + dev->udev = usb_get_dev(udev); + dev->intf = usb_get_intf(intf); + dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; + dev->quirks = id->driver_info | uvc_quirks_param; + + if (udev->product != NULL) + strncpy(dev->name, udev->product, sizeof dev->name); + else + snprintf(dev->name, sizeof dev->name, + "UVC Camera (%04x:%04x)", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + /* Parse the Video Class control descriptor */ + if (uvc_parse_control(dev) < 0) { + uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC " + "descriptors.\n"); + goto error; + } + + uvc_printk(KERN_INFO, "Found UVC %u.%02u device %s (%04x:%04x)\n", + dev->uvc_version >> 8, dev->uvc_version & 0xff, + udev->product ? udev->product : "<unnamed>", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + if (uvc_quirks_param != 0) { + uvc_printk(KERN_INFO, "Forcing device quirks 0x%x by module " + "parameter for testing purpose.\n", uvc_quirks_param); + uvc_printk(KERN_INFO, "Please report required quirks to the " + "linux-uvc-devel mailing list.\n"); + } + + /* Initialize controls */ + if (uvc_ctrl_init_device(dev) < 0) + goto error; + + /* Register the video devices */ + if (uvc_register_video(dev) < 0) + goto error; + + /* Save our data pointer in the interface data */ + usb_set_intfdata(intf, dev); + + /* Initialize the interrupt URB */ + if ((ret = uvc_status_init(dev)) < 0) { + uvc_printk(KERN_INFO, "Unable to initialize the status " + "endpoint (%d), status interrupt will not be " + "supported.\n", ret); + } + + uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n"); + return 0; + +error: + kref_put(&dev->kref, uvc_delete); + return -ENODEV; +} + +static void uvc_disconnect(struct usb_interface *intf) +{ + struct uvc_device *dev = usb_get_intfdata(intf); + + /* Set the USB interface data to NULL. This can be done outside the + * lock, as there's no other reader. + */ + usb_set_intfdata(intf, NULL); + + if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOSTREAMING) + return; + + /* uvc_v4l2_open() might race uvc_disconnect(). A static driver-wide + * lock is needed to prevent uvc_disconnect from releasing its + * reference to the uvc_device instance after uvc_v4l2_open() received + * the pointer to the device (video_devdata) but before it got the + * chance to increase the reference count (kref_get). + */ + mutex_lock(&uvc_driver.open_mutex); + + dev->state |= UVC_DEV_DISCONNECTED; + kref_put(&dev->kref, uvc_delete); + + mutex_unlock(&uvc_driver.open_mutex); +} + +static int uvc_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct uvc_device *dev = usb_get_intfdata(intf); + + uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n", + intf->cur_altsetting->desc.bInterfaceNumber); + + /* Controls are cached on the fly so they don't need to be saved. */ + if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL) + return uvc_status_suspend(dev); + + if (dev->video.streaming->intf != intf) { + uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB " + "interface mismatch.\n"); + return -EINVAL; + } + + return uvc_video_suspend(&dev->video); +} + +static int uvc_resume(struct usb_interface *intf) +{ + struct uvc_device *dev = usb_get_intfdata(intf); + int ret; + + uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n", + intf->cur_altsetting->desc.bInterfaceNumber); + + if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL) { + if ((ret = uvc_ctrl_resume_device(dev)) < 0) + return ret; + + return uvc_status_resume(dev); + } + + if (dev->video.streaming->intf != intf) { + uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB " + "interface mismatch.\n"); + return -EINVAL; + } + + return uvc_video_resume(&dev->video); +} + +/* ------------------------------------------------------------------------ + * Driver initialization and cleanup + */ + +/* + * The Logitech cameras listed below have their interface class set to + * VENDOR_SPEC because they don't announce themselves as UVC devices, even + * though they are compliant. + */ +static struct usb_device_id uvc_ids[] = { + /* ALi M5606 (Clevo M540SR) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0402, + .idProduct = 0x5606, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Creative Live! Optia */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x041e, + .idProduct = 0x4057, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Microsoft Lifecam NX-6000 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x045e, + .idProduct = 0x00f8, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Microsoft Lifecam VX-7000 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x045e, + .idProduct = 0x0723, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Logitech Quickcam Fusion */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c1, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam Orbit MP */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam Pro for Notebook */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c3, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam Pro 5000 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c5, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam OEM Dell Notebook */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c6, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam OEM Cisco VT Camera II */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c7, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Apple Built-In iSight */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x05ac, + .idProduct = 0x8501, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX + | UVC_QUIRK_BUILTIN_ISIGHT }, + /* Genesys Logic USB 2.0 PC Camera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x05e3, + .idProduct = 0x0505, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Silicon Motion SM371 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x090c, + .idProduct = 0xb371, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* MT6227 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0e8d, + .idProduct = 0x0004, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Syntek (HP Spartan) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x5212, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Syntek (Asus U3S) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x8a33, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Ecamm Pico iMage */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x18cd, + .idProduct = 0xcafe, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS }, + /* Bodelin ProScopeHR */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_DEV_HI + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x19ab, + .idProduct = 0x1000, + .bcdDevice_hi = 0x0126, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STATUS_INTERVAL }, + /* SiGma Micro USB Web Camera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x1c4f, + .idProduct = 0x3000, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX + | UVC_QUIRK_IGNORE_SELECTOR_UNIT}, + /* Acer OEM Webcam - Unknown vendor */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x5986, + .idProduct = 0x0100, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Packard Bell OEM Webcam */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x5986, + .idProduct = 0x0101, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Acer Crystal Eye webcam */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x5986, + .idProduct = 0x0102, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Acer OrbiCam - Unknown vendor */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x5986, + .idProduct = 0x0200, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Generic USB Video Class */ + { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, + {} +}; + +MODULE_DEVICE_TABLE(usb, uvc_ids); + +struct uvc_driver uvc_driver = { + .driver = { + .name = "uvcvideo", + .probe = uvc_probe, + .disconnect = uvc_disconnect, + .suspend = uvc_suspend, + .resume = uvc_resume, + .id_table = uvc_ids, + .supports_autosuspend = 1, + }, +}; + +static int __init uvc_init(void) +{ + int result; + + INIT_LIST_HEAD(&uvc_driver.devices); + INIT_LIST_HEAD(&uvc_driver.controls); + mutex_init(&uvc_driver.open_mutex); + mutex_init(&uvc_driver.ctrl_mutex); + + uvc_ctrl_init(); + + result = usb_register(&uvc_driver.driver); + if (result == 0) + printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n"); + return result; +} + +static void __exit uvc_cleanup(void) +{ + usb_deregister(&uvc_driver.driver); +} + +module_init(uvc_init); +module_exit(uvc_cleanup); + +module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(quirks, "Forced device quirks"); +module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(trace, "Trace level bitmask"); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/media/video/uvc/uvc_isight.c b/drivers/media/video/uvc/uvc_isight.c new file mode 100644 index 00000000000..37bdefdbead --- /dev/null +++ b/drivers/media/video/uvc/uvc_isight.c @@ -0,0 +1,134 @@ +/* + * uvc_isight.c -- USB Video Class driver - iSight support + * + * Copyright (C) 2006-2007 + * Ivan N. Zlatev <contact@i-nz.net> + * + * 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/usb.h> +#include <linux/kernel.h> +#include <linux/mm.h> + +#include "uvcvideo.h" + +/* Built-in iSight webcams implements most of UVC 1.0 except a + * different packet format. Instead of sending a header at the + * beginning of each isochronous transfer payload, the webcam sends a + * single header per image (on its own in a packet), followed by + * packets containing data only. + * + * Offset Size (bytes) Description + * ------------------------------------------------------------------ + * 0x00 1 Header length + * 0x01 1 Flags (UVC-compliant) + * 0x02 4 Always equal to '11223344' + * 0x06 8 Always equal to 'deadbeefdeadface' + * 0x0e 16 Unknown + * + * The header can be prefixed by an optional, unknown-purpose byte. + */ + +static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf, + const __u8 *data, unsigned int len) +{ + static const __u8 hdr[] = { + 0x11, 0x22, 0x33, 0x44, + 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xfa, 0xce + }; + + unsigned int maxlen, nbytes; + __u8 *mem; + int is_header = 0; + + if (buf == NULL) + return 0; + + if ((len >= 14 && memcmp(&data[2], hdr, 12) == 0) || + (len >= 15 && memcmp(&data[3], hdr, 12) == 0)) { + uvc_trace(UVC_TRACE_FRAME, "iSight header found\n"); + is_header = 1; + } + + /* Synchronize to the input stream by waiting for a header packet. */ + if (buf->state != UVC_BUF_STATE_ACTIVE) { + if (!is_header) { + uvc_trace(UVC_TRACE_FRAME, "Dropping packet (out of " + "sync).\n"); + return 0; + } + + buf->state = UVC_BUF_STATE_ACTIVE; + } + + /* Mark the buffer as done if we're at the beginning of a new frame. + * + * Empty buffers (bytesused == 0) don't trigger end of frame detection + * as it doesn't make sense to return an empty buffer. + */ + if (is_header && buf->buf.bytesused != 0) { + buf->state = UVC_BUF_STATE_DONE; + return -EAGAIN; + } + + /* Copy the video data to the buffer. Skip header packets, as they + * contain no data. + */ + if (!is_header) { + maxlen = buf->buf.length - buf->buf.bytesused; + mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused; + nbytes = min(len, maxlen); + memcpy(mem, data, nbytes); + buf->buf.bytesused += nbytes; + + if (len > maxlen || buf->buf.bytesused == buf->buf.length) { + uvc_trace(UVC_TRACE_FRAME, "Frame complete " + "(overflow).\n"); + buf->state = UVC_BUF_STATE_DONE; + } + } + + return 0; +} + +void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, + struct uvc_buffer *buf) +{ + int ret, i; + + for (i = 0; i < urb->number_of_packets; ++i) { + if (urb->iso_frame_desc[i].status < 0) { + uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame " + "lost (%d).\n", + urb->iso_frame_desc[i].status); + } + + /* Decode the payload packet. + * uvc_video_decode is entered twice when a frame transition + * has been detected because the end of frame can only be + * reliably detected when the first packet of the new frame + * is processed. The first pass detects the transition and + * closes the previous frame's buffer, the second pass + * processes the data of the first payload of the new frame. + */ + do { + ret = isight_decode(&video->queue, buf, + urb->transfer_buffer + + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + + if (buf == NULL) + break; + + if (buf->state == UVC_BUF_STATE_DONE || + buf->state == UVC_BUF_STATE_ERROR) + buf = uvc_queue_next_buffer(&video->queue, buf); + } while (ret == -EAGAIN); + } +} diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c new file mode 100644 index 00000000000..0923f0e3b3d --- /dev/null +++ b/drivers/media/video/uvc/uvc_queue.c @@ -0,0 +1,477 @@ +/* + * uvc_queue.c -- USB Video Class driver - Buffers management + * + * Copyright (C) 2005-2008 + * Laurent Pinchart (laurent.pinchart@skynet.be) + * + * 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/version.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/videodev2.h> +#include <linux/vmalloc.h> +#include <linux/wait.h> +#include <asm/atomic.h> + +#include "uvcvideo.h" + +/* ------------------------------------------------------------------------ + * Video buffers queue management. + * + * Video queues is initialized by uvc_queue_init(). The function performs + * basic initialization of the uvc_video_queue struct and never fails. + * + * Video buffer allocation and freeing are performed by uvc_alloc_buffers and + * uvc_free_buffers respectively. The former acquires the video queue lock, + * while the later must be called with the lock held (so that allocation can + * free previously allocated buffers). Trying to free buffers that are mapped + * to user space will return -EBUSY. + * + * Video buffers are managed using two queues. However, unlike most USB video + * drivers which use an in queue and an out queue, we use a main queue which + * holds all queued buffers (both 'empty' and 'done' buffers), and an irq + * queue which holds empty buffers. This design (copied from video-buf) + * minimizes locking in interrupt, as only one queue is shared between + * interrupt and user contexts. + * + * Use cases + * --------- + * + * Unless stated otherwise, all operations which modify the irq buffers queue + * are protected by the irq spinlock. + * + * 1. The user queues the buffers, starts streaming and dequeues a buffer. + * + * The buffers are added to the main and irq queues. Both operations are + * protected by the queue lock, and the latert is protected by the irq + * spinlock as well. + * + * The completion handler fetches a buffer from the irq queue and fills it + * with video data. If no buffer is available (irq queue empty), the handler + * returns immediately. + * + * When the buffer is full, the completion handler removes it from the irq + * queue, marks it as ready (UVC_BUF_STATE_DONE) and wake its wait queue. + * At that point, any process waiting on the buffer will be woken up. If a + * process tries to dequeue a buffer after it has been marked ready, the + * dequeing will succeed immediately. + * + * 2. Buffers are queued, user is waiting on a buffer and the device gets + * disconnected. + * + * When the device is disconnected, the kernel calls the completion handler + * with an appropriate status code. The handler marks all buffers in the + * irq queue as being erroneous (UVC_BUF_STATE_ERROR) and wakes them up so + * that any process waiting on a buffer gets woken up. + * + * Waking up up the first buffer on the irq list is not enough, as the + * process waiting on the buffer might restart the dequeue operation + * immediately. + * + */ + +void uvc_queue_init(struct uvc_video_queue *queue) +{ + mutex_init(&queue->mutex); + spin_lock_init(&queue->irqlock); + INIT_LIST_HEAD(&queue->mainqueue); + INIT_LIST_HEAD(&queue->irqqueue); +} + +/* + * Allocate the video buffers. + * + * Pages are reserved to make sure they will not be swaped, as they will be + * filled in URB completion handler. + * + * Buffers will be individually mapped, so they must all be page aligned. + */ +int uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers, + unsigned int buflength) +{ + unsigned int bufsize = PAGE_ALIGN(buflength); + unsigned int i; + void *mem = NULL; + int ret; + + if (nbuffers > UVC_MAX_VIDEO_BUFFERS) + nbuffers = UVC_MAX_VIDEO_BUFFERS; + + mutex_lock(&queue->mutex); + + if ((ret = uvc_free_buffers(queue)) < 0) + goto done; + + /* Bail out if no buffers should be allocated. */ + if (nbuffers == 0) + goto done; + + /* Decrement the number of buffers until allocation succeeds. */ + for (; nbuffers > 0; --nbuffers) { + mem = vmalloc_32(nbuffers * bufsize); + if (mem != NULL) + break; + } + + if (mem == NULL) { + ret = -ENOMEM; + goto done; + } + + for (i = 0; i < nbuffers; ++i) { + memset(&queue->buffer[i], 0, sizeof queue->buffer[i]); + queue->buffer[i].buf.index = i; + queue->buffer[i].buf.m.offset = i * bufsize; + queue->buffer[i].buf.length = buflength; + queue->buffer[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + queue->buffer[i].buf.sequence = 0; + queue->buffer[i].buf.field = V4L2_FIELD_NONE; + queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP; + queue->buffer[i].buf.flags = 0; + init_waitqueue_head(&queue->buffer[i].wait); + } + + queue->mem = mem; + queue->count = nbuffers; + queue->buf_size = bufsize; + ret = nbuffers; + +done: + mutex_unlock(&queue->mutex); + return ret; +} + +/* + * Free the video buffers. + * + * This function must be called with the queue lock held. + */ +int uvc_free_buffers(struct uvc_video_queue *queue) +{ + unsigned int i; + + for (i = 0; i < queue->count; ++i) { + if (queue->buffer[i].vma_use_count != 0) + return -EBUSY; + } + + if (queue->count) { + vfree(queue->mem); + queue->count = 0; + } + + return 0; +} + +static void __uvc_query_buffer(struct uvc_buffer *buf, + struct v4l2_buffer *v4l2_buf) +{ + memcpy(v4l2_buf, &buf->buf, sizeof *v4l2_buf); + + if (buf->vma_use_count) + v4l2_buf->flags |= V4L2_BUF_FLAG_MAPPED; + + switch (buf->state) { + case UVC_BUF_STATE_ERROR: + case UVC_BUF_STATE_DONE: + v4l2_buf->flags |= V4L2_BUF_FLAG_DONE; + break; + case UVC_BUF_STATE_QUEUED: + case UVC_BUF_STATE_ACTIVE: + v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED; + break; + case UVC_BUF_STATE_IDLE: + default: + break; + } +} + +int uvc_query_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf) +{ + int ret = 0; + + mutex_lock(&queue->mutex); + if (v4l2_buf->index >= queue->count) { + ret = -EINVAL; + goto done; + } + + __uvc_query_buffer(&queue->buffer[v4l2_buf->index], v4l2_buf); + +done: + mutex_unlock(&queue->mutex); + return ret; +} + +/* + * Queue a video buffer. Attempting to queue a buffer that has already been + * queued will return -EINVAL. + */ +int uvc_queue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf) +{ + struct uvc_buffer *buf; + unsigned long flags; + int ret = 0; + + uvc_trace(UVC_TRACE_CAPTURE, "Queuing buffer %u.\n", v4l2_buf->index); + + if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + v4l2_buf->memory != V4L2_MEMORY_MMAP) { + uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) " + "and/or memory (%u).\n", v4l2_buf->type, + v4l2_buf->memory); + return -EINVAL; + } + + mutex_lock(&queue->mutex); + if (v4l2_buf->index >= queue->count) { + uvc_trace(UVC_TRACE_CAPTURE, "[E] Out of range index.\n"); + ret = -EINVAL; + goto done; + } + + buf = &queue->buffer[v4l2_buf->index]; + if (buf->state != UVC_BUF_STATE_IDLE) { + uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state " + "(%u).\n", buf->state); + ret = -EINVAL; + goto done; + } + + spin_lock_irqsave(&queue->irqlock, flags); + if (queue->flags & UVC_QUEUE_DISCONNECTED) { + spin_unlock_irqrestore(&queue->irqlock, flags); + ret = -ENODEV; + goto done; + } + buf->state = UVC_BUF_STATE_QUEUED; + buf->buf.bytesused = 0; + list_add_tail(&buf->stream, &queue->mainqueue); + list_add_tail(&buf->queue, &queue->irqqueue); + spin_unlock_irqrestore(&queue->irqlock, flags); + +done: + mutex_unlock(&queue->mutex); + return ret; +} + +static int uvc_queue_waiton(struct uvc_buffer *buf, int nonblocking) +{ + if (nonblocking) { + return (buf->state != UVC_BUF_STATE_QUEUED && + buf->state != UVC_BUF_STATE_ACTIVE) + ? 0 : -EAGAIN; + } + + return wait_event_interruptible(buf->wait, + buf->state != UVC_BUF_STATE_QUEUED && + buf->state != UVC_BUF_STATE_ACTIVE); +} + +/* + * Dequeue a video buffer. If nonblocking is false, block until a buffer is + * available. + */ +int uvc_dequeue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf, int nonblocking) +{ + struct uvc_buffer *buf; + int ret = 0; + + if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + v4l2_buf->memory != V4L2_MEMORY_MMAP) { + uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) " + "and/or memory (%u).\n", v4l2_buf->type, + v4l2_buf->memory); + return -EINVAL; + } + + mutex_lock(&queue->mutex); + if (list_empty(&queue->mainqueue)) { + uvc_trace(UVC_TRACE_CAPTURE, "[E] Empty buffer queue.\n"); + ret = -EINVAL; + goto done; + } + + buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream); + if ((ret = uvc_queue_waiton(buf, nonblocking)) < 0) + goto done; + + uvc_trace(UVC_TRACE_CAPTURE, "Dequeuing buffer %u (%u, %u bytes).\n", + buf->buf.index, buf->state, buf->buf.bytesused); + + switch (buf->state) { + case UVC_BUF_STATE_ERROR: + uvc_trace(UVC_TRACE_CAPTURE, "[W] Corrupted data " + "(transmission error).\n"); + ret = -EIO; + case UVC_BUF_STATE_DONE: + buf->state = UVC_BUF_STATE_IDLE; + break; + + case UVC_BUF_STATE_IDLE: + case UVC_BUF_STATE_QUEUED: + case UVC_BUF_STATE_ACTIVE: + default: + uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state %u " + "(driver bug?).\n", buf->state); + ret = -EINVAL; + goto done; + } + + list_del(&buf->stream); + __uvc_query_buffer(buf, v4l2_buf); + +done: + mutex_unlock(&queue->mutex); + return ret; +} + +/* + * Poll the video queue. + * + * This function implements video queue polling and is intended to be used by + * the device poll handler. + */ +unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file, + poll_table *wait) +{ + struct uvc_buffer *buf; + unsigned int mask = 0; + + mutex_lock(&queue->mutex); + if (list_empty(&queue->mainqueue)) { + mask |= POLLERR; + goto done; + } + buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream); + + poll_wait(file, &buf->wait, wait); + if (buf->state == UVC_BUF_STATE_DONE || + buf->state == UVC_BUF_STATE_ERROR) + mask |= POLLIN | POLLRDNORM; + +done: + mutex_unlock(&queue->mutex); + return mask; +} + +/* + * Enable or disable the video buffers queue. + * + * The queue must be enabled before starting video acquisition and must be + * disabled after stopping it. This ensures that the video buffers queue + * state can be properly initialized before buffers are accessed from the + * interrupt handler. + * + * Enabling the video queue initializes parameters (such as sequence number, + * sync pattern, ...). If the queue is already enabled, return -EBUSY. + * + * Disabling the video queue cancels the queue and removes all buffers from + * the main queue. + * + * This function can't be called from interrupt context. Use + * uvc_queue_cancel() instead. + */ +int uvc_queue_enable(struct uvc_video_queue *queue, int enable) +{ + unsigned int i; + int ret = 0; + + mutex_lock(&queue->mutex); + if (enable) { + if (uvc_queue_streaming(queue)) { + ret = -EBUSY; + goto done; + } + queue->sequence = 0; + queue->flags |= UVC_QUEUE_STREAMING; + } else { + uvc_queue_cancel(queue, 0); + INIT_LIST_HEAD(&queue->mainqueue); + + for (i = 0; i < queue->count; ++i) + queue->buffer[i].state = UVC_BUF_STATE_IDLE; + + queue->flags &= ~UVC_QUEUE_STREAMING; + } + +done: + mutex_unlock(&queue->mutex); + return ret; +} + +/* + * Cancel the video buffers queue. + * + * Cancelling the queue marks all buffers on the irq queue as erroneous, + * wakes them up and remove them from the queue. + * + * If the disconnect parameter is set, further calls to uvc_queue_buffer will + * fail with -ENODEV. + * + * This function acquires the irq spinlock and can be called from interrupt + * context. + */ +void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect) +{ + struct uvc_buffer *buf; + unsigned long flags; + + spin_lock_irqsave(&queue->irqlock, flags); + while (!list_empty(&queue->irqqueue)) { + buf = list_first_entry(&queue->irqqueue, struct uvc_buffer, + queue); + list_del(&buf->queue); + buf->state = UVC_BUF_STATE_ERROR; + wake_up(&buf->wait); + } + /* This must be protected by the irqlock spinlock to avoid race + * conditions between uvc_queue_buffer and the disconnection event that + * could result in an interruptible wait in uvc_dequeue_buffer. Do not + * blindly replace this logic by checking for the UVC_DEV_DISCONNECTED + * state outside the queue code. + */ + if (disconnect) + queue->flags |= UVC_QUEUE_DISCONNECTED; + spin_unlock_irqrestore(&queue->irqlock, flags); +} + +struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, + struct uvc_buffer *buf) +{ + struct uvc_buffer *nextbuf; + unsigned long flags; + + if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) && + buf->buf.length != buf->buf.bytesused) { + buf->state = UVC_BUF_STATE_QUEUED; + buf->buf.bytesused = 0; + return buf; + } + + spin_lock_irqsave(&queue->irqlock, flags); + list_del(&buf->queue); + if (!list_empty(&queue->irqqueue)) + nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer, + queue); + else + nextbuf = NULL; + spin_unlock_irqrestore(&queue->irqlock, flags); + + buf->buf.sequence = queue->sequence++; + do_gettimeofday(&buf->buf.timestamp); + + wake_up(&buf->wait); + return nextbuf; +} diff --git a/drivers/media/video/uvc/uvc_status.c b/drivers/media/video/uvc/uvc_status.c new file mode 100644 index 00000000000..be9084e5eac --- /dev/null +++ b/drivers/media/video/uvc/uvc_status.c @@ -0,0 +1,207 @@ +/* + * uvc_status.c -- USB Video Class driver - Status endpoint + * + * Copyright (C) 2007-2008 + * Laurent Pinchart (laurent.pinchart@skynet.be) + * + * 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/version.h> +#include <linux/input.h> +#include <linux/usb.h> +#include <linux/usb/input.h> + +#include "uvcvideo.h" + +/* -------------------------------------------------------------------------- + * Input device + */ +static int uvc_input_init(struct uvc_device *dev) +{ + struct usb_device *udev = dev->udev; + struct input_dev *input; + char *phys = NULL; + int ret; + + input = input_allocate_device(); + if (input == NULL) + return -ENOMEM; + + phys = kmalloc(6 + strlen(udev->bus->bus_name) + strlen(udev->devpath), + GFP_KERNEL); + if (phys == NULL) { + ret = -ENOMEM; + goto error; + } + sprintf(phys, "usb-%s-%s", udev->bus->bus_name, udev->devpath); + + input->name = dev->name; + input->phys = phys; + usb_to_input_id(udev, &input->id); + input->dev.parent = &dev->intf->dev; + + set_bit(EV_KEY, input->evbit); + set_bit(BTN_0, input->keybit); + + if ((ret = input_register_device(input)) < 0) + goto error; + + dev->input = input; + return 0; + +error: + input_free_device(input); + kfree(phys); + return ret; +} + +static void uvc_input_cleanup(struct uvc_device *dev) +{ + if (dev->input) + input_unregister_device(dev->input); +} + +/* -------------------------------------------------------------------------- + * Status interrupt endpoint + */ +static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len) +{ + if (len < 3) { + uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event " + "received.\n"); + return; + } + + if (data[2] == 0) { + if (len < 4) + return; + uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n", + data[1], data[3] ? "pressed" : "released", len); + if (dev->input) + input_report_key(dev->input, BTN_0, data[3]); + } else { + uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x %02x " + "len %d.\n", data[1], data[2], data[3], len); + } +} + +static void uvc_event_control(struct uvc_device *dev, __u8 *data, int len) +{ + char *attrs[3] = { "value", "info", "failure" }; + + if (len < 6 || data[2] != 0 || data[4] > 2) { + uvc_trace(UVC_TRACE_STATUS, "Invalid control status event " + "received.\n"); + return; + } + + uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n", + data[1], data[3], attrs[data[4]], len); +} + +static void uvc_status_complete(struct urb *urb) +{ + struct uvc_device *dev = urb->context; + int len, ret; + + switch (urb->status) { + case 0: + break; + + case -ENOENT: /* usb_kill_urb() called. */ + case -ECONNRESET: /* usb_unlink_urb() called. */ + case -ESHUTDOWN: /* The endpoint is being disabled. */ + case -EPROTO: /* Device is disconnected (reported by some + * host controller). */ + return; + + default: + uvc_printk(KERN_WARNING, "Non-zero status (%d) in status " + "completion handler.\n", urb->status); + return; + } + + len = urb->actual_length; + if (len > 0) { + switch (dev->status[0] & 0x0f) { + case UVC_STATUS_TYPE_CONTROL: + uvc_event_control(dev, dev->status, len); + break; + + case UVC_STATUS_TYPE_STREAMING: + uvc_event_streaming(dev, dev->status, len); + break; + + default: + uvc_printk(KERN_INFO, "unknown event type %u.\n", + dev->status[0]); + break; + } + } + + /* Resubmit the URB. */ + urb->interval = dev->int_ep->desc.bInterval; + if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { + uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n", + ret); + } +} + +int uvc_status_init(struct uvc_device *dev) +{ + struct usb_host_endpoint *ep = dev->int_ep; + unsigned int pipe; + int interval; + + if (ep == NULL) + return 0; + + uvc_input_init(dev); + + dev->int_urb = usb_alloc_urb(0, GFP_KERNEL); + if (dev->int_urb == NULL) + return -ENOMEM; + + pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress); + + /* For high-speed interrupt endpoints, the bInterval value is used as + * an exponent of two. Some developers forgot about it. + */ + interval = ep->desc.bInterval; + if (interval > 16 && dev->udev->speed == USB_SPEED_HIGH && + (dev->quirks & UVC_QUIRK_STATUS_INTERVAL)) + interval = fls(interval) - 1; + + usb_fill_int_urb(dev->int_urb, dev->udev, pipe, + dev->status, sizeof dev->status, uvc_status_complete, + dev, interval); + + return usb_submit_urb(dev->int_urb, GFP_KERNEL); +} + +void uvc_status_cleanup(struct uvc_device *dev) +{ + usb_kill_urb(dev->int_urb); + usb_free_urb(dev->int_urb); + uvc_input_cleanup(dev); +} + +int uvc_status_suspend(struct uvc_device *dev) +{ + usb_kill_urb(dev->int_urb); + return 0; +} + +int uvc_status_resume(struct uvc_device *dev) +{ + if (dev->int_urb == NULL) + return 0; + + return usb_submit_urb(dev->int_urb, GFP_KERNEL); +} diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c new file mode 100644 index 00000000000..2e0a66575bb --- /dev/null +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -0,0 +1,1105 @@ +/* + * uvc_v4l2.c -- USB Video Class driver - V4L2 API + * + * Copyright (C) 2005-2008 + * Laurent Pinchart (laurent.pinchart@skynet.be) + * + * 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/version.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/videodev2.h> +#include <linux/vmalloc.h> +#include <linux/mm.h> +#include <linux/wait.h> +#include <asm/atomic.h> + +#include <media/v4l2-common.h> + +#include "uvcvideo.h" + +/* ------------------------------------------------------------------------ + * V4L2 interface + */ + +/* + * Mapping V4L2 controls to UVC controls can be straighforward if done well. + * Most of the UVC controls exist in V4L2, and can be mapped directly. Some + * must be grouped (for instance the Red Balance, Blue Balance and Do White + * Balance V4L2 controls use the White Balance Component UVC control) or + * otherwise translated. The approach we take here is to use a translation + * table for the controls which can be mapped directly, and handle the others + * manually. + */ +static int uvc_v4l2_query_menu(struct uvc_video_device *video, + struct v4l2_querymenu *query_menu) +{ + struct uvc_menu_info *menu_info; + struct uvc_control_mapping *mapping; + struct uvc_control *ctrl; + + ctrl = uvc_find_control(video, query_menu->id, &mapping); + if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU) + return -EINVAL; + + if (query_menu->index >= mapping->menu_count) + return -EINVAL; + + menu_info = &mapping->menu_info[query_menu->index]; + strncpy(query_menu->name, menu_info->name, 32); + return 0; +} + +/* + * Find the frame interval closest to the requested frame interval for the + * given frame format and size. This should be done by the device as part of + * the Video Probe and Commit negotiation, but some hardware don't implement + * that feature. + */ +static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval) +{ + unsigned int i; + + if (frame->bFrameIntervalType) { + __u32 best = -1, dist; + + for (i = 0; i < frame->bFrameIntervalType; ++i) { + dist = interval > frame->dwFrameInterval[i] + ? interval - frame->dwFrameInterval[i] + : frame->dwFrameInterval[i] - interval; + + if (dist > best) + break; + + best = dist; + } + + interval = frame->dwFrameInterval[i-1]; + } else { + const __u32 min = frame->dwFrameInterval[0]; + const __u32 max = frame->dwFrameInterval[1]; + const __u32 step = frame->dwFrameInterval[2]; + + interval = min + (interval - min + step/2) / step * step; + if (interval > max) + interval = max; + } + + return interval; +} + +static int uvc_v4l2_try_format(struct uvc_video_device *video, + struct v4l2_format *fmt, struct uvc_streaming_control *probe, + struct uvc_format **uvc_format, struct uvc_frame **uvc_frame) +{ + struct uvc_format *format = NULL; + struct uvc_frame *frame = NULL; + __u16 rw, rh; + unsigned int d, maxd; + unsigned int i; + __u32 interval; + int ret = 0; + __u8 *fcc; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + fcc = (__u8 *)&fmt->fmt.pix.pixelformat; + uvc_trace(UVC_TRACE_FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u.\n", + fmt->fmt.pix.pixelformat, + fcc[0], fcc[1], fcc[2], fcc[3], + fmt->fmt.pix.width, fmt->fmt.pix.height); + + /* Check if the hardware supports the requested format. */ + for (i = 0; i < video->streaming->nformats; ++i) { + format = &video->streaming->format[i]; + if (format->fcc == fmt->fmt.pix.pixelformat) + break; + } + + if (format == NULL || format->fcc != fmt->fmt.pix.pixelformat) { + uvc_trace(UVC_TRACE_FORMAT, "Unsupported format 0x%08x.\n", + fmt->fmt.pix.pixelformat); + return -EINVAL; + } + + /* Find the closest image size. The distance between image sizes is + * the size in pixels of the non-overlapping regions between the + * requested size and the frame-specified size. + */ + rw = fmt->fmt.pix.width; + rh = fmt->fmt.pix.height; + maxd = (unsigned int)-1; + + for (i = 0; i < format->nframes; ++i) { + __u16 w = format->frame[i].wWidth; + __u16 h = format->frame[i].wHeight; + + d = min(w, rw) * min(h, rh); + d = w*h + rw*rh - 2*d; + if (d < maxd) { + maxd = d; + frame = &format->frame[i]; + } + + if (maxd == 0) + break; + } + + if (frame == NULL) { + uvc_trace(UVC_TRACE_FORMAT, "Unsupported size %ux%u.\n", + fmt->fmt.pix.width, fmt->fmt.pix.height); + return -EINVAL; + } + + /* Use the default frame interval. */ + interval = frame->dwDefaultFrameInterval; + uvc_trace(UVC_TRACE_FORMAT, "Using default frame interval %u.%u us " + "(%u.%u fps).\n", interval/10, interval%10, 10000000/interval, + (100000000/interval)%10); + + /* Set the format index, frame index and frame interval. */ + memset(probe, 0, sizeof *probe); + probe->bmHint = 1; /* dwFrameInterval */ + probe->bFormatIndex = format->index; + probe->bFrameIndex = frame->bFrameIndex; + probe->dwFrameInterval = uvc_try_frame_interval(frame, interval); + /* Some webcams stall the probe control set request when the + * dwMaxVideoFrameSize field is set to zero. The UVC specification + * clearly states that the field is read-only from the host, so this + * is a webcam bug. Set dwMaxVideoFrameSize to the value reported by + * the webcam to work around the problem. + * + * The workaround could probably be enabled for all webcams, so the + * quirk can be removed if needed. It's currently useful to detect + * webcam bugs and fix them before they hit the market (providing + * developers test their webcams with the Linux driver as well as with + * the Windows driver). + */ + if (video->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS) + probe->dwMaxVideoFrameSize = + video->streaming->ctrl.dwMaxVideoFrameSize; + + /* Probe the device */ + if ((ret = uvc_probe_video(video, probe)) < 0) + goto done; + + fmt->fmt.pix.width = frame->wWidth; + fmt->fmt.pix.height = frame->wHeight; + fmt->fmt.pix.field = V4L2_FIELD_NONE; + fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8; + fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize; + fmt->fmt.pix.colorspace = format->colorspace; + fmt->fmt.pix.priv = 0; + + if (uvc_format != NULL) + *uvc_format = format; + if (uvc_frame != NULL) + *uvc_frame = frame; + +done: + return ret; +} + +static int uvc_v4l2_get_format(struct uvc_video_device *video, + struct v4l2_format *fmt) +{ + struct uvc_format *format = video->streaming->cur_format; + struct uvc_frame *frame = video->streaming->cur_frame; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (format == NULL || frame == NULL) + return -EINVAL; + + fmt->fmt.pix.pixelformat = format->fcc; + fmt->fmt.pix.width = frame->wWidth; + fmt->fmt.pix.height = frame->wHeight; + fmt->fmt.pix.field = V4L2_FIELD_NONE; + fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8; + fmt->fmt.pix.sizeimage = video->streaming->ctrl.dwMaxVideoFrameSize; + fmt->fmt.pix.colorspace = format->colorspace; + fmt->fmt.pix.priv = 0; + + return 0; +} + +static int uvc_v4l2_set_format(struct uvc_video_device *video, + struct v4l2_format *fmt) +{ + struct uvc_streaming_control probe; + struct uvc_format *format; + struct uvc_frame *frame; + int ret; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (uvc_queue_streaming(&video->queue)) + return -EBUSY; + + ret = uvc_v4l2_try_format(video, fmt, &probe, &format, &frame); + if (ret < 0) + return ret; + + if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0) + return ret; + + memcpy(&video->streaming->ctrl, &probe, sizeof probe); + video->streaming->cur_format = format; + video->streaming->cur_frame = frame; + + return 0; +} + +static int uvc_v4l2_get_streamparm(struct uvc_video_device *video, + struct v4l2_streamparm *parm) +{ + uint32_t numerator, denominator; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + numerator = video->streaming->ctrl.dwFrameInterval; + denominator = 10000000; + uvc_simplify_fraction(&numerator, &denominator, 8, 333); + + memset(parm, 0, sizeof *parm); + parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.capturemode = 0; + parm->parm.capture.timeperframe.numerator = numerator; + parm->parm.capture.timeperframe.denominator = denominator; + parm->parm.capture.extendedmode = 0; + parm->parm.capture.readbuffers = 0; + + return 0; +} + +static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, + struct v4l2_streamparm *parm) +{ + struct uvc_frame *frame = video->streaming->cur_frame; + struct uvc_streaming_control probe; + uint32_t interval; + int ret; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (uvc_queue_streaming(&video->queue)) + return -EBUSY; + + memcpy(&probe, &video->streaming->ctrl, sizeof probe); + interval = uvc_fraction_to_interval( + parm->parm.capture.timeperframe.numerator, + parm->parm.capture.timeperframe.denominator); + + uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n", + parm->parm.capture.timeperframe.numerator, + parm->parm.capture.timeperframe.denominator, + interval); + probe.dwFrameInterval = uvc_try_frame_interval(frame, interval); + + /* Probe the device with the new settings. */ + if ((ret = uvc_probe_video(video, &probe)) < 0) + return ret; + + /* Commit the new settings. */ + if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0) + return ret; + + memcpy(&video->streaming->ctrl, &probe, sizeof probe); + + /* Return the actual frame period. */ + parm->parm.capture.timeperframe.numerator = probe.dwFrameInterval; + parm->parm.capture.timeperframe.denominator = 10000000; + uvc_simplify_fraction(&parm->parm.capture.timeperframe.numerator, + &parm->parm.capture.timeperframe.denominator, + 8, 333); + + return 0; +} + +/* ------------------------------------------------------------------------ + * Privilege management + */ + +/* + * Privilege management is the multiple-open implementation basis. The current + * implementation is completely transparent for the end-user and doesn't + * require explicit use of the VIDIOC_G_PRIORITY and VIDIOC_S_PRIORITY ioctls. + * Those ioctls enable finer control on the device (by making possible for a + * user to request exclusive access to a device), but are not mature yet. + * Switching to the V4L2 priority mechanism might be considered in the future + * if this situation changes. + * + * Each open instance of a UVC device can either be in a privileged or + * unprivileged state. Only a single instance can be in a privileged state at + * a given time. Trying to perform an operation which requires privileges will + * automatically acquire the required privileges if possible, or return -EBUSY + * otherwise. Privileges are dismissed when closing the instance. + * + * Operations which require privileges are: + * + * - VIDIOC_S_INPUT + * - VIDIOC_S_PARM + * - VIDIOC_S_FMT + * - VIDIOC_TRY_FMT + * - VIDIOC_REQBUFS + */ +static int uvc_acquire_privileges(struct uvc_fh *handle) +{ + int ret = 0; + + /* Always succeed if the handle is already privileged. */ + if (handle->state == UVC_HANDLE_ACTIVE) + return 0; + + /* Check if the device already has a privileged handle. */ + mutex_lock(&uvc_driver.open_mutex); + if (atomic_inc_return(&handle->device->active) != 1) { + atomic_dec(&handle->device->active); + ret = -EBUSY; + goto done; + } + + handle->state = UVC_HANDLE_ACTIVE; + +done: + mutex_unlock(&uvc_driver.open_mutex); + return ret; +} + +static void uvc_dismiss_privileges(struct uvc_fh *handle) +{ + if (handle->state == UVC_HANDLE_ACTIVE) + atomic_dec(&handle->device->active); + + handle->state = UVC_HANDLE_PASSIVE; +} + +static int uvc_has_privileges(struct uvc_fh *handle) +{ + return handle->state == UVC_HANDLE_ACTIVE; +} + +/* ------------------------------------------------------------------------ + * V4L2 file operations + */ + +static int uvc_v4l2_open(struct inode *inode, struct file *file) +{ + struct video_device *vdev; + struct uvc_video_device *video; + struct uvc_fh *handle; + int ret = 0; + + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); + mutex_lock(&uvc_driver.open_mutex); + vdev = video_devdata(file); + video = video_get_drvdata(vdev); + + if (video->dev->state & UVC_DEV_DISCONNECTED) { + ret = -ENODEV; + goto done; + } + + ret = usb_autopm_get_interface(video->dev->intf); + if (ret < 0) + goto done; + + /* Create the device handle. */ + handle = kzalloc(sizeof *handle, GFP_KERNEL); + if (handle == NULL) { + usb_autopm_put_interface(video->dev->intf); + ret = -ENOMEM; + goto done; + } + + handle->device = video; + handle->state = UVC_HANDLE_PASSIVE; + file->private_data = handle; + + kref_get(&video->dev->kref); + +done: + mutex_unlock(&uvc_driver.open_mutex); + return ret; +} + +static int uvc_v4l2_release(struct inode *inode, struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_video_device *video = video_get_drvdata(vdev); + struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n"); + + /* Only free resources if this is a privileged handle. */ + if (uvc_has_privileges(handle)) { + uvc_video_enable(video, 0); + + mutex_lock(&video->queue.mutex); + if (uvc_free_buffers(&video->queue) < 0) + uvc_printk(KERN_ERR, "uvc_v4l2_release: Unable to " + "free buffers.\n"); + mutex_unlock(&video->queue.mutex); + } + + /* Release the file handle. */ + uvc_dismiss_privileges(handle); + kfree(handle); + file->private_data = NULL; + + usb_autopm_put_interface(video->dev->intf); + kref_put(&video->dev->kref, uvc_delete); + return 0; +} + +static int uvc_v4l2_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_video_device *video = video_get_drvdata(vdev); + struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + int ret = 0; + + if (uvc_trace_param & UVC_TRACE_IOCTL) + v4l_printk_ioctl(cmd); + + switch (cmd) { + /* Query capabilities */ + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = arg; + + memset(cap, 0, sizeof *cap); + strncpy(cap->driver, "uvcvideo", sizeof cap->driver); + strncpy(cap->card, vdev->name, 32); + strncpy(cap->bus_info, video->dev->udev->bus->bus_name, + sizeof cap->bus_info); + cap->version = DRIVER_VERSION_NUMBER; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE + | V4L2_CAP_STREAMING; + break; + } + + /* Get, Set & Query control */ + case VIDIOC_QUERYCTRL: + return uvc_query_v4l2_ctrl(video, arg); + + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl = arg; + struct v4l2_ext_control xctrl; + + memset(&xctrl, 0, sizeof xctrl); + xctrl.id = ctrl->id; + + uvc_ctrl_begin(video); + ret = uvc_ctrl_get(video, &xctrl); + uvc_ctrl_rollback(video); + if (ret >= 0) + ctrl->value = xctrl.value; + break; + } + + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl = arg; + struct v4l2_ext_control xctrl; + + memset(&xctrl, 0, sizeof xctrl); + xctrl.id = ctrl->id; + xctrl.value = ctrl->value; + + uvc_ctrl_begin(video); + ret = uvc_ctrl_set(video, &xctrl); + if (ret < 0) { + uvc_ctrl_rollback(video); + return ret; + } + ret = uvc_ctrl_commit(video); + break; + } + + case VIDIOC_QUERYMENU: + return uvc_v4l2_query_menu(video, arg); + + case VIDIOC_G_EXT_CTRLS: + { + struct v4l2_ext_controls *ctrls = arg; + struct v4l2_ext_control *ctrl = ctrls->controls; + unsigned int i; + + uvc_ctrl_begin(video); + for (i = 0; i < ctrls->count; ++ctrl, ++i) { + ret = uvc_ctrl_get(video, ctrl); + if (ret < 0) { + uvc_ctrl_rollback(video); + ctrls->error_idx = i; + return ret; + } + } + ctrls->error_idx = 0; + ret = uvc_ctrl_rollback(video); + break; + } + + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_TRY_EXT_CTRLS: + { + struct v4l2_ext_controls *ctrls = arg; + struct v4l2_ext_control *ctrl = ctrls->controls; + unsigned int i; + + ret = uvc_ctrl_begin(video); + if (ret < 0) + return ret; + + for (i = 0; i < ctrls->count; ++ctrl, ++i) { + ret = uvc_ctrl_set(video, ctrl); + if (ret < 0) { + uvc_ctrl_rollback(video); + ctrls->error_idx = i; + return ret; + } + } + + ctrls->error_idx = 0; + + if (cmd == VIDIOC_S_EXT_CTRLS) + ret = uvc_ctrl_commit(video); + else + ret = uvc_ctrl_rollback(video); + break; + } + + /* Get, Set & Enum input */ + case VIDIOC_ENUMINPUT: + { + const struct uvc_entity *selector = video->selector; + struct v4l2_input *input = arg; + struct uvc_entity *iterm = NULL; + u32 index = input->index; + int pin = 0; + + if (selector == NULL || + (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + if (index != 0) + return -EINVAL; + iterm = list_first_entry(&video->iterms, + struct uvc_entity, chain); + pin = iterm->id; + } else if (pin < selector->selector.bNrInPins) { + pin = selector->selector.baSourceID[index]; + list_for_each_entry(iterm, video->iterms.next, chain) { + if (iterm->id == pin) + break; + } + } + + if (iterm == NULL || iterm->id != pin) + return -EINVAL; + + memset(input, 0, sizeof *input); + input->index = index; + strncpy(input->name, iterm->name, sizeof input->name); + if (UVC_ENTITY_TYPE(iterm) == ITT_CAMERA) + input->type = V4L2_INPUT_TYPE_CAMERA; + break; + } + + case VIDIOC_G_INPUT: + { + u8 input; + + if (video->selector == NULL || + (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + *(int *)arg = 0; + break; + } + + ret = uvc_query_ctrl(video->dev, GET_CUR, video->selector->id, + video->dev->intfnum, SU_INPUT_SELECT_CONTROL, + &input, 1); + if (ret < 0) + return ret; + + *(int *)arg = input - 1; + break; + } + + case VIDIOC_S_INPUT: + { + u8 input = *(u32 *)arg + 1; + + if ((ret = uvc_acquire_privileges(handle)) < 0) + return ret; + + if (video->selector == NULL || + (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + if (input != 1) + return -EINVAL; + break; + } + + if (input > video->selector->selector.bNrInPins) + return -EINVAL; + + return uvc_query_ctrl(video->dev, SET_CUR, video->selector->id, + video->dev->intfnum, SU_INPUT_SELECT_CONTROL, + &input, 1); + } + + /* Try, Get, Set & Enum format */ + case VIDIOC_ENUM_FMT: + { + struct v4l2_fmtdesc *fmt = arg; + struct uvc_format *format; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + fmt->index >= video->streaming->nformats) + return -EINVAL; + + format = &video->streaming->format[fmt->index]; + fmt->flags = 0; + if (format->flags & UVC_FMT_FLAG_COMPRESSED) + fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; + strncpy(fmt->description, format->name, + sizeof fmt->description); + fmt->description[sizeof fmt->description - 1] = 0; + fmt->pixelformat = format->fcc; + break; + } + + case VIDIOC_TRY_FMT: + { + struct uvc_streaming_control probe; + + if ((ret = uvc_acquire_privileges(handle)) < 0) + return ret; + + return uvc_v4l2_try_format(video, arg, &probe, NULL, NULL); + } + + case VIDIOC_S_FMT: + if ((ret = uvc_acquire_privileges(handle)) < 0) + return ret; + + return uvc_v4l2_set_format(video, arg); + + case VIDIOC_G_FMT: + return uvc_v4l2_get_format(video, arg); + + /* Frame size enumeration */ + case VIDIOC_ENUM_FRAMESIZES: + { + struct v4l2_frmsizeenum *fsize = arg; + struct uvc_format *format = NULL; + struct uvc_frame *frame; + int i; + + /* Look for the given pixel format */ + for (i = 0; i < video->streaming->nformats; i++) { + if (video->streaming->format[i].fcc == + fsize->pixel_format) { + format = &video->streaming->format[i]; + break; + } + } + if (format == NULL) + return -EINVAL; + + if (fsize->index >= format->nframes) + return -EINVAL; + + frame = &format->frame[fsize->index]; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = frame->wWidth; + fsize->discrete.height = frame->wHeight; + break; + } + + /* Frame interval enumeration */ + case VIDIOC_ENUM_FRAMEINTERVALS: + { + struct v4l2_frmivalenum *fival = arg; + struct uvc_format *format = NULL; + struct uvc_frame *frame = NULL; + int i; + + /* Look for the given pixel format and frame size */ + for (i = 0; i < video->streaming->nformats; i++) { + if (video->streaming->format[i].fcc == + fival->pixel_format) { + format = &video->streaming->format[i]; + break; + } + } + if (format == NULL) + return -EINVAL; + + for (i = 0; i < format->nframes; i++) { + if (format->frame[i].wWidth == fival->width && + format->frame[i].wHeight == fival->height) { + frame = &format->frame[i]; + break; + } + } + if (frame == NULL) + return -EINVAL; + + if (frame->bFrameIntervalType) { + if (fival->index >= frame->bFrameIntervalType) + return -EINVAL; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.numerator = + frame->dwFrameInterval[fival->index]; + fival->discrete.denominator = 10000000; + uvc_simplify_fraction(&fival->discrete.numerator, + &fival->discrete.denominator, 8, 333); + } else { + fival->type = V4L2_FRMIVAL_TYPE_STEPWISE; + fival->stepwise.min.numerator = + frame->dwFrameInterval[0]; + fival->stepwise.min.denominator = 10000000; + fival->stepwise.max.numerator = + frame->dwFrameInterval[1]; + fival->stepwise.max.denominator = 10000000; + fival->stepwise.step.numerator = + frame->dwFrameInterval[2]; + fival->stepwise.step.denominator = 10000000; + uvc_simplify_fraction(&fival->stepwise.min.numerator, + &fival->stepwise.min.denominator, 8, 333); + uvc_simplify_fraction(&fival->stepwise.max.numerator, + &fival->stepwise.max.denominator, 8, 333); + uvc_simplify_fraction(&fival->stepwise.step.numerator, + &fival->stepwise.step.denominator, 8, 333); + } + break; + } + + /* Get & Set streaming parameters */ + case VIDIOC_G_PARM: + return uvc_v4l2_get_streamparm(video, arg); + + case VIDIOC_S_PARM: + if ((ret = uvc_acquire_privileges(handle)) < 0) + return ret; + + return uvc_v4l2_set_streamparm(video, arg); + + /* Cropping and scaling */ + case VIDIOC_CROPCAP: + { + struct v4l2_cropcap *ccap = arg; + struct uvc_frame *frame = video->streaming->cur_frame; + + if (ccap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + ccap->bounds.left = 0; + ccap->bounds.top = 0; + ccap->bounds.width = frame->wWidth; + ccap->bounds.height = frame->wHeight; + + ccap->defrect = ccap->bounds; + + ccap->pixelaspect.numerator = 1; + ccap->pixelaspect.denominator = 1; + break; + } + + case VIDIOC_G_CROP: + case VIDIOC_S_CROP: + return -EINVAL; + + /* Buffers & streaming */ + case VIDIOC_REQBUFS: + { + struct v4l2_requestbuffers *rb = arg; + unsigned int bufsize = + video->streaming->ctrl.dwMaxVideoFrameSize; + + if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + rb->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if ((ret = uvc_acquire_privileges(handle)) < 0) + return ret; + + ret = uvc_alloc_buffers(&video->queue, rb->count, bufsize); + if (ret < 0) + return ret; + + if (!(video->streaming->cur_format->flags & + UVC_FMT_FLAG_COMPRESSED)) + video->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE; + + rb->count = ret; + ret = 0; + break; + } + + case VIDIOC_QUERYBUF: + { + struct v4l2_buffer *buf = arg; + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (!uvc_has_privileges(handle)) + return -EBUSY; + + return uvc_query_buffer(&video->queue, buf); + } + + case VIDIOC_QBUF: + if (!uvc_has_privileges(handle)) + return -EBUSY; + + return uvc_queue_buffer(&video->queue, arg); + + case VIDIOC_DQBUF: + if (!uvc_has_privileges(handle)) + return -EBUSY; + + return uvc_dequeue_buffer(&video->queue, arg, + file->f_flags & O_NONBLOCK); + + case VIDIOC_STREAMON: + { + int *type = arg; + + if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (!uvc_has_privileges(handle)) + return -EBUSY; + + if ((ret = uvc_video_enable(video, 1)) < 0) + return ret; + break; + } + + case VIDIOC_STREAMOFF: + { + int *type = arg; + + if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (!uvc_has_privileges(handle)) + return -EBUSY; + + return uvc_video_enable(video, 0); + } + + /* Analog video standards make no sense for digital cameras. */ + case VIDIOC_ENUMSTD: + case VIDIOC_QUERYSTD: + case VIDIOC_G_STD: + case VIDIOC_S_STD: + + case VIDIOC_OVERLAY: + + case VIDIOC_ENUMAUDIO: + case VIDIOC_ENUMAUDOUT: + + case VIDIOC_ENUMOUTPUT: + uvc_trace(UVC_TRACE_IOCTL, "Unsupported ioctl 0x%08x\n", cmd); + return -EINVAL; + + /* Dynamic controls. */ + case UVCIOC_CTRL_ADD: + { + struct uvc_xu_control_info *xinfo = arg; + struct uvc_control_info *info; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + info = kmalloc(sizeof *info, GFP_KERNEL); + if (info == NULL) + return -ENOMEM; + + memcpy(info->entity, xinfo->entity, sizeof info->entity); + info->index = xinfo->index; + info->selector = xinfo->selector; + info->size = xinfo->size; + info->flags = xinfo->flags; + + info->flags |= UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX | + UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF; + + ret = uvc_ctrl_add_info(info); + if (ret < 0) + kfree(info); + break; + } + + case UVCIOC_CTRL_MAP: + { + struct uvc_xu_control_mapping *xmap = arg; + struct uvc_control_mapping *map; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + map = kmalloc(sizeof *map, GFP_KERNEL); + if (map == NULL) + return -ENOMEM; + + map->id = xmap->id; + memcpy(map->name, xmap->name, sizeof map->name); + memcpy(map->entity, xmap->entity, sizeof map->entity); + map->selector = xmap->selector; + map->size = xmap->size; + map->offset = xmap->offset; + map->v4l2_type = xmap->v4l2_type; + map->data_type = xmap->data_type; + + ret = uvc_ctrl_add_mapping(map); + if (ret < 0) + kfree(map); + break; + } + + case UVCIOC_CTRL_GET: + return uvc_xu_ctrl_query(video, arg, 0); + + case UVCIOC_CTRL_SET: + return uvc_xu_ctrl_query(video, arg, 1); + + default: + if ((ret = v4l_compat_translate_ioctl(inode, file, cmd, arg, + uvc_v4l2_do_ioctl)) == -ENOIOCTLCMD) + uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", + cmd); + return ret; + } + + return ret; +} + +static int uvc_v4l2_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_ioctl\n"); + return video_usercopy(inode, file, cmd, arg, uvc_v4l2_do_ioctl); +} + +static ssize_t uvc_v4l2_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n"); + return -ENODEV; +} + +/* + * VMA operations. + */ +static void uvc_vm_open(struct vm_area_struct *vma) +{ + struct uvc_buffer *buffer = vma->vm_private_data; + buffer->vma_use_count++; +} + +static void uvc_vm_close(struct vm_area_struct *vma) +{ + struct uvc_buffer *buffer = vma->vm_private_data; + buffer->vma_use_count--; +} + +static struct vm_operations_struct uvc_vm_ops = { + .open = uvc_vm_open, + .close = uvc_vm_close, +}; + +static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_video_device *video = video_get_drvdata(vdev); + struct uvc_buffer *buffer; + struct page *page; + unsigned long addr, start, size; + unsigned int i; + int ret = 0; + + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_mmap\n"); + + start = vma->vm_start; + size = vma->vm_end - vma->vm_start; + + mutex_lock(&video->queue.mutex); + + for (i = 0; i < video->queue.count; ++i) { + buffer = &video->queue.buffer[i]; + if ((buffer->buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) + break; + } + + if (i == video->queue.count || size != video->queue.buf_size) { + ret = -EINVAL; + goto done; + } + + /* + * VM_IO marks the area as being an mmaped region for I/O to a + * device. It also prevents the region from being core dumped. + */ + vma->vm_flags |= VM_IO; + + addr = (unsigned long)video->queue.mem + buffer->buf.m.offset; + while (size > 0) { + page = vmalloc_to_page((void *)addr); + if ((ret = vm_insert_page(vma, start, page)) < 0) + goto done; + + start += PAGE_SIZE; + addr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + vma->vm_ops = &uvc_vm_ops; + vma->vm_private_data = buffer; + uvc_vm_open(vma); + +done: + mutex_unlock(&video->queue.mutex); + return ret; +} + +static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_video_device *video = video_get_drvdata(vdev); + + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n"); + + return uvc_queue_poll(&video->queue, file, wait); +} + +struct file_operations uvc_fops = { + .owner = THIS_MODULE, + .open = uvc_v4l2_open, + .release = uvc_v4l2_release, + .ioctl = uvc_v4l2_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, + .read = uvc_v4l2_read, + .mmap = uvc_v4l2_mmap, + .poll = uvc_v4l2_poll, +}; diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c new file mode 100644 index 00000000000..6faf1fb2161 --- /dev/null +++ b/drivers/media/video/uvc/uvc_video.c @@ -0,0 +1,934 @@ +/* + * uvc_video.c -- USB Video Class driver - Video handling + * + * Copyright (C) 2005-2008 + * Laurent Pinchart (laurent.pinchart@skynet.be) + * + * 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/version.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/videodev2.h> +#include <linux/vmalloc.h> +#include <linux/wait.h> +#include <asm/atomic.h> +#include <asm/unaligned.h> + +#include <media/v4l2-common.h> + +#include "uvcvideo.h" + +/* ------------------------------------------------------------------------ + * UVC Controls + */ + +static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, + __u8 intfnum, __u8 cs, void *data, __u16 size, + int timeout) +{ + __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE; + unsigned int pipe; + int ret; + + pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0) + : usb_sndctrlpipe(dev->udev, 0); + type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT; + + ret = usb_control_msg(dev->udev, pipe, query, type, cs << 8, + unit << 8 | intfnum, data, size, timeout); + + if (ret != size) { + uvc_printk(KERN_ERR, "Failed to query (%u) UVC control %u " + "(unit %u) : %d (exp. %u).\n", query, cs, unit, ret, + size); + return -EIO; + } + + return 0; +} + +int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, + __u8 intfnum, __u8 cs, void *data, __u16 size) +{ + return __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size, + UVC_CTRL_CONTROL_TIMEOUT); +} + +static void uvc_fixup_buffer_size(struct uvc_video_device *video, + struct uvc_streaming_control *ctrl) +{ + struct uvc_format *format; + struct uvc_frame *frame; + + if (ctrl->bFormatIndex <= 0 || + ctrl->bFormatIndex > video->streaming->nformats) + return; + + format = &video->streaming->format[ctrl->bFormatIndex - 1]; + + if (ctrl->bFrameIndex <= 0 || + ctrl->bFrameIndex > format->nframes) + return; + + frame = &format->frame[ctrl->bFrameIndex - 1]; + + if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) || + (ctrl->dwMaxVideoFrameSize == 0 && + video->dev->uvc_version < 0x0110)) + ctrl->dwMaxVideoFrameSize = + frame->dwMaxVideoFrameBufferSize; +} + +static int uvc_get_video_ctrl(struct uvc_video_device *video, + struct uvc_streaming_control *ctrl, int probe, __u8 query) +{ + __u8 data[34]; + __u8 size; + int ret; + + size = video->dev->uvc_version >= 0x0110 ? 34 : 26; + ret = __uvc_query_ctrl(video->dev, query, 0, video->streaming->intfnum, + probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, &data, size, + UVC_CTRL_STREAMING_TIMEOUT); + + if (ret < 0) + return ret; + + ctrl->bmHint = le16_to_cpup((__le16 *)&data[0]); + ctrl->bFormatIndex = data[2]; + ctrl->bFrameIndex = data[3]; + ctrl->dwFrameInterval = le32_to_cpup((__le32 *)&data[4]); + ctrl->wKeyFrameRate = le16_to_cpup((__le16 *)&data[8]); + ctrl->wPFrameRate = le16_to_cpup((__le16 *)&data[10]); + ctrl->wCompQuality = le16_to_cpup((__le16 *)&data[12]); + ctrl->wCompWindowSize = le16_to_cpup((__le16 *)&data[14]); + ctrl->wDelay = le16_to_cpup((__le16 *)&data[16]); + ctrl->dwMaxVideoFrameSize = + le32_to_cpu(get_unaligned((__le32 *)&data[18])); + ctrl->dwMaxPayloadTransferSize = + le32_to_cpu(get_unaligned((__le32 *)&data[22])); + + if (size == 34) { + ctrl->dwClockFrequency = + le32_to_cpu(get_unaligned((__le32 *)&data[26])); + ctrl->bmFramingInfo = data[30]; + ctrl->bPreferedVersion = data[31]; + ctrl->bMinVersion = data[32]; + ctrl->bMaxVersion = data[33]; + } else { + ctrl->dwClockFrequency = video->dev->clock_frequency; + ctrl->bmFramingInfo = 0; + ctrl->bPreferedVersion = 0; + ctrl->bMinVersion = 0; + ctrl->bMaxVersion = 0; + } + + /* Some broken devices return a null or wrong dwMaxVideoFrameSize. + * Try to get the value from the format and frame descriptor. + */ + uvc_fixup_buffer_size(video, ctrl); + + return 0; +} + +int uvc_set_video_ctrl(struct uvc_video_device *video, + struct uvc_streaming_control *ctrl, int probe) +{ + __u8 data[34]; + __u8 size; + + size = video->dev->uvc_version >= 0x0110 ? 34 : 26; + memset(data, 0, sizeof data); + + *(__le16 *)&data[0] = cpu_to_le16(ctrl->bmHint); + data[2] = ctrl->bFormatIndex; + data[3] = ctrl->bFrameIndex; + *(__le32 *)&data[4] = cpu_to_le32(ctrl->dwFrameInterval); + *(__le16 *)&data[8] = cpu_to_le16(ctrl->wKeyFrameRate); + *(__le16 *)&data[10] = cpu_to_le16(ctrl->wPFrameRate); + *(__le16 *)&data[12] = cpu_to_le16(ctrl->wCompQuality); + *(__le16 *)&data[14] = cpu_to_le16(ctrl->wCompWindowSize); + *(__le16 *)&data[16] = cpu_to_le16(ctrl->wDelay); + /* Note: Some of the fields below are not required for IN devices (see + * UVC spec, 4.3.1.1), but we still copy them in case support for OUT + * devices is added in the future. */ + put_unaligned(cpu_to_le32(ctrl->dwMaxVideoFrameSize), + (__le32 *)&data[18]); + put_unaligned(cpu_to_le32(ctrl->dwMaxPayloadTransferSize), + (__le32 *)&data[22]); + + if (size == 34) { + put_unaligned(cpu_to_le32(ctrl->dwClockFrequency), + (__le32 *)&data[26]); + data[30] = ctrl->bmFramingInfo; + data[31] = ctrl->bPreferedVersion; + data[32] = ctrl->bMinVersion; + data[33] = ctrl->bMaxVersion; + } + + return __uvc_query_ctrl(video->dev, SET_CUR, 0, + video->streaming->intfnum, + probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, &data, size, + UVC_CTRL_STREAMING_TIMEOUT); +} + +int uvc_probe_video(struct uvc_video_device *video, + struct uvc_streaming_control *probe) +{ + struct uvc_streaming_control probe_min, probe_max; + __u16 bandwidth; + unsigned int i; + int ret; + + mutex_lock(&video->streaming->mutex); + + /* Perform probing. The device should adjust the requested values + * according to its capabilities. However, some devices, namely the + * first generation UVC Logitech webcams, don't implement the Video + * Probe control properly, and just return the needed bandwidth. For + * that reason, if the needed bandwidth exceeds the maximum available + * bandwidth, try to lower the quality. + */ + if ((ret = uvc_set_video_ctrl(video, probe, 1)) < 0) + goto done; + + /* Get the minimum and maximum values for compression settings. */ + if (!(video->dev->quirks & UVC_QUIRK_PROBE_MINMAX)) { + ret = uvc_get_video_ctrl(video, &probe_min, 1, GET_MIN); + if (ret < 0) + goto done; + ret = uvc_get_video_ctrl(video, &probe_max, 1, GET_MAX); + if (ret < 0) + goto done; + + probe->wCompQuality = probe_max.wCompQuality; + } + + for (i = 0; i < 2; ++i) { + if ((ret = uvc_set_video_ctrl(video, probe, 1)) < 0 || + (ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0) + goto done; + + if (video->streaming->intf->num_altsetting == 1) + break; + + bandwidth = probe->dwMaxPayloadTransferSize; + if (bandwidth <= video->streaming->maxpsize) + break; + + if (video->dev->quirks & UVC_QUIRK_PROBE_MINMAX) { + ret = -ENOSPC; + goto done; + } + + /* TODO: negotiate compression parameters */ + probe->wKeyFrameRate = probe_min.wKeyFrameRate; + probe->wPFrameRate = probe_min.wPFrameRate; + probe->wCompQuality = probe_max.wCompQuality; + probe->wCompWindowSize = probe_min.wCompWindowSize; + } + +done: + mutex_unlock(&video->streaming->mutex); + return ret; +} + +/* ------------------------------------------------------------------------ + * Video codecs + */ + +/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */ +#define UVC_STREAM_EOH (1 << 7) +#define UVC_STREAM_ERR (1 << 6) +#define UVC_STREAM_STI (1 << 5) +#define UVC_STREAM_RES (1 << 4) +#define UVC_STREAM_SCR (1 << 3) +#define UVC_STREAM_PTS (1 << 2) +#define UVC_STREAM_EOF (1 << 1) +#define UVC_STREAM_FID (1 << 0) + +/* Video payload decoding is handled by uvc_video_decode_start(), + * uvc_video_decode_data() and uvc_video_decode_end(). + * + * uvc_video_decode_start is called with URB data at the start of a bulk or + * isochronous payload. It processes header data and returns the header size + * in bytes if successful. If an error occurs, it returns a negative error + * code. The following error codes have special meanings. + * + * - EAGAIN informs the caller that the current video buffer should be marked + * as done, and that the function should be called again with the same data + * and a new video buffer. This is used when end of frame conditions can be + * reliably detected at the beginning of the next frame only. + * + * If an error other than -EAGAIN is returned, the caller will drop the current + * payload. No call to uvc_video_decode_data and uvc_video_decode_end will be + * made until the next payload. -ENODATA can be used to drop the current + * payload if no other error code is appropriate. + * + * uvc_video_decode_data is called for every URB with URB data. It copies the + * data to the video buffer. + * + * uvc_video_decode_end is called with header data at the end of a bulk or + * isochronous payload. It performs any additional header data processing and + * returns 0 or a negative error code if an error occured. As header data have + * already been processed by uvc_video_decode_start, this functions isn't + * required to perform sanity checks a second time. + * + * For isochronous transfers where a payload is always transfered in a single + * URB, the three functions will be called in a row. + * + * To let the decoder process header data and update its internal state even + * when no video buffer is available, uvc_video_decode_start must be prepared + * to be called with a NULL buf parameter. uvc_video_decode_data and + * uvc_video_decode_end will never be called with a NULL buffer. + */ +static int uvc_video_decode_start(struct uvc_video_device *video, + struct uvc_buffer *buf, const __u8 *data, int len) +{ + __u8 fid; + + /* Sanity checks: + * - packet must be at least 2 bytes long + * - bHeaderLength value must be at least 2 bytes (see above) + * - bHeaderLength value can't be larger than the packet size. + */ + if (len < 2 || data[0] < 2 || data[0] > len) + return -EINVAL; + + /* Skip payloads marked with the error bit ("error frames"). */ + if (data[1] & UVC_STREAM_ERR) { + uvc_trace(UVC_TRACE_FRAME, "Dropping payload (error bit " + "set).\n"); + return -ENODATA; + } + + fid = data[1] & UVC_STREAM_FID; + + /* Store the payload FID bit and return immediately when the buffer is + * NULL. + */ + if (buf == NULL) { + video->last_fid = fid; + return -ENODATA; + } + + /* Synchronize to the input stream by waiting for the FID bit to be + * toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE. + * queue->last_fid is initialized to -1, so the first isochronous + * frame will always be in sync. + * + * If the device doesn't toggle the FID bit, invert video->last_fid + * when the EOF bit is set to force synchronisation on the next packet. + */ + if (buf->state != UVC_BUF_STATE_ACTIVE) { + if (fid == video->last_fid) { + uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of " + "sync).\n"); + if ((video->dev->quirks & UVC_QUIRK_STREAM_NO_FID) && + (data[1] & UVC_STREAM_EOF)) + video->last_fid ^= UVC_STREAM_FID; + return -ENODATA; + } + + /* TODO: Handle PTS and SCR. */ + buf->state = UVC_BUF_STATE_ACTIVE; + } + + /* Mark the buffer as done if we're at the beginning of a new frame. + * End of frame detection is better implemented by checking the EOF + * bit (FID bit toggling is delayed by one frame compared to the EOF + * bit), but some devices don't set the bit at end of frame (and the + * last payload can be lost anyway). We thus must check if the FID has + * been toggled. + * + * queue->last_fid is initialized to -1, so the first isochronous + * frame will never trigger an end of frame detection. + * + * Empty buffers (bytesused == 0) don't trigger end of frame detection + * as it doesn't make sense to return an empty buffer. This also + * avoids detecting and of frame conditions at FID toggling if the + * previous payload had the EOF bit set. + */ + if (fid != video->last_fid && buf->buf.bytesused != 0) { + uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit " + "toggled).\n"); + buf->state = UVC_BUF_STATE_DONE; + return -EAGAIN; + } + + video->last_fid = fid; + + return data[0]; +} + +static void uvc_video_decode_data(struct uvc_video_device *video, + struct uvc_buffer *buf, const __u8 *data, int len) +{ + struct uvc_video_queue *queue = &video->queue; + unsigned int maxlen, nbytes; + void *mem; + + if (len <= 0) + return; + + /* Copy the video data to the buffer. */ + maxlen = buf->buf.length - buf->buf.bytesused; + mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused; + nbytes = min((unsigned int)len, maxlen); + memcpy(mem, data, nbytes); + buf->buf.bytesused += nbytes; + + /* Complete the current frame if the buffer size was exceeded. */ + if (len > maxlen) { + uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n"); + buf->state = UVC_BUF_STATE_DONE; + } +} + +static void uvc_video_decode_end(struct uvc_video_device *video, + struct uvc_buffer *buf, const __u8 *data, int len) +{ + /* Mark the buffer as done if the EOF marker is set. */ + if (data[1] & UVC_STREAM_EOF && buf->buf.bytesused != 0) { + uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n"); + if (data[0] == len) + uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n"); + buf->state = UVC_BUF_STATE_DONE; + if (video->dev->quirks & UVC_QUIRK_STREAM_NO_FID) + video->last_fid ^= UVC_STREAM_FID; + } +} + +/* ------------------------------------------------------------------------ + * URB handling + */ + +/* + * Completion handler for video URBs. + */ +static void uvc_video_decode_isoc(struct urb *urb, + struct uvc_video_device *video, struct uvc_buffer *buf) +{ + u8 *mem; + int ret, i; + + for (i = 0; i < urb->number_of_packets; ++i) { + if (urb->iso_frame_desc[i].status < 0) { + uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame " + "lost (%d).\n", urb->iso_frame_desc[i].status); + continue; + } + + /* Decode the payload header. */ + mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + do { + ret = uvc_video_decode_start(video, buf, mem, + urb->iso_frame_desc[i].actual_length); + if (ret == -EAGAIN) + buf = uvc_queue_next_buffer(&video->queue, buf); + } while (ret == -EAGAIN); + + if (ret < 0) + continue; + + /* Decode the payload data. */ + uvc_video_decode_data(video, buf, mem + ret, + urb->iso_frame_desc[i].actual_length - ret); + + /* Process the header again. */ + uvc_video_decode_end(video, buf, mem, ret); + + if (buf->state == UVC_BUF_STATE_DONE || + buf->state == UVC_BUF_STATE_ERROR) + buf = uvc_queue_next_buffer(&video->queue, buf); + } +} + +static void uvc_video_decode_bulk(struct urb *urb, + struct uvc_video_device *video, struct uvc_buffer *buf) +{ + u8 *mem; + int len, ret; + + mem = urb->transfer_buffer; + len = urb->actual_length; + video->bulk.payload_size += len; + + /* If the URB is the first of its payload, decode and save the + * header. + */ + if (video->bulk.header_size == 0) { + do { + ret = uvc_video_decode_start(video, buf, mem, len); + if (ret == -EAGAIN) + buf = uvc_queue_next_buffer(&video->queue, buf); + } while (ret == -EAGAIN); + + /* If an error occured skip the rest of the payload. */ + if (ret < 0 || buf == NULL) { + video->bulk.skip_payload = 1; + return; + } + + video->bulk.header_size = ret; + memcpy(video->bulk.header, mem, video->bulk.header_size); + + mem += ret; + len -= ret; + } + + /* The buffer queue might have been cancelled while a bulk transfer + * was in progress, so we can reach here with buf equal to NULL. Make + * sure buf is never dereferenced if NULL. + */ + + /* Process video data. */ + if (!video->bulk.skip_payload && buf != NULL) + uvc_video_decode_data(video, buf, mem, len); + + /* Detect the payload end by a URB smaller than the maximum size (or + * a payload size equal to the maximum) and process the header again. + */ + if (urb->actual_length < urb->transfer_buffer_length || + video->bulk.payload_size >= video->bulk.max_payload_size) { + if (!video->bulk.skip_payload && buf != NULL) { + uvc_video_decode_end(video, buf, video->bulk.header, + video->bulk.header_size); + if (buf->state == UVC_BUF_STATE_DONE || + buf->state == UVC_BUF_STATE_ERROR) + buf = uvc_queue_next_buffer(&video->queue, buf); + } + + video->bulk.header_size = 0; + video->bulk.skip_payload = 0; + video->bulk.payload_size = 0; + } +} + +static void uvc_video_complete(struct urb *urb) +{ + struct uvc_video_device *video = urb->context; + struct uvc_video_queue *queue = &video->queue; + struct uvc_buffer *buf = NULL; + unsigned long flags; + int ret; + + switch (urb->status) { + case 0: + break; + + default: + uvc_printk(KERN_WARNING, "Non-zero status (%d) in video " + "completion handler.\n", urb->status); + + case -ENOENT: /* usb_kill_urb() called. */ + if (video->frozen) + return; + + case -ECONNRESET: /* usb_unlink_urb() called. */ + case -ESHUTDOWN: /* The endpoint is being disabled. */ + uvc_queue_cancel(queue, urb->status == -ESHUTDOWN); + return; + } + + spin_lock_irqsave(&queue->irqlock, flags); + if (!list_empty(&queue->irqqueue)) + buf = list_first_entry(&queue->irqqueue, struct uvc_buffer, + queue); + spin_unlock_irqrestore(&queue->irqlock, flags); + + video->decode(urb, video, buf); + + if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { + uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n", + ret); + } +} + +/* + * Uninitialize isochronous/bulk URBs and free transfer buffers. + */ +static void uvc_uninit_video(struct uvc_video_device *video) +{ + struct urb *urb; + unsigned int i; + + for (i = 0; i < UVC_URBS; ++i) { + if ((urb = video->urb[i]) == NULL) + continue; + + usb_kill_urb(urb); + /* urb->transfer_buffer_length is not touched by USB core, so + * we can use it here as the buffer length. + */ + if (video->urb_buffer[i]) { + usb_buffer_free(video->dev->udev, + urb->transfer_buffer_length, + video->urb_buffer[i], urb->transfer_dma); + video->urb_buffer[i] = NULL; + } + + usb_free_urb(urb); + video->urb[i] = NULL; + } +} + +/* + * Initialize isochronous URBs and allocate transfer buffers. The packet size + * is given by the endpoint. + */ +static int uvc_init_video_isoc(struct uvc_video_device *video, + struct usb_host_endpoint *ep) +{ + struct urb *urb; + unsigned int npackets, i, j; + __u16 psize; + __u32 size; + + /* Compute the number of isochronous packets to allocate by dividing + * the maximum video frame size by the packet size. Limit the result + * to UVC_MAX_ISO_PACKETS. + */ + psize = le16_to_cpu(ep->desc.wMaxPacketSize); + psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + + size = video->streaming->ctrl.dwMaxVideoFrameSize; + if (size > UVC_MAX_FRAME_SIZE) + return -EINVAL; + + npackets = (size + psize - 1) / psize; + if (npackets > UVC_MAX_ISO_PACKETS) + npackets = UVC_MAX_ISO_PACKETS; + + size = npackets * psize; + + for (i = 0; i < UVC_URBS; ++i) { + urb = usb_alloc_urb(npackets, GFP_KERNEL); + if (urb == NULL) { + uvc_uninit_video(video); + return -ENOMEM; + } + + video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev, + size, GFP_KERNEL, &urb->transfer_dma); + if (video->urb_buffer[i] == NULL) { + usb_free_urb(urb); + uvc_uninit_video(video); + return -ENOMEM; + } + + urb->dev = video->dev->udev; + urb->context = video; + urb->pipe = usb_rcvisocpipe(video->dev->udev, + ep->desc.bEndpointAddress); + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + urb->interval = ep->desc.bInterval; + urb->transfer_buffer = video->urb_buffer[i]; + urb->complete = uvc_video_complete; + urb->number_of_packets = npackets; + urb->transfer_buffer_length = size; + + for (j = 0; j < npackets; ++j) { + urb->iso_frame_desc[j].offset = j * psize; + urb->iso_frame_desc[j].length = psize; + } + + video->urb[i] = urb; + } + + return 0; +} + +/* + * Initialize bulk URBs and allocate transfer buffers. The packet size is + * given by the endpoint. + */ +static int uvc_init_video_bulk(struct uvc_video_device *video, + struct usb_host_endpoint *ep) +{ + struct urb *urb; + unsigned int pipe, i; + __u16 psize; + __u32 size; + + /* Compute the bulk URB size. Some devices set the maximum payload + * size to a value too high for memory-constrained devices. We must + * then transfer the payload accross multiple URBs. To be consistant + * with isochronous mode, allocate maximum UVC_MAX_ISO_PACKETS per bulk + * URB. + */ + psize = le16_to_cpu(ep->desc.wMaxPacketSize) & 0x07ff; + size = video->streaming->ctrl.dwMaxPayloadTransferSize; + video->bulk.max_payload_size = size; + if (size > psize * UVC_MAX_ISO_PACKETS) + size = psize * UVC_MAX_ISO_PACKETS; + + pipe = usb_rcvbulkpipe(video->dev->udev, ep->desc.bEndpointAddress); + + for (i = 0; i < UVC_URBS; ++i) { + urb = usb_alloc_urb(0, GFP_KERNEL); + if (urb == NULL) { + uvc_uninit_video(video); + return -ENOMEM; + } + + video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev, + size, GFP_KERNEL, &urb->transfer_dma); + if (video->urb_buffer[i] == NULL) { + usb_free_urb(urb); + uvc_uninit_video(video); + return -ENOMEM; + } + + usb_fill_bulk_urb(urb, video->dev->udev, pipe, + video->urb_buffer[i], size, uvc_video_complete, + video); + urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + + video->urb[i] = urb; + } + + return 0; +} + +/* + * Initialize isochronous/bulk URBs and allocate transfer buffers. + */ +static int uvc_init_video(struct uvc_video_device *video) +{ + struct usb_interface *intf = video->streaming->intf; + struct usb_host_interface *alts; + struct usb_host_endpoint *ep = NULL; + int intfnum = video->streaming->intfnum; + unsigned int bandwidth, psize, i; + int ret; + + video->last_fid = -1; + video->bulk.header_size = 0; + video->bulk.skip_payload = 0; + video->bulk.payload_size = 0; + + if (intf->num_altsetting > 1) { + /* Isochronous endpoint, select the alternate setting. */ + bandwidth = video->streaming->ctrl.dwMaxPayloadTransferSize; + + if (bandwidth == 0) { + uvc_printk(KERN_WARNING, "device %s requested null " + "bandwidth, defaulting to lowest.\n", + video->vdev->name); + bandwidth = 1; + } + + for (i = 0; i < intf->num_altsetting; ++i) { + alts = &intf->altsetting[i]; + ep = uvc_find_endpoint(alts, + video->streaming->header.bEndpointAddress); + if (ep == NULL) + continue; + + /* Check if the bandwidth is high enough. */ + psize = le16_to_cpu(ep->desc.wMaxPacketSize); + psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + if (psize >= bandwidth) + break; + } + + if (i >= intf->num_altsetting) + return -EIO; + + if ((ret = usb_set_interface(video->dev->udev, intfnum, i)) < 0) + return ret; + + ret = uvc_init_video_isoc(video, ep); + } else { + /* Bulk endpoint, proceed to URB initialization. */ + ep = uvc_find_endpoint(&intf->altsetting[0], + video->streaming->header.bEndpointAddress); + if (ep == NULL) + return -EIO; + + ret = uvc_init_video_bulk(video, ep); + } + + if (ret < 0) + return ret; + + /* Submit the URBs. */ + for (i = 0; i < UVC_URBS; ++i) { + if ((ret = usb_submit_urb(video->urb[i], GFP_KERNEL)) < 0) { + uvc_printk(KERN_ERR, "Failed to submit URB %u " + "(%d).\n", i, ret); + uvc_uninit_video(video); + return ret; + } + } + + return 0; +} + +/* -------------------------------------------------------------------------- + * Suspend/resume + */ + +/* + * Stop streaming without disabling the video queue. + * + * To let userspace applications resume without trouble, we must not touch the + * video buffers in any way. We mark the device as frozen to make sure the URB + * completion handler won't try to cancel the queue when we kill the URBs. + */ +int uvc_video_suspend(struct uvc_video_device *video) +{ + if (!uvc_queue_streaming(&video->queue)) + return 0; + + video->frozen = 1; + uvc_uninit_video(video); + usb_set_interface(video->dev->udev, video->streaming->intfnum, 0); + return 0; +} + +/* + * Reconfigure the video interface and restart streaming if it was enable + * before suspend. + * + * If an error occurs, disable the video queue. This will wake all pending + * buffers, making sure userspace applications are notified of the problem + * instead of waiting forever. + */ +int uvc_video_resume(struct uvc_video_device *video) +{ + int ret; + + video->frozen = 0; + + if ((ret = uvc_set_video_ctrl(video, &video->streaming->ctrl, 0)) < 0) { + uvc_queue_enable(&video->queue, 0); + return ret; + } + + if (!uvc_queue_streaming(&video->queue)) + return 0; + + if ((ret = uvc_init_video(video)) < 0) + uvc_queue_enable(&video->queue, 0); + + return ret; +} + +/* ------------------------------------------------------------------------ + * Video device + */ + +/* + * Initialize the UVC video device by retrieving the default format and + * committing it. + * + * Some cameras (namely the Fuji Finepix) set the format and frame + * indexes to zero. The UVC standard doesn't clearly make this a spec + * violation, so try to silently fix the values if possible. + * + * This function is called before registering the device with V4L. + */ +int uvc_video_init(struct uvc_video_device *video) +{ + struct uvc_streaming_control *probe = &video->streaming->ctrl; + struct uvc_format *format = NULL; + struct uvc_frame *frame = NULL; + unsigned int i; + int ret; + + if (video->streaming->nformats == 0) { + uvc_printk(KERN_INFO, "No supported video formats found.\n"); + return -EINVAL; + } + + /* Alternate setting 0 should be the default, yet the XBox Live Vision + * Cam (and possibly other devices) crash or otherwise misbehave if + * they don't receive a SET_INTERFACE request before any other video + * control request. + */ + usb_set_interface(video->dev->udev, video->streaming->intfnum, 0); + + /* Some webcams don't suport GET_DEF request on the probe control. We + * fall back to GET_CUR if GET_DEF fails. + */ + if ((ret = uvc_get_video_ctrl(video, probe, 1, GET_DEF)) < 0 && + (ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0) + return ret; + + /* Check if the default format descriptor exists. Use the first + * available format otherwise. + */ + for (i = video->streaming->nformats; i > 0; --i) { + format = &video->streaming->format[i-1]; + if (format->index == probe->bFormatIndex) + break; + } + + if (format->nframes == 0) { + uvc_printk(KERN_INFO, "No frame descriptor found for the " + "default format.\n"); + return -EINVAL; + } + + /* Zero bFrameIndex might be correct. Stream-based formats (including + * MPEG-2 TS and DV) do not support frames but have a dummy frame + * descriptor with bFrameIndex set to zero. If the default frame + * descriptor is not found, use the first avalable frame. + */ + for (i = format->nframes; i > 0; --i) { + frame = &format->frame[i-1]; + if (frame->bFrameIndex == probe->bFrameIndex) + break; + } + + /* Commit the default settings. */ + probe->bFormatIndex = format->index; + probe->bFrameIndex = frame->bFrameIndex; + if ((ret = uvc_set_video_ctrl(video, probe, 0)) < 0) + return ret; + + video->streaming->cur_format = format; + video->streaming->cur_frame = frame; + atomic_set(&video->active, 0); + + /* Select the video decoding function */ + if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT) + video->decode = uvc_video_decode_isight; + else if (video->streaming->intf->num_altsetting > 1) + video->decode = uvc_video_decode_isoc; + else + video->decode = uvc_video_decode_bulk; + + return 0; +} + +/* + * Enable or disable the video stream. + */ +int uvc_video_enable(struct uvc_video_device *video, int enable) +{ + int ret; + + if (!enable) { + uvc_uninit_video(video); + usb_set_interface(video->dev->udev, + video->streaming->intfnum, 0); + uvc_queue_enable(&video->queue, 0); + return 0; + } + + if ((ret = uvc_queue_enable(&video->queue, 1)) < 0) + return ret; + + return uvc_init_video(video); +} diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h new file mode 100644 index 00000000000..a995a780db1 --- /dev/null +++ b/drivers/media/video/uvc/uvcvideo.h @@ -0,0 +1,796 @@ +#ifndef _USB_VIDEO_H_ +#define _USB_VIDEO_H_ + +#include <linux/kernel.h> +#include <linux/videodev2.h> + + +/* + * Dynamic controls + */ + +/* Data types for UVC control data */ +#define UVC_CTRL_DATA_TYPE_RAW 0 +#define UVC_CTRL_DATA_TYPE_SIGNED 1 +#define UVC_CTRL_DATA_TYPE_UNSIGNED 2 +#define UVC_CTRL_DATA_TYPE_BOOLEAN 3 +#define UVC_CTRL_DATA_TYPE_ENUM 4 +#define UVC_CTRL_DATA_TYPE_BITMASK 5 + +/* Control flags */ +#define UVC_CONTROL_SET_CUR (1 << 0) +#define UVC_CONTROL_GET_CUR (1 << 1) +#define UVC_CONTROL_GET_MIN (1 << 2) +#define UVC_CONTROL_GET_MAX (1 << 3) +#define UVC_CONTROL_GET_RES (1 << 4) +#define UVC_CONTROL_GET_DEF (1 << 5) +/* Control should be saved at suspend and restored at resume. */ +#define UVC_CONTROL_RESTORE (1 << 6) +/* Control can be updated by the camera. */ +#define UVC_CONTROL_AUTO_UPDATE (1 << 7) + +#define UVC_CONTROL_GET_RANGE (UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \ + UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \ + UVC_CONTROL_GET_DEF) + +struct uvc_xu_control_info { + __u8 entity[16]; + __u8 index; + __u8 selector; + __u16 size; + __u32 flags; +}; + +struct uvc_xu_control_mapping { + __u32 id; + __u8 name[32]; + __u8 entity[16]; + __u8 selector; + + __u8 size; + __u8 offset; + enum v4l2_ctrl_type v4l2_type; + __u32 data_type; +}; + +struct uvc_xu_control { + __u8 unit; + __u8 selector; + __u16 size; + __u8 __user *data; +}; + +#define UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info) +#define UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping) +#define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control) +#define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control) + +#ifdef __KERNEL__ + +#include <linux/poll.h> + +/* -------------------------------------------------------------------------- + * UVC constants + */ + +#define SC_UNDEFINED 0x00 +#define SC_VIDEOCONTROL 0x01 +#define SC_VIDEOSTREAMING 0x02 +#define SC_VIDEO_INTERFACE_COLLECTION 0x03 + +#define PC_PROTOCOL_UNDEFINED 0x00 + +#define CS_UNDEFINED 0x20 +#define CS_DEVICE 0x21 +#define CS_CONFIGURATION 0x22 +#define CS_STRING 0x23 +#define CS_INTERFACE 0x24 +#define CS_ENDPOINT 0x25 + +/* VideoControl class specific interface descriptor */ +#define VC_DESCRIPTOR_UNDEFINED 0x00 +#define VC_HEADER 0x01 +#define VC_INPUT_TERMINAL 0x02 +#define VC_OUTPUT_TERMINAL 0x03 +#define VC_SELECTOR_UNIT 0x04 +#define VC_PROCESSING_UNIT 0x05 +#define VC_EXTENSION_UNIT 0x06 + +/* VideoStreaming class specific interface descriptor */ +#define VS_UNDEFINED 0x00 +#define VS_INPUT_HEADER 0x01 +#define VS_OUTPUT_HEADER 0x02 +#define VS_STILL_IMAGE_FRAME 0x03 +#define VS_FORMAT_UNCOMPRESSED 0x04 +#define VS_FRAME_UNCOMPRESSED 0x05 +#define VS_FORMAT_MJPEG 0x06 +#define VS_FRAME_MJPEG 0x07 +#define VS_FORMAT_MPEG2TS 0x0a +#define VS_FORMAT_DV 0x0c +#define VS_COLORFORMAT 0x0d +#define VS_FORMAT_FRAME_BASED 0x10 +#define VS_FRAME_FRAME_BASED 0x11 +#define VS_FORMAT_STREAM_BASED 0x12 + +/* Endpoint type */ +#define EP_UNDEFINED 0x00 +#define EP_GENERAL 0x01 +#define EP_ENDPOINT 0x02 +#define EP_INTERRUPT 0x03 + +/* Request codes */ +#define RC_UNDEFINED 0x00 +#define SET_CUR 0x01 +#define GET_CUR 0x81 +#define GET_MIN 0x82 +#define GET_MAX 0x83 +#define GET_RES 0x84 +#define GET_LEN 0x85 +#define GET_INFO 0x86 +#define GET_DEF 0x87 + +/* VideoControl interface controls */ +#define VC_CONTROL_UNDEFINED 0x00 +#define VC_VIDEO_POWER_MODE_CONTROL 0x01 +#define VC_REQUEST_ERROR_CODE_CONTROL 0x02 + +/* Terminal controls */ +#define TE_CONTROL_UNDEFINED 0x00 + +/* Selector Unit controls */ +#define SU_CONTROL_UNDEFINED 0x00 +#define SU_INPUT_SELECT_CONTROL 0x01 + +/* Camera Terminal controls */ +#define CT_CONTROL_UNDEFINED 0x00 +#define CT_SCANNING_MODE_CONTROL 0x01 +#define CT_AE_MODE_CONTROL 0x02 +#define CT_AE_PRIORITY_CONTROL 0x03 +#define CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x04 +#define CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x05 +#define CT_FOCUS_ABSOLUTE_CONTROL 0x06 +#define CT_FOCUS_RELATIVE_CONTROL 0x07 +#define CT_FOCUS_AUTO_CONTROL 0x08 +#define CT_IRIS_ABSOLUTE_CONTROL 0x09 +#define CT_IRIS_RELATIVE_CONTROL 0x0a +#define CT_ZOOM_ABSOLUTE_CONTROL 0x0b +#define CT_ZOOM_RELATIVE_CONTROL 0x0c +#define CT_PANTILT_ABSOLUTE_CONTROL 0x0d +#define CT_PANTILT_RELATIVE_CONTROL 0x0e +#define CT_ROLL_ABSOLUTE_CONTROL 0x0f +#define CT_ROLL_RELATIVE_CONTROL 0x10 +#define CT_PRIVACY_CONTROL 0x11 + +/* Processing Unit controls */ +#define PU_CONTROL_UNDEFINED 0x00 +#define PU_BACKLIGHT_COMPENSATION_CONTROL 0x01 +#define PU_BRIGHTNESS_CONTROL 0x02 +#define PU_CONTRAST_CONTROL 0x03 +#define PU_GAIN_CONTROL 0x04 +#define PU_POWER_LINE_FREQUENCY_CONTROL 0x05 +#define PU_HUE_CONTROL 0x06 +#define PU_SATURATION_CONTROL 0x07 +#define PU_SHARPNESS_CONTROL 0x08 +#define PU_GAMMA_CONTROL 0x09 +#define PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x0a +#define PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x0b +#define PU_WHITE_BALANCE_COMPONENT_CONTROL 0x0c +#define PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x0d +#define PU_DIGITAL_MULTIPLIER_CONTROL 0x0e +#define PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x0f +#define PU_HUE_AUTO_CONTROL 0x10 +#define PU_ANALOG_VIDEO_STANDARD_CONTROL 0x11 +#define PU_ANALOG_LOCK_STATUS_CONTROL 0x12 + +#define LXU_MOTOR_PANTILT_RELATIVE_CONTROL 0x01 +#define LXU_MOTOR_PANTILT_RESET_CONTROL 0x02 +#define LXU_MOTOR_FOCUS_MOTOR_CONTROL 0x03 + +/* VideoStreaming interface controls */ +#define VS_CONTROL_UNDEFINED 0x00 +#define VS_PROBE_CONTROL 0x01 +#define VS_COMMIT_CONTROL 0x02 +#define VS_STILL_PROBE_CONTROL 0x03 +#define VS_STILL_COMMIT_CONTROL 0x04 +#define VS_STILL_IMAGE_TRIGGER_CONTROL 0x05 +#define VS_STREAM_ERROR_CODE_CONTROL 0x06 +#define VS_GENERATE_KEY_FRAME_CONTROL 0x07 +#define VS_UPDATE_FRAME_SEGMENT_CONTROL 0x08 +#define VS_SYNC_DELAY_CONTROL 0x09 + +#define TT_VENDOR_SPECIFIC 0x0100 +#define TT_STREAMING 0x0101 + +/* Input Terminal types */ +#define ITT_VENDOR_SPECIFIC 0x0200 +#define ITT_CAMERA 0x0201 +#define ITT_MEDIA_TRANSPORT_INPUT 0x0202 + +/* Output Terminal types */ +#define OTT_VENDOR_SPECIFIC 0x0300 +#define OTT_DISPLAY 0x0301 +#define OTT_MEDIA_TRANSPORT_OUTPUT 0x0302 + +/* External Terminal types */ +#define EXTERNAL_VENDOR_SPECIFIC 0x0400 +#define COMPOSITE_CONNECTOR 0x0401 +#define SVIDEO_CONNECTOR 0x0402 +#define COMPONENT_CONNECTOR 0x0403 + +#define UVC_TERM_INPUT 0x0000 +#define UVC_TERM_OUTPUT 0x8000 + +#define UVC_ENTITY_TYPE(entity) ((entity)->type & 0x7fff) +#define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0) +#define UVC_ENTITY_IS_TERM(entity) (((entity)->type & 0xff00) != 0) +#define UVC_ENTITY_IS_ITERM(entity) \ + (((entity)->type & 0x8000) == UVC_TERM_INPUT) +#define UVC_ENTITY_IS_OTERM(entity) \ + (((entity)->type & 0x8000) == UVC_TERM_OUTPUT) + +#define UVC_STATUS_TYPE_CONTROL 1 +#define UVC_STATUS_TYPE_STREAMING 2 + +/* ------------------------------------------------------------------------ + * GUIDs + */ +#define UVC_GUID_UVC_CAMERA \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01} +#define UVC_GUID_UVC_OUTPUT \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02} +#define UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03} +#define UVC_GUID_UVC_PROCESSING \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01} +#define UVC_GUID_UVC_SELECTOR \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02} + +#define UVC_GUID_LOGITECH_DEV_INFO \ + {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1e} +#define UVC_GUID_LOGITECH_USER_HW \ + {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f} +#define UVC_GUID_LOGITECH_VIDEO \ + {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x50} +#define UVC_GUID_LOGITECH_MOTOR \ + {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x56} + +#define UVC_GUID_FORMAT_MJPEG \ + { 'M', 'J', 'P', 'G', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_YUY2 \ + { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_NV12 \ + { 'N', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_YV12 \ + { 'Y', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_I420 \ + { 'I', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_UYVY \ + { 'U', 'Y', 'V', 'Y', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_Y800 \ + { 'Y', '8', '0', '0', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_BY8 \ + { 'B', 'Y', '8', ' ', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} + + +/* ------------------------------------------------------------------------ + * Driver specific constants. + */ + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 1, 0) + +/* Number of isochronous URBs. */ +#define UVC_URBS 5 +/* Maximum number of packets per isochronous URB. */ +#define UVC_MAX_ISO_PACKETS 40 +/* Maximum frame size in bytes, for sanity checking. */ +#define UVC_MAX_FRAME_SIZE (16*1024*1024) +/* Maximum number of video buffers. */ +#define UVC_MAX_VIDEO_BUFFERS 32 + +#define UVC_CTRL_CONTROL_TIMEOUT 300 +#define UVC_CTRL_STREAMING_TIMEOUT 1000 + +/* Devices quirks */ +#define UVC_QUIRK_STATUS_INTERVAL 0x00000001 +#define UVC_QUIRK_PROBE_MINMAX 0x00000002 +#define UVC_QUIRK_PROBE_EXTRAFIELDS 0x00000004 +#define UVC_QUIRK_BUILTIN_ISIGHT 0x00000008 +#define UVC_QUIRK_STREAM_NO_FID 0x00000010 +#define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020 + +/* Format flags */ +#define UVC_FMT_FLAG_COMPRESSED 0x00000001 +#define UVC_FMT_FLAG_STREAM 0x00000002 + +/* ------------------------------------------------------------------------ + * Structures. + */ + +struct uvc_device; + +/* TODO: Put the most frequently accessed fields at the beginning of + * structures to maximize cache efficiency. + */ +struct uvc_streaming_control { + __u16 bmHint; + __u8 bFormatIndex; + __u8 bFrameIndex; + __u32 dwFrameInterval; + __u16 wKeyFrameRate; + __u16 wPFrameRate; + __u16 wCompQuality; + __u16 wCompWindowSize; + __u16 wDelay; + __u32 dwMaxVideoFrameSize; + __u32 dwMaxPayloadTransferSize; + __u32 dwClockFrequency; + __u8 bmFramingInfo; + __u8 bPreferedVersion; + __u8 bMinVersion; + __u8 bMaxVersion; +}; + +struct uvc_menu_info { + __u32 value; + __u8 name[32]; +}; + +struct uvc_control_info { + struct list_head list; + struct list_head mappings; + + __u8 entity[16]; + __u8 index; + __u8 selector; + + __u16 size; + __u32 flags; +}; + +struct uvc_control_mapping { + struct list_head list; + + struct uvc_control_info *ctrl; + + __u32 id; + __u8 name[32]; + __u8 entity[16]; + __u8 selector; + + __u8 size; + __u8 offset; + enum v4l2_ctrl_type v4l2_type; + __u32 data_type; + + struct uvc_menu_info *menu_info; + __u32 menu_count; +}; + +struct uvc_control { + struct uvc_entity *entity; + struct uvc_control_info *info; + + __u8 index; /* Used to match the uvc_control entry with a + uvc_control_info. */ + __u8 dirty : 1, + loaded : 1, + modified : 1; + + __u8 *data; +}; + +struct uvc_format_desc { + char *name; + __u8 guid[16]; + __u32 fcc; +}; + +/* The term 'entity' refers to both UVC units and UVC terminals. + * + * The type field is either the terminal type (wTerminalType in the terminal + * descriptor), or the unit type (bDescriptorSubtype in the unit descriptor). + * As the bDescriptorSubtype field is one byte long, the type value will + * always have a null MSB for units. All terminal types defined by the UVC + * specification have a non-null MSB, so it is safe to use the MSB to + * differentiate between units and terminals as long as the descriptor parsing + * code makes sure terminal types have a non-null MSB. + * + * For terminals, the type's most significant bit stores the terminal + * direction (either UVC_TERM_INPUT or UVC_TERM_OUTPUT). The type field should + * always be accessed with the UVC_ENTITY_* macros and never directly. + */ + +struct uvc_entity { + struct list_head list; /* Entity as part of a UVC device. */ + struct list_head chain; /* Entity as part of a video device + * chain. */ + __u8 id; + __u16 type; + char name[64]; + + union { + struct { + __u16 wObjectiveFocalLengthMin; + __u16 wObjectiveFocalLengthMax; + __u16 wOcularFocalLength; + __u8 bControlSize; + __u8 *bmControls; + } camera; + + struct { + __u8 bControlSize; + __u8 *bmControls; + __u8 bTransportModeSize; + __u8 *bmTransportModes; + } media; + + struct { + __u8 bSourceID; + } output; + + struct { + __u8 bSourceID; + __u16 wMaxMultiplier; + __u8 bControlSize; + __u8 *bmControls; + __u8 bmVideoStandards; + } processing; + + struct { + __u8 bNrInPins; + __u8 *baSourceID; + } selector; + + struct { + __u8 guidExtensionCode[16]; + __u8 bNumControls; + __u8 bNrInPins; + __u8 *baSourceID; + __u8 bControlSize; + __u8 *bmControls; + __u8 *bmControlsType; + } extension; + }; + + unsigned int ncontrols; + struct uvc_control *controls; +}; + +struct uvc_frame { + __u8 bFrameIndex; + __u8 bmCapabilities; + __u16 wWidth; + __u16 wHeight; + __u32 dwMinBitRate; + __u32 dwMaxBitRate; + __u32 dwMaxVideoFrameBufferSize; + __u8 bFrameIntervalType; + __u32 dwDefaultFrameInterval; + __u32 *dwFrameInterval; +}; + +struct uvc_format { + __u8 type; + __u8 index; + __u8 bpp; + __u8 colorspace; + __u32 fcc; + __u32 flags; + + char name[32]; + + unsigned int nframes; + struct uvc_frame *frame; +}; + +struct uvc_streaming_header { + __u8 bNumFormats; + __u8 bEndpointAddress; + __u8 bTerminalLink; + __u8 bControlSize; + __u8 *bmaControls; + /* The following fields are used by input headers only. */ + __u8 bmInfo; + __u8 bStillCaptureMethod; + __u8 bTriggerSupport; + __u8 bTriggerUsage; +}; + +struct uvc_streaming { + struct list_head list; + + struct usb_interface *intf; + int intfnum; + __u16 maxpsize; + + struct uvc_streaming_header header; + + unsigned int nformats; + struct uvc_format *format; + + struct uvc_streaming_control ctrl; + struct uvc_format *cur_format; + struct uvc_frame *cur_frame; + + struct mutex mutex; +}; + +enum uvc_buffer_state { + UVC_BUF_STATE_IDLE = 0, + UVC_BUF_STATE_QUEUED = 1, + UVC_BUF_STATE_ACTIVE = 2, + UVC_BUF_STATE_DONE = 3, + UVC_BUF_STATE_ERROR = 4, +}; + +struct uvc_buffer { + unsigned long vma_use_count; + struct list_head stream; + + /* Touched by interrupt handler. */ + struct v4l2_buffer buf; + struct list_head queue; + wait_queue_head_t wait; + enum uvc_buffer_state state; +}; + +#define UVC_QUEUE_STREAMING (1 << 0) +#define UVC_QUEUE_DISCONNECTED (1 << 1) +#define UVC_QUEUE_DROP_INCOMPLETE (1 << 2) + +struct uvc_video_queue { + void *mem; + unsigned int flags; + __u32 sequence; + + unsigned int count; + unsigned int buf_size; + struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS]; + struct mutex mutex; /* protects buffers and mainqueue */ + spinlock_t irqlock; /* protects irqqueue */ + + struct list_head mainqueue; + struct list_head irqqueue; +}; + +struct uvc_video_device { + struct uvc_device *dev; + struct video_device *vdev; + atomic_t active; + unsigned int frozen : 1; + + struct list_head iterms; + struct uvc_entity *oterm; + struct uvc_entity *processing; + struct uvc_entity *selector; + struct list_head extensions; + struct mutex ctrl_mutex; + + struct uvc_video_queue queue; + + /* Video streaming object, must always be non-NULL. */ + struct uvc_streaming *streaming; + + void (*decode) (struct urb *urb, struct uvc_video_device *video, + struct uvc_buffer *buf); + + /* Context data used by the bulk completion handler. */ + struct { + __u8 header[256]; + unsigned int header_size; + int skip_payload; + __u32 payload_size; + __u32 max_payload_size; + } bulk; + + struct urb *urb[UVC_URBS]; + char *urb_buffer[UVC_URBS]; + + __u8 last_fid; +}; + +enum uvc_device_state { + UVC_DEV_DISCONNECTED = 1, +}; + +struct uvc_device { + struct usb_device *udev; + struct usb_interface *intf; + __u32 quirks; + int intfnum; + char name[32]; + + enum uvc_device_state state; + struct kref kref; + struct list_head list; + + /* Video control interface */ + __u16 uvc_version; + __u32 clock_frequency; + + struct list_head entities; + + struct uvc_video_device video; + + /* Status Interrupt Endpoint */ + struct usb_host_endpoint *int_ep; + struct urb *int_urb; + __u8 status[16]; + struct input_dev *input; + + /* Video Streaming interfaces */ + struct list_head streaming; +}; + +enum uvc_handle_state { + UVC_HANDLE_PASSIVE = 0, + UVC_HANDLE_ACTIVE = 1, +}; + +struct uvc_fh { + struct uvc_video_device *device; + enum uvc_handle_state state; +}; + +struct uvc_driver { + struct usb_driver driver; + + struct mutex open_mutex; /* protects from open/disconnect race */ + + struct list_head devices; /* struct uvc_device list */ + struct list_head controls; /* struct uvc_control_info list */ + struct mutex ctrl_mutex; /* protects controls and devices + lists */ +}; + +/* ------------------------------------------------------------------------ + * Debugging, printing and logging + */ + +#define UVC_TRACE_PROBE (1 << 0) +#define UVC_TRACE_DESCR (1 << 1) +#define UVC_TRACE_CONTROL (1 << 2) +#define UVC_TRACE_FORMAT (1 << 3) +#define UVC_TRACE_CAPTURE (1 << 4) +#define UVC_TRACE_CALLS (1 << 5) +#define UVC_TRACE_IOCTL (1 << 6) +#define UVC_TRACE_FRAME (1 << 7) +#define UVC_TRACE_SUSPEND (1 << 8) +#define UVC_TRACE_STATUS (1 << 9) + +extern unsigned int uvc_trace_param; + +#define uvc_trace(flag, msg...) \ + do { \ + if (uvc_trace_param & flag) \ + printk(KERN_DEBUG "uvcvideo: " msg); \ + } while (0) + +#define uvc_printk(level, msg...) \ + printk(level "uvcvideo: " msg) + +#define UVC_GUID_FORMAT "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" \ + "%02x%02x%02x%02x%02x%02x" +#define UVC_GUID_ARGS(guid) \ + (guid)[3], (guid)[2], (guid)[1], (guid)[0], \ + (guid)[5], (guid)[4], \ + (guid)[7], (guid)[6], \ + (guid)[8], (guid)[9], \ + (guid)[10], (guid)[11], (guid)[12], \ + (guid)[13], (guid)[14], (guid)[15] + +/* -------------------------------------------------------------------------- + * Internal functions. + */ + +/* Core driver */ +extern struct uvc_driver uvc_driver; +extern void uvc_delete(struct kref *kref); + +/* Video buffers queue management. */ +extern void uvc_queue_init(struct uvc_video_queue *queue); +extern int uvc_alloc_buffers(struct uvc_video_queue *queue, + unsigned int nbuffers, unsigned int buflength); +extern int uvc_free_buffers(struct uvc_video_queue *queue); +extern int uvc_query_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf); +extern int uvc_queue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf); +extern int uvc_dequeue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf, int nonblocking); +extern int uvc_queue_enable(struct uvc_video_queue *queue, int enable); +extern void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect); +extern struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, + struct uvc_buffer *buf); +extern unsigned int uvc_queue_poll(struct uvc_video_queue *queue, + struct file *file, poll_table *wait); +static inline int uvc_queue_streaming(struct uvc_video_queue *queue) +{ + return queue->flags & UVC_QUEUE_STREAMING; +} + +/* V4L2 interface */ +extern struct file_operations uvc_fops; + +/* Video */ +extern int uvc_video_init(struct uvc_video_device *video); +extern int uvc_video_suspend(struct uvc_video_device *video); +extern int uvc_video_resume(struct uvc_video_device *video); +extern int uvc_video_enable(struct uvc_video_device *video, int enable); +extern int uvc_probe_video(struct uvc_video_device *video, + struct uvc_streaming_control *probe); +extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, + __u8 intfnum, __u8 cs, void *data, __u16 size); +extern int uvc_set_video_ctrl(struct uvc_video_device *video, + struct uvc_streaming_control *ctrl, int probe); + +/* Status */ +extern int uvc_status_init(struct uvc_device *dev); +extern void uvc_status_cleanup(struct uvc_device *dev); +extern int uvc_status_suspend(struct uvc_device *dev); +extern int uvc_status_resume(struct uvc_device *dev); + +/* Controls */ +extern struct uvc_control *uvc_find_control(struct uvc_video_device *video, + __u32 v4l2_id, struct uvc_control_mapping **mapping); +extern int uvc_query_v4l2_ctrl(struct uvc_video_device *video, + struct v4l2_queryctrl *v4l2_ctrl); + +extern int uvc_ctrl_add_info(struct uvc_control_info *info); +extern int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping); +extern int uvc_ctrl_init_device(struct uvc_device *dev); +extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); +extern int uvc_ctrl_resume_device(struct uvc_device *dev); +extern void uvc_ctrl_init(void); + +extern int uvc_ctrl_begin(struct uvc_video_device *video); +extern int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback); +static inline int uvc_ctrl_commit(struct uvc_video_device *video) +{ + return __uvc_ctrl_commit(video, 0); +} +static inline int uvc_ctrl_rollback(struct uvc_video_device *video) +{ + return __uvc_ctrl_commit(video, 1); +} + +extern int uvc_ctrl_get(struct uvc_video_device *video, + struct v4l2_ext_control *xctrl); +extern int uvc_ctrl_set(struct uvc_video_device *video, + struct v4l2_ext_control *xctrl); + +extern int uvc_xu_ctrl_query(struct uvc_video_device *video, + struct uvc_xu_control *ctrl, int set); + +/* Utility functions */ +extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, + unsigned int n_terms, unsigned int threshold); +extern uint32_t uvc_fraction_to_interval(uint32_t numerator, + uint32_t denominator); +extern struct usb_host_endpoint *uvc_find_endpoint( + struct usb_host_interface *alts, __u8 epaddr); + +/* Quirks support */ +void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, + struct uvc_buffer *buf); + +#endif /* __KERNEL__ */ + +#endif diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index 31e8af0ba27..67a661cf521 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -51,12 +51,51 @@ #define VIDEO_NUM_DEVICES 256 #define VIDEO_NAME "video4linux" +struct std_descr { + v4l2_std_id std; + const char *descr; +}; + +static const struct std_descr standards[] = { + { V4L2_STD_NTSC, "NTSC" }, + { V4L2_STD_NTSC_M, "NTSC-M" }, + { V4L2_STD_NTSC_M_JP, "NTSC-M-JP" }, + { V4L2_STD_NTSC_M_KR, "NTSC-M-KR" }, + { V4L2_STD_NTSC_443, "NTSC-443" }, + { V4L2_STD_PAL, "PAL" }, + { V4L2_STD_PAL_BG, "PAL-BG" }, + { V4L2_STD_PAL_B, "PAL-B" }, + { V4L2_STD_PAL_B1, "PAL-B1" }, + { V4L2_STD_PAL_G, "PAL-G" }, + { V4L2_STD_PAL_H, "PAL-H" }, + { V4L2_STD_PAL_I, "PAL-I" }, + { V4L2_STD_PAL_DK, "PAL-DK" }, + { V4L2_STD_PAL_D, "PAL-D" }, + { V4L2_STD_PAL_D1, "PAL-D1" }, + { V4L2_STD_PAL_K, "PAL-K" }, + { V4L2_STD_PAL_M, "PAL-M" }, + { V4L2_STD_PAL_N, "PAL-N" }, + { V4L2_STD_PAL_Nc, "PAL-Nc" }, + { V4L2_STD_PAL_60, "PAL-60" }, + { V4L2_STD_SECAM, "SECAM" }, + { V4L2_STD_SECAM_B, "SECAM-B" }, + { V4L2_STD_SECAM_G, "SECAM-G" }, + { V4L2_STD_SECAM_H, "SECAM-H" }, + { V4L2_STD_SECAM_DK, "SECAM-DK" }, + { V4L2_STD_SECAM_D, "SECAM-D" }, + { V4L2_STD_SECAM_K, "SECAM-K" }, + { V4L2_STD_SECAM_K1, "SECAM-K1" }, + { V4L2_STD_SECAM_L, "SECAM-L" }, + { V4L2_STD_SECAM_LC, "SECAM-Lc" }, + { 0, "Unknown" } +}; + /* video4linux standard ID conversion to standard name */ -char *v4l2_norm_to_name(v4l2_std_id id) +const char *v4l2_norm_to_name(v4l2_std_id id) { - char *name; u32 myid = id; + int i; /* HACK: ppc32 architecture doesn't have __ucmpdi2 function to handle 64 bit comparations. So, on that architecture, with some gcc @@ -64,110 +103,17 @@ char *v4l2_norm_to_name(v4l2_std_id id) */ BUG_ON(myid != id); - switch (myid) { - case V4L2_STD_PAL: - name = "PAL"; - break; - case V4L2_STD_PAL_BG: - name = "PAL-BG"; - break; - case V4L2_STD_PAL_DK: - name = "PAL-DK"; - break; - case V4L2_STD_PAL_B: - name = "PAL-B"; - break; - case V4L2_STD_PAL_B1: - name = "PAL-B1"; - break; - case V4L2_STD_PAL_G: - name = "PAL-G"; - break; - case V4L2_STD_PAL_H: - name = "PAL-H"; - break; - case V4L2_STD_PAL_I: - name = "PAL-I"; - break; - case V4L2_STD_PAL_D: - name = "PAL-D"; - break; - case V4L2_STD_PAL_D1: - name = "PAL-D1"; - break; - case V4L2_STD_PAL_K: - name = "PAL-K"; - break; - case V4L2_STD_PAL_M: - name = "PAL-M"; - break; - case V4L2_STD_PAL_N: - name = "PAL-N"; - break; - case V4L2_STD_PAL_Nc: - name = "PAL-Nc"; - break; - case V4L2_STD_PAL_60: - name = "PAL-60"; - break; - case V4L2_STD_NTSC: - name = "NTSC"; - break; - case V4L2_STD_NTSC_M: - name = "NTSC-M"; - break; - case V4L2_STD_NTSC_M_JP: - name = "NTSC-M-JP"; - break; - case V4L2_STD_NTSC_443: - name = "NTSC-443"; - break; - case V4L2_STD_NTSC_M_KR: - name = "NTSC-M-KR"; - break; - case V4L2_STD_SECAM: - name = "SECAM"; - break; - case V4L2_STD_SECAM_DK: - name = "SECAM-DK"; - break; - case V4L2_STD_SECAM_B: - name = "SECAM-B"; - break; - case V4L2_STD_SECAM_D: - name = "SECAM-D"; - break; - case V4L2_STD_SECAM_G: - name = "SECAM-G"; - break; - case V4L2_STD_SECAM_H: - name = "SECAM-H"; - break; - case V4L2_STD_SECAM_K: - name = "SECAM-K"; - break; - case V4L2_STD_SECAM_K1: - name = "SECAM-K1"; - break; - case V4L2_STD_SECAM_L: - name = "SECAM-L"; - break; - case V4L2_STD_SECAM_LC: - name = "SECAM-LC"; - break; - default: - name = "Unknown"; - break; - } - - return name; + for (i = 0; standards[i].std; i++) + if (myid == standards[i].std) + break; + return standards[i].descr; } EXPORT_SYMBOL(v4l2_norm_to_name); /* Fill in the fields of a v4l2_standard structure according to the 'id' and 'transmission' parameters. Returns negative on error. */ int v4l2_video_std_construct(struct v4l2_standard *vs, - int id, char *name) + int id, const char *name) { u32 index = vs->index; @@ -1218,95 +1164,40 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_ENUMSTD: { struct v4l2_standard *p = arg; - v4l2_std_id id = vfd->tvnorms,curr_id=0; - unsigned int index = p->index,i; - - if (index<0) { - ret=-EINVAL; - break; - } - - /* Return norm array on a canonical way */ - for (i=0;i<= index && id; i++) { - if ( (id & V4L2_STD_PAL) == V4L2_STD_PAL) { - curr_id = V4L2_STD_PAL; - } else if ( (id & V4L2_STD_PAL_BG) == V4L2_STD_PAL_BG) { - curr_id = V4L2_STD_PAL_BG; - } else if ( (id & V4L2_STD_PAL_DK) == V4L2_STD_PAL_DK) { - curr_id = V4L2_STD_PAL_DK; - } else if ( (id & V4L2_STD_PAL_B) == V4L2_STD_PAL_B) { - curr_id = V4L2_STD_PAL_B; - } else if ( (id & V4L2_STD_PAL_B1) == V4L2_STD_PAL_B1) { - curr_id = V4L2_STD_PAL_B1; - } else if ( (id & V4L2_STD_PAL_G) == V4L2_STD_PAL_G) { - curr_id = V4L2_STD_PAL_G; - } else if ( (id & V4L2_STD_PAL_H) == V4L2_STD_PAL_H) { - curr_id = V4L2_STD_PAL_H; - } else if ( (id & V4L2_STD_PAL_I) == V4L2_STD_PAL_I) { - curr_id = V4L2_STD_PAL_I; - } else if ( (id & V4L2_STD_PAL_D) == V4L2_STD_PAL_D) { - curr_id = V4L2_STD_PAL_D; - } else if ( (id & V4L2_STD_PAL_D1) == V4L2_STD_PAL_D1) { - curr_id = V4L2_STD_PAL_D1; - } else if ( (id & V4L2_STD_PAL_K) == V4L2_STD_PAL_K) { - curr_id = V4L2_STD_PAL_K; - } else if ( (id & V4L2_STD_PAL_M) == V4L2_STD_PAL_M) { - curr_id = V4L2_STD_PAL_M; - } else if ( (id & V4L2_STD_PAL_N) == V4L2_STD_PAL_N) { - curr_id = V4L2_STD_PAL_N; - } else if ( (id & V4L2_STD_PAL_Nc) == V4L2_STD_PAL_Nc) { - curr_id = V4L2_STD_PAL_Nc; - } else if ( (id & V4L2_STD_PAL_60) == V4L2_STD_PAL_60) { - curr_id = V4L2_STD_PAL_60; - } else if ( (id & V4L2_STD_NTSC) == V4L2_STD_NTSC) { - curr_id = V4L2_STD_NTSC; - } else if ( (id & V4L2_STD_NTSC_M) == V4L2_STD_NTSC_M) { - curr_id = V4L2_STD_NTSC_M; - } else if ( (id & V4L2_STD_NTSC_M_JP) == V4L2_STD_NTSC_M_JP) { - curr_id = V4L2_STD_NTSC_M_JP; - } else if ( (id & V4L2_STD_NTSC_443) == V4L2_STD_NTSC_443) { - curr_id = V4L2_STD_NTSC_443; - } else if ( (id & V4L2_STD_NTSC_M_KR) == V4L2_STD_NTSC_M_KR) { - curr_id = V4L2_STD_NTSC_M_KR; - } else if ( (id & V4L2_STD_SECAM) == V4L2_STD_SECAM) { - curr_id = V4L2_STD_SECAM; - } else if ( (id & V4L2_STD_SECAM_DK) == V4L2_STD_SECAM_DK) { - curr_id = V4L2_STD_SECAM_DK; - } else if ( (id & V4L2_STD_SECAM_B) == V4L2_STD_SECAM_B) { - curr_id = V4L2_STD_SECAM_B; - } else if ( (id & V4L2_STD_SECAM_D) == V4L2_STD_SECAM_D) { - curr_id = V4L2_STD_SECAM_D; - } else if ( (id & V4L2_STD_SECAM_G) == V4L2_STD_SECAM_G) { - curr_id = V4L2_STD_SECAM_G; - } else if ( (id & V4L2_STD_SECAM_H) == V4L2_STD_SECAM_H) { - curr_id = V4L2_STD_SECAM_H; - } else if ( (id & V4L2_STD_SECAM_K) == V4L2_STD_SECAM_K) { - curr_id = V4L2_STD_SECAM_K; - } else if ( (id & V4L2_STD_SECAM_K1) == V4L2_STD_SECAM_K1) { - curr_id = V4L2_STD_SECAM_K1; - } else if ( (id & V4L2_STD_SECAM_L) == V4L2_STD_SECAM_L) { - curr_id = V4L2_STD_SECAM_L; - } else if ( (id & V4L2_STD_SECAM_LC) == V4L2_STD_SECAM_LC) { - curr_id = V4L2_STD_SECAM_LC; - } else { + v4l2_std_id id = vfd->tvnorms, curr_id = 0; + unsigned int index = p->index, i, j = 0; + const char *descr = ""; + + /* Return norm array in a canonical way */ + for (i = 0; i <= index && id; i++) { + /* last std value in the standards array is 0, so this + while always ends there since (id & 0) == 0. */ + while ((id & standards[j].std) != standards[j].std) + j++; + curr_id = standards[j].std; + descr = standards[j].descr; + j++; + if (curr_id == 0) break; - } - id &= ~curr_id; + if (curr_id != V4L2_STD_PAL && + curr_id != V4L2_STD_SECAM && + curr_id != V4L2_STD_NTSC) + id &= ~curr_id; } - if (i<=index) + if (i <= index) return -EINVAL; - v4l2_video_std_construct(p, curr_id,v4l2_norm_to_name(curr_id)); + v4l2_video_std_construct(p, curr_id, descr); p->index = index; - dbgarg (cmd, "index=%d, id=%Ld, name=%s, fps=%d/%d, " + dbgarg(cmd, "index=%d, id=%Ld, name=%s, fps=%d/%d, " "framelines=%d\n", p->index, (unsigned long long)p->id, p->name, p->frameperiod.numerator, p->frameperiod.denominator, p->framelines); - ret=0; + ret = 0; break; } case VIDIOC_G_STD: diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 845be1864f6..5ff9a58b613 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -327,13 +327,14 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) int hmax = buf->vb.height; int wmax = buf->vb.width; struct timeval ts; - char *tmpbuf = kmalloc(wmax * 2, GFP_ATOMIC); + char *tmpbuf; void *vbuf = videobuf_to_vmalloc(&buf->vb); - if (!tmpbuf) + if (!vbuf) return; - if (!vbuf) + tmpbuf = kmalloc(wmax * 2, GFP_ATOMIC); + if (!tmpbuf) return; for (h = 0; h < hmax; h++) { diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 65210fca37e..d89475d3698 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -114,6 +114,7 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) unsigned int nob = data->blocks; unsigned long long clks; unsigned int timeout; + bool dalgn = 0; u32 dcmd; int i; @@ -152,6 +153,9 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) host->sg_cpu[i].dcmd = dcmd | length; if (length & 31 && !(data->flags & MMC_DATA_READ)) host->sg_cpu[i].dcmd |= DCMD_ENDIRQEN; + /* Not aligned to 8-byte boundary? */ + if (sg_dma_address(&data->sg[i]) & 0x7) + dalgn = 1; if (data->flags & MMC_DATA_READ) { host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO; host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]); @@ -165,6 +169,15 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) host->sg_cpu[host->dma_len - 1].ddadr = DDADR_STOP; wmb(); + /* + * The PXA27x DMA controller encounters overhead when working with + * unaligned (to 8-byte boundaries) data, so switch on byte alignment + * mode only if we have unaligned data. + */ + if (dalgn) + DALGN |= (1 << host->dma); + else + DALGN &= (1 << host->dma); DDADR(host->dma) = host->sg_dma; DCSR(host->dma) = DCSR_RUN; } diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 07c2048b230..b413aa6c246 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -55,6 +55,10 @@ static unsigned int debug_quirks = 0; #define SDHCI_QUIRK_32BIT_DMA_SIZE (1<<7) /* Controller needs to be reset after each request to stay stable */ #define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<8) +/* Controller needs voltage and power writes to happen separately */ +#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<9) +/* Controller has an off-by-one issue with timeout value */ +#define SDHCI_QUIRK_INCR_TIMEOUT_CONTROL (1<<10) static const struct pci_device_id pci_ids[] __devinitdata = { { @@ -115,7 +119,8 @@ static const struct pci_device_id pci_ids[] __devinitdata = { .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE | - SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS, + SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS | + SDHCI_QUIRK_BROKEN_DMA, }, { @@ -124,7 +129,17 @@ static const struct pci_device_id pci_ids[] __devinitdata = { .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE | - SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS, + SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS | + SDHCI_QUIRK_BROKEN_DMA, + }, + + { + .vendor = PCI_VENDOR_ID_MARVELL, + .device = PCI_DEVICE_ID_MARVELL_CAFE_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | + SDHCI_QUIRK_INCR_TIMEOUT_CONTROL, }, { @@ -469,6 +484,13 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) break; } + /* + * Compensate for an off-by-one error in the CaFe hardware; otherwise, + * a too-small count gives us interrupt timeouts. + */ + if ((host->chip->quirks & SDHCI_QUIRK_INCR_TIMEOUT_CONTROL)) + count++; + if (count >= 0xF) { printk(KERN_WARNING "%s: Too large timeout requested!\n", mmc_hostname(host->mmc)); @@ -774,6 +796,14 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) BUG(); } + /* + * At least the CaFe chip gets confused if we set the voltage + * and set turn on power at the same time, so set the voltage first. + */ + if ((host->chip->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)) + writeb(pwr & ~SDHCI_POWER_ON, + host->ioaddr + SDHCI_POWER_CONTROL); + writeb(pwr, host->ioaddr + SDHCI_POWER_CONTROL); out: diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index 2edda8cc7f9..aabad8ce745 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -1768,9 +1768,10 @@ vortex_timer(unsigned long data) case XCVR_MII: case XCVR_NWAY: { ok = 1; - spin_lock_bh(&vp->lock); + /* Interrupts are already disabled */ + spin_lock(&vp->lock); vortex_check_media(dev, 0); - spin_unlock_bh(&vp->lock); + spin_unlock(&vp->lock); } break; default: /* Other media types handled by Tx timeouts. */ diff --git a/drivers/net/atlx/atl1.c b/drivers/net/atlx/atl1.c index 99e0b4cdc56..3c798ae5c34 100644 --- a/drivers/net/atlx/atl1.c +++ b/drivers/net/atlx/atl1.c @@ -471,7 +471,6 @@ static int atl1_get_permanent_address(struct atl1_hw *hw) memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN); return 0; } - return 1; } /* see if SPI FLAGS exist ? */ diff --git a/drivers/net/e100.c b/drivers/net/e100.c index f3cba5e24ec..1037b133231 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -1803,6 +1803,8 @@ static int e100_rx_alloc_skb(struct nic *nic, struct rx *rx) if (rx->prev->skb) { struct rfd *prev_rfd = (struct rfd *)rx->prev->skb->data; put_unaligned_le32(rx->dma_addr, &prev_rfd->link); + pci_dma_sync_single_for_device(nic->pdev, rx->prev->dma_addr, + sizeof(struct rfd), PCI_DMA_TODEVICE); } return 0; diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c index 701531e72e7..a3f6a9c72ec 100644 --- a/drivers/net/e1000/e1000_ethtool.c +++ b/drivers/net/e1000/e1000_ethtool.c @@ -347,7 +347,7 @@ e1000_set_tso(struct net_device *netdev, u32 data) else netdev->features &= ~NETIF_F_TSO; - if (data) + if (data && (adapter->hw.mac_type > e1000_82547_rev_2)) netdev->features |= NETIF_F_TSO6; else netdev->features &= ~NETIF_F_TSO6; diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index cab1835173c..648a87bbf46 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -2535,7 +2535,8 @@ void e1000e_down(struct e1000_adapter *adapter) adapter->link_speed = 0; adapter->link_duplex = 0; - e1000e_reset(adapter); + if (!pci_channel_offline(adapter->pdev)) + e1000e_reset(adapter); e1000_clean_tx_ring(adapter); e1000_clean_rx_ring(adapter); diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c index 46a90e9ec56..c05cb159c77 100644 --- a/drivers/net/enc28j60.c +++ b/drivers/net/enc28j60.c @@ -400,26 +400,31 @@ enc28j60_packet_write(struct enc28j60_net *priv, int len, const u8 *data) mutex_unlock(&priv->lock); } -/* - * Wait until the PHY operation is complete. - */ -static int wait_phy_ready(struct enc28j60_net *priv) +static unsigned long msec20_to_jiffies; + +static int poll_ready(struct enc28j60_net *priv, u8 reg, u8 mask, u8 val) { - unsigned long timeout = jiffies + 20 * HZ / 1000; - int ret = 1; + unsigned long timeout = jiffies + msec20_to_jiffies; /* 20 msec timeout read */ - while (nolock_regb_read(priv, MISTAT) & MISTAT_BUSY) { + while ((nolock_regb_read(priv, reg) & mask) != val) { if (time_after(jiffies, timeout)) { if (netif_msg_drv(priv)) - printk(KERN_DEBUG DRV_NAME - ": PHY ready timeout!\n"); - ret = 0; - break; + dev_dbg(&priv->spi->dev, + "reg %02x ready timeout!\n", reg); + return -ETIMEDOUT; } cpu_relax(); } - return ret; + return 0; +} + +/* + * Wait until the PHY operation is complete. + */ +static int wait_phy_ready(struct enc28j60_net *priv) +{ + return poll_ready(priv, MISTAT, MISTAT_BUSY, 0) ? 0 : 1; } /* @@ -594,6 +599,32 @@ static void nolock_txfifo_init(struct enc28j60_net *priv, u16 start, u16 end) nolock_regw_write(priv, ETXNDL, end); } +/* + * Low power mode shrinks power consumption about 100x, so we'd like + * the chip to be in that mode whenever it's inactive. (However, we + * can't stay in lowpower mode during suspend with WOL active.) + */ +static void enc28j60_lowpower(struct enc28j60_net *priv, bool is_low) +{ + if (netif_msg_drv(priv)) + dev_dbg(&priv->spi->dev, "%s power...\n", + is_low ? "low" : "high"); + + mutex_lock(&priv->lock); + if (is_low) { + nolock_reg_bfclr(priv, ECON1, ECON1_RXEN); + poll_ready(priv, ESTAT, ESTAT_RXBUSY, 0); + poll_ready(priv, ECON1, ECON1_TXRTS, 0); + /* ECON2_VRPS was set during initialization */ + nolock_reg_bfset(priv, ECON2, ECON2_PWRSV); + } else { + nolock_reg_bfclr(priv, ECON2, ECON2_PWRSV); + poll_ready(priv, ESTAT, ESTAT_CLKRDY, ESTAT_CLKRDY); + /* caller sets ECON1_RXEN */ + } + mutex_unlock(&priv->lock); +} + static int enc28j60_hw_init(struct enc28j60_net *priv) { u8 reg; @@ -612,8 +643,8 @@ static int enc28j60_hw_init(struct enc28j60_net *priv) priv->tx_retry_count = 0; priv->max_pk_counter = 0; priv->rxfilter = RXFILTER_NORMAL; - /* enable address auto increment */ - nolock_regb_write(priv, ECON2, ECON2_AUTOINC); + /* enable address auto increment and voltage regulator powersave */ + nolock_regb_write(priv, ECON2, ECON2_AUTOINC | ECON2_VRPS); nolock_rxfifo_init(priv, RXSTART_INIT, RXEND_INIT); nolock_txfifo_init(priv, TXSTART_INIT, TXEND_INIT); @@ -690,7 +721,7 @@ static int enc28j60_hw_init(struct enc28j60_net *priv) static void enc28j60_hw_enable(struct enc28j60_net *priv) { - /* enable interrutps */ + /* enable interrupts */ if (netif_msg_hw(priv)) printk(KERN_DEBUG DRV_NAME ": %s() enabling interrupts.\n", __FUNCTION__); @@ -726,15 +757,12 @@ enc28j60_setlink(struct net_device *ndev, u8 autoneg, u16 speed, u8 duplex) int ret = 0; if (!priv->hw_enable) { - if (autoneg == AUTONEG_DISABLE && speed == SPEED_10) { + /* link is in low power mode now; duplex setting + * will take effect on next enc28j60_hw_init(). + */ + if (autoneg == AUTONEG_DISABLE && speed == SPEED_10) priv->full_duplex = (duplex == DUPLEX_FULL); - if (!enc28j60_hw_init(priv)) { - if (netif_msg_drv(priv)) - dev_err(&ndev->dev, - "hw_reset() failed\n"); - ret = -EINVAL; - } - } else { + else { if (netif_msg_link(priv)) dev_warn(&ndev->dev, "unsupported link setting\n"); @@ -1307,7 +1335,8 @@ static int enc28j60_net_open(struct net_device *dev) } return -EADDRNOTAVAIL; } - /* Reset the hardware here */ + /* Reset the hardware here (and take it out of low power mode) */ + enc28j60_lowpower(priv, false); enc28j60_hw_disable(priv); if (!enc28j60_hw_init(priv)) { if (netif_msg_ifup(priv)) @@ -1337,6 +1366,7 @@ static int enc28j60_net_close(struct net_device *dev) printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __FUNCTION__); enc28j60_hw_disable(priv); + enc28j60_lowpower(priv, true); netif_stop_queue(dev); return 0; @@ -1537,6 +1567,8 @@ static int __devinit enc28j60_probe(struct spi_device *spi) dev->watchdog_timeo = TX_TIMEOUT; SET_ETHTOOL_OPS(dev, &enc28j60_ethtool_ops); + enc28j60_lowpower(priv, true); + ret = register_netdev(dev); if (ret) { if (netif_msg_probe(priv)) @@ -1556,7 +1588,7 @@ error_alloc: return ret; } -static int enc28j60_remove(struct spi_device *spi) +static int __devexit enc28j60_remove(struct spi_device *spi) { struct enc28j60_net *priv = dev_get_drvdata(&spi->dev); @@ -1573,15 +1605,16 @@ static int enc28j60_remove(struct spi_device *spi) static struct spi_driver enc28j60_driver = { .driver = { .name = DRV_NAME, - .bus = &spi_bus_type, .owner = THIS_MODULE, - }, + }, .probe = enc28j60_probe, .remove = __devexit_p(enc28j60_remove), }; static int __init enc28j60_init(void) { + msec20_to_jiffies = msecs_to_jiffies(20); + return spi_register_driver(&enc28j60_driver); } diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c index 0b94833e23f..e8cfadefa4b 100644 --- a/drivers/net/hamradio/dmascc.c +++ b/drivers/net/hamradio/dmascc.c @@ -1077,8 +1077,6 @@ static inline void rx_off(struct scc_priv *priv) static void start_timer(struct scc_priv *priv, int t, int r15) { - unsigned long flags; - outb(priv->tmr_mode, priv->tmr_ctrl); if (t == 0) { tm_isr(priv); diff --git a/drivers/net/ibm_newemac/Kconfig b/drivers/net/ibm_newemac/Kconfig index 0d3e7380bad..70a3272ee99 100644 --- a/drivers/net/ibm_newemac/Kconfig +++ b/drivers/net/ibm_newemac/Kconfig @@ -1,6 +1,7 @@ config IBM_NEW_EMAC tristate "IBM EMAC Ethernet support" depends on PPC_DCR && PPC_MERGE + select CRC32 help This driver supports the IBM EMAC family of Ethernet controllers typically found on 4xx embedded PowerPC chips, but also on the diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index ae398f04c7b..e79a26a886c 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -718,7 +718,8 @@ void igb_down(struct igb_adapter *adapter) adapter->link_speed = 0; adapter->link_duplex = 0; - igb_reset(adapter); + if (!pci_channel_offline(adapter->pdev)) + igb_reset(adapter); igb_clean_all_tx_rings(adapter); igb_clean_all_rx_rings(adapter); } diff --git a/drivers/net/ipg.c b/drivers/net/ipg.c index 679a0826780..2c03f4e2ccc 100644 --- a/drivers/net/ipg.c +++ b/drivers/net/ipg.c @@ -1271,7 +1271,7 @@ static void ipg_nic_rx_with_end(struct net_device *dev, framelen = le64_to_cpu(rxfd->rfs) & IPG_RFS_RXFRAMELEN; - endframeLen = framelen - jumbo->current_size; + endframelen = framelen - jumbo->current_size; /* if (framelen > IPG_RXFRAG_SIZE) framelen=IPG_RXFRAG_SIZE; @@ -1279,8 +1279,8 @@ static void ipg_nic_rx_with_end(struct net_device *dev, if (framelen > IPG_RXSUPPORT_SIZE) dev_kfree_skb_irq(jumbo->skb); else { - memcpy(skb_put(jumbo->skb, endframeLen), - skb->data, endframeLen); + memcpy(skb_put(jumbo->skb, endframelen), + skb->data, endframelen); jumbo->skb->protocol = eth_type_trans(jumbo->skb, dev); @@ -1352,16 +1352,16 @@ static int ipg_nic_rx(struct net_device *dev) switch (ipg_nic_rx_check_frame_type(dev)) { case FRAME_WITH_START_WITH_END: - ipg_nic_rx_with_start_and_end(dev, tp, rxfd, entry); + ipg_nic_rx_with_start_and_end(dev, sp, rxfd, entry); break; case FRAME_WITH_START: - ipg_nic_rx_with_start(dev, tp, rxfd, entry); + ipg_nic_rx_with_start(dev, sp, rxfd, entry); break; case FRAME_WITH_END: - ipg_nic_rx_with_end(dev, tp, rxfd, entry); + ipg_nic_rx_with_end(dev, sp, rxfd, entry); break; case FRAME_NO_START_NO_END: - ipg_nic_rx_no_start_no_end(dev, tp, rxfd, entry); + ipg_nic_rx_no_start_no_end(dev, sp, rxfd, entry); break; } } @@ -1808,7 +1808,7 @@ static int ipg_nic_open(struct net_device *dev) /* initialize JUMBO Frame control variable */ sp->jumbo.found_start = 0; sp->jumbo.current_size = 0; - sp->jumbo.skb = 0; + sp->jumbo.skb = NULL; dev->mtu = IPG_TXFRAG_SIZE; #endif diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index 7b859220c25..8f046090115 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -1969,7 +1969,8 @@ void ixgbe_down(struct ixgbe_adapter *adapter) netif_carrier_off(netdev); netif_stop_queue(netdev); - ixgbe_reset(adapter); + if (!pci_channel_offline(adapter->pdev)) + ixgbe_reset(adapter); ixgbe_clean_all_tx_rings(adapter); ixgbe_clean_all_rx_rings(adapter); diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h index 8cb29f5b103..da4c4fb9706 100644 --- a/drivers/net/netxen/netxen_nic.h +++ b/drivers/net/netxen/netxen_nic.h @@ -776,7 +776,6 @@ struct netxen_hardware_context { u8 revision_id; u16 board_type; - u16 max_ports; struct netxen_board_info boardcfg; u32 xg_linkup; u32 qg_linksup; @@ -863,6 +862,7 @@ struct netxen_adapter { unsigned char mac_addr[ETH_ALEN]; int mtu; int portnum; + u8 physical_port; struct work_struct watchdog_task; struct timer_list watchdog_timer; @@ -1034,7 +1034,6 @@ int netxen_rom_se(struct netxen_adapter *adapter, int addr); /* Functions from netxen_nic_isr.c */ void netxen_initialize_adapter_sw(struct netxen_adapter *adapter); -void netxen_initialize_adapter_hw(struct netxen_adapter *adapter); void *netxen_alloc(struct pci_dev *pdev, size_t sz, dma_addr_t * ptr, struct pci_dev **used_dev); void netxen_initialize_adapter_ops(struct netxen_adapter *adapter); @@ -1077,20 +1076,6 @@ static const struct netxen_brdinfo netxen_boards[] = { #define NUM_SUPPORTED_BOARDS ARRAY_SIZE(netxen_boards) -static inline void get_brd_port_by_type(u32 type, int *ports) -{ - int i, found = 0; - for (i = 0; i < NUM_SUPPORTED_BOARDS; ++i) { - if (netxen_boards[i].brdtype == type) { - *ports = netxen_boards[i].ports; - found = 1; - break; - } - } - if (!found) - *ports = 0; -} - static inline void get_brd_name_by_type(u32 type, char *name) { int i, found = 0; @@ -1169,5 +1154,4 @@ extern int netxen_rom_fast_read(struct netxen_adapter *adapter, int addr, extern struct ethtool_ops netxen_nic_ethtool_ops; -extern int physical_port[]; /* physical port # from virtual port.*/ #endif /* __NETXEN_NIC_H_ */ diff --git a/drivers/net/netxen/netxen_nic_ethtool.c b/drivers/net/netxen/netxen_nic_ethtool.c index 6e98d830eef..723487bf200 100644 --- a/drivers/net/netxen/netxen_nic_ethtool.c +++ b/drivers/net/netxen/netxen_nic_ethtool.c @@ -369,7 +369,7 @@ netxen_nic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) for (i = 3; niu_registers[mode].reg[i - 3] != -1; i++) { /* GB: port specific registers */ if (mode == 0 && i >= 19) - window = physical_port[adapter->portnum] * + window = adapter->physical_port * NETXEN_NIC_PORT_WINDOW; NETXEN_NIC_LOCKED_READ_REG(niu_registers[mode]. @@ -527,7 +527,7 @@ netxen_nic_get_pauseparam(struct net_device *dev, { struct netxen_adapter *adapter = netdev_priv(dev); __u32 val; - int port = physical_port[adapter->portnum]; + int port = adapter->physical_port; if (adapter->ahw.board_type == NETXEN_NIC_GBE) { if ((port < 0) || (port > NETXEN_NIU_MAX_GBE_PORTS)) @@ -573,7 +573,7 @@ netxen_nic_set_pauseparam(struct net_device *dev, { struct netxen_adapter *adapter = netdev_priv(dev); __u32 val; - int port = physical_port[adapter->portnum]; + int port = adapter->physical_port; /* read mode */ if (adapter->ahw.board_type == NETXEN_NIC_GBE) { if ((port < 0) || (port > NETXEN_NIU_MAX_GBE_PORTS)) diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c index af735646825..c43d06b8de9 100644 --- a/drivers/net/netxen/netxen_nic_hw.c +++ b/drivers/net/netxen/netxen_nic_hw.c @@ -396,11 +396,8 @@ int netxen_nic_hw_resources(struct netxen_adapter *adapter) } adapter->intr_scheme = readl( NETXEN_CRB_NORMALIZE(adapter, CRB_NIC_CAPABILITIES_FW)); - printk(KERN_NOTICE "%s: FW capabilities:0x%x\n", netxen_nic_driver_name, - adapter->intr_scheme); adapter->msi_mode = readl( NETXEN_CRB_NORMALIZE(adapter, CRB_NIC_MSI_MODE_FW)); - DPRINTK(INFO, "Receive Peg ready too. starting stuff\n"); addr = netxen_alloc(adapter->ahw.pdev, sizeof(struct netxen_ring_ctx) + @@ -408,8 +405,6 @@ int netxen_nic_hw_resources(struct netxen_adapter *adapter) (dma_addr_t *) & adapter->ctx_desc_phys_addr, &adapter->ctx_desc_pdev); - printk(KERN_INFO "ctx_desc_phys_addr: 0x%llx\n", - (unsigned long long) adapter->ctx_desc_phys_addr); if (addr == NULL) { DPRINTK(ERR, "bad return from pci_alloc_consistent\n"); err = -ENOMEM; @@ -429,8 +424,6 @@ int netxen_nic_hw_resources(struct netxen_adapter *adapter) adapter->max_tx_desc_count, (dma_addr_t *) & hw->cmd_desc_phys_addr, &adapter->ahw.cmd_desc_pdev); - printk(KERN_INFO "cmd_desc_phys_addr: 0x%llx\n", - (unsigned long long) hw->cmd_desc_phys_addr); if (addr == NULL) { DPRINTK(ERR, "bad return from pci_alloc_consistent\n"); @@ -1032,15 +1025,15 @@ int netxen_nic_get_board_info(struct netxen_adapter *adapter) int netxen_nic_set_mtu_gb(struct netxen_adapter *adapter, int new_mtu) { netxen_nic_write_w0(adapter, - NETXEN_NIU_GB_MAX_FRAME_SIZE( - physical_port[adapter->portnum]), new_mtu); + NETXEN_NIU_GB_MAX_FRAME_SIZE(adapter->physical_port), + new_mtu); return 0; } int netxen_nic_set_mtu_xgb(struct netxen_adapter *adapter, int new_mtu) { new_mtu += NETXEN_NIU_HDRSIZE + NETXEN_NIU_TLRSIZE; - if (physical_port[adapter->portnum] == 0) + if (adapter->physical_port == 0) netxen_nic_write_w0(adapter, NETXEN_NIU_XGE_MAX_FRAME_SIZE, new_mtu); else @@ -1051,7 +1044,7 @@ int netxen_nic_set_mtu_xgb(struct netxen_adapter *adapter, int new_mtu) void netxen_nic_init_niu_gb(struct netxen_adapter *adapter) { - netxen_niu_gbe_init_port(adapter, physical_port[adapter->portnum]); + netxen_niu_gbe_init_port(adapter, adapter->physical_port); } void @@ -1127,7 +1120,6 @@ void netxen_nic_set_link_parameters(struct netxen_adapter *adapter) void netxen_nic_flash_print(struct netxen_adapter *adapter) { - int valid = 1; u32 fw_major = 0; u32 fw_minor = 0; u32 fw_build = 0; @@ -1137,70 +1129,62 @@ void netxen_nic_flash_print(struct netxen_adapter *adapter) __le32 *ptr32; struct netxen_board_info *board_info = &(adapter->ahw.boardcfg); - if (board_info->magic != NETXEN_BDINFO_MAGIC) { - printk - ("NetXen Unknown board config, Read 0x%x expected as 0x%x\n", - board_info->magic, NETXEN_BDINFO_MAGIC); - valid = 0; - } - if (board_info->header_version != NETXEN_BDINFO_VERSION) { - printk("NetXen Unknown board config version." - " Read %x, expected %x\n", - board_info->header_version, NETXEN_BDINFO_VERSION); - valid = 0; - } - if (valid) { - ptr32 = (u32 *)&serial_num; - addr = NETXEN_USER_START + - offsetof(struct netxen_new_user_info, serial_num); - for (i = 0; i < 8; i++) { - if (netxen_rom_fast_read(adapter, addr, ptr32) == -1) { - printk("%s: ERROR reading %s board userarea.\n", - netxen_nic_driver_name, - netxen_nic_driver_name); - return; - } - ptr32++; - addr += sizeof(u32); + + adapter->driver_mismatch = 0; + + ptr32 = (u32 *)&serial_num; + addr = NETXEN_USER_START + + offsetof(struct netxen_new_user_info, serial_num); + for (i = 0; i < 8; i++) { + if (netxen_rom_fast_read(adapter, addr, ptr32) == -1) { + printk("%s: ERROR reading %s board userarea.\n", + netxen_nic_driver_name, + netxen_nic_driver_name); + adapter->driver_mismatch = 1; + return; } + ptr32++; + addr += sizeof(u32); + } + + fw_major = readl(NETXEN_CRB_NORMALIZE(adapter, + NETXEN_FW_VERSION_MAJOR)); + fw_minor = readl(NETXEN_CRB_NORMALIZE(adapter, + NETXEN_FW_VERSION_MINOR)); + fw_build = + readl(NETXEN_CRB_NORMALIZE(adapter, NETXEN_FW_VERSION_SUB)); + if (adapter->portnum == 0) { get_brd_name_by_type(board_info->board_type, brd_name); printk("NetXen %s Board S/N %s Chip id 0x%x\n", - brd_name, serial_num, board_info->chip_id); - - printk("NetXen %s Board #%d, Chip id 0x%x\n", - board_info->board_type == 0x0b ? "XGB" : "GBE", - board_info->board_num, board_info->chip_id); - fw_major = readl(NETXEN_CRB_NORMALIZE(adapter, - NETXEN_FW_VERSION_MAJOR)); - fw_minor = readl(NETXEN_CRB_NORMALIZE(adapter, - NETXEN_FW_VERSION_MINOR)); - fw_build = - readl(NETXEN_CRB_NORMALIZE(adapter, NETXEN_FW_VERSION_SUB)); - - printk("NetXen Firmware version %d.%d.%d\n", fw_major, fw_minor, - fw_build); + brd_name, serial_num, board_info->chip_id); + printk("NetXen Firmware version %d.%d.%d\n", fw_major, + fw_minor, fw_build); } + if (fw_major != _NETXEN_NIC_LINUX_MAJOR) { - printk(KERN_ERR "The mismatch in driver version and firmware " - "version major number\n" - "Driver version major number = %d \t" - "Firmware version major number = %d \n", - _NETXEN_NIC_LINUX_MAJOR, fw_major); adapter->driver_mismatch = 1; } if (fw_minor != _NETXEN_NIC_LINUX_MINOR && fw_minor != (_NETXEN_NIC_LINUX_MINOR + 1)) { - printk(KERN_ERR "The mismatch in driver version and firmware " - "version minor number\n" - "Driver version minor number = %d \t" - "Firmware version minor number = %d \n", - _NETXEN_NIC_LINUX_MINOR, fw_minor); adapter->driver_mismatch = 1; } - if (adapter->driver_mismatch) - printk(KERN_INFO "Use the driver with version no %d.%d.xxx\n", - fw_major, fw_minor); + if (adapter->driver_mismatch) { + printk(KERN_ERR "%s: driver and firmware version mismatch\n", + adapter->netdev->name); + return; + } + + switch (adapter->ahw.board_type) { + case NETXEN_NIC_GBE: + dev_info(&adapter->pdev->dev, "%s: GbE port initialized\n", + adapter->netdev->name); + break; + case NETXEN_NIC_XGBE: + dev_info(&adapter->pdev->dev, "%s: XGbE port initialized\n", + adapter->netdev->name); + break; + } } diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c index 45fa33e0cb9..70d1b22ced2 100644 --- a/drivers/net/netxen/netxen_nic_init.c +++ b/drivers/net/netxen/netxen_nic_init.c @@ -203,21 +203,6 @@ void netxen_initialize_adapter_sw(struct netxen_adapter *adapter) } } -void netxen_initialize_adapter_hw(struct netxen_adapter *adapter) -{ - int ports = 0; - struct netxen_board_info *board_info = &(adapter->ahw.boardcfg); - - if (netxen_nic_get_board_info(adapter) != 0) - printk("%s: Error getting board config info.\n", - netxen_nic_driver_name); - get_brd_port_by_type(board_info->board_type, &ports); - if (ports == 0) - printk(KERN_ERR "%s: Unknown board type\n", - netxen_nic_driver_name); - adapter->ahw.max_ports = ports; -} - void netxen_initialize_adapter_ops(struct netxen_adapter *adapter) { switch (adapter->ahw.board_type) { @@ -765,18 +750,13 @@ int netxen_flash_unlock(struct netxen_adapter *adapter) int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose) { - int addr, val, status; + int addr, val; int n, i; int init_delay = 0; struct crb_addr_pair *buf; u32 off; /* resetall */ - status = netxen_nic_get_board_info(adapter); - if (status) - printk("%s: netxen_pinit_from_rom: Error getting board info\n", - netxen_nic_driver_name); - netxen_crb_writelit_adapter(adapter, NETXEN_ROMUSB_GLB_SW_RESET, NETXEN_ROMBUS_RESET); @@ -860,10 +840,10 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose) netxen_nic_pci_change_crbwindow(adapter, 1); } if (init_delay == 1) { - msleep(2000); + msleep(1000); init_delay = 0; } - msleep(20); + msleep(1); } kfree(buf); @@ -938,12 +918,28 @@ int netxen_initialize_adapter_offload(struct netxen_adapter *adapter) void netxen_free_adapter_offload(struct netxen_adapter *adapter) { + int i; + if (adapter->dummy_dma.addr) { - pci_free_consistent(adapter->ahw.pdev, + i = 100; + do { + if (dma_watchdog_shutdown_request(adapter) == 1) + break; + msleep(50); + if (dma_watchdog_shutdown_poll_result(adapter) == 1) + break; + } while (--i); + + if (i) { + pci_free_consistent(adapter->ahw.pdev, NETXEN_HOST_DUMMY_DMA_SIZE, adapter->dummy_dma.addr, adapter->dummy_dma.phys_addr); - adapter->dummy_dma.addr = NULL; + adapter->dummy_dma.addr = NULL; + } else { + printk(KERN_ERR "%s: dma_watchdog_shutdown failed\n", + adapter->netdev->name); + } } } diff --git a/drivers/net/netxen/netxen_nic_isr.c b/drivers/net/netxen/netxen_nic_isr.c index f487615f406..96cec41f901 100644 --- a/drivers/net/netxen/netxen_nic_isr.c +++ b/drivers/net/netxen/netxen_nic_isr.c @@ -145,7 +145,7 @@ static void netxen_nic_isr_other(struct netxen_adapter *adapter) /* verify the offset */ val = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_XG_STATE)); - val = val >> physical_port[adapter->portnum]; + val = val >> adapter->physical_port; if (val == adapter->ahw.qg_linksup) return; @@ -199,7 +199,7 @@ void netxen_nic_xgbe_handle_phy_intr(struct netxen_adapter *adapter) /* WINDOW = 1 */ val = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_XG_STATE)); - val >>= (physical_port[adapter->portnum] * 8); + val >>= (adapter->physical_port * 8); val &= 0xff; if (adapter->ahw.xg_linkup == 1 && val != XG_LINK_UP) { diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index 7144c255ce5..63cd67b931e 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -70,17 +70,19 @@ static void netxen_nic_poll_controller(struct net_device *netdev); static irqreturn_t netxen_intr(int irq, void *data); static irqreturn_t netxen_msi_intr(int irq, void *data); -int physical_port[] = {0, 1, 2, 3}; - /* PCI Device ID Table */ +#define ENTRY(device) \ + {PCI_DEVICE(0x4040, (device)), \ + .class = PCI_CLASS_NETWORK_ETHERNET << 8, .class_mask = ~0} + static struct pci_device_id netxen_pci_tbl[] __devinitdata = { - {PCI_DEVICE(0x4040, 0x0001)}, - {PCI_DEVICE(0x4040, 0x0002)}, - {PCI_DEVICE(0x4040, 0x0003)}, - {PCI_DEVICE(0x4040, 0x0004)}, - {PCI_DEVICE(0x4040, 0x0005)}, - {PCI_DEVICE(0x4040, 0x0024)}, - {PCI_DEVICE(0x4040, 0x0025)}, + ENTRY(0x0001), + ENTRY(0x0002), + ENTRY(0x0003), + ENTRY(0x0004), + ENTRY(0x0005), + ENTRY(0x0024), + ENTRY(0x0025), {0,} }; @@ -288,10 +290,11 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) int pci_func_id = PCI_FUNC(pdev->devfn); DECLARE_MAC_BUF(mac); - printk(KERN_INFO "%s \n", netxen_nic_driver_string); + if (pci_func_id == 0) + printk(KERN_INFO "%s \n", netxen_nic_driver_string); if (pdev->class != 0x020000) { - printk(KERN_ERR"NetXen function %d, class %x will not " + printk(KERN_DEBUG "NetXen function %d, class %x will not " "be enabled.\n",pci_func_id, pdev->class); return -ENODEV; } @@ -450,8 +453,12 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) */ adapter->curr_window = 255; - /* initialize the adapter */ - netxen_initialize_adapter_hw(adapter); + if (netxen_nic_get_board_info(adapter) != 0) { + printk("%s: Error getting board config info.\n", + netxen_nic_driver_name); + err = -EIO; + goto err_out_iounmap; + } /* * Adapter in our case is quad port so initialize it before @@ -530,17 +537,15 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netxen_initialize_adapter_sw(adapter); /* initialize the buffers in adapter */ /* Mezz cards have PCI function 0,2,3 enabled */ - if ((adapter->ahw.boardcfg.board_type == NETXEN_BRDTYPE_P2_SB31_10G_IMEZ) - && (pci_func_id >= 2)) + switch (adapter->ahw.boardcfg.board_type) { + case NETXEN_BRDTYPE_P2_SB31_10G_IMEZ: + case NETXEN_BRDTYPE_P2_SB31_10G_HMEZ: + if (pci_func_id >= 2) adapter->portnum = pci_func_id - 2; - -#ifdef CONFIG_IA64 - if(adapter->portnum == 0) { - netxen_pinit_from_rom(adapter, 0); - udelay(500); - netxen_load_firmware(adapter); + break; + default: + break; } -#endif init_timer(&adapter->watchdog_timer); adapter->ahw.xg_linkup = 0; @@ -613,11 +618,18 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = -ENODEV; goto err_out_free_dev; } + } else { + writel(0, NETXEN_CRB_NORMALIZE(adapter, + CRB_CMDPEG_STATE)); + netxen_pinit_from_rom(adapter, 0); + msleep(1); + netxen_load_firmware(adapter); + netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE); } /* clear the register for future unloads/loads */ writel(0, NETXEN_CRB_NORMALIZE(adapter, NETXEN_CAM_RAM(0x1fc))); - printk(KERN_INFO "State: 0x%0x\n", + dev_info(&pdev->dev, "cmdpeg state: 0x%0x\n", readl(NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE))); /* @@ -639,9 +651,10 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* * See if the firmware gave us a virtual-physical port mapping. */ + adapter->physical_port = adapter->portnum; i = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_V2P(adapter->portnum))); if (i != 0x55555555) - physical_port[adapter->portnum] = i; + adapter->physical_port = i; netif_carrier_off(netdev); netif_stop_queue(netdev); @@ -654,22 +667,9 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_free_dev; } + netxen_nic_flash_print(adapter); pci_set_drvdata(pdev, adapter); - switch (adapter->ahw.board_type) { - case NETXEN_NIC_GBE: - printk(KERN_INFO "%s: QUAD GbE board initialized\n", - netxen_nic_driver_name); - break; - - case NETXEN_NIC_XGBE: - printk(KERN_INFO "%s: XGbE board initialized\n", - netxen_nic_driver_name); - break; - } - - adapter->driver_mismatch = 0; - return 0; err_out_free_dev: @@ -760,55 +760,8 @@ static void __devexit netxen_nic_remove(struct pci_dev *pdev) vfree(adapter->cmd_buf_arr); - if (adapter->portnum == 0) { - if (init_firmware_done) { - i = 100; - do { - if (dma_watchdog_shutdown_request(adapter) == 1) - break; - msleep(100); - if (dma_watchdog_shutdown_poll_result(adapter) == 1) - break; - } while (--i); - - if (i == 0) - printk(KERN_ERR "%s: dma_watchdog_shutdown failed\n", - netdev->name); - - /* clear the register for future unloads/loads */ - writel(0, NETXEN_CRB_NORMALIZE(adapter, NETXEN_CAM_RAM(0x1fc))); - printk(KERN_INFO "State: 0x%0x\n", - readl(NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE))); - - /* leave the hw in the same state as reboot */ - writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE)); - netxen_pinit_from_rom(adapter, 0); - msleep(1); - netxen_load_firmware(adapter); - netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE); - } - - /* clear the register for future unloads/loads */ - writel(0, NETXEN_CRB_NORMALIZE(adapter, NETXEN_CAM_RAM(0x1fc))); - printk(KERN_INFO "State: 0x%0x\n", - readl(NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE))); - - i = 100; - do { - if (dma_watchdog_shutdown_request(adapter) == 1) - break; - msleep(100); - if (dma_watchdog_shutdown_poll_result(adapter) == 1) - break; - } while (--i); - - if (i) { - netxen_free_adapter_offload(adapter); - } else { - printk(KERN_ERR "%s: dma_watchdog_shutdown failed\n", - netdev->name); - } - } + if (adapter->portnum == 0) + netxen_free_adapter_offload(adapter); if (adapter->irq) free_irq(adapter->irq, adapter); @@ -840,13 +793,15 @@ static int netxen_nic_open(struct net_device *netdev) irq_handler_t handler; unsigned long flags = IRQF_SAMPLE_RANDOM; + if (adapter->driver_mismatch) + return -EIO; + if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC) { err = netxen_init_firmware(adapter); if (err != 0) { printk(KERN_ERR "Failed to init firmware\n"); return -EIO; } - netxen_nic_flash_print(adapter); /* setup all the resources for the Phantom... */ /* this include the descriptors for rcv, tx, and status */ @@ -895,14 +850,12 @@ static int netxen_nic_open(struct net_device *netdev) if (adapter->set_mtu) adapter->set_mtu(adapter, netdev->mtu); - if (!adapter->driver_mismatch) - mod_timer(&adapter->watchdog_timer, jiffies); + mod_timer(&adapter->watchdog_timer, jiffies); napi_enable(&adapter->napi); netxen_nic_enable_int(adapter); - if (!adapter->driver_mismatch) - netif_start_queue(netdev); + netif_start_queue(netdev); return 0; } diff --git a/drivers/net/netxen/netxen_nic_niu.c b/drivers/net/netxen/netxen_nic_niu.c index 1c852a76c80..a3bc7cc67a6 100644 --- a/drivers/net/netxen/netxen_nic_niu.c +++ b/drivers/net/netxen/netxen_nic_niu.c @@ -94,7 +94,7 @@ int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long reg, long timeout = 0; long result = 0; long restore = 0; - long phy = physical_port[adapter->portnum]; + long phy = adapter->physical_port; __u32 address; __u32 command; __u32 status; @@ -190,7 +190,7 @@ int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter, long reg, long timeout = 0; long result = 0; long restore = 0; - long phy = physical_port[adapter->portnum]; + long phy = adapter->physical_port; __u32 address; __u32 command; __u32 status; @@ -456,7 +456,7 @@ int netxen_niu_gbe_init_port(struct netxen_adapter *adapter, int port) int netxen_niu_xg_init_port(struct netxen_adapter *adapter, int port) { - u32 portnum = physical_port[adapter->portnum]; + u32 portnum = adapter->physical_port; netxen_crb_writelit_adapter(adapter, NETXEN_NIU_XGE_CONFIG_1+(0x10000*portnum), 0x1447); @@ -573,7 +573,7 @@ static int netxen_niu_macaddr_get(struct netxen_adapter *adapter, { u32 stationhigh; u32 stationlow; - int phy = physical_port[adapter->portnum]; + int phy = adapter->physical_port; u8 val[8]; if (addr == NULL) @@ -604,7 +604,7 @@ int netxen_niu_macaddr_set(struct netxen_adapter *adapter, { u8 temp[4]; u32 val; - int phy = physical_port[adapter->portnum]; + int phy = adapter->physical_port; unsigned char mac_addr[6]; int i; DECLARE_MAC_BUF(mac); @@ -724,7 +724,7 @@ int netxen_niu_enable_gbe_port(struct netxen_adapter *adapter, int netxen_niu_disable_gbe_port(struct netxen_adapter *adapter) { __u32 mac_cfg0; - u32 port = physical_port[adapter->portnum]; + u32 port = adapter->physical_port; if (port > NETXEN_NIU_MAX_GBE_PORTS) return -EINVAL; @@ -740,7 +740,7 @@ int netxen_niu_disable_gbe_port(struct netxen_adapter *adapter) int netxen_niu_disable_xg_port(struct netxen_adapter *adapter) { __u32 mac_cfg; - u32 port = physical_port[adapter->portnum]; + u32 port = adapter->physical_port; if (port > NETXEN_NIU_MAX_XG_PORTS) return -EINVAL; @@ -757,7 +757,7 @@ int netxen_niu_set_promiscuous_mode(struct netxen_adapter *adapter, netxen_niu_prom_mode_t mode) { __u32 reg; - u32 port = physical_port[adapter->portnum]; + u32 port = adapter->physical_port; if (port > NETXEN_NIU_MAX_GBE_PORTS) return -EINVAL; @@ -814,7 +814,7 @@ int netxen_niu_set_promiscuous_mode(struct netxen_adapter *adapter, int netxen_niu_xg_macaddr_set(struct netxen_adapter *adapter, netxen_ethernet_macaddr_t addr) { - int phy = physical_port[adapter->portnum]; + int phy = adapter->physical_port; u8 temp[4]; u32 val; @@ -867,7 +867,7 @@ int netxen_niu_xg_macaddr_set(struct netxen_adapter *adapter, int netxen_niu_xg_macaddr_get(struct netxen_adapter *adapter, netxen_ethernet_macaddr_t * addr) { - int phy = physical_port[adapter->portnum]; + int phy = adapter->physical_port; u32 stationhigh; u32 stationlow; u8 val[8]; @@ -896,7 +896,7 @@ int netxen_niu_xg_set_promiscuous_mode(struct netxen_adapter *adapter, netxen_niu_prom_mode_t mode) { __u32 reg; - u32 port = physical_port[adapter->portnum]; + u32 port = adapter->physical_port; if (port > NETXEN_NIU_MAX_XG_PORTS) return -EINVAL; diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c index ce95c5d168f..70d012e90dc 100644 --- a/drivers/net/pcmcia/axnet_cs.c +++ b/drivers/net/pcmcia/axnet_cs.c @@ -525,12 +525,14 @@ static int axnet_open(struct net_device *dev) int ret; axnet_dev_t *info = PRIV(dev); struct pcmcia_device *link = info->p_dev; + unsigned int nic_base = dev->base_addr; DEBUG(2, "axnet_open('%s')\n", dev->name); if (!pcmcia_dev_present(link)) return -ENODEV; + outb_p(0xFF, nic_base + EN0_ISR); /* Clear bogus intr. */ ret = request_irq(dev->irq, ei_irq_wrapper, IRQF_SHARED, "axnet_cs", dev); if (ret) return ret; diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c index fd8158a86f6..2d4c4ad89b8 100644 --- a/drivers/net/pcmcia/pcnet_cs.c +++ b/drivers/net/pcmcia/pcnet_cs.c @@ -969,6 +969,7 @@ static int pcnet_open(struct net_device *dev) int ret; pcnet_dev_t *info = PRIV(dev); struct pcmcia_device *link = info->p_dev; + unsigned int nic_base = dev->base_addr; DEBUG(2, "pcnet_open('%s')\n", dev->name); @@ -976,6 +977,8 @@ static int pcnet_open(struct net_device *dev) return -ENODEV; set_misc_reg(dev); + + outb_p(0xFF, nic_base + EN0_ISR); /* Clear bogus intr. */ ret = request_irq(dev->irq, ei_irq_wrapper, IRQF_SHARED, dev_info, dev); if (ret) return ret; diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c index bafb69b6f7c..fc6f4b8c64b 100644 --- a/drivers/net/pppoe.c +++ b/drivers/net/pppoe.c @@ -942,7 +942,7 @@ static int pppoe_recvmsg(struct kiocb *iocb, struct socket *sock, m->msg_namelen = 0; if (skb) { - total_len = min(total_len, skb->len); + total_len = min_t(size_t, total_len, skb->len); error = skb_copy_datagram_iovec(skb, 0, m->msg_iov, total_len); if (error == 0) error = total_len; diff --git a/drivers/net/qla3xxx.c b/drivers/net/qla3xxx.c index b7f7b2227d5..bccee68bd48 100644 --- a/drivers/net/qla3xxx.c +++ b/drivers/net/qla3xxx.c @@ -3701,7 +3701,9 @@ static int ql_cycle_adapter(struct ql3_adapter *qdev, int reset) printk(KERN_ERR PFX "%s: Driver up/down cycle failed, " "closing device\n",qdev->ndev->name); + rtnl_lock(); dev_close(qdev->ndev); + rtnl_unlock(); return -1; } return 0; diff --git a/drivers/net/r6040.c b/drivers/net/r6040.c index 858b191517b..504a48ff73c 100644 --- a/drivers/net/r6040.c +++ b/drivers/net/r6040.c @@ -273,7 +273,7 @@ static void r6040_init_ring_desc(struct r6040_descriptor *desc_ring, dma_addr_t mapping = desc_dma; while (size-- > 0) { - mapping += sizeof(sizeof(*desc)); + mapping += sizeof(*desc); desc->ndesc = cpu_to_le32(mapping); desc->vndescp = desc + 1; desc++; diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index b5c1e663417..ae7b697456b 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -2625,9 +2625,7 @@ static int fill_rx_buffers(struct ring_info *ring) rxdp1->Buffer0_ptr = pci_map_single (ring->pdev, skb->data, size - NET_IP_ALIGN, PCI_DMA_FROMDEVICE); - if( (rxdp1->Buffer0_ptr == 0) || - (rxdp1->Buffer0_ptr == - DMA_ERROR_CODE)) + if(pci_dma_mapping_error(rxdp1->Buffer0_ptr)) goto pci_map_failed; rxdp->Control_2 = @@ -2657,6 +2655,7 @@ static int fill_rx_buffers(struct ring_info *ring) skb->data = (void *) (unsigned long)tmp; skb_reset_tail_pointer(skb); + /* AK: check is wrong. 0 can be valid dma address */ if (!(rxdp3->Buffer0_ptr)) rxdp3->Buffer0_ptr = pci_map_single(ring->pdev, ba->ba_0, @@ -2665,8 +2664,7 @@ static int fill_rx_buffers(struct ring_info *ring) pci_dma_sync_single_for_device(ring->pdev, (dma_addr_t) rxdp3->Buffer0_ptr, BUF0_LEN, PCI_DMA_FROMDEVICE); - if( (rxdp3->Buffer0_ptr == 0) || - (rxdp3->Buffer0_ptr == DMA_ERROR_CODE)) + if (pci_dma_mapping_error(rxdp3->Buffer0_ptr)) goto pci_map_failed; rxdp->Control_2 = SET_BUFFER0_SIZE_3(BUF0_LEN); @@ -2681,18 +2679,17 @@ static int fill_rx_buffers(struct ring_info *ring) (ring->pdev, skb->data, ring->mtu + 4, PCI_DMA_FROMDEVICE); - if( (rxdp3->Buffer2_ptr == 0) || - (rxdp3->Buffer2_ptr == DMA_ERROR_CODE)) + if (pci_dma_mapping_error(rxdp3->Buffer2_ptr)) goto pci_map_failed; + /* AK: check is wrong */ if (!rxdp3->Buffer1_ptr) rxdp3->Buffer1_ptr = pci_map_single(ring->pdev, ba->ba_1, BUF1_LEN, PCI_DMA_FROMDEVICE); - if( (rxdp3->Buffer1_ptr == 0) || - (rxdp3->Buffer1_ptr == DMA_ERROR_CODE)) { + if (pci_dma_mapping_error(rxdp3->Buffer1_ptr)) { pci_unmap_single (ring->pdev, (dma_addr_t)(unsigned long) @@ -4264,16 +4261,14 @@ static int s2io_xmit(struct sk_buff *skb, struct net_device *dev) txdp->Buffer_Pointer = pci_map_single(sp->pdev, fifo->ufo_in_band_v, sizeof(u64), PCI_DMA_TODEVICE); - if((txdp->Buffer_Pointer == 0) || - (txdp->Buffer_Pointer == DMA_ERROR_CODE)) + if (pci_dma_mapping_error(txdp->Buffer_Pointer)) goto pci_map_failed; txdp++; } txdp->Buffer_Pointer = pci_map_single (sp->pdev, skb->data, frg_len, PCI_DMA_TODEVICE); - if((txdp->Buffer_Pointer == 0) || - (txdp->Buffer_Pointer == DMA_ERROR_CODE)) + if (pci_dma_mapping_error(txdp->Buffer_Pointer)) goto pci_map_failed; txdp->Host_Control = (unsigned long) skb; @@ -6884,10 +6879,8 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp, pci_map_single( sp->pdev, (*skb)->data, size - NET_IP_ALIGN, PCI_DMA_FROMDEVICE); - if( (rxdp1->Buffer0_ptr == 0) || - (rxdp1->Buffer0_ptr == DMA_ERROR_CODE)) { + if (pci_dma_mapping_error(rxdp1->Buffer0_ptr)) goto memalloc_failed; - } rxdp->Host_Control = (unsigned long) (*skb); } } else if ((sp->rxd_mode == RXD_MODE_3B) && (rxdp->Host_Control == 0)) { @@ -6913,15 +6906,12 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp, pci_map_single(sp->pdev, (*skb)->data, dev->mtu + 4, PCI_DMA_FROMDEVICE); - if( (rxdp3->Buffer2_ptr == 0) || - (rxdp3->Buffer2_ptr == DMA_ERROR_CODE)) { + if (pci_dma_mapping_error(rxdp3->Buffer2_ptr)) goto memalloc_failed; - } rxdp3->Buffer0_ptr = *temp0 = pci_map_single( sp->pdev, ba->ba_0, BUF0_LEN, PCI_DMA_FROMDEVICE); - if( (rxdp3->Buffer0_ptr == 0) || - (rxdp3->Buffer0_ptr == DMA_ERROR_CODE)) { + if (pci_dma_mapping_error(rxdp3->Buffer0_ptr)) { pci_unmap_single (sp->pdev, (dma_addr_t)rxdp3->Buffer2_ptr, dev->mtu + 4, PCI_DMA_FROMDEVICE); @@ -6933,8 +6923,7 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp, rxdp3->Buffer1_ptr = *temp1 = pci_map_single(sp->pdev, ba->ba_1, BUF1_LEN, PCI_DMA_FROMDEVICE); - if( (rxdp3->Buffer1_ptr == 0) || - (rxdp3->Buffer1_ptr == DMA_ERROR_CODE)) { + if (pci_dma_mapping_error(rxdp3->Buffer1_ptr)) { pci_unmap_single (sp->pdev, (dma_addr_t)rxdp3->Buffer0_ptr, BUF0_LEN, PCI_DMA_FROMDEVICE); diff --git a/drivers/net/s2io.h b/drivers/net/s2io.h index 4706f7f9acb..1827b6686c9 100644 --- a/drivers/net/s2io.h +++ b/drivers/net/s2io.h @@ -75,10 +75,6 @@ static int debug_level = ERR_DBG; /* DEBUG message print. */ #define DBG_PRINT(dbg_level, args...) if(!(debug_level<dbg_level)) printk(args) -#ifndef DMA_ERROR_CODE -#define DMA_ERROR_CODE (~(dma_addr_t)0x0) -#endif - /* Protocol assist features of the NIC */ #define L3_CKSUM_OK 0xFFFF #define L4_CKSUM_OK 0xFFFF diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 62436b3a18c..c8a5ef2d75f 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -118,6 +118,7 @@ static const struct pci_device_id sky2_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4352) }, /* 88E8038 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4353) }, /* 88E8039 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4354) }, /* 88E8040 */ + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4355) }, /* 88E8040T */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4356) }, /* 88EC033 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4357) }, /* 88E8042 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x435A) }, /* 88E8048 */ diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c index 10e4e85da3f..b07b8cbadea 100644 --- a/drivers/net/tc35815.c +++ b/drivers/net/tc35815.c @@ -1394,6 +1394,7 @@ tc35815_open(struct net_device *dev) tc35815_chip_init(dev); spin_unlock_irq(&lp->lock); + netif_carrier_off(dev); /* schedule a link state check */ phy_start(lp->phy_dev); @@ -1735,7 +1736,6 @@ tc35815_rx(struct net_device *dev) skb = lp->rx_skbs[cur_bd].skb; prefetch(skb->data); lp->rx_skbs[cur_bd].skb = NULL; - lp->fbl_count--; pci_unmap_single(lp->pci_dev, lp->rx_skbs[cur_bd].skb_dma, RX_BUF_SIZE, PCI_DMA_FROMDEVICE); @@ -1791,6 +1791,7 @@ tc35815_rx(struct net_device *dev) #ifdef TC35815_USE_PACKEDBUFFER while (lp->fbl_curid != id) #else + lp->fbl_count--; while (lp->fbl_count < RX_BUF_NUM) #endif { @@ -2453,6 +2454,7 @@ static int tc35815_resume(struct pci_dev *pdev) return 0; pci_set_power_state(pdev, PCI_D0); tc35815_restart(dev); + netif_carrier_off(dev); if (lp->phy_dev) phy_start(lp->phy_dev); netif_device_attach(dev); diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 0ce07a339c7..7ab94c825b5 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -313,6 +313,21 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv, switch (tun->flags & TUN_TYPE_MASK) { case TUN_TUN_DEV: + if (tun->flags & TUN_NO_PI) { + switch (skb->data[0] & 0xf0) { + case 0x40: + pi.proto = htons(ETH_P_IP); + break; + case 0x60: + pi.proto = htons(ETH_P_IPV6); + break; + default: + tun->dev->stats.rx_dropped++; + kfree_skb(skb); + return -EINVAL; + } + } + skb_reset_mac_header(skb); skb->protocol = pi.proto; skb->dev = tun->dev; diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c index 249e18053d5..069f8bb0a99 100644 --- a/drivers/net/wan/x25_asy.c +++ b/drivers/net/wan/x25_asy.c @@ -32,6 +32,7 @@ #include <linux/x25.h> #include <linux/lapb.h> #include <linux/init.h> +#include <linux/rtnetlink.h> #include "x25_asy.h" #include <net/x25device.h> @@ -601,8 +602,10 @@ static void x25_asy_close_tty(struct tty_struct *tty) if (!sl || sl->magic != X25_ASY_MAGIC) return; + rtnl_lock(); if (sl->dev->flags & IFF_UP) dev_close(sl->dev); + rtnl_unlock(); tty->disc_data = NULL; sl->tty = NULL; diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index dfa4bdd5597..d3db298c05f 100644 --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h @@ -630,7 +630,6 @@ struct b43_pio { /* Context information for a noise calculation (Link Quality). */ struct b43_noise_calculation { - u8 channel_at_start; bool calculation_running; u8 nr_samples; s8 samples[8][4]; diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c index 6dcbb3c87e7..e23f2f172bd 100644 --- a/drivers/net/wireless/b43/dma.c +++ b/drivers/net/wireless/b43/dma.c @@ -795,24 +795,49 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, { struct b43_dmaring *ring; int err; - int nr_slots; dma_addr_t dma_test; ring = kzalloc(sizeof(*ring), GFP_KERNEL); if (!ring) goto out; - ring->type = type; - nr_slots = B43_RXRING_SLOTS; + ring->nr_slots = B43_RXRING_SLOTS; if (for_tx) - nr_slots = B43_TXRING_SLOTS; + ring->nr_slots = B43_TXRING_SLOTS; - ring->meta = kcalloc(nr_slots, sizeof(struct b43_dmadesc_meta), + ring->meta = kcalloc(ring->nr_slots, sizeof(struct b43_dmadesc_meta), GFP_KERNEL); if (!ring->meta) goto err_kfree_ring; + + ring->type = type; + ring->dev = dev; + ring->mmio_base = b43_dmacontroller_base(type, controller_index); + ring->index = controller_index; + if (type == B43_DMA_64BIT) + ring->ops = &dma64_ops; + else + ring->ops = &dma32_ops; if (for_tx) { - ring->txhdr_cache = kcalloc(nr_slots, + ring->tx = 1; + ring->current_slot = -1; + } else { + if (ring->index == 0) { + ring->rx_buffersize = B43_DMA0_RX_BUFFERSIZE; + ring->frameoffset = B43_DMA0_RX_FRAMEOFFSET; + } else if (ring->index == 3) { + ring->rx_buffersize = B43_DMA3_RX_BUFFERSIZE; + ring->frameoffset = B43_DMA3_RX_FRAMEOFFSET; + } else + B43_WARN_ON(1); + } + spin_lock_init(&ring->lock); +#ifdef CONFIG_B43_DEBUG + ring->last_injected_overflow = jiffies; +#endif + + if (for_tx) { + ring->txhdr_cache = kcalloc(ring->nr_slots, b43_txhdr_size(dev), GFP_KERNEL); if (!ring->txhdr_cache) @@ -828,7 +853,7 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, b43_txhdr_size(dev), 1)) { /* ugh realloc */ kfree(ring->txhdr_cache); - ring->txhdr_cache = kcalloc(nr_slots, + ring->txhdr_cache = kcalloc(ring->nr_slots, b43_txhdr_size(dev), GFP_KERNEL | GFP_DMA); if (!ring->txhdr_cache) @@ -853,32 +878,6 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, DMA_TO_DEVICE); } - ring->dev = dev; - ring->nr_slots = nr_slots; - ring->mmio_base = b43_dmacontroller_base(type, controller_index); - ring->index = controller_index; - if (type == B43_DMA_64BIT) - ring->ops = &dma64_ops; - else - ring->ops = &dma32_ops; - if (for_tx) { - ring->tx = 1; - ring->current_slot = -1; - } else { - if (ring->index == 0) { - ring->rx_buffersize = B43_DMA0_RX_BUFFERSIZE; - ring->frameoffset = B43_DMA0_RX_FRAMEOFFSET; - } else if (ring->index == 3) { - ring->rx_buffersize = B43_DMA3_RX_BUFFERSIZE; - ring->frameoffset = B43_DMA3_RX_FRAMEOFFSET; - } else - B43_WARN_ON(1); - } - spin_lock_init(&ring->lock); -#ifdef CONFIG_B43_DEBUG - ring->last_injected_overflow = jiffies; -#endif - err = alloc_ringmemory(ring); if (err) goto err_kfree_txhdr_cache; diff --git a/drivers/net/wireless/b43/leds.c b/drivers/net/wireless/b43/leds.c index 36a9c42df83..76f4c7bad8b 100644 --- a/drivers/net/wireless/b43/leds.c +++ b/drivers/net/wireless/b43/leds.c @@ -72,6 +72,9 @@ static void b43_led_brightness_set(struct led_classdev *led_dev, struct b43_wldev *dev = led->dev; bool radio_enabled; + if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED)) + return; + /* Checking the radio-enabled status here is slightly racy, * but we want to avoid the locking overhead and we don't care * whether the LED has the wrong state for a second. */ diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 6c3d9ea0a9f..a7082779308 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -1145,7 +1145,6 @@ static void b43_generate_noise_sample(struct b43_wldev *dev) b43_jssi_write(dev, 0x7F7F7F7F); b43_write32(dev, B43_MMIO_MACCMD, b43_read32(dev, B43_MMIO_MACCMD) | B43_MACCMD_BGNOISE); - B43_WARN_ON(dev->noisecalc.channel_at_start != dev->phy.channel); } static void b43_calculate_link_quality(struct b43_wldev *dev) @@ -1154,7 +1153,6 @@ static void b43_calculate_link_quality(struct b43_wldev *dev) if (dev->noisecalc.calculation_running) return; - dev->noisecalc.channel_at_start = dev->phy.channel; dev->noisecalc.calculation_running = 1; dev->noisecalc.nr_samples = 0; @@ -1171,9 +1169,16 @@ static void handle_irq_noise(struct b43_wldev *dev) /* Bottom half of Link Quality calculation. */ + /* Possible race condition: It might be possible that the user + * changed to a different channel in the meantime since we + * started the calculation. We ignore that fact, since it's + * not really that much of a problem. The background noise is + * an estimation only anyway. Slightly wrong results will get damped + * by the averaging of the 8 sample rounds. Additionally the + * value is shortlived. So it will be replaced by the next noise + * calculation round soon. */ + B43_WARN_ON(!dev->noisecalc.calculation_running); - if (dev->noisecalc.channel_at_start != phy->channel) - goto drop_calculation; *((__le32 *)noise) = cpu_to_le32(b43_jssi_read(dev)); if (noise[0] == 0x7F || noise[1] == 0x7F || noise[2] == 0x7F || noise[3] == 0x7F) @@ -1214,11 +1219,10 @@ static void handle_irq_noise(struct b43_wldev *dev) average -= 48; dev->stats.link_noise = average; - drop_calculation: dev->noisecalc.calculation_running = 0; return; } - generate_new: +generate_new: b43_generate_noise_sample(dev); } @@ -2879,12 +2883,11 @@ static int b43_op_tx(struct ieee80211_hw *hw, if (unlikely(skb->len < 2 + 2 + 6)) { /* Too short, this can't be a valid frame. */ - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; + goto drop_packet; } B43_WARN_ON(skb_shinfo(skb)->nr_frags); if (unlikely(!dev)) - return NETDEV_TX_BUSY; + goto drop_packet; /* Transmissions on seperate queues can run concurrently. */ read_lock_irqsave(&wl->tx_lock, flags); @@ -2900,7 +2903,12 @@ static int b43_op_tx(struct ieee80211_hw *hw, read_unlock_irqrestore(&wl->tx_lock, flags); if (unlikely(err)) - return NETDEV_TX_BUSY; + goto drop_packet; + return NETDEV_TX_OK; + +drop_packet: + /* We can not transmit this packet. Drop it. */ + dev_kfree_skb_any(skb); return NETDEV_TX_OK; } diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c index c990f87b107..93ddc1cbcc8 100644 --- a/drivers/net/wireless/b43legacy/dma.c +++ b/drivers/net/wireless/b43legacy/dma.c @@ -876,6 +876,7 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, if (!ring) goto out; ring->type = type; + ring->dev = dev; nr_slots = B43legacy_RXRING_SLOTS; if (for_tx) @@ -922,7 +923,6 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, DMA_TO_DEVICE); } - ring->dev = dev; ring->nr_slots = nr_slots; ring->mmio_base = b43legacy_dmacontroller_base(type, controller_index); ring->index = controller_index; diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 204077c1387..3e612d0a13e 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -2378,8 +2378,10 @@ static int b43legacy_op_tx(struct ieee80211_hw *hw, } else err = b43legacy_dma_tx(dev, skb, ctl); out: - if (unlikely(err)) - return NETDEV_TX_BUSY; + if (unlikely(err)) { + /* Drop the packet. */ + dev_kfree_skb_any(skb); + } return NETDEV_TX_OK; } diff --git a/drivers/net/wireless/hostap/hostap_80211_rx.c b/drivers/net/wireless/hostap/hostap_80211_rx.c index 4fd73809602..020f450e9db 100644 --- a/drivers/net/wireless/hostap/hostap_80211_rx.c +++ b/drivers/net/wireless/hostap/hostap_80211_rx.c @@ -64,7 +64,7 @@ int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb, int hdrlen, phdrlen, head_need, tail_need; u16 fc; int prism_header, ret; - struct ieee80211_hdr_4addr *hdr; + struct ieee80211_hdr_4addr *fhdr; iface = netdev_priv(dev); local = iface->local; @@ -83,8 +83,8 @@ int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb, phdrlen = 0; } - hdr = (struct ieee80211_hdr_4addr *) skb->data; - fc = le16_to_cpu(hdr->frame_ctl); + fhdr = (struct ieee80211_hdr_4addr *) skb->data; + fc = le16_to_cpu(fhdr->frame_ctl); if (type == PRISM2_RX_MGMT && (fc & IEEE80211_FCTL_VERS)) { printk(KERN_DEBUG "%s: dropped management frame with header " @@ -551,7 +551,7 @@ hostap_rx_frame_wds(local_info_t *local, struct ieee80211_hdr_4addr *hdr, hdr->addr1[2] != 0xff || hdr->addr1[3] != 0xff || hdr->addr1[4] != 0xff || hdr->addr1[5] != 0xff)) { /* RA (or BSSID) is not ours - drop */ - PDEBUG(DEBUG_EXTRA, "%s: received WDS frame with " + PDEBUG(DEBUG_EXTRA2, "%s: received WDS frame with " "not own or broadcast %s=%s\n", local->dev->name, fc & IEEE80211_FCTL_FROMDS ? "RA" : "BSSID", diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c index 0acd9589c48..ab981afd481 100644 --- a/drivers/net/wireless/hostap/hostap_ap.c +++ b/drivers/net/wireless/hostap/hostap_ap.c @@ -1930,7 +1930,7 @@ static void handle_pspoll(local_info_t *local, PDEBUG(DEBUG_PS, " PSPOLL and AID[15:14] not set\n"); return; } - aid &= ~BIT(15) & ~BIT(14); + aid &= ~(BIT(15) | BIT(14)); if (aid == 0 || aid > MAX_AID_TABLE_SIZE) { PDEBUG(DEBUG_PS, " invalid aid=%d\n", aid); return; diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c index ed4317a17cb..80039a0ae02 100644 --- a/drivers/net/wireless/hostap/hostap_cs.c +++ b/drivers/net/wireless/hostap/hostap_cs.c @@ -533,10 +533,10 @@ static void prism2_detach(struct pcmcia_device *link) do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) #define CFG_CHECK2(fn, retf) \ -do { int ret = (retf); \ -if (ret != 0) { \ - PDEBUG(DEBUG_EXTRA, "CardServices(" #fn ") returned %d\n", ret); \ - cs_error(link, fn, ret); \ +do { int _ret = (retf); \ +if (_ret != 0) { \ + PDEBUG(DEBUG_EXTRA, "CardServices(" #fn ") returned %d\n", _ret); \ + cs_error(link, fn, _ret); \ goto next_entry; \ } \ } while (0) diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c index cdf90c40f11..936f52e3d95 100644 --- a/drivers/net/wireless/hostap/hostap_hw.c +++ b/drivers/net/wireless/hostap/hostap_hw.c @@ -2835,7 +2835,7 @@ static void hostap_passive_scan(unsigned long data) { local_info_t *local = (local_info_t *) data; struct net_device *dev = local->dev; - u16 channel; + u16 chan; if (local->passive_scan_interval <= 0) return; @@ -2872,11 +2872,11 @@ static void hostap_passive_scan(unsigned long data) printk(KERN_DEBUG "%s: passive scan channel %d\n", dev->name, local->passive_scan_channel); - channel = local->passive_scan_channel; + chan = local->passive_scan_channel; local->passive_scan_state = PASSIVE_SCAN_WAIT; local->passive_scan_timer.expires = jiffies + HZ / 10; } else { - channel = local->channel; + chan = local->channel; local->passive_scan_state = PASSIVE_SCAN_LISTEN; local->passive_scan_timer.expires = jiffies + local->passive_scan_interval * HZ; @@ -2884,9 +2884,9 @@ static void hostap_passive_scan(unsigned long data) if (hfa384x_cmd_callback(dev, HFA384X_CMDCODE_TEST | (HFA384X_TEST_CHANGE_CHANNEL << 8), - channel, NULL, 0)) + chan, NULL, 0)) printk(KERN_ERR "%s: passive scan channel set %d " - "failed\n", dev->name, channel); + "failed\n", dev->name, chan); add_timer(&local->passive_scan_timer); } diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c index f7aec9309d0..a38e85f334d 100644 --- a/drivers/net/wireless/hostap/hostap_main.c +++ b/drivers/net/wireless/hostap/hostap_main.c @@ -594,7 +594,8 @@ void hostap_dump_tx_header(const char *name, const struct hfa384x_tx_frame *tx) } -int hostap_80211_header_parse(const struct sk_buff *skb, unsigned char *haddr) +static int hostap_80211_header_parse(const struct sk_buff *skb, + unsigned char *haddr) { struct hostap_interface *iface = netdev_priv(skb->dev); local_info_t *local = iface->local; @@ -857,7 +858,6 @@ const struct header_ops hostap_80211_ops = { .rebuild = eth_rebuild_header, .cache = eth_header_cache, .cache_update = eth_header_cache_update, - .parse = hostap_80211_header_parse, }; EXPORT_SYMBOL(hostap_80211_ops); @@ -1150,7 +1150,6 @@ EXPORT_SYMBOL(hostap_set_roaming); EXPORT_SYMBOL(hostap_set_auth_algs); EXPORT_SYMBOL(hostap_dump_rx_header); EXPORT_SYMBOL(hostap_dump_tx_header); -EXPORT_SYMBOL(hostap_80211_header_parse); EXPORT_SYMBOL(hostap_80211_get_hdrlen); EXPORT_SYMBOL(hostap_get_stats); EXPORT_SYMBOL(hostap_setup_dev); diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 13925b627e3..b1b3c523185 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -2227,7 +2227,10 @@ static int iwl3945_scan_initiate(struct iwl3945_priv *priv) } IWL_DEBUG_INFO("Starting scan...\n"); - priv->scan_bands = 2; + if (priv->cfg->sku & IWL_SKU_G) + priv->scan_bands |= BIT(IEEE80211_BAND_2GHZ); + if (priv->cfg->sku & IWL_SKU_A) + priv->scan_bands |= BIT(IEEE80211_BAND_5GHZ); set_bit(STATUS_SCANNING, &priv->status); priv->scan_start = jiffies; priv->scan_pass_start = priv->scan_start; @@ -3352,13 +3355,18 @@ static void iwl3945_rx_scan_complete_notif(struct iwl3945_priv *priv, cancel_delayed_work(&priv->scan_check); IWL_DEBUG_INFO("Scan pass on %sGHz took %dms\n", - (priv->scan_bands == 2) ? "2.4" : "5.2", + (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) ? + "2.4" : "5.2", jiffies_to_msecs(elapsed_jiffies (priv->scan_pass_start, jiffies))); - /* Remove this scanned band from the list - * of pending bands to scan */ - priv->scan_bands--; + /* Remove this scanned band from the list of pending + * bands to scan, band G precedes A in order of scanning + * as seen in iwl3945_bg_request_scan */ + if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) + priv->scan_bands &= ~BIT(IEEE80211_BAND_2GHZ); + else if (priv->scan_bands & BIT(IEEE80211_BAND_5GHZ)) + priv->scan_bands &= ~BIT(IEEE80211_BAND_5GHZ); /* If a request to abort was given, or the scan did not succeed * then we reset the scan state machine and terminate, @@ -4972,7 +4980,7 @@ static int iwl3945_get_channels_for_scan(struct iwl3945_priv *priv, ch_info = iwl3945_get_channel_info(priv, band, scan_ch->channel); if (!is_channel_valid(ch_info)) { - IWL_DEBUG_SCAN("Channel %d is INVALID for this SKU.\n", + IWL_DEBUG_SCAN("Channel %d is INVALID for this band.\n", scan_ch->channel); continue; } @@ -6315,21 +6323,16 @@ static void iwl3945_bg_request_scan(struct work_struct *data) /* flags + rate selection */ - switch (priv->scan_bands) { - case 2: + if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) { scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; scan->tx_cmd.rate = IWL_RATE_1M_PLCP; scan->good_CRC_th = 0; band = IEEE80211_BAND_2GHZ; - break; - - case 1: + } else if (priv->scan_bands & BIT(IEEE80211_BAND_5GHZ)) { scan->tx_cmd.rate = IWL_RATE_6M_PLCP; scan->good_CRC_th = IWL_GOOD_CRC_TH; band = IEEE80211_BAND_5GHZ; - break; - - default: + } else { IWL_WARNING("Invalid scan band count\n"); goto done; } @@ -6770,7 +6773,7 @@ static int iwl3945_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co ch_info = iwl3945_get_channel_info(priv, conf->channel->band, conf->channel->hw_value); if (!is_channel_valid(ch_info)) { - IWL_DEBUG_SCAN("Channel %d [%d] is INVALID for this SKU.\n", + IWL_DEBUG_SCAN("Channel %d [%d] is INVALID for this band.\n", conf->channel->hw_value, conf->channel->band); IWL_DEBUG_MAC80211("leave - invalid channel\n"); spin_unlock_irqrestore(&priv->lock, flags); diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c index 883b42f7e99..5ed16ce7846 100644 --- a/drivers/net/wireless/iwlwifi/iwl4965-base.c +++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c @@ -1774,7 +1774,10 @@ static int iwl4965_scan_initiate(struct iwl_priv *priv) } IWL_DEBUG_INFO("Starting scan...\n"); - priv->scan_bands = 2; + if (priv->cfg->sku & IWL_SKU_G) + priv->scan_bands |= BIT(IEEE80211_BAND_2GHZ); + if (priv->cfg->sku & IWL_SKU_A) + priv->scan_bands |= BIT(IEEE80211_BAND_5GHZ); set_bit(STATUS_SCANNING, &priv->status); priv->scan_start = jiffies; priv->scan_pass_start = priv->scan_start; @@ -3023,8 +3026,9 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv, IWL_DEBUG_TX_REPLY("Tx queue reclaim %d\n", index); if (index != -1) { - int freed = iwl4965_tx_queue_reclaim(priv, txq_id, index); #ifdef CONFIG_IWL4965_HT + int freed = iwl4965_tx_queue_reclaim(priv, txq_id, index); + if (tid != MAX_TID_COUNT) priv->stations[sta_id].tid[tid].tfds_in_queue -= freed; if (iwl4965_queue_space(&txq->q) > txq->q.low_mark && @@ -3276,13 +3280,18 @@ static void iwl4965_rx_scan_complete_notif(struct iwl_priv *priv, cancel_delayed_work(&priv->scan_check); IWL_DEBUG_INFO("Scan pass on %sGHz took %dms\n", - (priv->scan_bands == 2) ? "2.4" : "5.2", + (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) ? + "2.4" : "5.2", jiffies_to_msecs(elapsed_jiffies (priv->scan_pass_start, jiffies))); - /* Remove this scanned band from the list - * of pending bands to scan */ - priv->scan_bands--; + /* Remove this scanned band from the list of pending + * bands to scan, band G precedes A in order of scanning + * as seen in iwl_bg_request_scan */ + if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) + priv->scan_bands &= ~BIT(IEEE80211_BAND_2GHZ); + else if (priv->scan_bands & BIT(IEEE80211_BAND_5GHZ)) + priv->scan_bands &= ~BIT(IEEE80211_BAND_5GHZ); /* If a request to abort was given, or the scan did not succeed * then we reset the scan state machine and terminate, @@ -3292,7 +3301,7 @@ static void iwl4965_rx_scan_complete_notif(struct iwl_priv *priv, clear_bit(STATUS_SCAN_ABORTING, &priv->status); } else { /* If there are more bands on this scan pass reschedule */ - if (priv->scan_bands > 0) + if (priv->scan_bands) goto reschedule; } @@ -4635,10 +4644,9 @@ static int iwl4965_get_channels_for_scan(struct iwl_priv *priv, scan_ch->channel = ieee80211_frequency_to_channel(channels[i].center_freq); - ch_info = iwl_get_channel_info(priv, band, - scan_ch->channel); + ch_info = iwl_get_channel_info(priv, band, scan_ch->channel); if (!is_channel_valid(ch_info)) { - IWL_DEBUG_SCAN("Channel %d is INVALID for this SKU.\n", + IWL_DEBUG_SCAN("Channel %d is INVALID for this band.\n", scan_ch->channel); continue; } @@ -5830,8 +5838,7 @@ static void iwl4965_bg_request_scan(struct work_struct *data) scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - switch (priv->scan_bands) { - case 2: + if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) { scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; scan->tx_cmd.rate_n_flags = iwl4965_hw_set_rate_n_flags(IWL_RATE_1M_PLCP, @@ -5839,17 +5846,13 @@ static void iwl4965_bg_request_scan(struct work_struct *data) scan->good_CRC_th = 0; band = IEEE80211_BAND_2GHZ; - break; - - case 1: + } else if (priv->scan_bands & BIT(IEEE80211_BAND_5GHZ)) { scan->tx_cmd.rate_n_flags = iwl4965_hw_set_rate_n_flags(IWL_RATE_6M_PLCP, RATE_MCS_ANT_B_MSK); scan->good_CRC_th = IWL_GOOD_CRC_TH; band = IEEE80211_BAND_5GHZ; - break; - - default: + } else { IWL_WARNING("Invalid scan band count\n"); goto done; } diff --git a/drivers/net/wireless/prism54/islpci_eth.c b/drivers/net/wireless/prism54/islpci_eth.c index 762e85bef55..e43bae97ed8 100644 --- a/drivers/net/wireless/prism54/islpci_eth.c +++ b/drivers/net/wireless/prism54/islpci_eth.c @@ -290,7 +290,7 @@ islpci_monitor_rx(islpci_private *priv, struct sk_buff **skb) avs->version = cpu_to_be32(P80211CAPTURE_VERSION); avs->length = cpu_to_be32(sizeof (struct avs_80211_1_header)); - avs->mactime = cpu_to_be64(le64_to_cpu(clock)); + avs->mactime = cpu_to_be64(clock); avs->hosttime = cpu_to_be64(jiffies); avs->phytype = cpu_to_be32(6); /*OFDM: 6 for (g), 8 for (a) */ avs->channel = cpu_to_be32(channel_of_freq(freq)); diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig index ab1029e7988..2d611876bbe 100644 --- a/drivers/net/wireless/rt2x00/Kconfig +++ b/drivers/net/wireless/rt2x00/Kconfig @@ -32,12 +32,13 @@ config RT2X00_LIB_FIRMWARE config RT2X00_LIB_RFKILL boolean depends on RT2X00_LIB + depends on INPUT select RFKILL select INPUT_POLLDEV config RT2X00_LIB_LEDS boolean - depends on RT2X00_LIB + depends on RT2X00_LIB && NEW_LEDS config RT2400PCI tristate "Ralink rt2400 pci/pcmcia support" @@ -51,7 +52,7 @@ config RT2400PCI config RT2400PCI_RFKILL bool "RT2400 rfkill support" - depends on RT2400PCI + depends on RT2400PCI && INPUT select RT2X00_LIB_RFKILL ---help--- This adds support for integrated rt2400 devices that feature a @@ -60,7 +61,7 @@ config RT2400PCI_RFKILL config RT2400PCI_LEDS bool "RT2400 leds support" - depends on RT2400PCI + depends on RT2400PCI && NEW_LEDS select LEDS_CLASS select RT2X00_LIB_LEDS ---help--- @@ -78,7 +79,7 @@ config RT2500PCI config RT2500PCI_RFKILL bool "RT2500 rfkill support" - depends on RT2500PCI + depends on RT2500PCI && INPUT select RT2X00_LIB_RFKILL ---help--- This adds support for integrated rt2500 devices that feature a @@ -87,7 +88,7 @@ config RT2500PCI_RFKILL config RT2500PCI_LEDS bool "RT2500 leds support" - depends on RT2500PCI + depends on RT2500PCI && NEW_LEDS select LEDS_CLASS select RT2X00_LIB_LEDS ---help--- @@ -107,7 +108,7 @@ config RT61PCI config RT61PCI_RFKILL bool "RT61 rfkill support" - depends on RT61PCI + depends on RT61PCI && INPUT select RT2X00_LIB_RFKILL ---help--- This adds support for integrated rt61 devices that feature a @@ -116,7 +117,7 @@ config RT61PCI_RFKILL config RT61PCI_LEDS bool "RT61 leds support" - depends on RT61PCI + depends on RT61PCI && NEW_LEDS select LEDS_CLASS select RT2X00_LIB_LEDS ---help--- @@ -133,7 +134,7 @@ config RT2500USB config RT2500USB_LEDS bool "RT2500 leds support" - depends on RT2500USB + depends on RT2500USB && NEW_LEDS select LEDS_CLASS select RT2X00_LIB_LEDS ---help--- @@ -152,7 +153,7 @@ config RT73USB config RT73USB_LEDS bool "RT73 leds support" - depends on RT73USB + depends on RT73USB && NEW_LEDS select LEDS_CLASS select RT2X00_LIB_LEDS ---help--- diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index fdbd0ef2be4..61e59c17a60 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -138,11 +138,8 @@ static void rt2500usb_bbp_write(struct rt2x00_dev *rt2x00dev, * Wait until the BBP becomes ready. */ reg = rt2500usb_bbp_check(rt2x00dev); - if (rt2x00_get_field16(reg, PHY_CSR8_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR8 register busy. Write failed.\n"); - mutex_unlock(&rt2x00dev->usb_cache_mutex); - return; - } + if (rt2x00_get_field16(reg, PHY_CSR8_BUSY)) + goto exit_fail; /* * Write the data into the BBP. @@ -155,6 +152,13 @@ static void rt2500usb_bbp_write(struct rt2x00_dev *rt2x00dev, rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg); mutex_unlock(&rt2x00dev->usb_cache_mutex); + + return; + +exit_fail: + mutex_unlock(&rt2x00dev->usb_cache_mutex); + + ERROR(rt2x00dev, "PHY_CSR8 register busy. Write failed.\n"); } static void rt2500usb_bbp_read(struct rt2x00_dev *rt2x00dev, @@ -168,10 +172,8 @@ static void rt2500usb_bbp_read(struct rt2x00_dev *rt2x00dev, * Wait until the BBP becomes ready. */ reg = rt2500usb_bbp_check(rt2x00dev); - if (rt2x00_get_field16(reg, PHY_CSR8_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR8 register busy. Read failed.\n"); - return; - } + if (rt2x00_get_field16(reg, PHY_CSR8_BUSY)) + goto exit_fail; /* * Write the request into the BBP. @@ -186,17 +188,21 @@ static void rt2500usb_bbp_read(struct rt2x00_dev *rt2x00dev, * Wait until the BBP becomes ready. */ reg = rt2500usb_bbp_check(rt2x00dev); - if (rt2x00_get_field16(reg, PHY_CSR8_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR8 register busy. Read failed.\n"); - *value = 0xff; - mutex_unlock(&rt2x00dev->usb_cache_mutex); - return; - } + if (rt2x00_get_field16(reg, PHY_CSR8_BUSY)) + goto exit_fail; rt2500usb_register_read_lock(rt2x00dev, PHY_CSR7, ®); *value = rt2x00_get_field16(reg, PHY_CSR7_DATA); mutex_unlock(&rt2x00dev->usb_cache_mutex); + + return; + +exit_fail: + mutex_unlock(&rt2x00dev->usb_cache_mutex); + + ERROR(rt2x00dev, "PHY_CSR8 register busy. Read failed.\n"); + *value = 0xff; } static void rt2500usb_rf_write(struct rt2x00_dev *rt2x00dev, diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 611d9832059..b4bf1e09cf9 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -821,6 +821,7 @@ struct rt2x00_dev { /* * Scheduled work. */ + struct workqueue_struct *workqueue; struct work_struct intf_work; struct work_struct filter_work; diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 2673d568bca..c997d4f28ab 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -75,7 +75,7 @@ static void rt2x00lib_start_link_tuner(struct rt2x00_dev *rt2x00dev) rt2x00lib_reset_link_tuner(rt2x00dev); - queue_delayed_work(rt2x00dev->hw->workqueue, + queue_delayed_work(rt2x00dev->workqueue, &rt2x00dev->link.work, LINK_TUNE_INTERVAL); } @@ -137,14 +137,6 @@ void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev) return; /* - * Stop all scheduled work. - */ - if (work_pending(&rt2x00dev->intf_work)) - cancel_work_sync(&rt2x00dev->intf_work); - if (work_pending(&rt2x00dev->filter_work)) - cancel_work_sync(&rt2x00dev->filter_work); - - /* * Stop the TX queues. */ ieee80211_stop_queues(rt2x00dev->hw); @@ -398,8 +390,8 @@ static void rt2x00lib_link_tuner(struct work_struct *work) * Increase tuner counter, and reschedule the next link tuner run. */ rt2x00dev->link.count++; - queue_delayed_work(rt2x00dev->hw->workqueue, &rt2x00dev->link.work, - LINK_TUNE_INTERVAL); + queue_delayed_work(rt2x00dev->workqueue, + &rt2x00dev->link.work, LINK_TUNE_INTERVAL); } static void rt2x00lib_packetfilter_scheduled(struct work_struct *work) @@ -433,6 +425,15 @@ static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac, spin_unlock(&intf->lock); + /* + * It is possible the radio was disabled while the work had been + * scheduled. If that happens we should return here immediately, + * note that in the spinlock protected area above the delayed_flags + * have been cleared correctly. + */ + if (!test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) + return; + if (delayed_flags & DELAYED_UPDATE_BEACON) { skb = ieee80211_beacon_get(rt2x00dev->hw, vif, &control); if (skb && rt2x00dev->ops->hw->beacon_update(rt2x00dev->hw, @@ -441,7 +442,7 @@ static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac, } if (delayed_flags & DELAYED_CONFIG_ERP) - rt2x00lib_config_erp(rt2x00dev, intf, &intf->conf); + rt2x00lib_config_erp(rt2x00dev, intf, &conf); if (delayed_flags & DELAYED_LED_ASSOC) rt2x00leds_led_assoc(rt2x00dev, !!rt2x00dev->intf_associated); @@ -487,7 +488,7 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) rt2x00lib_beacondone_iter, rt2x00dev); - queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->intf_work); + queue_work(rt2x00dev->workqueue, &rt2x00dev->intf_work); } EXPORT_SYMBOL_GPL(rt2x00lib_beacondone); @@ -1130,6 +1131,10 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) /* * Initialize configuration work. */ + rt2x00dev->workqueue = create_singlethread_workqueue("rt2x00lib"); + if (!rt2x00dev->workqueue) + goto exit; + INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled); INIT_WORK(&rt2x00dev->filter_work, rt2x00lib_packetfilter_scheduled); INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00lib_link_tuner); @@ -1190,6 +1195,13 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) rt2x00leds_unregister(rt2x00dev); /* + * Stop all queued work. Note that most tasks will already be halted + * during rt2x00lib_disable_radio() and rt2x00lib_uninitialize(). + */ + flush_workqueue(rt2x00dev->workqueue); + destroy_workqueue(rt2x00dev->workqueue); + + /* * Free ieee80211_hw memory. */ rt2x00lib_remove_hw(rt2x00dev); diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index 87e280a2197..9cb023edd2e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -428,7 +428,7 @@ void rt2x00mac_configure_filter(struct ieee80211_hw *hw, if (!test_bit(DRIVER_REQUIRE_SCHEDULED, &rt2x00dev->flags)) rt2x00dev->ops->lib->config_filter(rt2x00dev, *total_flags); else - queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->filter_work); + queue_work(rt2x00dev->workqueue, &rt2x00dev->filter_work); } EXPORT_SYMBOL_GPL(rt2x00mac_configure_filter); @@ -509,7 +509,7 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, memcpy(&intf->conf, bss_conf, sizeof(*bss_conf)); if (delayed) { intf->delayed_flags |= delayed; - queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->intf_work); + queue_work(rt2x00dev->workqueue, &rt2x00dev->intf_work); } spin_unlock(&intf->lock); } diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.c b/drivers/net/wireless/rt2x00/rt2x00pci.c index 971af2546b5..60893de3bf8 100644 --- a/drivers/net/wireless/rt2x00/rt2x00pci.c +++ b/drivers/net/wireless/rt2x00/rt2x00pci.c @@ -412,8 +412,7 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) if (pci_set_mwi(pci_dev)) ERROR_PROBE("MWI not available.\n"); - if (pci_set_dma_mask(pci_dev, DMA_64BIT_MASK) && - pci_set_dma_mask(pci_dev, DMA_32BIT_MASK)) { + if (pci_set_dma_mask(pci_dev, DMA_32BIT_MASK)) { ERROR_PROBE("PCI DMA not supported.\n"); retval = -EIO; goto exit_disable_device; diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 5a331674dcb..e5ceae805b5 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -362,6 +362,12 @@ void rt2x00usb_disable_radio(struct rt2x00_dev *rt2x00dev) } } + /* + * Kill guardian urb (if required by driver). + */ + if (!test_bit(DRIVER_REQUIRE_BEACON_GUARD, &rt2x00dev->flags)) + return; + for (i = 0; i < rt2x00dev->bcn->limit; i++) { priv_bcn = rt2x00dev->bcn->entries[i].priv_data; usb_kill_urb(priv_bcn->urb); diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index da19a3a91f4..83cc0147f69 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -134,11 +134,8 @@ static void rt73usb_bbp_write(struct rt2x00_dev *rt2x00dev, * Wait until the BBP becomes ready. */ reg = rt73usb_bbp_check(rt2x00dev); - if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR3 register busy. Write failed.\n"); - mutex_unlock(&rt2x00dev->usb_cache_mutex); - return; - } + if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) + goto exit_fail; /* * Write the data into the BBP. @@ -151,6 +148,13 @@ static void rt73usb_bbp_write(struct rt2x00_dev *rt2x00dev, rt73usb_register_write_lock(rt2x00dev, PHY_CSR3, reg); mutex_unlock(&rt2x00dev->usb_cache_mutex); + + return; + +exit_fail: + mutex_unlock(&rt2x00dev->usb_cache_mutex); + + ERROR(rt2x00dev, "PHY_CSR3 register busy. Write failed.\n"); } static void rt73usb_bbp_read(struct rt2x00_dev *rt2x00dev, @@ -164,11 +168,8 @@ static void rt73usb_bbp_read(struct rt2x00_dev *rt2x00dev, * Wait until the BBP becomes ready. */ reg = rt73usb_bbp_check(rt2x00dev); - if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR3 register busy. Read failed.\n"); - mutex_unlock(&rt2x00dev->usb_cache_mutex); - return; - } + if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) + goto exit_fail; /* * Write the request into the BBP. @@ -184,14 +185,19 @@ static void rt73usb_bbp_read(struct rt2x00_dev *rt2x00dev, * Wait until the BBP becomes ready. */ reg = rt73usb_bbp_check(rt2x00dev); - if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR3 register busy. Read failed.\n"); - *value = 0xff; - return; - } + if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) + goto exit_fail; *value = rt2x00_get_field32(reg, PHY_CSR3_VALUE); mutex_unlock(&rt2x00dev->usb_cache_mutex); + + return; + +exit_fail: + mutex_unlock(&rt2x00dev->usb_cache_mutex); + + ERROR(rt2x00dev, "PHY_CSR3 register busy. Read failed.\n"); + *value = 0xff; } static void rt73usb_rf_write(struct rt2x00_dev *rt2x00dev, @@ -2131,6 +2137,7 @@ static struct usb_device_id rt73usb_device_table[] = { /* D-Link */ { USB_DEVICE(0x07d1, 0x3c03), USB_DEVICE_DATA(&rt73usb_ops) }, { USB_DEVICE(0x07d1, 0x3c04), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x07d1, 0x3c06), USB_DEVICE_DATA(&rt73usb_ops) }, { USB_DEVICE(0x07d1, 0x3c07), USB_DEVICE_DATA(&rt73usb_ops) }, /* Gemtek */ { USB_DEVICE(0x15a9, 0x0004), USB_DEVICE_DATA(&rt73usb_ops) }, diff --git a/drivers/pci/access.c b/drivers/pci/access.c index ec8f7002b09..39bb96b413e 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -178,8 +178,7 @@ static int pci_vpd_pci22_read(struct pci_dev *dev, int pos, int size, int ret; int begin, end, i; - if (pos < 0 || pos > PCI_VPD_PCI22_SIZE || - size > PCI_VPD_PCI22_SIZE - pos) + if (pos < 0 || pos > vpd->base.len || size > vpd->base.len - pos) return -EINVAL; if (size == 0) return 0; @@ -223,8 +222,8 @@ static int pci_vpd_pci22_write(struct pci_dev *dev, int pos, int size, u32 val; int ret; - if (pos < 0 || pos > PCI_VPD_PCI22_SIZE || pos & 3 || - size > PCI_VPD_PCI22_SIZE - pos || size < 4) + if (pos < 0 || pos > vpd->base.len || pos & 3 || + size > vpd->base.len - pos || size < 4) return -EINVAL; val = (u8) *buf++; @@ -255,11 +254,6 @@ out: return 4; } -static int pci_vpd_pci22_get_size(struct pci_dev *dev) -{ - return PCI_VPD_PCI22_SIZE; -} - static void pci_vpd_pci22_release(struct pci_dev *dev) { kfree(container_of(dev->vpd, struct pci_vpd_pci22, base)); @@ -268,7 +262,6 @@ static void pci_vpd_pci22_release(struct pci_dev *dev) static struct pci_vpd_ops pci_vpd_pci22_ops = { .read = pci_vpd_pci22_read, .write = pci_vpd_pci22_write, - .get_size = pci_vpd_pci22_get_size, .release = pci_vpd_pci22_release, }; @@ -284,6 +277,7 @@ int pci_vpd_pci22_init(struct pci_dev *dev) if (!vpd) return -ENOMEM; + vpd->base.len = PCI_VPD_PCI22_SIZE; vpd->base.ops = &pci_vpd_pci22_ops; spin_lock_init(&vpd->lock); vpd->cap = cap; diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 648596d469f..91156f85a92 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -700,9 +700,10 @@ cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, cleanup_p2p_bridge, NULL, NULL); - if (!(bridge = acpiphp_handle_to_bridge(handle))) - return AE_OK; - cleanup_bridge(bridge); + bridge = acpiphp_handle_to_bridge(handle); + if (bridge) + cleanup_bridge(bridge); + return AE_OK; } @@ -715,9 +716,19 @@ static void remove_bridge(acpi_handle handle) acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, cleanup_p2p_bridge, NULL, NULL); + /* + * On root bridges with hotplug slots directly underneath (ie, + * no p2p bridge inbetween), we call cleanup_bridge(). + * + * The else clause cleans up root bridges that either had no + * hotplug slots at all, or had a p2p bridge underneath. + */ bridge = acpiphp_handle_to_bridge(handle); if (bridge) cleanup_bridge(bridge); + else + acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + handle_hotplug_event_bridge); } static struct pci_dev * get_apic_pci_info(acpi_handle handle) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 6f3c7446c32..9c718583a23 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -736,9 +736,9 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) attr = kzalloc(sizeof(*attr), GFP_ATOMIC); if (attr) { pdev->vpd->attr = attr; - attr->size = pdev->vpd->ops->get_size(pdev); + attr->size = pdev->vpd->len; attr->attr.name = "vpd"; - attr->attr.mode = S_IRUGO | S_IWUSR; + attr->attr.mode = S_IRUSR | S_IWUSR; attr->read = pci_read_vpd; attr->write = pci_write_vpd; retval = sysfs_create_bin_file(&pdev->dev.kobj, attr); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 0a497c1b422..00408c97e5f 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -21,11 +21,11 @@ extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val); struct pci_vpd_ops { int (*read)(struct pci_dev *dev, int pos, int size, char *buf); int (*write)(struct pci_dev *dev, int pos, int size, const char *buf); - int (*get_size)(struct pci_dev *dev); void (*release)(struct pci_dev *dev); }; struct pci_vpd { + unsigned int len; struct pci_vpd_ops *ops; struct bin_attribute *attr; /* descriptor for sysfs VPD entry */ }; diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index dabb563f51d..338a3f94b4d 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1670,6 +1670,48 @@ static void __devinit quirk_via_cx700_pci_parking_caching(struct pci_dev *dev) } DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_VIA, 0x324e, quirk_via_cx700_pci_parking_caching); +/* + * For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the + * VPD end tag will hang the device. This problem was initially + * observed when a vpd entry was created in sysfs + * ('/sys/bus/pci/devices/<id>/vpd'). A read to this sysfs entry + * will dump 32k of data. Reading a full 32k will cause an access + * beyond the VPD end tag causing the device to hang. Once the device + * is hung, the bnx2 driver will not be able to reset the device. + * We believe that it is legal to read beyond the end tag and + * therefore the solution is to limit the read/write length. + */ +static void __devinit quirk_brcm_570x_limit_vpd(struct pci_dev *dev) +{ + /* Only disable the VPD capability for 5706, 5708, and 5709 rev. A */ + if ((dev->device == PCI_DEVICE_ID_NX2_5706) || + (dev->device == PCI_DEVICE_ID_NX2_5708) || + ((dev->device == PCI_DEVICE_ID_NX2_5709) && + (dev->revision & 0xf0) == 0x0)) { + if (dev->vpd) + dev->vpd->len = 0x80; + } +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5706, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5706S, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5708, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5708S, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5709, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5709S, + quirk_brcm_570x_limit_vpd); + #ifdef CONFIG_PCI_MSI /* Some chipsets do not support MSI. We cannot easily rely on setting * PCI_BUS_FLAGS_NO_MSI in its bus flags because there are actually @@ -1685,6 +1727,7 @@ static void __init quirk_disable_all_msi(struct pci_dev *dev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_GCNB_LE, quirk_disable_all_msi); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS400_200, quirk_disable_all_msi); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS480, quirk_disable_all_msi); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3336, quirk_disable_all_msi); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3351, quirk_disable_all_msi); /* Disable MSI on chipsets that are known to not support it */ diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 7e3ad4f3b34..58b7336640f 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -126,12 +126,25 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) int err; struct rtc_time before, now; int first_time = 1; + unsigned long t_now, t_alm; + enum { none, day, month, year } missing = none; + unsigned days; - /* The lower level RTC driver may not be capable of filling - * in all fields of the rtc_time struct (eg. rtc-cmos), - * and so might instead return -1 in some fields. - * We deal with that here by grabbing a current RTC timestamp - * and using values from that for any missing (-1) values. + /* The lower level RTC driver may return -1 in some fields, + * creating invalid alarm->time values, for reasons like: + * + * - The hardware may not be capable of filling them in; + * many alarms match only on time-of-day fields, not + * day/month/year calendar data. + * + * - Some hardware uses illegal values as "wildcard" match + * values, which non-Linux firmware (like a BIOS) may try + * to set up as e.g. "alarm 15 minutes after each hour". + * Linux uses only oneshot alarms. + * + * When we see that here, we deal with it by using values from + * a current RTC timestamp for any missing (-1) values. The + * RTC driver prevents "periodic alarm" modes. * * But this can be racey, because some fields of the RTC timestamp * may have wrapped in the interval since we read the RTC alarm, @@ -174,6 +187,10 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) if (!alarm->enabled) return 0; + /* full-function RTCs won't have such missing fields */ + if (rtc_valid_tm(&alarm->time) == 0) + return 0; + /* get the "after" timestamp, to detect wrapped fields */ err = rtc_read_time(rtc, &now); if (err < 0) @@ -183,22 +200,85 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) } while ( before.tm_min != now.tm_min || before.tm_hour != now.tm_hour || before.tm_mon != now.tm_mon - || before.tm_year != now.tm_year - || before.tm_isdst != now.tm_isdst); + || before.tm_year != now.tm_year); - /* Fill in any missing alarm fields using the timestamp */ + /* Fill in the missing alarm fields using the timestamp; we + * know there's at least one since alarm->time is invalid. + */ if (alarm->time.tm_sec == -1) alarm->time.tm_sec = now.tm_sec; if (alarm->time.tm_min == -1) alarm->time.tm_min = now.tm_min; if (alarm->time.tm_hour == -1) alarm->time.tm_hour = now.tm_hour; - if (alarm->time.tm_mday == -1) + + /* For simplicity, only support date rollover for now */ + if (alarm->time.tm_mday == -1) { alarm->time.tm_mday = now.tm_mday; - if (alarm->time.tm_mon == -1) + missing = day; + } + if (alarm->time.tm_mon == -1) { alarm->time.tm_mon = now.tm_mon; - if (alarm->time.tm_year == -1) + if (missing == none) + missing = month; + } + if (alarm->time.tm_year == -1) { alarm->time.tm_year = now.tm_year; + if (missing == none) + missing = year; + } + + /* with luck, no rollover is needed */ + rtc_tm_to_time(&now, &t_now); + rtc_tm_to_time(&alarm->time, &t_alm); + if (t_now < t_alm) + goto done; + + switch (missing) { + + /* 24 hour rollover ... if it's now 10am Monday, an alarm that + * that will trigger at 5am will do so at 5am Tuesday, which + * could also be in the next month or year. This is a common + * case, especially for PCs. + */ + case day: + dev_dbg(&rtc->dev, "alarm rollover: %s\n", "day"); + t_alm += 24 * 60 * 60; + rtc_time_to_tm(t_alm, &alarm->time); + break; + + /* Month rollover ... if it's the 31th, an alarm on the 3rd will + * be next month. An alarm matching on the 30th, 29th, or 28th + * may end up in the month after that! Many newer PCs support + * this type of alarm. + */ + case month: + dev_dbg(&rtc->dev, "alarm rollover: %s\n", "month"); + do { + if (alarm->time.tm_mon < 11) + alarm->time.tm_mon++; + else { + alarm->time.tm_mon = 0; + alarm->time.tm_year++; + } + days = rtc_month_days(alarm->time.tm_mon, + alarm->time.tm_year); + } while (days < alarm->time.tm_mday); + break; + + /* Year rollover ... easy except for leap years! */ + case year: + dev_dbg(&rtc->dev, "alarm rollover: %s\n", "year"); + do { + alarm->time.tm_year++; + } while (!rtc_valid_tm(&alarm->time)); + break; + + default: + dev_warn(&rtc->dev, "alarm rollover not handled\n"); + } + +done: return 0; } EXPORT_SYMBOL_GPL(rtc_read_alarm); diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index 82f62d25f92..67421b0d3a7 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -331,14 +331,14 @@ static int sa1100_rtc_probe(struct platform_device *pdev) RCNR = 0; } + device_init_wakeup(&pdev->dev, 1); + rtc = rtc_device_register(pdev->name, &pdev->dev, &sa1100_rtc_ops, THIS_MODULE); if (IS_ERR(rtc)) return PTR_ERR(rtc); - device_init_wakeup(&pdev->dev, 1); - platform_set_drvdata(pdev, rtc); return 0; diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c index eaf55945f21..7dcfba1bbfe 100644 --- a/drivers/rtc/rtc-x1205.c +++ b/drivers/rtc/rtc-x1205.c @@ -71,6 +71,7 @@ #define X1205_SR_RTCF 0x01 /* Clock failure */ #define X1205_SR_WEL 0x02 /* Write Enable Latch */ #define X1205_SR_RWEL 0x04 /* Register Write Enable */ +#define X1205_SR_AL0 0x20 /* Alarm 0 match */ #define X1205_DTR_DTR0 0x01 #define X1205_DTR_DTR1 0x02 @@ -78,6 +79,8 @@ #define X1205_HR_MIL 0x80 /* Set in ccr.hour for 24 hr mode */ +#define X1205_INT_AL0E 0x20 /* Alarm 0 enable */ + static struct i2c_driver x1205_driver; /* @@ -89,8 +92,8 @@ static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm, unsigned char reg_base) { unsigned char dt_addr[2] = { 0, reg_base }; - unsigned char buf[8]; + int i; struct i2c_msg msgs[] = { { client->addr, 0, 2, dt_addr }, /* setup read ptr */ @@ -98,7 +101,7 @@ static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm, }; /* read date registers */ - if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { + if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) { dev_err(&client->dev, "%s: read error\n", __func__); return -EIO; } @@ -110,6 +113,11 @@ static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + /* Mask out the enable bits if these are alarm registers */ + if (reg_base < X1205_CCR_BASE) + for (i = 0; i <= 4; i++) + buf[i] &= 0x7F; + tm->tm_sec = BCD2BIN(buf[CCR_SEC]); tm->tm_min = BCD2BIN(buf[CCR_MIN]); tm->tm_hour = BCD2BIN(buf[CCR_HOUR] & 0x3F); /* hr is 0-23 */ @@ -138,7 +146,7 @@ static int x1205_get_status(struct i2c_client *client, unsigned char *sr) }; /* read status register */ - if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { + if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) { dev_err(&client->dev, "%s: read error\n", __func__); return -EIO; } @@ -147,10 +155,11 @@ static int x1205_get_status(struct i2c_client *client, unsigned char *sr) } static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm, - int datetoo, u8 reg_base) + int datetoo, u8 reg_base, unsigned char alm_enable) { - int i, xfer; + int i, xfer, nbytes; unsigned char buf[8]; + unsigned char rdata[10] = { 0, reg_base }; static const unsigned char wel[3] = { 0, X1205_REG_SR, X1205_SR_WEL }; @@ -189,6 +198,11 @@ static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm, buf[CCR_Y2K] = BIN2BCD(tm->tm_year / 100); } + /* If writing alarm registers, set compare bits on registers 0-4 */ + if (reg_base < X1205_CCR_BASE) + for (i = 0; i <= 4; i++) + buf[i] |= 0x80; + /* this sequence is required to unlock the chip */ if ((xfer = i2c_master_send(client, wel, 3)) != 3) { dev_err(&client->dev, "%s: wel - %d\n", __func__, xfer); @@ -200,19 +214,57 @@ static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm, return -EIO; } + /* write register's data */ - for (i = 0; i < (datetoo ? 8 : 3); i++) { - unsigned char rdata[3] = { 0, reg_base + i, buf[i] }; + if (datetoo) + nbytes = 8; + else + nbytes = 3; + for (i = 0; i < nbytes; i++) + rdata[2+i] = buf[i]; + + xfer = i2c_master_send(client, rdata, nbytes+2); + if (xfer != nbytes+2) { + dev_err(&client->dev, + "%s: result=%d addr=%02x, data=%02x\n", + __func__, + xfer, rdata[1], rdata[2]); + return -EIO; + } + + /* If we wrote to the nonvolatile region, wait 10msec for write cycle*/ + if (reg_base < X1205_CCR_BASE) { + unsigned char al0e[3] = { 0, X1205_REG_INT, 0 }; + + msleep(10); - xfer = i2c_master_send(client, rdata, 3); + /* ...and set or clear the AL0E bit in the INT register */ + + /* Need to set RWEL again as the write has cleared it */ + xfer = i2c_master_send(client, rwel, 3); if (xfer != 3) { dev_err(&client->dev, - "%s: xfer=%d addr=%02x, data=%02x\n", + "%s: aloe rwel - %d\n", __func__, - xfer, rdata[1], rdata[2]); + xfer); + return -EIO; + } + + if (alm_enable) + al0e[2] = X1205_INT_AL0E; + + xfer = i2c_master_send(client, al0e, 3); + if (xfer != 3) { + dev_err(&client->dev, + "%s: al0e - %d\n", + __func__, + xfer); return -EIO; } - }; + + /* and wait 10msec again for this write to complete */ + msleep(10); + } /* disable further writes */ if ((xfer = i2c_master_send(client, diswe, 3)) != 3) { @@ -230,9 +282,9 @@ static int x1205_fix_osc(struct i2c_client *client) tm.tm_hour = tm.tm_min = tm.tm_sec = 0; - if ((err = x1205_set_datetime(client, &tm, 0, X1205_CCR_BASE)) < 0) - dev_err(&client->dev, - "unable to restart the oscillator\n"); + err = x1205_set_datetime(client, &tm, 0, X1205_CCR_BASE, 0); + if (err < 0) + dev_err(&client->dev, "unable to restart the oscillator\n"); return err; } @@ -248,7 +300,7 @@ static int x1205_get_dtrim(struct i2c_client *client, int *trim) }; /* read dtr register */ - if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { + if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) { dev_err(&client->dev, "%s: read error\n", __func__); return -EIO; } @@ -280,7 +332,7 @@ static int x1205_get_atrim(struct i2c_client *client, int *trim) }; /* read atr register */ - if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { + if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) { dev_err(&client->dev, "%s: read error\n", __func__); return -EIO; } @@ -403,14 +455,33 @@ static int x1205_validate_client(struct i2c_client *client) static int x1205_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) { - return x1205_get_datetime(to_i2c_client(dev), - &alrm->time, X1205_ALM0_BASE); + int err; + unsigned char intreg, status; + static unsigned char int_addr[2] = { 0, X1205_REG_INT }; + struct i2c_client *client = to_i2c_client(dev); + struct i2c_msg msgs[] = { + { client->addr, 0, 2, int_addr }, /* setup read ptr */ + { client->addr, I2C_M_RD, 1, &intreg }, /* read INT register */ + }; + + /* read interrupt register and status register */ + if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) { + dev_err(&client->dev, "%s: read error\n", __func__); + return -EIO; + } + err = x1205_get_status(client, &status); + if (err == 0) { + alrm->pending = (status & X1205_SR_AL0) ? 1 : 0; + alrm->enabled = (intreg & X1205_INT_AL0E) ? 1 : 0; + err = x1205_get_datetime(client, &alrm->time, X1205_ALM0_BASE); + } + return err; } static int x1205_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) { return x1205_set_datetime(to_i2c_client(dev), - &alrm->time, 1, X1205_ALM0_BASE); + &alrm->time, 1, X1205_ALM0_BASE, alrm->enabled); } static int x1205_rtc_read_time(struct device *dev, struct rtc_time *tm) @@ -422,7 +493,7 @@ static int x1205_rtc_read_time(struct device *dev, struct rtc_time *tm) static int x1205_rtc_set_time(struct device *dev, struct rtc_time *tm) { return x1205_set_datetime(to_i2c_client(dev), - tm, 1, X1205_CCR_BASE); + tm, 1, X1205_CCR_BASE, 0); } static int x1205_rtc_proc(struct device *dev, struct seq_file *seq) diff --git a/drivers/scsi/dpt/dptsig.h b/drivers/scsi/dpt/dptsig.h index 72c8992fdf2..a6644b332b5 100644 --- a/drivers/scsi/dpt/dptsig.h +++ b/drivers/scsi/dpt/dptsig.h @@ -85,7 +85,7 @@ typedef unsigned int sigINT; /* ------------------------------------------------------------------ */ /* What type of processor the file is meant to run on. */ /* This will let us know whether to read sigWORDs as high/low or low/high. */ -#define PROC_INTEL 0x00 /* Intel 80x86 */ +#define PROC_INTEL 0x00 /* Intel 80x86/ia64 */ #define PROC_MOTOROLA 0x01 /* Motorola 68K */ #define PROC_MIPS4000 0x02 /* MIPS RISC 4000 */ #define PROC_ALPHA 0x03 /* DEC Alpha */ @@ -104,6 +104,7 @@ typedef unsigned int sigINT; #define PROC_486 0x08 /* Intel 80486 */ #define PROC_PENTIUM 0x10 /* Intel 586 aka P5 aka Pentium */ #define PROC_SEXIUM 0x20 /* Intel 686 aka P6 aka Pentium Pro or MMX */ +#define PROC_IA64 0x40 /* Intel IA64 processor */ /* PROC_i960: */ #define PROC_960RX 0x01 /* Intel 80960RC/RD */ diff --git a/drivers/scsi/esp_scsi.c b/drivers/scsi/esp_scsi.c index a0b6d414953..59fbef08d69 100644 --- a/drivers/scsi/esp_scsi.c +++ b/drivers/scsi/esp_scsi.c @@ -2359,6 +2359,24 @@ void scsi_esp_unregister(struct esp *esp) } EXPORT_SYMBOL(scsi_esp_unregister); +static int esp_target_alloc(struct scsi_target *starget) +{ + struct esp *esp = shost_priv(dev_to_shost(&starget->dev)); + struct esp_target_data *tp = &esp->target[starget->id]; + + tp->starget = starget; + + return 0; +} + +static void esp_target_destroy(struct scsi_target *starget) +{ + struct esp *esp = shost_priv(dev_to_shost(&starget->dev)); + struct esp_target_data *tp = &esp->target[starget->id]; + + tp->starget = NULL; +} + static int esp_slave_alloc(struct scsi_device *dev) { struct esp *esp = shost_priv(dev->host); @@ -2370,8 +2388,6 @@ static int esp_slave_alloc(struct scsi_device *dev) return -ENOMEM; dev->hostdata = lp; - tp->starget = dev->sdev_target; - spi_min_period(tp->starget) = esp->min_period; spi_max_offset(tp->starget) = 15; @@ -2608,6 +2624,8 @@ struct scsi_host_template scsi_esp_template = { .name = "esp", .info = esp_info, .queuecommand = esp_queuecommand, + .target_alloc = esp_target_alloc, + .target_destroy = esp_target_destroy, .slave_alloc = esp_slave_alloc, .slave_configure = esp_slave_configure, .slave_destroy = esp_slave_destroy, diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 3690360d7a7..c6457bfc8a4 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -456,6 +456,10 @@ static int __scsi_host_match(struct device *dev, void *data) * * Return value: * A pointer to located Scsi_Host or NULL. + * + * The caller must do a scsi_host_put() to drop the reference + * that scsi_host_get() took. The put_device() below dropped + * the reference from class_find_device(). **/ struct Scsi_Host *scsi_host_lookup(unsigned short hostnum) { @@ -463,9 +467,10 @@ struct Scsi_Host *scsi_host_lookup(unsigned short hostnum) struct Scsi_Host *shost = ERR_PTR(-ENXIO); cdev = class_find_device(&shost_class, &hostnum, __scsi_host_match); - if (cdev) + if (cdev) { shost = scsi_host_get(class_to_shost(cdev)); - + put_device(cdev); + } return shost; } EXPORT_SYMBOL(scsi_host_lookup); diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 45df83b9d84..0fe031f003e 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -61,7 +61,7 @@ static int ses_probe(struct device *dev) return err; } -#define SES_TIMEOUT 30 +#define SES_TIMEOUT (30 * HZ) #define SES_RETRIES 3 static int ses_recv_diag(struct scsi_device *sdev, int page_code, diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 7ee86d4a761..c82df8bd4d8 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -178,6 +178,9 @@ int sr_test_unit_ready(struct scsi_device *sdev, struct scsi_sense_hdr *sshdr) the_result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, sshdr, SR_TIMEOUT, retries--); + if (scsi_sense_valid(sshdr) && + sshdr->sense_key == UNIT_ATTENTION) + sdev->changed = 1; } while (retries > 0 && (!scsi_status_is_good(the_result) || diff --git a/drivers/serial/bfin_5xx.c b/drivers/serial/bfin_5xx.c index f20952c43cb..fd9bb777df2 100644 --- a/drivers/serial/bfin_5xx.c +++ b/drivers/serial/bfin_5xx.c @@ -49,6 +49,7 @@ #define DMA_RX_YCOUNT (PAGE_SIZE / DMA_RX_XCOUNT) #define DMA_RX_FLUSH_JIFFIES (HZ / 50) +#define CTS_CHECK_JIFFIES (HZ / 50) #ifdef CONFIG_SERIAL_BFIN_DMA static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart); @@ -290,11 +291,6 @@ static void bfin_serial_tx_chars(struct bfin_serial_port *uart) { struct circ_buf *xmit = &uart->port.info->xmit; - if (uart->port.x_char) { - UART_PUT_CHAR(uart, uart->port.x_char); - uart->port.icount.tx++; - uart->port.x_char = 0; - } /* * Check the modem control lines before * transmitting anything. @@ -306,6 +302,12 @@ static void bfin_serial_tx_chars(struct bfin_serial_port *uart) return; } + if (uart->port.x_char) { + UART_PUT_CHAR(uart, uart->port.x_char); + uart->port.icount.tx++; + uart->port.x_char = 0; + } + while ((UART_GET_LSR(uart) & THRE) && xmit->tail != xmit->head) { UART_PUT_CHAR(uart, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); @@ -345,15 +347,6 @@ static irqreturn_t bfin_serial_tx_int(int irq, void *dev_id) } #endif -#ifdef CONFIG_SERIAL_BFIN_CTSRTS -static void bfin_serial_do_work(struct work_struct *work) -{ - struct bfin_serial_port *uart = container_of(work, struct bfin_serial_port, cts_workqueue); - - bfin_serial_mctrl_check(uart); -} -#endif - #ifdef CONFIG_SERIAL_BFIN_DMA static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart) { @@ -361,6 +354,12 @@ static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart) uart->tx_done = 0; + /* + * Check the modem control lines before + * transmitting anything. + */ + bfin_serial_mctrl_check(uart); + if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { uart->tx_count = 0; uart->tx_done = 1; @@ -373,12 +372,6 @@ static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart) uart->port.x_char = 0; } - /* - * Check the modem control lines before - * transmitting anything. - */ - bfin_serial_mctrl_check(uart); - uart->tx_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE); if (uart->tx_count > (UART_XMIT_SIZE - xmit->tail)) uart->tx_count = UART_XMIT_SIZE - xmit->tail; @@ -565,7 +558,10 @@ static void bfin_serial_mctrl_check(struct bfin_serial_port *uart) uart_handle_cts_change(&uart->port, status & TIOCM_CTS); if (!(status & TIOCM_CTS)) { tty->hw_stopped = 1; - schedule_work(&uart->cts_workqueue); + uart->cts_timer.data = (unsigned long)(uart); + uart->cts_timer.function = (void *)bfin_serial_mctrl_check; + uart->cts_timer.expires = jiffies + CTS_CHECK_JIFFIES; + add_timer(&(uart->cts_timer)); } else { tty->hw_stopped = 0; } @@ -885,7 +881,7 @@ static void __init bfin_serial_init_ports(void) init_timer(&(bfin_serial_ports[i].rx_dma_timer)); #endif #ifdef CONFIG_SERIAL_BFIN_CTSRTS - INIT_WORK(&bfin_serial_ports[i].cts_workqueue, bfin_serial_do_work); + init_timer(&(bfin_serial_ports[i].cts_timer)); bfin_serial_ports[i].cts_pin = bfin_serial_resource[i].uart_cts_pin; bfin_serial_ports[i].rts_pin = diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index c9b64e73c98..42d2e108b67 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -1991,7 +1991,9 @@ struct uart_match { static int serial_match_port(struct device *dev, void *data) { struct uart_match *match = data; - dev_t devt = MKDEV(match->driver->major, match->driver->minor) + match->port->line; + struct tty_driver *tty_drv = match->driver->tty_driver; + dev_t devt = MKDEV(tty_drv->major, tty_drv->minor_start) + + match->port->line; return dev->devt == devt; /* Actually, only one tty per port */ } diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 799337f7fde..f5b60c70389 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -167,14 +167,14 @@ spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) mutex_lock(&spidev->buf_lock); status = spidev_sync_read(spidev, count); - if (status == 0) { + if (status > 0) { unsigned long missing; - missing = copy_to_user(buf, spidev->buffer, count); - if (count && missing == count) + missing = copy_to_user(buf, spidev->buffer, status); + if (missing == status) status = -EFAULT; else - status = count - missing; + status = status - missing; } mutex_unlock(&spidev->buf_lock); @@ -200,8 +200,6 @@ spidev_write(struct file *filp, const char __user *buf, missing = copy_from_user(spidev->buffer, buf, count); if (missing == 0) { status = spidev_sync_write(spidev, count); - if (status == 0) - status = count; } else status = -EFAULT; mutex_unlock(&spidev->buf_lock); diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index 7cf8851286b..d184f2aea78 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -1168,15 +1168,21 @@ EXPORT_SYMBOL(ssb_dma_translation); int ssb_dma_set_mask(struct ssb_device *ssb_dev, u64 mask) { struct device *dma_dev = ssb_dev->dma_dev; + int err = 0; #ifdef CONFIG_SSB_PCIHOST - if (ssb_dev->bus->bustype == SSB_BUSTYPE_PCI) - return dma_set_mask(dma_dev, mask); + if (ssb_dev->bus->bustype == SSB_BUSTYPE_PCI) { + err = pci_set_dma_mask(ssb_dev->bus->host_pci, mask); + if (err) + return err; + err = pci_set_consistent_dma_mask(ssb_dev->bus->host_pci, mask); + return err; + } #endif dma_dev->coherent_dma_mask = mask; dma_dev->dma_mask = &dma_dev->coherent_dma_mask; - return 0; + return err; } EXPORT_SYMBOL(ssb_dma_set_mask); diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 4b628526df0..a86e952ed4c 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -12,3 +12,12 @@ menuconfig THERMAL cooling devices. All platforms with ACPI thermal support can use this driver. If you want this support, you should say Y or M here. + +config THERMAL_HWMON + bool "Hardware monitoring support" + depends on HWMON=y || HWMON=THERMAL + help + The generic thermal sysfs driver's hardware monitoring support + requires a 2.10.7/3.0.2 or later lm-sensors userspace. + + Say Y if your user-space is new enough. diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 6098787341f..fe07462d594 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -295,8 +295,8 @@ thermal_cooling_device_trip_point_show(struct device *dev, /* Device management */ -#if defined(CONFIG_HWMON) || \ - (defined(CONFIG_HWMON_MODULE) && defined(CONFIG_THERMAL_MODULE)) +#if defined(CONFIG_THERMAL_HWMON) + /* hwmon sys I/F */ #include <linux/hwmon.h> static LIST_HEAD(thermal_hwmon_list); diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 63c34043b4d..c3201affa0b 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1125,9 +1125,6 @@ static void stop_data_traffic(struct acm *acm) for (i = 0; i < acm->rx_buflimit; i++) usb_kill_urb(acm->ru[i].urb); - INIT_LIST_HEAD(&acm->filled_read_bufs); - INIT_LIST_HEAD(&acm->spare_read_bufs); - tasklet_enable(&acm->urb_task); cancel_work_sync(&acm->work); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 09a53e7f332..42a436478b7 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -924,6 +924,15 @@ static int register_root_hub(struct usb_hcd *hcd) return retval; } +void usb_enable_root_hub_irq (struct usb_bus *bus) +{ + struct usb_hcd *hcd; + + hcd = container_of (bus, struct usb_hcd, self); + if (hcd->driver->hub_irq_enable && hcd->state != HC_STATE_HALT) + hcd->driver->hub_irq_enable (hcd); +} + /*-------------------------------------------------------------------------*/ @@ -1684,19 +1693,30 @@ EXPORT_SYMBOL_GPL(usb_bus_start_enum); irqreturn_t usb_hcd_irq (int irq, void *__hcd) { struct usb_hcd *hcd = __hcd; - int start = hcd->state; + unsigned long flags; + irqreturn_t rc; - if (unlikely(start == HC_STATE_HALT || - !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) - return IRQ_NONE; - if (hcd->driver->irq (hcd) == IRQ_NONE) - return IRQ_NONE; + /* IRQF_DISABLED doesn't work correctly with shared IRQs + * when the first handler doesn't use it. So let's just + * assume it's never used. + */ + local_irq_save(flags); - set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); + if (unlikely(hcd->state == HC_STATE_HALT || + !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) { + rc = IRQ_NONE; + } else if (hcd->driver->irq(hcd) == IRQ_NONE) { + rc = IRQ_NONE; + } else { + set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); + + if (unlikely(hcd->state == HC_STATE_HALT)) + usb_hc_died(hcd); + rc = IRQ_HANDLED; + } - if (unlikely(hcd->state == HC_STATE_HALT)) - usb_hc_died (hcd); - return IRQ_HANDLED; + local_irq_restore(flags); + return rc; } /*-------------------------------------------------------------------------*/ @@ -1860,6 +1880,13 @@ int usb_add_hcd(struct usb_hcd *hcd, /* enable irqs just before we start the controller */ if (hcd->driver->irq) { + + /* IRQF_DISABLED doesn't work as advertised when used together + * with IRQF_SHARED. As usb_hcd_irq() will always disable + * interrupts we can remove it here. + */ + irqflags &= ~IRQF_DISABLED; + snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d", hcd->driver->description, hcd->self.busnum); if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags, diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index a0bf5df6cb6..b9de1569b39 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -210,6 +210,8 @@ struct hc_driver { int (*bus_suspend)(struct usb_hcd *); int (*bus_resume)(struct usb_hcd *); int (*start_port_reset)(struct usb_hcd *, unsigned port_num); + void (*hub_irq_enable)(struct usb_hcd *); + /* Needed only if port-change IRQs are level-triggered */ /* force handover of high-speed port to full-speed companion */ void (*relinquish_port)(struct usb_hcd *, int); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 94789be54ca..4cfe32a16c3 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -713,18 +713,11 @@ static void hub_restart(struct usb_hub *hub, int type) } /* Was the power session lost while we were suspended? */ - switch (type) { - case HUB_RESET_RESUME: - portstatus = 0; - portchange = USB_PORT_STAT_C_CONNECTION; - break; + status = hub_port_status(hub, port1, &portstatus, &portchange); - case HUB_RESET: - case HUB_RESUME: - status = hub_port_status(hub, port1, - &portstatus, &portchange); - break; - } + /* If the device is gone, khubd will handle it later */ + if (status == 0 && !(portstatus & USB_PORT_STAT_CONNECTION)) + continue; /* For "USB_PERSIST"-enabled children we must * mark the child device for reset-resume and @@ -2080,6 +2073,8 @@ int usb_port_resume(struct usb_device *udev) } clear_bit(port1, hub->busy_bits); + if (!hub->hdev->parent && !hub->busy_bits[0]) + usb_enable_root_hub_irq(hub->hdev->bus); if (status == 0) status = finish_port_resume(udev); @@ -3009,6 +3004,11 @@ static void hub_events(void) hub->activating = 0; + /* If this is a root hub, tell the HCD it's okay to + * re-enable port-change interrupts now. */ + if (!hdev->parent && !hub->busy_bits[0]) + usb_enable_root_hub_irq(hdev->bus); + loop_autopm: /* Allow autosuspend if we're not going to run again */ if (list_empty(&hub->event_list)) @@ -3234,6 +3234,8 @@ int usb_reset_device(struct usb_device *udev) break; } clear_bit(port1, parent_hub->busy_bits); + if (!parent_hdev->parent && !parent_hub->busy_bits[0]) + usb_enable_root_hub_irq(parent_hdev->bus); if (ret < 0) goto re_enumerate; diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 3da1ab4b389..c070b34b669 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -47,6 +47,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* Edirol SD-20 */ { USB_DEVICE(0x0582, 0x0027), .driver_info = USB_QUIRK_RESET_RESUME }, + /* appletouch */ + { USB_DEVICE(0x05ac, 0x021a), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Avision AV600U */ { USB_DEVICE(0x0638, 0x0a13), .driver_info = USB_QUIRK_STRING_FETCH_255 }, diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 35a03095757..90245fd8bac 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -177,6 +177,15 @@ timer_action_done (struct ehci_hcd *ehci, enum ehci_timer_action action) static inline void timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) { + /* Don't override timeouts which shrink or (later) disable + * the async ring; just the I/O watchdog. Note that if a + * SHRINK were pending, OFF would never be requested. + */ + if (timer_pending(&ehci->watchdog) + && ((BIT(TIMER_ASYNC_SHRINK) | BIT(TIMER_ASYNC_OFF)) + & ehci->actions)) + return; + if (!test_and_set_bit (action, &ehci->actions)) { unsigned long t; @@ -192,15 +201,7 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) t = EHCI_SHRINK_JIFFIES; break; } - t += jiffies; - // all timings except IAA watchdog can be overridden. - // async queue SHRINK often precedes IAA. while it's ready - // to go OFF neither can matter, and afterwards the IO - // watchdog stops unless there's still periodic traffic. - if (time_before_eq(t, ehci->watchdog.expires) - && timer_pending (&ehci->watchdog)) - return; - mod_timer (&ehci->watchdog, t); + mod_timer(&ehci->watchdog, t + jiffies); } } diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index c96db1153dc..e534f9de0f0 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -261,6 +261,7 @@ static const struct hc_driver ohci_at91_hc_driver = { */ .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, #ifdef CONFIG_PM .bus_suspend = ohci_bus_suspend, .bus_resume = ohci_bus_resume, diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c index 1b9abdba920..f90fe0c7373 100644 --- a/drivers/usb/host/ohci-au1xxx.c +++ b/drivers/usb/host/ohci-au1xxx.c @@ -288,6 +288,7 @@ static const struct hc_driver ohci_au1xxx_hc_driver = { */ .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, #ifdef CONFIG_PM .bus_suspend = ohci_bus_suspend, .bus_resume = ohci_bus_resume, diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c index 06aadfb0ec2..5adaf36e47d 100644 --- a/drivers/usb/host/ohci-ep93xx.c +++ b/drivers/usb/host/ohci-ep93xx.c @@ -135,6 +135,7 @@ static struct hc_driver ohci_ep93xx_hc_driver = { .get_frame_number = ohci_get_frame, .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, #ifdef CONFIG_PM .bus_suspend = ohci_bus_suspend, .bus_resume = ohci_bus_resume, diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 33f1c1c32ed..a8160d65f32 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1054,7 +1054,7 @@ MODULE_LICENSE ("GPL"); #ifdef CONFIG_MFD_SM501 #include "ohci-sm501.c" -#define PLATFORM_DRIVER ohci_hcd_sm501_driver +#define SM501_OHCI_DRIVER ohci_hcd_sm501_driver #endif #if !defined(PCI_DRIVER) && \ @@ -1062,6 +1062,7 @@ MODULE_LICENSE ("GPL"); !defined(OF_PLATFORM_DRIVER) && \ !defined(SA1111_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && \ + !defined(SM501_OHCI_DRIVER) && \ !defined(SSB_OHCI_DRIVER) #error "missing bus glue for ohci-hcd" #endif @@ -1121,9 +1122,18 @@ static int __init ohci_hcd_mod_init(void) goto error_ssb; #endif +#ifdef SM501_OHCI_DRIVER + retval = platform_driver_register(&SM501_OHCI_DRIVER); + if (retval < 0) + goto error_sm501; +#endif + return retval; /* Error path */ +#ifdef SM501_OHCI_DRIVER + error_sm501: +#endif #ifdef SSB_OHCI_DRIVER error_ssb: #endif @@ -1159,6 +1169,9 @@ module_init(ohci_hcd_mod_init); static void __exit ohci_hcd_mod_exit(void) { +#ifdef SM501_OHCI_DRIVER + platform_driver_unregister(&SM501_OHCI_DRIVER); +#endif #ifdef SSB_OHCI_DRIVER ssb_driver_unregister(&SSB_OHCI_DRIVER); #endif diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 79a78029f89..b56739221d1 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -36,6 +36,18 @@ /*-------------------------------------------------------------------------*/ +/* hcd->hub_irq_enable() */ +static void ohci_rhsc_enable (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + + spin_lock_irq(&ohci->lock); + if (!ohci->autostop) + del_timer(&hcd->rh_timer); /* Prevent next poll */ + ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); + spin_unlock_irq(&ohci->lock); +} + #define OHCI_SCHED_ENABLES \ (OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE) @@ -362,28 +374,18 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, int any_connected) { int poll_rh = 1; - int rhsc; - rhsc = ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC; switch (ohci->hc_control & OHCI_CTRL_HCFS) { case OHCI_USB_OPER: - /* If no status changes are pending, enable status-change - * interrupts. - */ - if (!rhsc && !changed) { - rhsc = OHCI_INTR_RHSC; - ohci_writel(ohci, rhsc, &ohci->regs->intrenable); - } - - /* Keep on polling until we know a device is connected - * and RHSC is enabled, or until we autostop. - */ + /* keep on polling until we know a device is connected + * and RHSC is enabled */ if (!ohci->autostop) { if (any_connected || !device_may_wakeup(&ohci_to_hcd(ohci) ->self.root_hub->dev)) { - if (rhsc) + if (ohci_readl(ohci, &ohci->regs->intrenable) & + OHCI_INTR_RHSC) poll_rh = 0; } else { ohci->autostop = 1; @@ -396,13 +398,12 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, ohci->autostop = 0; ohci->next_statechange = jiffies + STATECHANGE_DELAY; - } else if (rhsc && time_after_eq(jiffies, + } else if (time_after_eq(jiffies, ohci->next_statechange) && !ohci->ed_rm_list && !(ohci->hc_control & OHCI_SCHED_ENABLES)) { ohci_rh_suspend(ohci, 1); - poll_rh = 0; } } break; @@ -416,12 +417,6 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, else usb_hcd_resume_root_hub(ohci_to_hcd(ohci)); } else { - if (!rhsc && (ohci->autostop || - ohci_to_hcd(ohci)->self.root_hub-> - do_remote_wakeup)) - ohci_writel(ohci, OHCI_INTR_RHSC, - &ohci->regs->intrenable); - /* everything is idle, no need for polling */ poll_rh = 0; } @@ -443,16 +438,12 @@ static inline int ohci_rh_resume(struct ohci_hcd *ohci) static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, int any_connected) { - /* If RHSC is enabled, don't poll */ - if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC) - return 0; + int poll_rh = 1; - /* If no status changes are pending, enable status-change interrupts */ - if (!changed) { - ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); - return 0; - } - return 1; + /* keep on polling until RHSC is enabled */ + if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC) + poll_rh = 0; + return poll_rh; } #endif /* CONFIG_PM */ diff --git a/drivers/usb/host/ohci-lh7a404.c b/drivers/usb/host/ohci-lh7a404.c index 96d14fa1d83..13c12ed2225 100644 --- a/drivers/usb/host/ohci-lh7a404.c +++ b/drivers/usb/host/ohci-lh7a404.c @@ -193,6 +193,7 @@ static const struct hc_driver ohci_lh7a404_hc_driver = { */ .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, #ifdef CONFIG_PM .bus_suspend = ohci_bus_suspend, .bus_resume = ohci_bus_resume, diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 6859fb5f1d6..3a7c24c0367 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -466,6 +466,7 @@ static const struct hc_driver ohci_omap_hc_driver = { */ .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, #ifdef CONFIG_PM .bus_suspend = ohci_bus_suspend, .bus_resume = ohci_bus_resume, diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 3bf175d95a2..4696cc912e1 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -327,6 +327,7 @@ static const struct hc_driver ohci_pci_hc_driver = { */ .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, #ifdef CONFIG_PM .bus_suspend = ohci_bus_suspend, .bus_resume = ohci_bus_resume, diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c index 664f07ee873..28b458f20cc 100644 --- a/drivers/usb/host/ohci-pnx4008.c +++ b/drivers/usb/host/ohci-pnx4008.c @@ -280,6 +280,7 @@ static const struct hc_driver ohci_pnx4008_hc_driver = { */ .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, #ifdef CONFIG_PM .bus_suspend = ohci_bus_suspend, .bus_resume = ohci_bus_resume, diff --git a/drivers/usb/host/ohci-pnx8550.c b/drivers/usb/host/ohci-pnx8550.c index 28467e288a9..605d59cba28 100644 --- a/drivers/usb/host/ohci-pnx8550.c +++ b/drivers/usb/host/ohci-pnx8550.c @@ -201,6 +201,7 @@ static const struct hc_driver ohci_pnx8550_hc_driver = { */ .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, #ifdef CONFIG_PM .bus_suspend = ohci_bus_suspend, .bus_resume = ohci_bus_resume, diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index 50e55db1363..a6725279122 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -72,6 +72,7 @@ static const struct hc_driver ohci_ppc_of_hc_driver = { */ .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, #ifdef CONFIG_PM .bus_suspend = ohci_bus_suspend, .bus_resume = ohci_bus_resume, diff --git a/drivers/usb/host/ohci-ppc-soc.c b/drivers/usb/host/ohci-ppc-soc.c index cd3398b675b..523c3012557 100644 --- a/drivers/usb/host/ohci-ppc-soc.c +++ b/drivers/usb/host/ohci-ppc-soc.c @@ -172,6 +172,7 @@ static const struct hc_driver ohci_ppc_soc_hc_driver = { */ .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, #ifdef CONFIG_PM .bus_suspend = ohci_bus_suspend, .bus_resume = ohci_bus_resume, diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c index bfdeb0d22d0..c1935ae537f 100644 --- a/drivers/usb/host/ohci-ps3.c +++ b/drivers/usb/host/ohci-ps3.c @@ -68,6 +68,7 @@ static const struct hc_driver ps3_ohci_hc_driver = { .get_frame_number = ohci_get_frame, .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, .start_port_reset = ohci_start_port_reset, #if defined(CONFIG_PM) .bus_suspend = ohci_bus_suspend, diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 70b0d4b459e..d4ee27d92be 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -298,6 +298,7 @@ static const struct hc_driver ohci_pxa27x_hc_driver = { */ .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, #ifdef CONFIG_PM .bus_suspend = ohci_bus_suspend, .bus_resume = ohci_bus_resume, diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 9c9f3b59186..9b547407c93 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -952,6 +952,7 @@ rescan_this: struct urb *urb; urb_priv_t *urb_priv; __hc32 savebits; + u32 tdINFO; td = list_entry (entry, struct td, td_list); urb = td->urb; @@ -966,6 +967,17 @@ rescan_this: savebits = *prev & ~cpu_to_hc32 (ohci, TD_MASK); *prev = td->hwNextTD | savebits; + /* If this was unlinked, the TD may not have been + * retired ... so manually save the data toggle. + * The controller ignores the value we save for + * control and ISO endpoints. + */ + tdINFO = hc32_to_cpup(ohci, &td->hwINFO); + if ((tdINFO & TD_T) == TD_T_DATA0) + ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_C); + else if ((tdINFO & TD_T) == TD_T_DATA1) + ed->hwHeadP |= cpu_to_hc32(ohci, ED_C); + /* HC may have partly processed this TD */ td_done (ohci, urb, td); urb_priv->td_cnt++; diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index a73d2ff322e..ead4772f0f2 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -466,6 +466,7 @@ static const struct hc_driver ohci_s3c2410_hc_driver = { */ .hub_status_data = ohci_s3c2410_hub_status_data, .hub_control = ohci_s3c2410_hub_control, + .hub_irq_enable = ohci_rhsc_enable, #ifdef CONFIG_PM .bus_suspend = ohci_bus_suspend, .bus_resume = ohci_bus_resume, diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c index 99438c65981..0f48f2d9922 100644 --- a/drivers/usb/host/ohci-sa1111.c +++ b/drivers/usb/host/ohci-sa1111.c @@ -231,6 +231,7 @@ static const struct hc_driver ohci_sa1111_hc_driver = { */ .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, #ifdef CONFIG_PM .bus_suspend = ohci_bus_suspend, .bus_resume = ohci_bus_resume, diff --git a/drivers/usb/host/ohci-sh.c b/drivers/usb/host/ohci-sh.c index 60f03cc7ec4..e7ee607278f 100644 --- a/drivers/usb/host/ohci-sh.c +++ b/drivers/usb/host/ohci-sh.c @@ -68,6 +68,7 @@ static const struct hc_driver ohci_sh_hc_driver = { */ .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, #ifdef CONFIG_PM .bus_suspend = ohci_bus_suspend, .bus_resume = ohci_bus_resume, diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index e899a77dfb8..e610698c6b6 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c @@ -75,6 +75,7 @@ static const struct hc_driver ohci_sm501_hc_driver = { */ .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, #ifdef CONFIG_PM .bus_suspend = ohci_bus_suspend, .bus_resume = ohci_bus_resume, diff --git a/drivers/usb/host/ohci-ssb.c b/drivers/usb/host/ohci-ssb.c index c4265caec78..7275186db31 100644 --- a/drivers/usb/host/ohci-ssb.c +++ b/drivers/usb/host/ohci-ssb.c @@ -81,6 +81,7 @@ static const struct hc_driver ssb_ohci_hc_driver = { .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, #ifdef CONFIG_PM .bus_suspend = ohci_bus_suspend, .bus_resume = ohci_bus_resume, diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index f29307405bb..9b6323f768b 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -2934,6 +2934,16 @@ static int u132_start_port_reset(struct usb_hcd *hcd, unsigned port_num) return 0; } +static void u132_hub_irq_enable(struct usb_hcd *hcd) +{ + struct u132 *u132 = hcd_to_u132(hcd); + if (u132->going > 1) { + dev_err(&u132->platform_dev->dev, "device has been removed %d\n" + , u132->going); + } else if (u132->going > 0) + dev_err(&u132->platform_dev->dev, "device is being removed\n"); +} + #ifdef CONFIG_PM static int u132_bus_suspend(struct usb_hcd *hcd) @@ -2985,6 +2995,7 @@ static struct hc_driver u132_hc_driver = { .bus_suspend = u132_bus_suspend, .bus_resume = u132_bus_resume, .start_port_reset = u132_start_port_reset, + .hub_irq_enable = u132_hub_irq_enable, }; /* diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index cb7fa0eaf3a..33182f4c226 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -3264,8 +3264,6 @@ static void sisusb_disconnect(struct usb_interface *intf) /* decrement our usage count */ kref_put(&sisusb->kref, sisusb_delete); - - dev_info(&sisusb->sisusb_dev->dev, "Disconnected\n"); } static struct usb_device_id sisusb_table [] = { diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 5234e7a3bd2..0ff4a3971e4 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -637,6 +637,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) }, + { USB_DEVICE(FTDI_VID, FTDI_REU_TINY_PID) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 06e0ecabb3e..8302eca893e 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -828,6 +828,9 @@ /* Propox devices */ #define FTDI_PROPOX_JTAGCABLEII_PID 0xD738 +/* Rig Expert Ukraine devices */ +#define FTDI_REU_TINY_PID 0xED22 /* RigExpert Tiny */ + /* Commands */ #define FTDI_SIO_RESET 0 /* Reset the port */ #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index ea924dc4849..d9fb3768a2d 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -570,7 +570,12 @@ static struct usb_serial_driver ipaq_device = { .description = "PocketPC PDA", .usb_driver = &ipaq_driver, .id_table = ipaq_id_table, - .num_ports = 2, + /* + * some devices have an extra endpoint, which + * must be ignored as it would make the core + * create a second port which oopses when used + */ + .num_ports = 1, .open = ipaq_open, .close = ipaq_close, .attach = ipaq_startup, diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 43cfde83a93..a73420dd052 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -306,6 +306,7 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) }, { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) }, { USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC680) }, + { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */ { USB_DEVICE(MAXON_VENDOR_ID, 0x6280) }, /* BP3-USB & BP3-EXT HSDPA */ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864E) }, diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 103195abd41..2a0dd1b50dc 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -57,6 +57,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_PHAROS) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ALDIGA) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MMX) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GPRS) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) }, { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) }, diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index cff160abb13..6ac3bbcf7a2 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -15,6 +15,7 @@ #define PL2303_PRODUCT_ID_RSAQ3 0xaaa2 #define PL2303_PRODUCT_ID_ALDIGA 0x0611 #define PL2303_PRODUCT_ID_MMX 0x0612 +#define PL2303_PRODUCT_ID_GPRS 0x0609 #define ATEN_VENDOR_ID 0x0557 #define ATEN_VENDOR_ID2 0x0547 diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 45fe3663fa7..39a7c11795c 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -402,11 +402,19 @@ UNUSUAL_DEV( 0x04a5, 0x3010, 0x0100, 0x0100, US_FL_IGNORE_RESIDUE ), #ifdef CONFIG_USB_STORAGE_CYPRESS_ATACB +/* CY7C68300 : support atacb */ UNUSUAL_DEV( 0x04b4, 0x6830, 0x0000, 0x9999, "Cypress", "Cypress AT2LP", US_SC_CYP_ATACB, US_PR_DEVICE, NULL, 0), + +/* CY7C68310 : support atacb and atacb2 */ +UNUSUAL_DEV( 0x04b4, 0x6831, 0x0000, 0x9999, + "Cypress", + "Cypress ISD-300LP", + US_SC_CYP_ATACB, US_PR_DEVICE, NULL, + 0), #endif /* Reported by Simon Levitt <simon@whattf.com> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 002b61b4f0f..e0c5f96b273 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1825,12 +1825,13 @@ config FB_FSL_DIU config FB_W100 tristate "W100 frame buffer support" - depends on FB && PXA_SHARPSL + depends on FB && ARCH_PXA select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT ---help--- Frame buffer driver for the w100 as found on the Sharp SL-Cxx series. + It can also drive the w3220 chip found on iPAQ hx4700. This driver is also available as a module ( = code which can be inserted and removed from the running kernel whenever you want). The diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index 0a2785361ca..712dabc6269 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -286,7 +286,7 @@ static struct diu_pool pool; * rheap and make the furture large allocation fail. */ -void *fsl_diu_alloc(unsigned long size, phys_addr_t *phys) +static void *fsl_diu_alloc(unsigned long size, phys_addr_t *phys) { void *virt; @@ -311,12 +311,12 @@ void *fsl_diu_alloc(unsigned long size, phys_addr_t *phys) memset(virt, 0, size); } - pr_debug("rh virt=%p phys=%lx\n", virt, *phys); + pr_debug("rh virt=%p phys=%llx\n", virt, (unsigned long long)*phys); return virt; } -void fsl_diu_free(void *p, unsigned long size) +static void fsl_diu_free(void *p, unsigned long size) { pr_debug("p=%p size=%lu\n", p, size); @@ -770,7 +770,7 @@ static int map_video_memory(struct fb_info *info) info->fix.smem_len = info->fix.line_length * info->var.yres_virtual; pr_debug("MAP_VIDEO_MEMORY: smem_len = %d\n", info->fix.smem_len); info->screen_base = fsl_diu_alloc(info->fix.smem_len, &phys); - if (info->screen_base == 0) { + if (info->screen_base == NULL) { printk(KERN_ERR "Unable to allocate fb memory\n"); return -ENOMEM; } @@ -788,7 +788,7 @@ static int map_video_memory(struct fb_info *info) static void unmap_video_memory(struct fb_info *info) { fsl_diu_free(info->screen_base, info->fix.smem_len); - info->screen_base = 0; + info->screen_base = NULL; info->fix.smem_start = 0; info->fix.smem_len = 0; } @@ -1158,7 +1158,7 @@ static int init_fbinfo(struct fb_info *info) return 0; } -static int install_fb(struct fb_info *info) +static int __devinit install_fb(struct fb_info *info) { int rc; struct mfb_info *mfbi = info->par; @@ -1233,7 +1233,7 @@ static int install_fb(struct fb_info *info) return 0; } -static void __exit uninstall_fb(struct fb_info *info) +static void uninstall_fb(struct fb_info *info) { struct mfb_info *mfbi = info->par; @@ -1287,7 +1287,7 @@ static int request_irq_local(int irq) /* Read to clear the status */ status = in_be32(&hw->int_status); - ret = request_irq(irq, fsl_diu_isr, 0, "diu", 0); + ret = request_irq(irq, fsl_diu_isr, 0, "diu", NULL); if (ret) pr_info("Request diu IRQ failed.\n"); else { @@ -1312,7 +1312,7 @@ static void free_irq_local(int irq) /* Disable all LCDC interrupt */ out_be32(&hw->int_mask, 0x1f); - free_irq(irq, 0); + free_irq(irq, NULL); } #ifdef CONFIG_PM @@ -1324,7 +1324,7 @@ static int fsl_diu_suspend(struct of_device *ofdev, pm_message_t state) { struct fsl_diu_data *machine_data; - machine_data = dev_get_drvdata(&ofdev->dev); + machine_data = dev_get_drvdata(&dev->dev); disable_lcdc(machine_data->fsl_diu_info[0]); return 0; @@ -1334,7 +1334,7 @@ static int fsl_diu_resume(struct of_device *ofdev) { struct fsl_diu_data *machine_data; - machine_data = dev_get_drvdata(&ofdev->dev); + machine_data = dev_get_drvdata(&dev->dev); enable_lcdc(machine_data->fsl_diu_info[0]); return 0; @@ -1353,7 +1353,8 @@ static int allocate_buf(struct diu_addr *buf, u32 size, u32 bytes_align) dma_addr_t paddr = 0; ssize = size + bytes_align; - buf->vaddr = dma_alloc_coherent(0, ssize, &paddr, GFP_DMA | __GFP_ZERO); + buf->vaddr = dma_alloc_coherent(NULL, ssize, &paddr, GFP_DMA | + __GFP_ZERO); if (!buf->vaddr) return -ENOMEM; @@ -1371,7 +1372,7 @@ static int allocate_buf(struct diu_addr *buf, u32 size, u32 bytes_align) static void free_buf(struct diu_addr *buf, u32 size, u32 bytes_align) { - dma_free_coherent(0, size + bytes_align, + dma_free_coherent(NULL, size + bytes_align, buf->vaddr, (buf->paddr - buf->offset)); return; } @@ -1411,7 +1412,7 @@ static ssize_t show_monitor(struct device *device, return diu_ops.show_monitor_port(machine_data->monitor_port, buf); } -static int fsl_diu_probe(struct of_device *ofdev, +static int __devinit fsl_diu_probe(struct of_device *ofdev, const struct of_device_id *match) { struct device_node *np = ofdev->node; diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index 7dcda187d9b..fafe7db20d6 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -1246,7 +1246,7 @@ static int pxafb_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 pxafb_map_video_memory(struct pxafb_info *fbi) +static int __devinit pxafb_map_video_memory(struct pxafb_info *fbi) { /* * We reserve one page for the palette, plus the size @@ -1348,7 +1348,7 @@ decode_mode: pxafb_decode_mode_info(fbi, inf->modes, inf->num_modes); } -static struct pxafb_info * __init pxafb_init_fbinfo(struct device *dev) +static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev) { struct pxafb_info *fbi; void *addr; @@ -1410,7 +1410,7 @@ static struct pxafb_info * __init pxafb_init_fbinfo(struct device *dev) } #ifdef CONFIG_FB_PXA_PARAMETERS -static int __init parse_opt_mode(struct device *dev, const char *this_opt) +static int __devinit parse_opt_mode(struct device *dev, const char *this_opt) { struct pxafb_mach_info *inf = dev->platform_data; @@ -1469,7 +1469,7 @@ done: return 0; } -static int __init parse_opt(struct device *dev, char *this_opt) +static int __devinit parse_opt(struct device *dev, char *this_opt) { struct pxafb_mach_info *inf = dev->platform_data; struct pxafb_mode_info *mode = &inf->modes[0]; @@ -1567,7 +1567,7 @@ static int __init parse_opt(struct device *dev, char *this_opt) return 0; } -static int __init pxafb_parse_options(struct device *dev, char *options) +static int __devinit pxafb_parse_options(struct device *dev, char *options) { char *this_opt; int ret; @@ -1588,8 +1588,8 @@ static int __init pxafb_parse_options(struct device *dev, char *options) static char g_options[256] __devinitdata = ""; -#ifndef CONFIG_MODULES -static int __devinit pxafb_setup_options(void) +#ifndef MODULE +static int __init pxafb_setup_options(void) { char *options = NULL; @@ -1613,7 +1613,7 @@ MODULE_PARM_DESC(options, "LCD parameters (see Documentation/fb/pxafb.txt)"); #define pxafb_setup_options() (0) #endif -static int __init pxafb_probe(struct platform_device *dev) +static int __devinit pxafb_probe(struct platform_device *dev) { struct pxafb_info *fbi; struct pxafb_mach_info *inf; @@ -1685,14 +1685,14 @@ static int __init pxafb_probe(struct platform_device *dev) if (r == NULL) { dev_err(&dev->dev, "no I/O memory resource defined\n"); ret = -ENODEV; - goto failed; + goto failed_fbi; } r = request_mem_region(r->start, r->end - r->start + 1, dev->name); if (r == NULL) { dev_err(&dev->dev, "failed to request I/O memory\n"); ret = -EBUSY; - goto failed; + goto failed_fbi; } fbi->mmio_base = ioremap(r->start, r->end - r->start + 1); @@ -1735,8 +1735,17 @@ static int __init pxafb_probe(struct platform_device *dev) * This makes sure that our colour bitfield * descriptors are correctly initialised. */ - pxafb_check_var(&fbi->fb.var, &fbi->fb); - pxafb_set_par(&fbi->fb); + ret = pxafb_check_var(&fbi->fb.var, &fbi->fb); + if (ret) { + dev_err(&dev->dev, "failed to get suitable mode\n"); + goto failed_free_irq; + } + + ret = pxafb_set_par(&fbi->fb); + if (ret) { + dev_err(&dev->dev, "Failed to set parameters\n"); + goto failed_free_irq; + } platform_set_drvdata(dev, fbi); @@ -1744,7 +1753,7 @@ static int __init pxafb_probe(struct platform_device *dev) if (ret < 0) { dev_err(&dev->dev, "Failed to register framebuffer device: %d\n", ret); - goto failed_free_irq; + goto failed_free_cmap; } #ifdef CONFIG_CPU_FREQ @@ -1763,18 +1772,23 @@ static int __init pxafb_probe(struct platform_device *dev) return 0; +failed_free_cmap: + if (fbi->fb.cmap.len) + fb_dealloc_cmap(&fbi->fb.cmap); failed_free_irq: free_irq(irq, fbi); -failed_free_res: - release_mem_region(r->start, r->end - r->start + 1); -failed_free_io: - iounmap(fbi->mmio_base); failed_free_mem: dma_free_writecombine(&dev->dev, fbi->map_size, fbi->map_cpu, fbi->map_dma); -failed: +failed_free_io: + iounmap(fbi->mmio_base); +failed_free_res: + release_mem_region(r->start, r->end - r->start + 1); +failed_fbi: + clk_put(fbi->clk); platform_set_drvdata(dev, NULL); kfree(fbi); +failed: return ret; } @@ -1787,7 +1801,7 @@ static struct platform_driver pxafb_driver = { }, }; -static int __devinit pxafb_init(void) +static int __init pxafb_init(void) { if (pxafb_setup_options()) return -EINVAL; diff --git a/drivers/video/w100fb.c b/drivers/video/w100fb.c index 30469bf906e..d0674f1e3f1 100644 --- a/drivers/video/w100fb.c +++ b/drivers/video/w100fb.c @@ -1003,6 +1003,7 @@ static struct w100_pll_info xtal_14318000[] = { static struct w100_pll_info xtal_16000000[] = { /*freq M N_int N_fac tfgoal lock_time */ { 72, 1, 8, 0, 0xe0, 48}, /* tfgoal guessed */ + { 80, 1, 9, 0, 0xe0, 13}, /* tfgoal guessed */ { 95, 1, 10, 7, 0xe0, 38}, /* tfgoal guessed */ { 96, 1, 11, 0, 0xe0, 36}, /* tfgoal guessed */ { 0, 0, 0, 0, 0, 0}, diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c index 619a6f8d65a..47ed39b52f9 100644 --- a/drivers/video/xen-fbfront.c +++ b/drivers/video/xen-fbfront.c @@ -18,6 +18,7 @@ * frame buffer. */ +#include <linux/console.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/fb.h> @@ -42,37 +43,68 @@ struct xenfb_info { struct xenfb_page *page; unsigned long *mfns; int update_wanted; /* XENFB_TYPE_UPDATE wanted */ + int feature_resize; /* XENFB_TYPE_RESIZE ok */ + struct xenfb_resize resize; /* protected by resize_lock */ + int resize_dpy; /* ditto */ + spinlock_t resize_lock; struct xenbus_device *xbdev; }; -static u32 xenfb_mem_len = XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8; +#define XENFB_DEFAULT_FB_LEN (XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8) +enum { KPARAM_MEM, KPARAM_WIDTH, KPARAM_HEIGHT, KPARAM_CNT }; +static int video[KPARAM_CNT] = { 2, XENFB_WIDTH, XENFB_HEIGHT }; +module_param_array(video, int, NULL, 0); +MODULE_PARM_DESC(video, + "Video memory size in MB, width, height in pixels (default 2,800,600)"); + +static void xenfb_make_preferred_console(void); static int xenfb_remove(struct xenbus_device *); -static void xenfb_init_shared_page(struct xenfb_info *); +static void xenfb_init_shared_page(struct xenfb_info *, struct fb_info *); static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *); static void xenfb_disconnect_backend(struct xenfb_info *); +static void xenfb_send_event(struct xenfb_info *info, + union xenfb_out_event *event) +{ + u32 prod; + + prod = info->page->out_prod; + /* caller ensures !xenfb_queue_full() */ + mb(); /* ensure ring space available */ + XENFB_OUT_RING_REF(info->page, prod) = *event; + wmb(); /* ensure ring contents visible */ + info->page->out_prod = prod + 1; + + notify_remote_via_irq(info->irq); +} + static void xenfb_do_update(struct xenfb_info *info, int x, int y, int w, int h) { union xenfb_out_event event; - u32 prod; + memset(&event, 0, sizeof(event)); event.type = XENFB_TYPE_UPDATE; event.update.x = x; event.update.y = y; event.update.width = w; event.update.height = h; - prod = info->page->out_prod; /* caller ensures !xenfb_queue_full() */ - mb(); /* ensure ring space available */ - XENFB_OUT_RING_REF(info->page, prod) = event; - wmb(); /* ensure ring contents visible */ - info->page->out_prod = prod + 1; + xenfb_send_event(info, &event); +} - notify_remote_via_irq(info->irq); +static void xenfb_do_resize(struct xenfb_info *info) +{ + union xenfb_out_event event; + + memset(&event, 0, sizeof(event)); + event.resize = info->resize; + + /* caller ensures !xenfb_queue_full() */ + xenfb_send_event(info, &event); } static int xenfb_queue_full(struct xenfb_info *info) @@ -84,12 +116,28 @@ static int xenfb_queue_full(struct xenfb_info *info) return prod - cons == XENFB_OUT_RING_LEN; } +static void xenfb_handle_resize_dpy(struct xenfb_info *info) +{ + unsigned long flags; + + spin_lock_irqsave(&info->resize_lock, flags); + if (info->resize_dpy) { + if (!xenfb_queue_full(info)) { + info->resize_dpy = 0; + xenfb_do_resize(info); + } + } + spin_unlock_irqrestore(&info->resize_lock, flags); +} + static void xenfb_refresh(struct xenfb_info *info, int x1, int y1, int w, int h) { unsigned long flags; - int y2 = y1 + h - 1; int x2 = x1 + w - 1; + int y2 = y1 + h - 1; + + xenfb_handle_resize_dpy(info); if (!info->update_wanted) return; @@ -222,6 +270,57 @@ static ssize_t xenfb_write(struct fb_info *p, const char __user *buf, return res; } +static int +xenfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct xenfb_info *xenfb_info; + int required_mem_len; + + xenfb_info = info->par; + + if (!xenfb_info->feature_resize) { + if (var->xres == video[KPARAM_WIDTH] && + var->yres == video[KPARAM_HEIGHT] && + var->bits_per_pixel == xenfb_info->page->depth) { + return 0; + } + return -EINVAL; + } + + /* Can't resize past initial width and height */ + if (var->xres > video[KPARAM_WIDTH] || var->yres > video[KPARAM_HEIGHT]) + return -EINVAL; + + required_mem_len = var->xres * var->yres * xenfb_info->page->depth / 8; + if (var->bits_per_pixel == xenfb_info->page->depth && + var->xres <= info->fix.line_length / (XENFB_DEPTH / 8) && + required_mem_len <= info->fix.smem_len) { + var->xres_virtual = var->xres; + var->yres_virtual = var->yres; + return 0; + } + return -EINVAL; +} + +static int xenfb_set_par(struct fb_info *info) +{ + struct xenfb_info *xenfb_info; + unsigned long flags; + + xenfb_info = info->par; + + spin_lock_irqsave(&xenfb_info->resize_lock, flags); + xenfb_info->resize.type = XENFB_TYPE_RESIZE; + xenfb_info->resize.width = info->var.xres; + xenfb_info->resize.height = info->var.yres; + xenfb_info->resize.stride = info->fix.line_length; + xenfb_info->resize.depth = info->var.bits_per_pixel; + xenfb_info->resize.offset = 0; + xenfb_info->resize_dpy = 1; + spin_unlock_irqrestore(&xenfb_info->resize_lock, flags); + return 0; +} + static struct fb_ops xenfb_fb_ops = { .owner = THIS_MODULE, .fb_read = fb_sys_read, @@ -230,6 +329,8 @@ static struct fb_ops xenfb_fb_ops = { .fb_fillrect = xenfb_fillrect, .fb_copyarea = xenfb_copyarea, .fb_imageblit = xenfb_imageblit, + .fb_check_var = xenfb_check_var, + .fb_set_par = xenfb_set_par, }; static irqreturn_t xenfb_event_handler(int rq, void *dev_id) @@ -258,6 +359,8 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, { struct xenfb_info *info; struct fb_info *fb_info; + int fb_size; + int val; int ret; info = kzalloc(sizeof(*info), GFP_KERNEL); @@ -265,18 +368,35 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); return -ENOMEM; } + + /* Limit kernel param videoram amount to what is in xenstore */ + if (xenbus_scanf(XBT_NIL, dev->otherend, "videoram", "%d", &val) == 1) { + if (val < video[KPARAM_MEM]) + video[KPARAM_MEM] = val; + } + + /* If requested res does not fit in available memory, use default */ + fb_size = video[KPARAM_MEM] * 1024 * 1024; + if (video[KPARAM_WIDTH] * video[KPARAM_HEIGHT] * XENFB_DEPTH / 8 + > fb_size) { + video[KPARAM_WIDTH] = XENFB_WIDTH; + video[KPARAM_HEIGHT] = XENFB_HEIGHT; + fb_size = XENFB_DEFAULT_FB_LEN; + } + dev->dev.driver_data = info; info->xbdev = dev; info->irq = -1; info->x1 = info->y1 = INT_MAX; spin_lock_init(&info->dirty_lock); + spin_lock_init(&info->resize_lock); - info->fb = vmalloc(xenfb_mem_len); + info->fb = vmalloc(fb_size); if (info->fb == NULL) goto error_nomem; - memset(info->fb, 0, xenfb_mem_len); + memset(info->fb, 0, fb_size); - info->nr_pages = (xenfb_mem_len + PAGE_SIZE - 1) >> PAGE_SHIFT; + info->nr_pages = (fb_size + PAGE_SIZE - 1) >> PAGE_SHIFT; info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages); if (!info->mfns) @@ -287,8 +407,6 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, if (!info->page) goto error_nomem; - xenfb_init_shared_page(info); - /* abusing framebuffer_alloc() to allocate pseudo_palette */ fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL); if (fb_info == NULL) @@ -301,9 +419,9 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, fb_info->screen_base = info->fb; fb_info->fbops = &xenfb_fb_ops; - fb_info->var.xres_virtual = fb_info->var.xres = info->page->width; - fb_info->var.yres_virtual = fb_info->var.yres = info->page->height; - fb_info->var.bits_per_pixel = info->page->depth; + fb_info->var.xres_virtual = fb_info->var.xres = video[KPARAM_WIDTH]; + fb_info->var.yres_virtual = fb_info->var.yres = video[KPARAM_HEIGHT]; + fb_info->var.bits_per_pixel = XENFB_DEPTH; fb_info->var.red = (struct fb_bitfield){16, 8, 0}; fb_info->var.green = (struct fb_bitfield){8, 8, 0}; @@ -315,9 +433,9 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, fb_info->var.vmode = FB_VMODE_NONINTERLACED; fb_info->fix.visual = FB_VISUAL_TRUECOLOR; - fb_info->fix.line_length = info->page->line_length; + fb_info->fix.line_length = fb_info->var.xres * XENFB_DEPTH / 8; fb_info->fix.smem_start = 0; - fb_info->fix.smem_len = xenfb_mem_len; + fb_info->fix.smem_len = fb_size; strcpy(fb_info->fix.id, "xen"); fb_info->fix.type = FB_TYPE_PACKED_PIXELS; fb_info->fix.accel = FB_ACCEL_NONE; @@ -334,6 +452,8 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, fb_info->fbdefio = &xenfb_defio; fb_deferred_io_init(fb_info); + xenfb_init_shared_page(info, fb_info); + ret = register_framebuffer(fb_info); if (ret) { fb_deferred_io_cleanup(fb_info); @@ -348,6 +468,7 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, if (ret < 0) goto error; + xenfb_make_preferred_console(); return 0; error_nomem: @@ -358,12 +479,34 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, return ret; } +static __devinit void +xenfb_make_preferred_console(void) +{ + struct console *c; + + if (console_set_on_cmdline) + return; + + acquire_console_sem(); + for (c = console_drivers; c; c = c->next) { + if (!strcmp(c->name, "tty") && c->index == 0) + break; + } + release_console_sem(); + if (c) { + unregister_console(c); + c->flags |= CON_CONSDEV; + c->flags &= ~CON_PRINTBUFFER; /* don't print again */ + register_console(c); + } +} + static int xenfb_resume(struct xenbus_device *dev) { struct xenfb_info *info = dev->dev.driver_data; xenfb_disconnect_backend(info); - xenfb_init_shared_page(info); + xenfb_init_shared_page(info, info->fb_info); return xenfb_connect_backend(dev, info); } @@ -391,20 +534,23 @@ static unsigned long vmalloc_to_mfn(void *address) return pfn_to_mfn(vmalloc_to_pfn(address)); } -static void xenfb_init_shared_page(struct xenfb_info *info) +static void xenfb_init_shared_page(struct xenfb_info *info, + struct fb_info *fb_info) { int i; + int epd = PAGE_SIZE / sizeof(info->mfns[0]); for (i = 0; i < info->nr_pages; i++) info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE); - info->page->pd[0] = vmalloc_to_mfn(info->mfns); - info->page->pd[1] = 0; - info->page->width = XENFB_WIDTH; - info->page->height = XENFB_HEIGHT; - info->page->depth = XENFB_DEPTH; - info->page->line_length = (info->page->depth / 8) * info->page->width; - info->page->mem_length = xenfb_mem_len; + for (i = 0; i * epd < info->nr_pages; i++) + info->page->pd[i] = vmalloc_to_mfn(&info->mfns[i * epd]); + + info->page->width = fb_info->var.xres; + info->page->height = fb_info->var.yres; + info->page->depth = fb_info->var.bits_per_pixel; + info->page->line_length = fb_info->fix.line_length; + info->page->mem_length = fb_info->fix.smem_len; info->page->in_cons = info->page->in_prod = 0; info->page->out_cons = info->page->out_prod = 0; } @@ -504,6 +650,11 @@ InitWait: val = 0; if (val) info->update_wanted = 1; + + if (xenbus_scanf(XBT_NIL, dev->otherend, + "feature-resize", "%d", &val) < 0) + val = 0; + info->feature_resize = val; break; case XenbusStateClosing: @@ -547,4 +698,6 @@ static void __exit xenfb_cleanup(void) module_init(xenfb_init); module_exit(xenfb_cleanup); +MODULE_DESCRIPTION("Xen virtual framebuffer device frontend"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("xen:vfb"); diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 6a63535fc04..eaa3f2a79ff 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -140,49 +140,53 @@ static struct pci_device_id hpwdt_devices[] = { }; MODULE_DEVICE_TABLE(pci, hpwdt_devices); +extern asmlinkage void asminline_call(struct cmn_registers *pi86Regs, unsigned long *pRomEntry); + #ifndef CONFIG_X86_64 /* --32 Bit Bios------------------------------------------------------------ */ #define HPWDT_ARCH 32 -static void asminline_call(struct cmn_registers *pi86Regs, - unsigned long *pRomEntry) -{ - asm("pushl %ebp \n\t" - "movl %esp, %ebp \n\t" - "pusha \n\t" - "pushf \n\t" - "push %es \n\t" - "push %ds \n\t" - "pop %es \n\t" - "movl 8(%ebp),%eax \n\t" - "movl 4(%eax),%ebx \n\t" - "movl 8(%eax),%ecx \n\t" - "movl 12(%eax),%edx \n\t" - "movl 16(%eax),%esi \n\t" - "movl 20(%eax),%edi \n\t" - "movl (%eax),%eax \n\t" - "push %cs \n\t" - "call *12(%ebp) \n\t" - "pushf \n\t" - "pushl %eax \n\t" - "movl 8(%ebp),%eax \n\t" - "movl %ebx,4(%eax) \n\t" - "movl %ecx,8(%eax) \n\t" - "movl %edx,12(%eax) \n\t" - "movl %esi,16(%eax) \n\t" - "movl %edi,20(%eax) \n\t" - "movw %ds,24(%eax) \n\t" - "movw %es,26(%eax) \n\t" - "popl %ebx \n\t" - "movl %ebx,(%eax) \n\t" - "popl %ebx \n\t" - "movl %ebx,28(%eax) \n\t" - "pop %es \n\t" - "popf \n\t" - "popa \n\t" - "leave \n\t" "ret"); -} +asm(".text \n\t" + ".align 4 \n" + "asminline_call: \n\t" + "pushl %ebp \n\t" + "movl %esp, %ebp \n\t" + "pusha \n\t" + "pushf \n\t" + "push %es \n\t" + "push %ds \n\t" + "pop %es \n\t" + "movl 8(%ebp),%eax \n\t" + "movl 4(%eax),%ebx \n\t" + "movl 8(%eax),%ecx \n\t" + "movl 12(%eax),%edx \n\t" + "movl 16(%eax),%esi \n\t" + "movl 20(%eax),%edi \n\t" + "movl (%eax),%eax \n\t" + "push %cs \n\t" + "call *12(%ebp) \n\t" + "pushf \n\t" + "pushl %eax \n\t" + "movl 8(%ebp),%eax \n\t" + "movl %ebx,4(%eax) \n\t" + "movl %ecx,8(%eax) \n\t" + "movl %edx,12(%eax) \n\t" + "movl %esi,16(%eax) \n\t" + "movl %edi,20(%eax) \n\t" + "movw %ds,24(%eax) \n\t" + "movw %es,26(%eax) \n\t" + "popl %ebx \n\t" + "movl %ebx,(%eax) \n\t" + "popl %ebx \n\t" + "movl %ebx,28(%eax) \n\t" + "pop %es \n\t" + "popf \n\t" + "popa \n\t" + "leave \n\t" + "ret \n\t" + ".previous"); + /* * cru_detect @@ -333,43 +337,44 @@ static int __devinit detect_cru_service(void) #define HPWDT_ARCH 64 -static void asminline_call(struct cmn_registers *pi86Regs, - unsigned long *pRomEntry) -{ - asm("pushq %rbp \n\t" - "movq %rsp, %rbp \n\t" - "pushq %rax \n\t" - "pushq %rbx \n\t" - "pushq %rdx \n\t" - "pushq %r12 \n\t" - "pushq %r9 \n\t" - "movq %rsi, %r12 \n\t" - "movq %rdi, %r9 \n\t" - "movl 4(%r9),%ebx \n\t" - "movl 8(%r9),%ecx \n\t" - "movl 12(%r9),%edx \n\t" - "movl 16(%r9),%esi \n\t" - "movl 20(%r9),%edi \n\t" - "movl (%r9),%eax \n\t" - "call *%r12 \n\t" - "pushfq \n\t" - "popq %r12 \n\t" - "popfq \n\t" - "movl %eax, (%r9) \n\t" - "movl %ebx, 4(%r9) \n\t" - "movl %ecx, 8(%r9) \n\t" - "movl %edx, 12(%r9) \n\t" - "movl %esi, 16(%r9) \n\t" - "movl %edi, 20(%r9) \n\t" - "movq %r12, %rax \n\t" - "movl %eax, 28(%r9) \n\t" - "popq %r9 \n\t" - "popq %r12 \n\t" - "popq %rdx \n\t" - "popq %rbx \n\t" - "popq %rax \n\t" - "leave \n\t" "ret"); -} +asm(".text \n\t" + ".align 4 \n" + "asminline_call: \n\t" + "pushq %rbp \n\t" + "movq %rsp, %rbp \n\t" + "pushq %rax \n\t" + "pushq %rbx \n\t" + "pushq %rdx \n\t" + "pushq %r12 \n\t" + "pushq %r9 \n\t" + "movq %rsi, %r12 \n\t" + "movq %rdi, %r9 \n\t" + "movl 4(%r9),%ebx \n\t" + "movl 8(%r9),%ecx \n\t" + "movl 12(%r9),%edx \n\t" + "movl 16(%r9),%esi \n\t" + "movl 20(%r9),%edi \n\t" + "movl (%r9),%eax \n\t" + "call *%r12 \n\t" + "pushfq \n\t" + "popq %r12 \n\t" + "popfq \n\t" + "movl %eax, (%r9) \n\t" + "movl %ebx, 4(%r9) \n\t" + "movl %ecx, 8(%r9) \n\t" + "movl %edx, 12(%r9) \n\t" + "movl %esi, 16(%r9) \n\t" + "movl %edi, 20(%r9) \n\t" + "movq %r12, %rax \n\t" + "movl %eax, 28(%r9) \n\t" + "popq %r9 \n\t" + "popq %r12 \n\t" + "popq %rdx \n\t" + "popq %rbx \n\t" + "popq %rax \n\t" + "leave \n\t" + "ret \n\t" + ".previous"); /* * dmi_find_cru @@ -418,20 +423,23 @@ static int hpwdt_pretimeout(struct notifier_block *nb, unsigned long ulReason, static unsigned long rom_pl; static int die_nmi_called; - if (ulReason == DIE_NMI || ulReason == DIE_NMI_IPI) { - spin_lock_irqsave(&rom_lock, rom_pl); - if (!die_nmi_called) - asminline_call(&cmn_regs, cru_rom_addr); - die_nmi_called = 1; - spin_unlock_irqrestore(&rom_lock, rom_pl); - if (cmn_regs.u1.ral != 0) { - panic("An NMI occurred, please see the Integrated " - "Management Log for details.\n"); - } + if (ulReason != DIE_NMI && ulReason != DIE_NMI_IPI) + return NOTIFY_OK; + + spin_lock_irqsave(&rom_lock, rom_pl); + if (!die_nmi_called) + asminline_call(&cmn_regs, cru_rom_addr); + die_nmi_called = 1; + spin_unlock_irqrestore(&rom_lock, rom_pl); + if (cmn_regs.u1.ral == 0) { + printk(KERN_WARNING "hpwdt: An NMI occurred, " + "but unable to determine source.\n"); + } else { + panic("An NMI occurred, please see the Integrated " + "Management Log for details.\n"); } - die_nmi_called = 0; - return NOTIFY_DONE; + return NOTIFY_STOP; } /* diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index 37af04f1ffd..363286c5429 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -1,4 +1,4 @@ -obj-y += grant-table.o features.o events.o +obj-y += grant-table.o features.o events.o manage.o obj-y += xenbus/ obj-$(CONFIG_XEN_XENCOMM) += xencomm.o obj-$(CONFIG_XEN_BALLOON) += balloon.o diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index ab25ba6cbbb..591bc29b55f 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -225,7 +225,7 @@ static int increase_reservation(unsigned long nr_pages) page = balloon_next_page(page); } - reservation.extent_start = (unsigned long)frame_list; + set_xen_guest_handle(reservation.extent_start, frame_list); reservation.nr_extents = nr_pages; rc = HYPERVISOR_memory_op( XENMEM_populate_physmap, &reservation); @@ -321,7 +321,7 @@ static int decrease_reservation(unsigned long nr_pages) balloon_append(pfn_to_page(pfn)); } - reservation.extent_start = (unsigned long)frame_list; + set_xen_guest_handle(reservation.extent_start, frame_list); reservation.nr_extents = nr_pages; ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation); BUG_ON(ret != nr_pages); @@ -368,7 +368,7 @@ static void balloon_process(struct work_struct *work) } /* Resets the Xen limit, sets new target, and kicks off processing. */ -void balloon_set_new_target(unsigned long target) +static void balloon_set_new_target(unsigned long target) { /* No need for lock. Not read-modify-write updates. */ balloon_stats.hard_limit = ~0UL; @@ -483,7 +483,7 @@ static int dealloc_pte_fn( .extent_order = 0, .domid = DOMID_SELF }; - reservation.extent_start = (unsigned long)&mfn; + set_xen_guest_handle(reservation.extent_start, &mfn); set_pte_at(&init_mm, addr, pte, __pte_ma(0ull)); set_phys_to_machine(__pa(addr) >> PAGE_SHIFT, INVALID_P2M_ENTRY); ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation); @@ -519,7 +519,7 @@ static struct page **alloc_empty_pages_and_pagevec(int nr_pages) .extent_order = 0, .domid = DOMID_SELF }; - reservation.extent_start = (unsigned long)&gmfn; + set_xen_guest_handle(reservation.extent_start, &gmfn); ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation); if (ret == 1) diff --git a/drivers/xen/events.c b/drivers/xen/events.c index 4f0f22b020e..332dd63750a 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -355,7 +355,7 @@ static void unbind_from_irq(unsigned int irq) spin_lock(&irq_mapping_update_lock); - if (VALID_EVTCHN(evtchn) && (--irq_bindcount[irq] == 0)) { + if ((--irq_bindcount[irq] == 0) && VALID_EVTCHN(evtchn)) { close.port = evtchn; if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close) != 0) BUG(); @@ -375,7 +375,7 @@ static void unbind_from_irq(unsigned int irq) evtchn_to_irq[evtchn] = -1; irq_info[irq] = IRQ_UNBOUND; - dynamic_irq_init(irq); + dynamic_irq_cleanup(irq); } spin_unlock(&irq_mapping_update_lock); @@ -529,7 +529,7 @@ void xen_evtchn_do_upcall(struct pt_regs *regs) #ifndef CONFIG_X86 /* No need for a barrier -- XCHG is a barrier on x86. */ /* Clear master flag /before/ clearing selector flag. */ - rmb(); + wmb(); #endif pending_words = xchg(&vcpu_info->evtchn_pending_sel, 0); while (pending_words != 0) { @@ -557,6 +557,33 @@ out: put_cpu(); } +/* Rebind a new event channel to an existing irq. */ +void rebind_evtchn_irq(int evtchn, int irq) +{ + /* Make sure the irq is masked, since the new event channel + will also be masked. */ + disable_irq(irq); + + spin_lock(&irq_mapping_update_lock); + + /* After resume the irq<->evtchn mappings are all cleared out */ + BUG_ON(evtchn_to_irq[evtchn] != -1); + /* Expect irq to have been bound before, + so the bindcount should be non-0 */ + BUG_ON(irq_bindcount[irq] == 0); + + evtchn_to_irq[evtchn] = irq; + irq_info[irq] = mk_irq_info(IRQT_EVTCHN, 0, evtchn); + + spin_unlock(&irq_mapping_update_lock); + + /* new event channels are always bound to cpu 0 */ + irq_set_affinity(irq, cpumask_of_cpu(0)); + + /* Unmask the event channel. */ + enable_irq(irq); +} + /* Rebind an evtchn so that it gets delivered to a specific cpu */ static void rebind_irq_to_cpu(unsigned irq, unsigned tcpu) { @@ -647,6 +674,89 @@ static int retrigger_dynirq(unsigned int irq) return ret; } +static void restore_cpu_virqs(unsigned int cpu) +{ + struct evtchn_bind_virq bind_virq; + int virq, irq, evtchn; + + for (virq = 0; virq < NR_VIRQS; virq++) { + if ((irq = per_cpu(virq_to_irq, cpu)[virq]) == -1) + continue; + + BUG_ON(irq_info[irq].type != IRQT_VIRQ); + BUG_ON(irq_info[irq].index != virq); + + /* Get a new binding from Xen. */ + bind_virq.virq = virq; + bind_virq.vcpu = cpu; + if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, + &bind_virq) != 0) + BUG(); + evtchn = bind_virq.port; + + /* Record the new mapping. */ + evtchn_to_irq[evtchn] = irq; + irq_info[irq] = mk_irq_info(IRQT_VIRQ, virq, evtchn); + bind_evtchn_to_cpu(evtchn, cpu); + + /* Ready for use. */ + unmask_evtchn(evtchn); + } +} + +static void restore_cpu_ipis(unsigned int cpu) +{ + struct evtchn_bind_ipi bind_ipi; + int ipi, irq, evtchn; + + for (ipi = 0; ipi < XEN_NR_IPIS; ipi++) { + if ((irq = per_cpu(ipi_to_irq, cpu)[ipi]) == -1) + continue; + + BUG_ON(irq_info[irq].type != IRQT_IPI); + BUG_ON(irq_info[irq].index != ipi); + + /* Get a new binding from Xen. */ + bind_ipi.vcpu = cpu; + if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi, + &bind_ipi) != 0) + BUG(); + evtchn = bind_ipi.port; + + /* Record the new mapping. */ + evtchn_to_irq[evtchn] = irq; + irq_info[irq] = mk_irq_info(IRQT_IPI, ipi, evtchn); + bind_evtchn_to_cpu(evtchn, cpu); + + /* Ready for use. */ + unmask_evtchn(evtchn); + + } +} + +void xen_irq_resume(void) +{ + unsigned int cpu, irq, evtchn; + + init_evtchn_cpu_bindings(); + + /* New event-channel space is not 'live' yet. */ + for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++) + mask_evtchn(evtchn); + + /* No IRQ <-> event-channel mappings. */ + for (irq = 0; irq < NR_IRQS; irq++) + irq_info[irq].evtchn = 0; /* zap event-channel binding */ + + for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++) + evtchn_to_irq[evtchn] = -1; + + for_each_possible_cpu(cpu) { + restore_cpu_virqs(cpu); + restore_cpu_ipis(cpu); + } +} + static struct irq_chip xen_dynamic_chip __read_mostly = { .name = "xen-dyn", .mask = disable_dynirq, diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 52b6b41b909..e9e11168616 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -471,14 +471,14 @@ static int gnttab_map(unsigned int start_idx, unsigned int end_idx) return 0; } -static int gnttab_resume(void) +int gnttab_resume(void) { if (max_nr_grant_frames() < nr_grant_frames) return -ENOSYS; return gnttab_map(0, nr_grant_frames - 1); } -static int gnttab_suspend(void) +int gnttab_suspend(void) { arch_gnttab_unmap_shared(shared, nr_grant_frames); return 0; diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c new file mode 100644 index 00000000000..5b546e365f0 --- /dev/null +++ b/drivers/xen/manage.c @@ -0,0 +1,252 @@ +/* + * Handle extern requests for shutdown, reboot and sysrq + */ +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/reboot.h> +#include <linux/sysrq.h> +#include <linux/stop_machine.h> +#include <linux/freezer.h> + +#include <xen/xenbus.h> +#include <xen/grant_table.h> +#include <xen/events.h> +#include <xen/hvc-console.h> +#include <xen/xen-ops.h> + +#include <asm/xen/hypercall.h> +#include <asm/xen/page.h> + +enum shutdown_state { + SHUTDOWN_INVALID = -1, + SHUTDOWN_POWEROFF = 0, + SHUTDOWN_SUSPEND = 2, + /* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only + report a crash, not be instructed to crash! + HALT is the same as POWEROFF, as far as we're concerned. The tools use + the distinction when we return the reason code to them. */ + SHUTDOWN_HALT = 4, +}; + +/* Ignore multiple shutdown requests. */ +static enum shutdown_state shutting_down = SHUTDOWN_INVALID; + +#ifdef CONFIG_PM_SLEEP +static int xen_suspend(void *data) +{ + int *cancelled = data; + int err; + + BUG_ON(!irqs_disabled()); + + load_cr3(swapper_pg_dir); + + err = device_power_down(PMSG_SUSPEND); + if (err) { + printk(KERN_ERR "xen_suspend: device_power_down failed: %d\n", + err); + return err; + } + + xen_mm_pin_all(); + gnttab_suspend(); + xen_pre_suspend(); + + /* + * This hypercall returns 1 if suspend was cancelled + * or the domain was merely checkpointed, and 0 if it + * is resuming in a new domain. + */ + *cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info)); + + xen_post_suspend(*cancelled); + gnttab_resume(); + xen_mm_unpin_all(); + + device_power_up(); + + if (!*cancelled) { + xen_irq_resume(); + xen_console_resume(); + } + + return 0; +} + +static void do_suspend(void) +{ + int err; + int cancelled = 1; + + shutting_down = SHUTDOWN_SUSPEND; + +#ifdef CONFIG_PREEMPT + /* If the kernel is preemptible, we need to freeze all the processes + to prevent them from being in the middle of a pagetable update + during suspend. */ + err = freeze_processes(); + if (err) { + printk(KERN_ERR "xen suspend: freeze failed %d\n", err); + return; + } +#endif + + err = device_suspend(PMSG_SUSPEND); + if (err) { + printk(KERN_ERR "xen suspend: device_suspend %d\n", err); + goto out; + } + + printk("suspending xenbus...\n"); + /* XXX use normal device tree? */ + xenbus_suspend(); + + err = stop_machine_run(xen_suspend, &cancelled, 0); + if (err) { + printk(KERN_ERR "failed to start xen_suspend: %d\n", err); + goto out; + } + + if (!cancelled) + xenbus_resume(); + else + xenbus_suspend_cancel(); + + device_resume(); + + /* Make sure timer events get retriggered on all CPUs */ + clock_was_set(); +out: +#ifdef CONFIG_PREEMPT + thaw_processes(); +#endif + shutting_down = SHUTDOWN_INVALID; +} +#endif /* CONFIG_PM_SLEEP */ + +static void shutdown_handler(struct xenbus_watch *watch, + const char **vec, unsigned int len) +{ + char *str; + struct xenbus_transaction xbt; + int err; + + if (shutting_down != SHUTDOWN_INVALID) + return; + + again: + err = xenbus_transaction_start(&xbt); + if (err) + return; + + str = (char *)xenbus_read(xbt, "control", "shutdown", NULL); + /* Ignore read errors and empty reads. */ + if (XENBUS_IS_ERR_READ(str)) { + xenbus_transaction_end(xbt, 1); + return; + } + + xenbus_write(xbt, "control", "shutdown", ""); + + err = xenbus_transaction_end(xbt, 0); + if (err == -EAGAIN) { + kfree(str); + goto again; + } + + if (strcmp(str, "poweroff") == 0 || + strcmp(str, "halt") == 0) { + shutting_down = SHUTDOWN_POWEROFF; + orderly_poweroff(false); + } else if (strcmp(str, "reboot") == 0) { + shutting_down = SHUTDOWN_POWEROFF; /* ? */ + ctrl_alt_del(); +#ifdef CONFIG_PM_SLEEP + } else if (strcmp(str, "suspend") == 0) { + do_suspend(); +#endif + } else { + printk(KERN_INFO "Ignoring shutdown request: %s\n", str); + shutting_down = SHUTDOWN_INVALID; + } + + kfree(str); +} + +static void sysrq_handler(struct xenbus_watch *watch, const char **vec, + unsigned int len) +{ + char sysrq_key = '\0'; + struct xenbus_transaction xbt; + int err; + + again: + err = xenbus_transaction_start(&xbt); + if (err) + return; + if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) { + printk(KERN_ERR "Unable to read sysrq code in " + "control/sysrq\n"); + xenbus_transaction_end(xbt, 1); + return; + } + + if (sysrq_key != '\0') + xenbus_printf(xbt, "control", "sysrq", "%c", '\0'); + + err = xenbus_transaction_end(xbt, 0); + if (err == -EAGAIN) + goto again; + + if (sysrq_key != '\0') + handle_sysrq(sysrq_key, NULL); +} + +static struct xenbus_watch shutdown_watch = { + .node = "control/shutdown", + .callback = shutdown_handler +}; + +static struct xenbus_watch sysrq_watch = { + .node = "control/sysrq", + .callback = sysrq_handler +}; + +static int setup_shutdown_watcher(void) +{ + int err; + + err = register_xenbus_watch(&shutdown_watch); + if (err) { + printk(KERN_ERR "Failed to set shutdown watcher\n"); + return err; + } + + err = register_xenbus_watch(&sysrq_watch); + if (err) { + printk(KERN_ERR "Failed to set sysrq watcher\n"); + return err; + } + + return 0; +} + +static int shutdown_event(struct notifier_block *notifier, + unsigned long event, + void *data) +{ + setup_shutdown_watcher(); + return NOTIFY_DONE; +} + +static int __init setup_shutdown_event(void) +{ + static struct notifier_block xenstore_notifier = { + .notifier_call = shutdown_event + }; + register_xenstore_notifier(&xenstore_notifier); + + return 0; +} + +subsys_initcall(setup_shutdown_event); diff --git a/drivers/xen/xenbus/xenbus_comms.c b/drivers/xen/xenbus/xenbus_comms.c index 6efbe3f29ca..090c61ee8fd 100644 --- a/drivers/xen/xenbus/xenbus_comms.c +++ b/drivers/xen/xenbus/xenbus_comms.c @@ -203,7 +203,6 @@ int xb_read(void *data, unsigned len) int xb_init_comms(void) { struct xenstore_domain_interface *intf = xen_store_interface; - int err; if (intf->req_prod != intf->req_cons) printk(KERN_ERR "XENBUS request ring is not quiescent " @@ -216,18 +215,20 @@ int xb_init_comms(void) intf->rsp_cons = intf->rsp_prod; } - if (xenbus_irq) - unbind_from_irqhandler(xenbus_irq, &xb_waitq); + if (xenbus_irq) { + /* Already have an irq; assume we're resuming */ + rebind_evtchn_irq(xen_store_evtchn, xenbus_irq); + } else { + int err; + err = bind_evtchn_to_irqhandler(xen_store_evtchn, wake_waiting, + 0, "xenbus", &xb_waitq); + if (err <= 0) { + printk(KERN_ERR "XENBUS request irq failed %i\n", err); + return err; + } - err = bind_evtchn_to_irqhandler( - xen_store_evtchn, wake_waiting, - 0, "xenbus", &xb_waitq); - if (err <= 0) { - printk(KERN_ERR "XENBUS request irq failed %i\n", err); - return err; + xenbus_irq = err; } - xenbus_irq = err; - return 0; } |